Implement GetGpuTime, support CancelSynchronization, fix issue on InsertWaitingMutex, proper double buffering support (again, not working properly for commercial games, only hb)

This commit is contained in:
gdkchan 2018-05-06 16:22:41 -03:00
commit d06c6604fa
13 changed files with 153 additions and 131 deletions

View file

@ -417,9 +417,9 @@ namespace Ryujinx.Core.Gpu
private int GetVertexCountFromIndexBuffer( private int GetVertexCountFromIndexBuffer(
NvGpuVmm Vmm, NvGpuVmm Vmm,
long IndexPosition, long IndexPosition,
int IndexCount, int IndexCount,
int IndexSize) int IndexSize)
{ {
int MaxIndex = -1; int MaxIndex = -1;
@ -427,7 +427,7 @@ namespace Ryujinx.Core.Gpu
{ {
while (IndexCount -- > 0) while (IndexCount -- > 0)
{ {
ushort Value = (ushort)Vmm.ReadInt16(IndexPosition); ushort Value = Vmm.ReadUInt16(IndexPosition);
IndexPosition += 2; IndexPosition += 2;
@ -453,7 +453,7 @@ namespace Ryujinx.Core.Gpu
{ {
while (IndexCount -- > 0) while (IndexCount -- > 0)
{ {
uint Value = (uint)Vmm.ReadInt32(IndexPosition); uint Value = Vmm.ReadUInt32(IndexPosition);
IndexPosition += 2; IndexPosition += 2;

View file

@ -32,9 +32,9 @@ namespace Ryujinx.Core.Loaders
this.ImageBase = ImageBase; this.ImageBase = ImageBase;
this.ImageEnd = ImageBase; this.ImageEnd = ImageBase;
WriteData(ImageBase + Exe.TextOffset, Exe.Text, MemoryType.CodeStatic, AMemoryPerm.RX); WriteData(ImageBase + Exe.TextOffset, Exe.Text, MemoryType.CodeStatic, AMemoryPerm.RX);
WriteData(ImageBase + Exe.ROOffset, Exe.RO, MemoryType.Normal, AMemoryPerm.Read); WriteData(ImageBase + Exe.ROOffset, Exe.RO, MemoryType.CodeMutable, AMemoryPerm.Read);
WriteData(ImageBase + Exe.DataOffset, Exe.Data, MemoryType.Normal, AMemoryPerm.RW); WriteData(ImageBase + Exe.DataOffset, Exe.Data, MemoryType.CodeMutable, AMemoryPerm.RW);
if (Exe.Mod0Offset == 0) if (Exe.Mod0Offset == 0)
{ {

View file

@ -7,7 +7,7 @@ namespace Ryujinx.Core.OsHle.Ipc
{ {
static class IpcHandler static class IpcHandler
{ {
public static void IpcCall( public static long IpcCall(
Switch Ns, Switch Ns,
Process Process, Process Process,
AMemory Memory, AMemory Memory,
@ -94,6 +94,8 @@ namespace Ryujinx.Core.OsHle.Ipc
AMemoryHelper.WriteBytes(Memory, CmdPtr, Response.GetBytes(CmdPtr)); AMemoryHelper.WriteBytes(Memory, CmdPtr, Response.GetBytes(CmdPtr));
} }
return 0;
} }
private static IpcMessage FillResponse(IpcMessage Response, long Result, params int[] Values) private static IpcMessage FillResponse(IpcMessage Response, long Result, params int[] Values)

View file

@ -7,6 +7,8 @@ namespace Ryujinx.Core.OsHle.Kernel
public const int InvalidMemRange = 110; public const int InvalidMemRange = 110;
public const int InvalidHandle = 114; public const int InvalidHandle = 114;
public const int Timeout = 117; public const int Timeout = 117;
public const int Canceled = 118;
public const int CountOutOfRange = 119;
public const int InvalidInfo = 120; public const int InvalidInfo = 120;
} }
} }

View file

@ -5,6 +5,8 @@ using Ryujinx.Core.Logging;
using Ryujinx.Core.OsHle.Handles; using Ryujinx.Core.OsHle.Handles;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Threading;
namespace Ryujinx.Core.OsHle.Kernel namespace Ryujinx.Core.OsHle.Kernel
{ {
@ -18,12 +20,16 @@ namespace Ryujinx.Core.OsHle.Kernel
private Process Process; private Process Process;
private AMemory Memory; private AMemory Memory;
private ConcurrentDictionary<KThread, AutoResetEvent> SyncWaits;
private object CondVarLock; private object CondVarLock;
private HashSet<(HSharedMem, long)> MappedSharedMems; private HashSet<(HSharedMem, long)> MappedSharedMems;
private ulong CurrentHeapSize; private ulong CurrentHeapSize;
private const uint SelfHandle = 0xffff8001;
private static Random Rng; private static Random Rng;
public SvcHandler(Switch Ns, Process Process) public SvcHandler(Switch Ns, Process Process)
@ -51,6 +57,7 @@ namespace Ryujinx.Core.OsHle.Kernel
{ 0x16, SvcCloseHandle }, { 0x16, SvcCloseHandle },
{ 0x17, SvcResetSignal }, { 0x17, SvcResetSignal },
{ 0x18, SvcWaitSynchronization }, { 0x18, SvcWaitSynchronization },
{ 0x19, SvcCancelSynchronization },
{ 0x1a, SvcArbitrateLock }, { 0x1a, SvcArbitrateLock },
{ 0x1b, SvcArbitrateUnlock }, { 0x1b, SvcArbitrateUnlock },
{ 0x1c, SvcWaitProcessWideKeyAtomic }, { 0x1c, SvcWaitProcessWideKeyAtomic },
@ -70,6 +77,8 @@ namespace Ryujinx.Core.OsHle.Kernel
this.Process = Process; this.Process = Process;
this.Memory = Process.Memory; this.Memory = Process.Memory;
SyncWaits = new ConcurrentDictionary<KThread, AutoResetEvent>();
CondVarLock = new object(); CondVarLock = new object();
MappedSharedMems = new HashSet<(HSharedMem, long)>(); MappedSharedMems = new HashSet<(HSharedMem, long)>();
@ -100,6 +109,18 @@ namespace Ryujinx.Core.OsHle.Kernel
} }
} }
private KThread GetThread(long Tpidr, int Handle)
{
if ((uint)Handle == SelfHandle)
{
return Process.GetThread(Tpidr);
}
else
{
return Process.HandleTable.GetData<KThread>(Handle);
}
}
public void Dispose() public void Dispose()
{ {
Dispose(true); Dispose(true);

View file

@ -88,9 +88,21 @@ namespace Ryujinx.Core.OsHle.Kernel
int HandlesCount = (int)ThreadState.X2; int HandlesCount = (int)ThreadState.X2;
ulong Timeout = ThreadState.X3; ulong Timeout = ThreadState.X3;
Ns.Log.PrintDebug(LogClass.KernelSvc,
"HandlesPtr = " + HandlesPtr .ToString("x16") + ", " +
"HandlesCount = " + HandlesCount.ToString("x8") + ", " +
"Timeout = " + Timeout .ToString("x16"));
if ((uint)HandlesCount > 0x40)
{
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.CountOutOfRange);
return;
}
KThread CurrThread = Process.GetThread(ThreadState.Tpidr); KThread CurrThread = Process.GetThread(ThreadState.Tpidr);
WaitHandle[] Handles = new WaitHandle[HandlesCount]; WaitHandle[] Handles = new WaitHandle[HandlesCount + 1];
for (int Index = 0; Index < HandlesCount; Index++) for (int Index = 0; Index < HandlesCount; Index++)
{ {
@ -110,34 +122,73 @@ namespace Ryujinx.Core.OsHle.Kernel
Handles[Index] = SyncObj.WaitEvent; Handles[Index] = SyncObj.WaitEvent;
} }
Process.Scheduler.Suspend(CurrThread.ProcessorId); using (AutoResetEvent WaitEvent = new AutoResetEvent(false))
int HandleIndex;
ulong Result = 0;
if (Timeout != ulong.MaxValue)
{ {
HandleIndex = WaitHandle.WaitAny(Handles, NsTimeConverter.GetTimeMs(Timeout)); if (!SyncWaits.TryAdd(CurrThread, WaitEvent))
{
throw new InvalidOperationException();
}
Handles[HandlesCount] = WaitEvent;
Process.Scheduler.Suspend(CurrThread.ProcessorId);
int HandleIndex;
ulong Result = 0;
if (Timeout != ulong.MaxValue)
{
HandleIndex = WaitHandle.WaitAny(Handles, NsTimeConverter.GetTimeMs(Timeout));
}
else
{
HandleIndex = WaitHandle.WaitAny(Handles);
}
if (HandleIndex == WaitHandle.WaitTimeout) if (HandleIndex == WaitHandle.WaitTimeout)
{ {
Result = MakeError(ErrorModule.Kernel, KernelErr.Timeout); Result = MakeError(ErrorModule.Kernel, KernelErr.Timeout);
} }
else if (HandleIndex == HandlesCount)
{
Result = MakeError(ErrorModule.Kernel, KernelErr.Canceled);
}
SyncWaits.TryRemove(CurrThread, out _);
Process.Scheduler.Resume(CurrThread);
ThreadState.X0 = Result;
if (Result == 0)
{
ThreadState.X1 = (ulong)HandleIndex;
}
} }
else }
private void SvcCancelSynchronization(AThreadState ThreadState)
{
int ThreadHandle = (int)ThreadState.X0;
KThread Thread = GetThread(ThreadState.Tpidr, ThreadHandle);
if (Thread == null)
{ {
HandleIndex = WaitHandle.WaitAny(Handles); Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{ThreadHandle:x8}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
return;
} }
Process.Scheduler.Resume(CurrThread); if (SyncWaits.TryRemove(Thread, out AutoResetEvent WaitEvent))
ThreadState.X0 = Result;
if (Result == 0)
{ {
ThreadState.X1 = (ulong)HandleIndex; WaitEvent.Set();
} }
ThreadState.X0 = 0;
} }
private void SvcGetSystemTick(AThreadState ThreadState) private void SvcGetSystemTick(AThreadState ThreadState)
@ -178,6 +229,8 @@ namespace Ryujinx.Core.OsHle.Kernel
private void SendSyncRequest(AThreadState ThreadState, long CmdPtr, long Size, int Handle) private void SendSyncRequest(AThreadState ThreadState, long CmdPtr, long Size, int Handle)
{ {
//Process.PrintStackTrace(ThreadState);
KThread CurrThread = Process.GetThread(ThreadState.Tpidr); KThread CurrThread = Process.GetThread(ThreadState.Tpidr);
byte[] CmdData = AMemoryHelper.ReadBytes(Memory, CmdPtr, Size); byte[] CmdData = AMemoryHelper.ReadBytes(Memory, CmdPtr, Size);
@ -190,13 +243,13 @@ namespace Ryujinx.Core.OsHle.Kernel
IpcMessage Cmd = new IpcMessage(CmdData, CmdPtr); IpcMessage Cmd = new IpcMessage(CmdData, CmdPtr);
IpcHandler.IpcCall(Ns, Process, Memory, Session, Cmd, CmdPtr); long Result = IpcHandler.IpcCall(Ns, Process, Memory, Session, Cmd, CmdPtr);
Thread.Yield(); Thread.Yield();
Process.Scheduler.Resume(CurrThread); Process.Scheduler.Resume(CurrThread);
ThreadState.X0 = 0; ThreadState.X0 = (ulong)Result;
} }
else else
{ {

View file

@ -191,6 +191,8 @@ namespace Ryujinx.Core.OsHle.Kernel
InsertWaitingMutexThread(OwnerThreadHandle, WaitThread); InsertWaitingMutexThread(OwnerThreadHandle, WaitThread);
Ns.Log.PrintDebug(LogClass.KernelSvc, "Entering wait state...");
Process.Scheduler.EnterWait(CurrThread); Process.Scheduler.EnterWait(CurrThread);
} }
@ -297,6 +299,8 @@ namespace Ryujinx.Core.OsHle.Kernel
} }
} }
Ns.Log.PrintDebug(LogClass.KernelSvc, "Entering wait state...");
if (Timeout != ulong.MaxValue) if (Timeout != ulong.MaxValue)
{ {
return Process.Scheduler.EnterWait(WaitThread, NsTimeConverter.GetTimeMs(Timeout)); return Process.Scheduler.EnterWait(WaitThread, NsTimeConverter.GetTimeMs(Timeout));
@ -407,7 +411,7 @@ namespace Ryujinx.Core.OsHle.Kernel
if (CurrThread != WaitThread) if (CurrThread != WaitThread)
{ {
if (WaitThread.NextCondVarThread != null) if (WaitThread.NextMutexThread != null)
{ {
throw new InvalidOperationException(); throw new InvalidOperationException();
} }

View file

@ -58,7 +58,7 @@ namespace Ryujinx.Core.OsHle.Services.Nv.NvHostChannel
{ {
long Gpfifo = Context.Memory.ReadInt64(InputPosition + 0x18 + Index * 8); long Gpfifo = Context.Memory.ReadInt64(InputPosition + 0x18 + Index * 8);
long VA = Gpfifo & 0xffffffffff; long VA = Gpfifo & 0xff_ffff_ffff;
int Size = (int)(Gpfifo >> 40) & 0x7ffffc; int Size = (int)(Gpfifo >> 40) & 0x7ffffc;
@ -69,6 +69,10 @@ namespace Ryujinx.Core.OsHle.Services.Nv.NvHostChannel
Context.Ns.Gpu.Fifo.PushBuffer(Vmm, PushBuffer); Context.Ns.Gpu.Fifo.PushBuffer(Vmm, PushBuffer);
} }
Args.SyncptId = 5;
AMemoryHelper.Write(Context.Memory, OutputPosition, Args);
return NvResult.Success; return NvResult.Success;
} }

View file

@ -229,6 +229,8 @@ namespace Ryujinx.Core.OsHle.Services.Nv.NvHostCtrl
NvHostSyncpt SyncPt = GetSyncPt(Context); NvHostSyncpt SyncPt = GetSyncPt(Context);
Context.Ns.Log.PrintInfo(LogClass.ServiceNv, Args.Id + " " + Args.Thresh + " " + Args.Timeout + " " + Args.Value + " " + Async + " " + SyncPt.GetMin(Args.Id));
if (SyncPt.MinCompare(Args.Id, Args.Thresh)) if (SyncPt.MinCompare(Args.Id, Args.Thresh))
{ {
Args.Value = SyncPt.GetMin(Args.Id); Args.Value = SyncPt.GetMin(Args.Id);

View file

@ -1,11 +1,25 @@
using ChocolArm64.Memory; using ChocolArm64.Memory;
using Ryujinx.Core.Logging; using Ryujinx.Core.Logging;
using System; using System;
using System.Diagnostics;
namespace Ryujinx.Core.OsHle.Services.Nv.NvHostCtrlGpu namespace Ryujinx.Core.OsHle.Services.Nv.NvHostCtrlGpu
{ {
class NvHostCtrlGpuIoctl class NvHostCtrlGpuIoctl
{ {
private static Stopwatch PTimer;
private static double TicksToNs;
static NvHostCtrlGpuIoctl()
{
PTimer = new Stopwatch();
PTimer.Start();
TicksToNs = (1.0 / Stopwatch.Frequency) * 1_000_000_000;
}
public static int ProcessIoctl(ServiceCtx Context, int Cmd) public static int ProcessIoctl(ServiceCtx Context, int Cmd)
{ {
switch (Cmd & 0xffff) switch (Cmd & 0xffff)
@ -16,6 +30,7 @@ namespace Ryujinx.Core.OsHle.Services.Nv.NvHostCtrlGpu
case 0x4705: return GetCharacteristics(Context); case 0x4705: return GetCharacteristics(Context);
case 0x4706: return GetTpcMasks (Context); case 0x4706: return GetTpcMasks (Context);
case 0x4714: return GetActiveSlotMask (Context); case 0x4714: return GetActiveSlotMask (Context);
case 0x471c: return GetGpuTime (Context);
} }
throw new NotImplementedException(Cmd.ToString("x8")); throw new NotImplementedException(Cmd.ToString("x8"));
@ -120,5 +135,21 @@ namespace Ryujinx.Core.OsHle.Services.Nv.NvHostCtrlGpu
return NvResult.Success; return NvResult.Success;
} }
private static int GetGpuTime(ServiceCtx Context)
{
long OutputPosition = Context.Request.GetBufferType0x22Position();
Context.Memory.WriteInt64(OutputPosition, GetPTimerNanoSeconds());
return NvResult.Success;
}
private static long GetPTimerNanoSeconds()
{
double Ticks = PTimer.ElapsedTicks;
return (long)(Ticks * TicksToNs) & 0xff_ffff_ffff_ffff;
}
} }
} }

View file

@ -282,11 +282,12 @@ namespace Ryujinx.Core.OsHle.Services.Android
int FbWidth = 1280; int FbWidth = 1280;
int FbHeight = 720; int FbHeight = 720;
NvMapHandle Map = GetNvMap(Context, Slot); int NvMapHandle = BitConverter.ToInt32(BufferQueue[Slot].Data.RawData, 0x4c);
int BufferOffset = BitConverter.ToInt32(BufferQueue[Slot].Data.RawData, 0x50);
NvMapHandle MapFb = NvMapIoctl.GetNvMapWithFb(Context, 0); NvMapHandle Map = NvMapIoctl.GetNvMap(Context, NvMapHandle);;
long FbAddr = Map.Address + MapFb.Address; long FbAddr = Map.Address + BufferOffset;
BufferQueue[Slot].State = BufferState.Acquired; BufferQueue[Slot].State = BufferState.Acquired;
@ -363,22 +364,6 @@ namespace Ryujinx.Core.OsHle.Services.Android
Context.Ns.Gpu.Renderer.QueueAction(() => ReleaseBuffer(Slot)); Context.Ns.Gpu.Renderer.QueueAction(() => ReleaseBuffer(Slot));
} }
private NvMapHandle GetNvMap(ServiceCtx Context, int Slot)
{
int NvMapHandle = BitConverter.ToInt32(BufferQueue[Slot].Data.RawData, 0x4c);
if (!BitConverter.IsLittleEndian)
{
byte[] RawValue = BitConverter.GetBytes(NvMapHandle);
Array.Reverse(RawValue);
NvMapHandle = BitConverter.ToInt32(RawValue, 0);
}
return NvMapIoctl.GetNvMap(Context, NvMapHandle);
}
private void ReleaseBuffer(int Slot) private void ReleaseBuffer(int Slot)
{ {
BufferQueue[Slot].State = BufferState.Free; BufferQueue[Slot].State = BufferState.Free;

View file

@ -1,44 +0,0 @@
using ChocolArm64.Memory;
namespace Ryujinx.Core.OsHle.Utilities
{
class MemReader
{
private AMemory Memory;
public long Position { get; private set; }
public MemReader(AMemory Memory, long Position)
{
this.Memory = Memory;
this.Position = Position;
}
public byte ReadByte()
{
byte Value = Memory.ReadByte(Position);
Position++;
return Value;
}
public int ReadInt32()
{
int Value = Memory.ReadInt32(Position);
Position += 4;
return Value;
}
public long ReadInt64()
{
long Value = Memory.ReadInt64(Position);
Position += 8;
return Value;
}
}
}

View file

@ -1,38 +0,0 @@
using ChocolArm64.Memory;
namespace Ryujinx.Core.OsHle.Utilities
{
class MemWriter
{
private AMemory Memory;
public long Position { get; private set; }
public MemWriter(AMemory Memory, long Position)
{
this.Memory = Memory;
this.Position = Position;
}
public void WriteByte(byte Value)
{
Memory.WriteByte(Position, Value);
Position++;
}
public void WriteInt32(int Value)
{
Memory.WriteInt32(Position, Value);
Position += 4;
}
public void WriteInt64(long Value)
{
Memory.WriteInt64(Position, Value);
Position += 8;
}
}
}