diff --git a/Ryujinx.Core/Gpu/NvGpuEngine3d.cs b/Ryujinx.Core/Gpu/NvGpuEngine3d.cs index 422ac4d965..f94cb0a7de 100644 --- a/Ryujinx.Core/Gpu/NvGpuEngine3d.cs +++ b/Ryujinx.Core/Gpu/NvGpuEngine3d.cs @@ -417,9 +417,9 @@ namespace Ryujinx.Core.Gpu private int GetVertexCountFromIndexBuffer( NvGpuVmm Vmm, - long IndexPosition, - int IndexCount, - int IndexSize) + long IndexPosition, + int IndexCount, + int IndexSize) { int MaxIndex = -1; @@ -427,7 +427,7 @@ namespace Ryujinx.Core.Gpu { while (IndexCount -- > 0) { - ushort Value = (ushort)Vmm.ReadInt16(IndexPosition); + ushort Value = Vmm.ReadUInt16(IndexPosition); IndexPosition += 2; @@ -453,7 +453,7 @@ namespace Ryujinx.Core.Gpu { while (IndexCount -- > 0) { - uint Value = (uint)Vmm.ReadInt32(IndexPosition); + uint Value = Vmm.ReadUInt32(IndexPosition); IndexPosition += 2; diff --git a/Ryujinx.Core/Loaders/Executable.cs b/Ryujinx.Core/Loaders/Executable.cs index 39ee9618b7..ab7e1979db 100644 --- a/Ryujinx.Core/Loaders/Executable.cs +++ b/Ryujinx.Core/Loaders/Executable.cs @@ -32,9 +32,9 @@ namespace Ryujinx.Core.Loaders this.ImageBase = ImageBase; this.ImageEnd = ImageBase; - WriteData(ImageBase + Exe.TextOffset, Exe.Text, MemoryType.CodeStatic, AMemoryPerm.RX); - WriteData(ImageBase + Exe.ROOffset, Exe.RO, MemoryType.Normal, AMemoryPerm.Read); - WriteData(ImageBase + Exe.DataOffset, Exe.Data, MemoryType.Normal, AMemoryPerm.RW); + WriteData(ImageBase + Exe.TextOffset, Exe.Text, MemoryType.CodeStatic, AMemoryPerm.RX); + WriteData(ImageBase + Exe.ROOffset, Exe.RO, MemoryType.CodeMutable, AMemoryPerm.Read); + WriteData(ImageBase + Exe.DataOffset, Exe.Data, MemoryType.CodeMutable, AMemoryPerm.RW); if (Exe.Mod0Offset == 0) { diff --git a/Ryujinx.Core/OsHle/Ipc/IpcHandler.cs b/Ryujinx.Core/OsHle/Ipc/IpcHandler.cs index 42322d41a7..420890ebba 100644 --- a/Ryujinx.Core/OsHle/Ipc/IpcHandler.cs +++ b/Ryujinx.Core/OsHle/Ipc/IpcHandler.cs @@ -7,7 +7,7 @@ namespace Ryujinx.Core.OsHle.Ipc { static class IpcHandler { - public static void IpcCall( + public static long IpcCall( Switch Ns, Process Process, AMemory Memory, @@ -94,6 +94,8 @@ namespace Ryujinx.Core.OsHle.Ipc AMemoryHelper.WriteBytes(Memory, CmdPtr, Response.GetBytes(CmdPtr)); } + + return 0; } private static IpcMessage FillResponse(IpcMessage Response, long Result, params int[] Values) diff --git a/Ryujinx.Core/OsHle/Kernel/KernelErr.cs b/Ryujinx.Core/OsHle/Kernel/KernelErr.cs index b568405b7c..87f9cf3b34 100644 --- a/Ryujinx.Core/OsHle/Kernel/KernelErr.cs +++ b/Ryujinx.Core/OsHle/Kernel/KernelErr.cs @@ -7,6 +7,8 @@ namespace Ryujinx.Core.OsHle.Kernel public const int InvalidMemRange = 110; public const int InvalidHandle = 114; public const int Timeout = 117; + public const int Canceled = 118; + public const int CountOutOfRange = 119; public const int InvalidInfo = 120; } } \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Kernel/SvcHandler.cs b/Ryujinx.Core/OsHle/Kernel/SvcHandler.cs index e855b77df0..1874360b59 100644 --- a/Ryujinx.Core/OsHle/Kernel/SvcHandler.cs +++ b/Ryujinx.Core/OsHle/Kernel/SvcHandler.cs @@ -5,6 +5,8 @@ using Ryujinx.Core.Logging; using Ryujinx.Core.OsHle.Handles; using System; using System.Collections.Generic; +using System.Collections.Concurrent; +using System.Threading; namespace Ryujinx.Core.OsHle.Kernel { @@ -18,12 +20,16 @@ namespace Ryujinx.Core.OsHle.Kernel private Process Process; private AMemory Memory; + private ConcurrentDictionary SyncWaits; + private object CondVarLock; private HashSet<(HSharedMem, long)> MappedSharedMems; private ulong CurrentHeapSize; + private const uint SelfHandle = 0xffff8001; + private static Random Rng; public SvcHandler(Switch Ns, Process Process) @@ -51,6 +57,7 @@ namespace Ryujinx.Core.OsHle.Kernel { 0x16, SvcCloseHandle }, { 0x17, SvcResetSignal }, { 0x18, SvcWaitSynchronization }, + { 0x19, SvcCancelSynchronization }, { 0x1a, SvcArbitrateLock }, { 0x1b, SvcArbitrateUnlock }, { 0x1c, SvcWaitProcessWideKeyAtomic }, @@ -70,6 +77,8 @@ namespace Ryujinx.Core.OsHle.Kernel this.Process = Process; this.Memory = Process.Memory; + SyncWaits = new ConcurrentDictionary(); + CondVarLock = new object(); 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(Handle); + } + } + public void Dispose() { Dispose(true); diff --git a/Ryujinx.Core/OsHle/Kernel/SvcSystem.cs b/Ryujinx.Core/OsHle/Kernel/SvcSystem.cs index 601b211c59..18dc0dc3eb 100644 --- a/Ryujinx.Core/OsHle/Kernel/SvcSystem.cs +++ b/Ryujinx.Core/OsHle/Kernel/SvcSystem.cs @@ -88,9 +88,21 @@ namespace Ryujinx.Core.OsHle.Kernel int HandlesCount = (int)ThreadState.X2; 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); - WaitHandle[] Handles = new WaitHandle[HandlesCount]; + WaitHandle[] Handles = new WaitHandle[HandlesCount + 1]; for (int Index = 0; Index < HandlesCount; Index++) { @@ -110,34 +122,73 @@ namespace Ryujinx.Core.OsHle.Kernel Handles[Index] = SyncObj.WaitEvent; } - Process.Scheduler.Suspend(CurrThread.ProcessorId); - - int HandleIndex; - - ulong Result = 0; - - if (Timeout != ulong.MaxValue) + using (AutoResetEvent WaitEvent = new AutoResetEvent(false)) { - 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) { 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); - - ThreadState.X0 = Result; - - if (Result == 0) + if (SyncWaits.TryRemove(Thread, out AutoResetEvent WaitEvent)) { - ThreadState.X1 = (ulong)HandleIndex; + WaitEvent.Set(); } + + ThreadState.X0 = 0; } 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) { + //Process.PrintStackTrace(ThreadState); + KThread CurrThread = Process.GetThread(ThreadState.Tpidr); byte[] CmdData = AMemoryHelper.ReadBytes(Memory, CmdPtr, Size); @@ -190,13 +243,13 @@ namespace Ryujinx.Core.OsHle.Kernel 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(); Process.Scheduler.Resume(CurrThread); - ThreadState.X0 = 0; + ThreadState.X0 = (ulong)Result; } else { diff --git a/Ryujinx.Core/OsHle/Kernel/SvcThreadSync.cs b/Ryujinx.Core/OsHle/Kernel/SvcThreadSync.cs index e382cf7597..57608cda43 100644 --- a/Ryujinx.Core/OsHle/Kernel/SvcThreadSync.cs +++ b/Ryujinx.Core/OsHle/Kernel/SvcThreadSync.cs @@ -191,6 +191,8 @@ namespace Ryujinx.Core.OsHle.Kernel InsertWaitingMutexThread(OwnerThreadHandle, WaitThread); + Ns.Log.PrintDebug(LogClass.KernelSvc, "Entering wait state..."); + 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) { return Process.Scheduler.EnterWait(WaitThread, NsTimeConverter.GetTimeMs(Timeout)); @@ -407,7 +411,7 @@ namespace Ryujinx.Core.OsHle.Kernel if (CurrThread != WaitThread) { - if (WaitThread.NextCondVarThread != null) + if (WaitThread.NextMutexThread != null) { throw new InvalidOperationException(); } diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs b/Ryujinx.Core/OsHle/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs index 4483a5defe..78334014cb 100644 --- a/Ryujinx.Core/OsHle/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs +++ b/Ryujinx.Core/OsHle/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs @@ -58,7 +58,7 @@ namespace Ryujinx.Core.OsHle.Services.Nv.NvHostChannel { 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; @@ -69,6 +69,10 @@ namespace Ryujinx.Core.OsHle.Services.Nv.NvHostChannel Context.Ns.Gpu.Fifo.PushBuffer(Vmm, PushBuffer); } + Args.SyncptId = 5; + + AMemoryHelper.Write(Context.Memory, OutputPosition, Args); + return NvResult.Success; } diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlIoctl.cs b/Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlIoctl.cs index b5703b29e8..9028d95b99 100644 --- a/Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlIoctl.cs +++ b/Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlIoctl.cs @@ -229,6 +229,8 @@ namespace Ryujinx.Core.OsHle.Services.Nv.NvHostCtrl 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)) { Args.Value = SyncPt.GetMin(Args.Id); diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvHostCtrlGpu/NvHostCtrlGpuIoctl.cs b/Ryujinx.Core/OsHle/Services/Nv/NvHostCtrlGpu/NvHostCtrlGpuIoctl.cs index f0c8df4fc4..e90fd988c5 100644 --- a/Ryujinx.Core/OsHle/Services/Nv/NvHostCtrlGpu/NvHostCtrlGpuIoctl.cs +++ b/Ryujinx.Core/OsHle/Services/Nv/NvHostCtrlGpu/NvHostCtrlGpuIoctl.cs @@ -1,11 +1,25 @@ using ChocolArm64.Memory; using Ryujinx.Core.Logging; using System; +using System.Diagnostics; namespace Ryujinx.Core.OsHle.Services.Nv.NvHostCtrlGpu { 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) { switch (Cmd & 0xffff) @@ -16,6 +30,7 @@ namespace Ryujinx.Core.OsHle.Services.Nv.NvHostCtrlGpu case 0x4705: return GetCharacteristics(Context); case 0x4706: return GetTpcMasks (Context); case 0x4714: return GetActiveSlotMask (Context); + case 0x471c: return GetGpuTime (Context); } throw new NotImplementedException(Cmd.ToString("x8")); @@ -120,5 +135,21 @@ namespace Ryujinx.Core.OsHle.Services.Nv.NvHostCtrlGpu 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; + } } } \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs b/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs index 1b1e6c1d16..db2f7fa265 100644 --- a/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs +++ b/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs @@ -282,11 +282,12 @@ namespace Ryujinx.Core.OsHle.Services.Android int FbWidth = 1280; 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; @@ -363,22 +364,6 @@ namespace Ryujinx.Core.OsHle.Services.Android 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) { BufferQueue[Slot].State = BufferState.Free; diff --git a/Ryujinx.Core/OsHle/Utilities/MemReader.cs b/Ryujinx.Core/OsHle/Utilities/MemReader.cs deleted file mode 100644 index fe92f68fd2..0000000000 --- a/Ryujinx.Core/OsHle/Utilities/MemReader.cs +++ /dev/null @@ -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; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Utilities/MemWriter.cs b/Ryujinx.Core/OsHle/Utilities/MemWriter.cs deleted file mode 100644 index 21b6a3b653..0000000000 --- a/Ryujinx.Core/OsHle/Utilities/MemWriter.cs +++ /dev/null @@ -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; - } - } -} \ No newline at end of file