From 34037701c708cb70bbf44dea71ee0912f7b4102b Mon Sep 17 00:00:00 2001 From: gdkchan Date: Mon, 7 May 2018 15:53:23 -0300 Subject: [PATCH] NvServices refactoring (#120) * Initial implementation of NvMap/NvHostCtrl * More work on NvHostCtrl * Refactoring of nvservices, move GPU Vmm, make Vmm per-process, refactor most gpu devices, move Gpu to Core, fix CbBind * Implement GetGpuTime, support CancelSynchronization, fix issue on InsertWaitingMutex, proper double buffering support (again, not working properly for commercial games, only hb) * Try to fix perf regression reading/writing textures, moved syncpts and events to a UserCtx class, delete global state when the process exits, other minor tweaks * Remove now unused code, add comment about probably wrong result codes --- ChocolArm64/Memory/AMemory.cs | 2 +- ChocolArm64/Memory/IAMemory.cs | 37 + .../Gpu/BlockLinearSwizzle.cs | 2 +- Ryujinx.Core/Gpu/INvGpuEngine.cs | 9 + .../Gpu/ISwizzle.cs | 2 +- .../Gpu/LinearSwizzle.cs | 2 +- .../Gpu/MacroInterpreter.cs | 35 +- .../Gpu/NsGpu.cs => Ryujinx.Core/Gpu/NvGpu.cs | 35 +- .../Gpu/NvGpuEngine.cs | 2 +- .../Gpu/NvGpuEngine2d.cs | 40 +- .../Gpu/NvGpuEngine2dReg.cs | 2 +- .../Gpu/NvGpuEngine3d.cs | 195 ++--- .../Gpu/NvGpuEngine3dReg.cs | 8 +- .../Gpu/NvGpuFifo.cs | 47 +- .../Gpu/NvGpuFifoMeth.cs | 2 +- Ryujinx.Core/Gpu/NvGpuMethod.cs | 4 + .../Gpu/NvGpuPBEntry.cs | 6 +- .../Gpu/NvGpuPushBuffer.cs | 18 +- Ryujinx.Core/Gpu/NvGpuVmm.cs | 398 ++++++++++ .../Gpu/Texture.cs | 2 +- .../Gpu/TextureFactory.cs | 19 +- .../Gpu/TextureHelper.cs | 15 +- .../Gpu/TextureReader.cs | 38 +- .../Gpu/TextureSwizzle.cs | 2 +- .../Gpu/TextureWriter.cs | 15 +- Ryujinx.Core/Loaders/Executable.cs | 6 +- Ryujinx.Core/OsHle/Horizon.cs | 4 + Ryujinx.Core/OsHle/Ipc/IpcHandler.cs | 4 +- Ryujinx.Core/OsHle/Ipc/IpcMessage.cs | 25 - Ryujinx.Core/OsHle/Kernel/KernelErr.cs | 2 + Ryujinx.Core/OsHle/Kernel/SvcHandler.cs | 21 + Ryujinx.Core/OsHle/Kernel/SvcSystem.cs | 91 ++- Ryujinx.Core/OsHle/Kernel/SvcThreadSync.cs | 6 +- Ryujinx.Core/OsHle/MemoryAllocator.cs | 12 + Ryujinx.Core/OsHle/Process.cs | 6 +- .../OsHle/Services/Nv/INvDrvServices.cs | 748 ++---------------- Ryujinx.Core/OsHle/Services/Nv/NvChNvMap.cs | 31 - .../Services/Nv/NvGpuAS/NvGpuASAllocSpace.cs | 11 + .../OsHle/Services/Nv/NvGpuAS/NvGpuASIoctl.cs | 245 ++++++ .../Services/Nv/NvGpuAS/NvGpuASMapBufferEx.cs | 13 + .../OsHle/Services/Nv/NvGpuAS/NvGpuASRemap.cs | 12 + .../Services/Nv/NvGpuAS/NvGpuASUnmapBuffer.cs | 7 + .../Nv/NvGpuGpu/NvGpuGpuGetCharacteristics.cs | 43 + .../Services/Nv/NvGpuGpu/NvGpuGpuIoctl.cs | 155 ++++ Ryujinx.Core/OsHle/Services/Nv/NvHelper.cs | 10 + .../Nv/NvHostChannel/NvHostChannelIoctl.cs | 130 +++ .../NvHostChannelSubmitGpfifo.cs | 11 + .../Services/Nv/NvHostCtrl/NvHostCtrlIoctl.cs | 355 +++++++++ .../Nv/NvHostCtrl/NvHostCtrlSyncPtRead.cs | 8 + .../Nv/NvHostCtrl/NvHostCtrlSyncPtWait.cs | 9 + .../Nv/NvHostCtrl/NvHostCtrlSyncPtWaitEx.cs | 10 + .../Nv/NvHostCtrl/NvHostCtrlUserCtx.cs | 19 + .../Services/Nv/NvHostCtrl/NvHostEvent.cs | 10 + .../Nv/NvHostCtrl/NvHostEventState.cs | 10 + .../Services/Nv/NvHostCtrl/NvHostSyncPt.cs | 107 +++ Ryujinx.Core/OsHle/Services/Nv/NvMap.cs | 20 - .../OsHle/Services/Nv/NvMap/NvMapAlloc.cs | 12 + .../OsHle/Services/Nv/NvMap/NvMapCreate.cs | 8 + .../OsHle/Services/Nv/NvMap/NvMapFree.cs | 11 + .../OsHle/Services/Nv/NvMap/NvMapFromId.cs | 8 + .../OsHle/Services/Nv/NvMap/NvMapGetId.cs | 8 + .../OsHle/Services/Nv/NvMap/NvMapHandle.cs | 37 + .../Services/Nv/NvMap/NvMapHandleParam.cs | 12 + .../OsHle/Services/Nv/NvMap/NvMapIoctl.cs | 302 +++++++ .../OsHle/Services/Nv/NvMap/NvMapParam.cs | 9 + Ryujinx.Core/OsHle/Services/Nv/NvMapFb.cs | 40 - Ryujinx.Core/OsHle/Services/Nv/NvResult.cs | 13 + Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs | 42 +- Ryujinx.Core/OsHle/Utilities/IntUtils.cs | 15 + Ryujinx.Core/OsHle/Utilities/MemReader.cs | 44 -- Ryujinx.Core/OsHle/Utilities/MemWriter.cs | 38 - Ryujinx.Core/Switch.cs | 6 +- Ryujinx.Graphics/Gpu/INvGpuEngine.cs | 11 - Ryujinx.Graphics/Gpu/NsGpuMemoryMgr.cs | 212 ----- Ryujinx.Graphics/Gpu/NvGpuMethod.cs | 6 - 75 files changed, 2472 insertions(+), 1440 deletions(-) create mode 100644 ChocolArm64/Memory/IAMemory.cs rename {Ryujinx.Graphics => Ryujinx.Core}/Gpu/BlockLinearSwizzle.cs (97%) create mode 100644 Ryujinx.Core/Gpu/INvGpuEngine.cs rename {Ryujinx.Graphics => Ryujinx.Core}/Gpu/ISwizzle.cs (72%) rename {Ryujinx.Graphics => Ryujinx.Core}/Gpu/LinearSwizzle.cs (91%) rename {Ryujinx.Graphics => Ryujinx.Core}/Gpu/MacroInterpreter.cs (92%) rename Ryujinx.Graphics/Gpu/NsGpu.cs => Ryujinx.Core/Gpu/NvGpu.cs (50%) rename {Ryujinx.Graphics => Ryujinx.Core}/Gpu/NvGpuEngine.cs (84%) rename {Ryujinx.Graphics => Ryujinx.Core}/Gpu/NvGpuEngine2d.cs (74%) rename {Ryujinx.Graphics => Ryujinx.Core}/Gpu/NvGpuEngine2dReg.cs (95%) rename {Ryujinx.Graphics => Ryujinx.Core}/Gpu/NvGpuEngine3d.cs (73%) rename {Ryujinx.Graphics => Ryujinx.Core}/Gpu/NvGpuEngine3dReg.cs (93%) rename {Ryujinx.Graphics => Ryujinx.Core}/Gpu/NvGpuFifo.cs (70%) rename {Ryujinx.Graphics => Ryujinx.Core}/Gpu/NvGpuFifoMeth.cs (87%) create mode 100644 Ryujinx.Core/Gpu/NvGpuMethod.cs rename Ryujinx.Graphics/Gpu/NsGpuPBEntry.cs => Ryujinx.Core/Gpu/NvGpuPBEntry.cs (78%) rename {Ryujinx.Graphics => Ryujinx.Core}/Gpu/NvGpuPushBuffer.cs (85%) create mode 100644 Ryujinx.Core/Gpu/NvGpuVmm.cs rename {Ryujinx.Graphics => Ryujinx.Core}/Gpu/Texture.cs (97%) rename {Ryujinx.Graphics => Ryujinx.Core}/Gpu/TextureFactory.cs (77%) rename {Ryujinx.Graphics => Ryujinx.Core}/Gpu/TextureHelper.cs (62%) rename {Ryujinx.Graphics => Ryujinx.Core}/Gpu/TextureReader.cs (73%) rename {Ryujinx.Graphics => Ryujinx.Core}/Gpu/TextureSwizzle.cs (87%) rename {Ryujinx.Graphics => Ryujinx.Core}/Gpu/TextureWriter.cs (63%) create mode 100644 Ryujinx.Core/OsHle/MemoryAllocator.cs delete mode 100644 Ryujinx.Core/OsHle/Services/Nv/NvChNvMap.cs create mode 100644 Ryujinx.Core/OsHle/Services/Nv/NvGpuAS/NvGpuASAllocSpace.cs create mode 100644 Ryujinx.Core/OsHle/Services/Nv/NvGpuAS/NvGpuASIoctl.cs create mode 100644 Ryujinx.Core/OsHle/Services/Nv/NvGpuAS/NvGpuASMapBufferEx.cs create mode 100644 Ryujinx.Core/OsHle/Services/Nv/NvGpuAS/NvGpuASRemap.cs create mode 100644 Ryujinx.Core/OsHle/Services/Nv/NvGpuAS/NvGpuASUnmapBuffer.cs create mode 100644 Ryujinx.Core/OsHle/Services/Nv/NvGpuGpu/NvGpuGpuGetCharacteristics.cs create mode 100644 Ryujinx.Core/OsHle/Services/Nv/NvGpuGpu/NvGpuGpuIoctl.cs create mode 100644 Ryujinx.Core/OsHle/Services/Nv/NvHelper.cs create mode 100644 Ryujinx.Core/OsHle/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs create mode 100644 Ryujinx.Core/OsHle/Services/Nv/NvHostChannel/NvHostChannelSubmitGpfifo.cs create mode 100644 Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlIoctl.cs create mode 100644 Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlSyncPtRead.cs create mode 100644 Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlSyncPtWait.cs create mode 100644 Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlSyncPtWaitEx.cs create mode 100644 Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlUserCtx.cs create mode 100644 Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostEvent.cs create mode 100644 Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostEventState.cs create mode 100644 Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostSyncPt.cs delete mode 100644 Ryujinx.Core/OsHle/Services/Nv/NvMap.cs create mode 100644 Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapAlloc.cs create mode 100644 Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapCreate.cs create mode 100644 Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapFree.cs create mode 100644 Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapFromId.cs create mode 100644 Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapGetId.cs create mode 100644 Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapHandle.cs create mode 100644 Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapHandleParam.cs create mode 100644 Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapIoctl.cs create mode 100644 Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapParam.cs delete mode 100644 Ryujinx.Core/OsHle/Services/Nv/NvMapFb.cs create mode 100644 Ryujinx.Core/OsHle/Services/Nv/NvResult.cs create mode 100644 Ryujinx.Core/OsHle/Utilities/IntUtils.cs delete mode 100644 Ryujinx.Core/OsHle/Utilities/MemReader.cs delete mode 100644 Ryujinx.Core/OsHle/Utilities/MemWriter.cs delete mode 100644 Ryujinx.Graphics/Gpu/INvGpuEngine.cs delete mode 100644 Ryujinx.Graphics/Gpu/NsGpuMemoryMgr.cs delete mode 100644 Ryujinx.Graphics/Gpu/NvGpuMethod.cs diff --git a/ChocolArm64/Memory/AMemory.cs b/ChocolArm64/Memory/AMemory.cs index d7e11189..6625966f 100644 --- a/ChocolArm64/Memory/AMemory.cs +++ b/ChocolArm64/Memory/AMemory.cs @@ -6,7 +6,7 @@ using System.Runtime.InteropServices; namespace ChocolArm64.Memory { - public unsafe class AMemory : IDisposable + public unsafe class AMemory : IAMemory, IDisposable { private const long ErgMask = (4 << AThreadState.ErgSizeLog2) - 1; diff --git a/ChocolArm64/Memory/IAMemory.cs b/ChocolArm64/Memory/IAMemory.cs new file mode 100644 index 00000000..5b7d17bb --- /dev/null +++ b/ChocolArm64/Memory/IAMemory.cs @@ -0,0 +1,37 @@ +namespace ChocolArm64.Memory +{ + public interface IAMemory + { + sbyte ReadSByte(long Position); + + short ReadInt16(long Position); + + int ReadInt32(long Position); + + long ReadInt64(long Position); + + byte ReadByte(long Position); + + ushort ReadUInt16(long Position); + + uint ReadUInt32(long Position); + + ulong ReadUInt64(long Position); + + void WriteSByte(long Position, sbyte Value); + + void WriteInt16(long Position, short Value); + + void WriteInt32(long Position, int Value); + + void WriteInt64(long Position, long Value); + + void WriteByte(long Position, byte Value); + + void WriteUInt16(long Position, ushort Value); + + void WriteUInt32(long Position, uint Value); + + void WriteUInt64(long Position, ulong Value); + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gpu/BlockLinearSwizzle.cs b/Ryujinx.Core/Gpu/BlockLinearSwizzle.cs similarity index 97% rename from Ryujinx.Graphics/Gpu/BlockLinearSwizzle.cs rename to Ryujinx.Core/Gpu/BlockLinearSwizzle.cs index d2cbb144..eb41ea30 100644 --- a/Ryujinx.Graphics/Gpu/BlockLinearSwizzle.cs +++ b/Ryujinx.Core/Gpu/BlockLinearSwizzle.cs @@ -1,4 +1,4 @@ -namespace Ryujinx.Graphics.Gpu +namespace Ryujinx.Core.Gpu { class BlockLinearSwizzle : ISwizzle { diff --git a/Ryujinx.Core/Gpu/INvGpuEngine.cs b/Ryujinx.Core/Gpu/INvGpuEngine.cs new file mode 100644 index 00000000..865ea8ba --- /dev/null +++ b/Ryujinx.Core/Gpu/INvGpuEngine.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.Core.Gpu +{ + interface INvGpuEngine + { + int[] Registers { get; } + + void CallMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry); + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gpu/ISwizzle.cs b/Ryujinx.Core/Gpu/ISwizzle.cs similarity index 72% rename from Ryujinx.Graphics/Gpu/ISwizzle.cs rename to Ryujinx.Core/Gpu/ISwizzle.cs index 755051d0..f475be6e 100644 --- a/Ryujinx.Graphics/Gpu/ISwizzle.cs +++ b/Ryujinx.Core/Gpu/ISwizzle.cs @@ -1,4 +1,4 @@ -namespace Ryujinx.Graphics.Gpu +namespace Ryujinx.Core.Gpu { interface ISwizzle { diff --git a/Ryujinx.Graphics/Gpu/LinearSwizzle.cs b/Ryujinx.Core/Gpu/LinearSwizzle.cs similarity index 91% rename from Ryujinx.Graphics/Gpu/LinearSwizzle.cs rename to Ryujinx.Core/Gpu/LinearSwizzle.cs index c7a6b304..5f8dfdde 100644 --- a/Ryujinx.Graphics/Gpu/LinearSwizzle.cs +++ b/Ryujinx.Core/Gpu/LinearSwizzle.cs @@ -1,4 +1,4 @@ -namespace Ryujinx.Graphics.Gpu +namespace Ryujinx.Core.Gpu { class LinearSwizzle : ISwizzle { diff --git a/Ryujinx.Graphics/Gpu/MacroInterpreter.cs b/Ryujinx.Core/Gpu/MacroInterpreter.cs similarity index 92% rename from Ryujinx.Graphics/Gpu/MacroInterpreter.cs rename to Ryujinx.Core/Gpu/MacroInterpreter.cs index 233baac8..b799f98f 100644 --- a/Ryujinx.Graphics/Gpu/MacroInterpreter.cs +++ b/Ryujinx.Core/Gpu/MacroInterpreter.cs @@ -1,8 +1,7 @@ -using ChocolArm64.Memory; using System; using System.Collections.Generic; -namespace Ryujinx.Graphics.Gpu +namespace Ryujinx.Core.Gpu { class MacroInterpreter { @@ -69,7 +68,7 @@ namespace Ryujinx.Graphics.Gpu Gprs = new int[8]; } - public void Execute(AMemory Memory, long Position, int Param) + public void Execute(NvGpuVmm Vmm, long Position, int Param) { Reset(); @@ -77,13 +76,13 @@ namespace Ryujinx.Graphics.Gpu Pc = Position; - FetchOpCode(Memory); + FetchOpCode(Vmm); - while (Step(Memory)); + while (Step(Vmm)); //Due to the delay slot, we still need to execute //one more instruction before we actually exit. - Step(Memory); + Step(Vmm); } private void Reset() @@ -99,11 +98,11 @@ namespace Ryujinx.Graphics.Gpu Carry = false; } - private bool Step(AMemory Memory) + private bool Step(NvGpuVmm Vmm) { long BaseAddr = Pc - 4; - FetchOpCode(Memory); + FetchOpCode(Vmm); if ((OpCode & 7) < 7) { @@ -145,7 +144,7 @@ namespace Ryujinx.Graphics.Gpu { SetDstGpr(FetchParam()); - Send(Memory, Result); + Send(Vmm, Result); break; } @@ -155,7 +154,7 @@ namespace Ryujinx.Graphics.Gpu { SetDstGpr(Result); - Send(Memory, Result); + Send(Vmm, Result); break; } @@ -177,7 +176,7 @@ namespace Ryujinx.Graphics.Gpu SetMethAddr(Result); - Send(Memory, FetchParam()); + Send(Vmm, FetchParam()); break; } @@ -189,7 +188,7 @@ namespace Ryujinx.Graphics.Gpu SetMethAddr(Result); - Send(Memory, (Result >> 12) & 0x3f); + Send(Vmm, (Result >> 12) & 0x3f); break; } @@ -212,7 +211,7 @@ namespace Ryujinx.Graphics.Gpu if (NoDelays) { - FetchOpCode(Memory); + FetchOpCode(Vmm); } return true; @@ -224,11 +223,11 @@ namespace Ryujinx.Graphics.Gpu return !Exit; } - private void FetchOpCode(AMemory Memory) + private void FetchOpCode(NvGpuVmm Vmm) { OpCode = PipeOp; - PipeOp = Memory.ReadInt32(Pc); + PipeOp = Vmm.ReadInt32(Pc); Pc += 4; } @@ -408,11 +407,11 @@ namespace Ryujinx.Graphics.Gpu return Engine.Registers[Reg]; } - private void Send(AMemory Memory, int Value) + private void Send(NvGpuVmm Vmm, int Value) { - NsGpuPBEntry PBEntry = new NsGpuPBEntry(MethAddr, 0, Value); + NvGpuPBEntry PBEntry = new NvGpuPBEntry(MethAddr, 0, Value); - Engine.CallMethod(Memory, PBEntry); + Engine.CallMethod(Vmm, PBEntry); MethAddr += MethIncr; } diff --git a/Ryujinx.Graphics/Gpu/NsGpu.cs b/Ryujinx.Core/Gpu/NvGpu.cs similarity index 50% rename from Ryujinx.Graphics/Gpu/NsGpu.cs rename to Ryujinx.Core/Gpu/NvGpu.cs index e1088982..71df76ff 100644 --- a/Ryujinx.Graphics/Gpu/NsGpu.cs +++ b/Ryujinx.Core/Gpu/NvGpu.cs @@ -1,14 +1,12 @@ using Ryujinx.Graphics.Gal; using System.Threading; -namespace Ryujinx.Graphics.Gpu +namespace Ryujinx.Core.Gpu { - public class NsGpu + public class NvGpu { public IGalRenderer Renderer { get; private set; } - public NsGpuMemoryMgr MemoryMgr { get; private set; } - public NvGpuFifo Fifo { get; private set; } public NvGpuEngine2d Engine2d { get; private set; } @@ -18,12 +16,10 @@ namespace Ryujinx.Graphics.Gpu private bool KeepRunning; - public NsGpu(IGalRenderer Renderer) + public NvGpu(IGalRenderer Renderer) { this.Renderer = Renderer; - MemoryMgr = new NsGpuMemoryMgr(); - Fifo = new NvGpuFifo(this); Engine2d = new NvGpuEngine2d(this); @@ -36,31 +32,6 @@ namespace Ryujinx.Graphics.Gpu FifoProcessing.Start(); } - public long GetCpuAddr(long Position) - { - return MemoryMgr.GetCpuAddr(Position); - } - - public long MapMemory(long CpuAddr, long Size) - { - return MemoryMgr.Map(CpuAddr, Size); - } - - public long MapMemory(long CpuAddr, long GpuAddr, long Size) - { - return MemoryMgr.Map(CpuAddr, GpuAddr, Size); - } - - public long ReserveMemory(long Size, long Align) - { - return MemoryMgr.Reserve(Size, Align); - } - - public long ReserveMemory(long GpuAddr, long Size, long Align) - { - return MemoryMgr.Reserve(GpuAddr, Size, Align); - } - private void ProcessFifo() { while (KeepRunning) diff --git a/Ryujinx.Graphics/Gpu/NvGpuEngine.cs b/Ryujinx.Core/Gpu/NvGpuEngine.cs similarity index 84% rename from Ryujinx.Graphics/Gpu/NvGpuEngine.cs rename to Ryujinx.Core/Gpu/NvGpuEngine.cs index 624915d0..ee0420f7 100644 --- a/Ryujinx.Graphics/Gpu/NvGpuEngine.cs +++ b/Ryujinx.Core/Gpu/NvGpuEngine.cs @@ -1,4 +1,4 @@ -namespace Ryujinx.Graphics.Gpu +namespace Ryujinx.Core.Gpu { enum NvGpuEngine { diff --git a/Ryujinx.Graphics/Gpu/NvGpuEngine2d.cs b/Ryujinx.Core/Gpu/NvGpuEngine2d.cs similarity index 74% rename from Ryujinx.Graphics/Gpu/NvGpuEngine2d.cs rename to Ryujinx.Core/Gpu/NvGpuEngine2d.cs index c2bee167..88395b7a 100644 --- a/Ryujinx.Graphics/Gpu/NvGpuEngine2d.cs +++ b/Ryujinx.Core/Gpu/NvGpuEngine2d.cs @@ -1,8 +1,7 @@ -using ChocolArm64.Memory; using Ryujinx.Graphics.Gal; using System.Collections.Generic; -namespace Ryujinx.Graphics.Gpu +namespace Ryujinx.Core.Gpu { public class NvGpuEngine2d : INvGpuEngine { @@ -19,11 +18,11 @@ namespace Ryujinx.Graphics.Gpu public int[] Registers { get; private set; } - private NsGpu Gpu; + private NvGpu Gpu; private Dictionary Methods; - public NvGpuEngine2d(NsGpu Gpu) + public NvGpuEngine2d(NvGpu Gpu) { this.Gpu = Gpu; @@ -44,11 +43,11 @@ namespace Ryujinx.Graphics.Gpu AddMethod(0xb5, 1, 1, TextureCopy); } - public void CallMethod(AMemory Memory, NsGpuPBEntry PBEntry) + public void CallMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) { if (Methods.TryGetValue(PBEntry.Method, out NvGpuMethod Method)) { - Method(Memory, PBEntry); + Method(Vmm, PBEntry); } else { @@ -56,7 +55,7 @@ namespace Ryujinx.Graphics.Gpu } } - private void TextureCopy(AMemory Memory, NsGpuPBEntry PBEntry) + private void TextureCopy(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) { CopyOperation Operation = (CopyOperation)ReadRegister(NvGpuEngine2dReg.CopyOperation); @@ -76,10 +75,10 @@ namespace Ryujinx.Graphics.Gpu int DstBlockHeight = 1 << ((DstBlkDim >> 4) & 0xf); - long Tag = MakeInt64From2xInt32(NvGpuEngine2dReg.SrcAddress); + long Tag = Vmm.GetPhysicalAddress(MakeInt64From2xInt32(NvGpuEngine2dReg.SrcAddress)); - TryGetCpuAddr(NvGpuEngine2dReg.SrcAddress, out long SrcAddress); - TryGetCpuAddr(NvGpuEngine2dReg.DstAddress, out long DstAddress); + long SrcAddress = MakeInt64From2xInt32(NvGpuEngine2dReg.SrcAddress); + long DstAddress = MakeInt64From2xInt32(NvGpuEngine2dReg.DstAddress); bool IsFbTexture = Gpu.Engine3d.IsFrameBufferPosition(Tag); @@ -101,31 +100,22 @@ namespace Ryujinx.Graphics.Gpu { Gpu.Renderer.GetFrameBufferData(Tag, (byte[] Buffer) => { - CopyTexture(Memory, DstTexture, Buffer); + CopyTexture(Vmm, DstTexture, Buffer); }); } else { long Size = SrcWidth * SrcHeight * 4; - byte[] Buffer = AMemoryHelper.ReadBytes(Memory, SrcAddress, Size); + byte[] Buffer = Vmm.ReadBytes(SrcAddress, Size); - CopyTexture(Memory, DstTexture, Buffer); + CopyTexture(Vmm, DstTexture, Buffer); } } - private void CopyTexture(AMemory Memory, Texture Texture, byte[] Buffer) + private void CopyTexture(NvGpuVmm Vmm, Texture Texture, byte[] Buffer) { - TextureWriter.Write(Memory, Texture, Buffer); - } - - private bool TryGetCpuAddr(NvGpuEngine2dReg Reg, out long Position) - { - Position = MakeInt64From2xInt32(Reg); - - Position = Gpu.GetCpuAddr(Position); - - return Position != -1; + TextureWriter.Write(Vmm, Texture, Buffer); } private long MakeInt64From2xInt32(NvGpuEngine2dReg Reg) @@ -135,7 +125,7 @@ namespace Ryujinx.Graphics.Gpu (uint)Registers[(int)Reg + 1]; } - private void WriteRegister(NsGpuPBEntry PBEntry) + private void WriteRegister(NvGpuPBEntry PBEntry) { int ArgsCount = PBEntry.Arguments.Count; diff --git a/Ryujinx.Graphics/Gpu/NvGpuEngine2dReg.cs b/Ryujinx.Core/Gpu/NvGpuEngine2dReg.cs similarity index 95% rename from Ryujinx.Graphics/Gpu/NvGpuEngine2dReg.cs rename to Ryujinx.Core/Gpu/NvGpuEngine2dReg.cs index 903baca8..b4abad00 100644 --- a/Ryujinx.Graphics/Gpu/NvGpuEngine2dReg.cs +++ b/Ryujinx.Core/Gpu/NvGpuEngine2dReg.cs @@ -1,4 +1,4 @@ -namespace Ryujinx.Graphics.Gpu +namespace Ryujinx.Core.Gpu { enum NvGpuEngine2dReg { diff --git a/Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs b/Ryujinx.Core/Gpu/NvGpuEngine3d.cs similarity index 73% rename from Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs rename to Ryujinx.Core/Gpu/NvGpuEngine3d.cs index a6696650..b08b9496 100644 --- a/Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs +++ b/Ryujinx.Core/Gpu/NvGpuEngine3d.cs @@ -1,15 +1,14 @@ -using ChocolArm64.Memory; using Ryujinx.Graphics.Gal; using System; using System.Collections.Generic; -namespace Ryujinx.Graphics.Gpu +namespace Ryujinx.Core.Gpu { public class NvGpuEngine3d : INvGpuEngine { public int[] Registers { get; private set; } - private NsGpu Gpu; + private NvGpu Gpu; private Dictionary Methods; @@ -20,11 +19,11 @@ namespace Ryujinx.Graphics.Gpu public int Size; } - private ConstBuffer[] ConstBuffers; + private ConstBuffer[][] ConstBuffers; private HashSet FrameBuffers; - public NvGpuEngine3d(NsGpu Gpu) + public NvGpuEngine3d(NvGpu Gpu) { this.Gpu = Gpu; @@ -46,18 +45,23 @@ namespace Ryujinx.Graphics.Gpu AddMethod(0x674, 1, 1, ClearBuffers); AddMethod(0x6c3, 1, 1, QueryControl); AddMethod(0x8e4, 16, 1, CbData); - AddMethod(0x904, 1, 1, CbBind); + AddMethod(0x904, 5, 8, CbBind); - ConstBuffers = new ConstBuffer[18]; + ConstBuffers = new ConstBuffer[6][]; + + for (int Index = 0; Index < ConstBuffers.Length; Index++) + { + ConstBuffers[Index] = new ConstBuffer[18]; + } FrameBuffers = new HashSet(); } - public void CallMethod(AMemory Memory, NsGpuPBEntry PBEntry) + public void CallMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) { if (Methods.TryGetValue(PBEntry.Method, out NvGpuMethod Method)) { - Method(Memory, PBEntry); + Method(Vmm, PBEntry); } else { @@ -65,22 +69,22 @@ namespace Ryujinx.Graphics.Gpu } } - private void VertexEndGl(AMemory Memory, NsGpuPBEntry PBEntry) + private void VertexEndGl(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) { - SetFrameBuffer(0); + SetFrameBuffer(Vmm, 0); - long[] Tags = UploadShaders(Memory); + long[] Tags = UploadShaders(Vmm); Gpu.Renderer.BindProgram(); SetAlphaBlending(); - UploadTextures(Memory, Tags); - UploadUniforms(Memory); - UploadVertexArrays(Memory); + UploadTextures(Vmm, Tags); + UploadUniforms(Vmm); + UploadVertexArrays(Vmm); } - private void ClearBuffers(AMemory Memory, NsGpuPBEntry PBEntry) + private void ClearBuffers(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) { int Arg0 = PBEntry.Arguments[0]; @@ -90,28 +94,30 @@ namespace Ryujinx.Graphics.Gpu GalClearBufferFlags Flags = (GalClearBufferFlags)(Arg0 & 0x3f); - SetFrameBuffer(0); + SetFrameBuffer(Vmm, 0); //TODO: Enable this once the frame buffer problems are fixed. //Gpu.Renderer.ClearBuffers(Layer, Flags); } - private void SetFrameBuffer(int FbIndex) + private void SetFrameBuffer(NvGpuVmm Vmm, int FbIndex) { - long Address = MakeInt64From2xInt32(NvGpuEngine3dReg.FrameBufferNAddress + FbIndex * 0x10); + long VA = MakeInt64From2xInt32(NvGpuEngine3dReg.FrameBufferNAddress + FbIndex * 0x10); - FrameBuffers.Add(Address); + long PA = Vmm.GetPhysicalAddress(VA); + + FrameBuffers.Add(PA); int Width = ReadRegister(NvGpuEngine3dReg.FrameBufferNWidth + FbIndex * 0x10); int Height = ReadRegister(NvGpuEngine3dReg.FrameBufferNHeight + FbIndex * 0x10); //Note: Using the Width/Height results seems to give incorrect results. //Maybe the size of all frame buffers is hardcoded to screen size? This seems unlikely. - Gpu.Renderer.CreateFrameBuffer(Address, 1280, 720); - Gpu.Renderer.BindFrameBuffer(Address); + Gpu.Renderer.CreateFrameBuffer(PA, 1280, 720); + Gpu.Renderer.BindFrameBuffer(PA); } - private long[] UploadShaders(AMemory Memory) + private long[] UploadShaders(NvGpuVmm Vmm) { long[] Tags = new long[5]; @@ -132,12 +138,10 @@ namespace Ryujinx.Graphics.Gpu long Tag = BasePosition + (uint)Offset; - long Position = Gpu.GetCpuAddr(Tag); - //TODO: Find a better way to calculate the size. int Size = 0x20000; - byte[] Code = AMemoryHelper.ReadBytes(Memory, Position, (uint)Size); + byte[] Code = Vmm.ReadBytes(Tag, Size); GalShaderType ShaderType = GetTypeFromProgram(Index); @@ -211,16 +215,12 @@ namespace Ryujinx.Graphics.Gpu } } - private void UploadTextures(AMemory Memory, long[] Tags) + private void UploadTextures(NvGpuVmm Vmm, long[] Tags) { long BaseShPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.ShaderAddress); int TextureCbIndex = ReadRegister(NvGpuEngine3dReg.TextureCbIndex); - long BasePosition = ConstBuffers[TextureCbIndex].Position; - - long Size = (uint)ConstBuffers[TextureCbIndex].Size; - //Note: On the emulator renderer, Texture Unit 0 is //reserved for drawing the frame buffer. int TexIndex = 1; @@ -229,9 +229,9 @@ namespace Ryujinx.Graphics.Gpu { foreach (ShaderDeclInfo DeclInfo in Gpu.Renderer.GetTextureUsage(Tags[Index])) { - long Position = BasePosition + Index * Size; + long Position = ConstBuffers[Index][TextureCbIndex].Position; - UploadTexture(Memory, Position, TexIndex, DeclInfo.Index); + UploadTexture(Vmm, Position, TexIndex, DeclInfo.Index); Gpu.Renderer.SetUniform1(DeclInfo.Name, TexIndex); @@ -240,26 +240,28 @@ namespace Ryujinx.Graphics.Gpu } } - private void UploadTexture(AMemory Memory, long BasePosition, int TexIndex, int HndIndex) + private void UploadTexture(NvGpuVmm Vmm, long BasePosition, int TexIndex, int HndIndex) { long Position = BasePosition + HndIndex * 4; - int TextureHandle = Memory.ReadInt32(Position); + int TextureHandle = Vmm.ReadInt32(Position); int TicIndex = (TextureHandle >> 0) & 0xfffff; int TscIndex = (TextureHandle >> 20) & 0xfff; - TryGetCpuAddr(NvGpuEngine3dReg.TexHeaderPoolOffset, out long TicPosition); - TryGetCpuAddr(NvGpuEngine3dReg.TexSamplerPoolOffset, out long TscPosition); + long TicPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.TexHeaderPoolOffset); + long TscPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.TexSamplerPoolOffset); TicPosition += TicIndex * 0x20; TscPosition += TscIndex * 0x20; - GalTextureSampler Sampler = TextureFactory.MakeSampler(Gpu, Memory, TscPosition); + GalTextureSampler Sampler = TextureFactory.MakeSampler(Gpu, Vmm, TscPosition); - long TextureAddress = Memory.ReadInt64(TicPosition + 4) & 0xffffffffffff; + long TextureAddress = Vmm.ReadInt64(TicPosition + 4) & 0xffffffffffff; - if (FrameBuffers.Contains(TextureAddress)) + TextureAddress = Vmm.GetPhysicalAddress(TextureAddress); + + if (IsFrameBufferPosition(TextureAddress)) { //This texture is a frame buffer texture, //we shouldn't read anything from memory and bind @@ -269,14 +271,14 @@ namespace Ryujinx.Graphics.Gpu } else { - GalTexture Texture = TextureFactory.MakeTexture(Gpu, Memory, TicPosition); + GalTexture Texture = TextureFactory.MakeTexture(Gpu, Vmm, TicPosition); Gpu.Renderer.SetTextureAndSampler(TexIndex, Texture, Sampler); Gpu.Renderer.BindTexture(TexIndex); } } - private void UploadUniforms(AMemory Memory) + private void UploadUniforms(NvGpuVmm Vmm) { long BasePosition = MakeInt64From2xInt32(NvGpuEngine3dReg.ShaderAddress); @@ -295,13 +297,11 @@ namespace Ryujinx.Graphics.Gpu for (int Cbuf = 0; Cbuf < ConstBuffers.Length; Cbuf++) { - ConstBuffer Cb = ConstBuffers[Cbuf]; + ConstBuffer Cb = ConstBuffers[Index][Cbuf]; if (Cb.Enabled) { - long CbPosition = Cb.Position + Index * Cb.Size; - - byte[] Data = AMemoryHelper.ReadBytes(Memory, CbPosition, (uint)Cb.Size); + byte[] Data = Vmm.ReadBytes(Cb.Position, (uint)Cb.Size); Gpu.Renderer.SetConstBuffer(BasePosition + (uint)Offset, Cbuf, Data); } @@ -309,7 +309,7 @@ namespace Ryujinx.Graphics.Gpu } } - private void UploadVertexArrays(AMemory Memory) + private void UploadVertexArrays(NvGpuVmm Vmm) { long IndexPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.IndexArrayAddress); @@ -328,11 +328,9 @@ namespace Ryujinx.Graphics.Gpu if (IndexSize != 0) { - IndexPosition = Gpu.GetCpuAddr(IndexPosition); - int BufferSize = IndexCount * IndexSize; - byte[] Data = AMemoryHelper.ReadBytes(Memory, IndexPosition, BufferSize); + byte[] Data = Vmm.ReadBytes(IndexPosition, BufferSize); Gpu.Renderer.SetIndexArray(Data, IndexFormat); } @@ -382,7 +380,7 @@ namespace Ryujinx.Graphics.Gpu if (IndexCount != 0) { Size = GetVertexCountFromIndexBuffer( - Memory, + Vmm, IndexPosition, IndexCount, IndexSize); @@ -396,9 +394,7 @@ namespace Ryujinx.Graphics.Gpu //In this case, we need to use the size of the attribute. Size *= Stride; - VertexPosition = Gpu.GetCpuAddr(VertexPosition); - - byte[] Data = AMemoryHelper.ReadBytes(Memory, VertexPosition, Size); + byte[] Data = Vmm.ReadBytes(VertexPosition, Size); GalVertexAttrib[] AttribArray = Attribs[Index]?.ToArray() ?? new GalVertexAttrib[0]; @@ -420,10 +416,10 @@ namespace Ryujinx.Graphics.Gpu } private int GetVertexCountFromIndexBuffer( - AMemory Memory, - long IndexPosition, - int IndexCount, - int IndexSize) + NvGpuVmm Vmm, + long IndexPosition, + int IndexCount, + int IndexSize) { int MaxIndex = -1; @@ -431,7 +427,7 @@ namespace Ryujinx.Graphics.Gpu { while (IndexCount -- > 0) { - ushort Value = Memory.ReadUInt16(IndexPosition); + ushort Value = Vmm.ReadUInt16(IndexPosition); IndexPosition += 2; @@ -445,7 +441,7 @@ namespace Ryujinx.Graphics.Gpu { while (IndexCount -- > 0) { - byte Value = Memory.ReadByte(IndexPosition++); + byte Value = Vmm.ReadByte(IndexPosition++); if (MaxIndex < Value) { @@ -457,7 +453,7 @@ namespace Ryujinx.Graphics.Gpu { while (IndexCount -- > 0) { - uint Value = Memory.ReadUInt32(IndexPosition); + uint Value = Vmm.ReadUInt32(IndexPosition); IndexPosition += 2; @@ -475,75 +471,56 @@ namespace Ryujinx.Graphics.Gpu return MaxIndex + 1; } - private void QueryControl(AMemory Memory, NsGpuPBEntry PBEntry) + private void QueryControl(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) { - if (TryGetCpuAddr(NvGpuEngine3dReg.QueryAddress, out long Position)) + long Position = MakeInt64From2xInt32(NvGpuEngine3dReg.QueryAddress); + + int Seq = Registers[(int)NvGpuEngine3dReg.QuerySequence]; + int Ctrl = Registers[(int)NvGpuEngine3dReg.QueryControl]; + + int Mode = Ctrl & 3; + + if (Mode == 0) { - int Seq = Registers[(int)NvGpuEngine3dReg.QuerySequence]; - int Ctrl = Registers[(int)NvGpuEngine3dReg.QueryControl]; - - int Mode = Ctrl & 3; - - if (Mode == 0) - { - //Write mode. - Memory.WriteInt32(Position, Seq); - } + //Write mode. + Vmm.WriteInt32(Position, Seq); } WriteRegister(PBEntry); } - private void CbData(AMemory Memory, NsGpuPBEntry PBEntry) + private void CbData(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) { - if (TryGetCpuAddr(NvGpuEngine3dReg.ConstBufferNAddress, out long Position)) + long Position = MakeInt64From2xInt32(NvGpuEngine3dReg.ConstBufferAddress); + + int Offset = ReadRegister(NvGpuEngine3dReg.ConstBufferOffset); + + foreach (int Arg in PBEntry.Arguments) { - int Offset = ReadRegister(NvGpuEngine3dReg.ConstBufferNOffset); + Vmm.WriteInt32(Position + Offset, Arg); - foreach (int Arg in PBEntry.Arguments) - { - Memory.WriteInt32(Position + Offset, Arg); - - Offset += 4; - } - - WriteRegister(NvGpuEngine3dReg.ConstBufferNOffset, Offset); + Offset += 4; } + + WriteRegister(NvGpuEngine3dReg.ConstBufferOffset, Offset); } - private void CbBind(AMemory Memory, NsGpuPBEntry PBEntry) + private void CbBind(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) { + int Stage = (PBEntry.Method - 0x904) >> 3; + int Index = PBEntry.Arguments[0]; bool Enabled = (Index & 1) != 0; Index = (Index >> 4) & 0x1f; - if (TryGetCpuAddr(NvGpuEngine3dReg.ConstBufferNAddress, out long Position)) - { - ConstBuffers[Index].Position = Position; - ConstBuffers[Index].Enabled = Enabled; + long Position = MakeInt64From2xInt32(NvGpuEngine3dReg.ConstBufferAddress); - ConstBuffers[Index].Size = ReadRegister(NvGpuEngine3dReg.ConstBufferNSize); - } - } + ConstBuffers[Stage][Index].Position = Position; + ConstBuffers[Stage][Index].Enabled = Enabled; - private int ReadCb(AMemory Memory, int Cbuf, int Offset) - { - long Position = ConstBuffers[Cbuf].Position; - - int Value = Memory.ReadInt32(Position + Offset); - - return Value; - } - - private bool TryGetCpuAddr(NvGpuEngine3dReg Reg, out long Position) - { - Position = MakeInt64From2xInt32(Reg); - - Position = Gpu.GetCpuAddr(Position); - - return Position != -1; + ConstBuffers[Stage][Index].Size = ReadRegister(NvGpuEngine3dReg.ConstBufferSize); } private long MakeInt64From2xInt32(NvGpuEngine3dReg Reg) @@ -553,7 +530,7 @@ namespace Ryujinx.Graphics.Gpu (uint)Registers[(int)Reg + 1]; } - private void WriteRegister(NsGpuPBEntry PBEntry) + private void WriteRegister(NvGpuPBEntry PBEntry) { int ArgsCount = PBEntry.Arguments.Count; diff --git a/Ryujinx.Graphics/Gpu/NvGpuEngine3dReg.cs b/Ryujinx.Core/Gpu/NvGpuEngine3dReg.cs similarity index 93% rename from Ryujinx.Graphics/Gpu/NvGpuEngine3dReg.cs rename to Ryujinx.Core/Gpu/NvGpuEngine3dReg.cs index 0d995619..823885ff 100644 --- a/Ryujinx.Graphics/Gpu/NvGpuEngine3dReg.cs +++ b/Ryujinx.Core/Gpu/NvGpuEngine3dReg.cs @@ -1,4 +1,4 @@ -namespace Ryujinx.Graphics.Gpu +namespace Ryujinx.Core.Gpu { enum NvGpuEngine3dReg { @@ -53,9 +53,9 @@ namespace Ryujinx.Graphics.Gpu ShaderNOffset = 0x801, ShaderNMaxGprs = 0x803, ShaderNType = 0x804, - ConstBufferNSize = 0x8e0, - ConstBufferNAddress = 0x8e1, - ConstBufferNOffset = 0x8e3, + ConstBufferSize = 0x8e0, + ConstBufferAddress = 0x8e1, + ConstBufferOffset = 0x8e3, TextureCbIndex = 0x982 } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gpu/NvGpuFifo.cs b/Ryujinx.Core/Gpu/NvGpuFifo.cs similarity index 70% rename from Ryujinx.Graphics/Gpu/NvGpuFifo.cs rename to Ryujinx.Core/Gpu/NvGpuFifo.cs index 68c2902a..d0e6fc14 100644 --- a/Ryujinx.Graphics/Gpu/NvGpuFifo.cs +++ b/Ryujinx.Core/Gpu/NvGpuFifo.cs @@ -1,16 +1,15 @@ -using ChocolArm64.Memory; using System.Collections.Concurrent; -namespace Ryujinx.Graphics.Gpu +namespace Ryujinx.Core.Gpu { public class NvGpuFifo { private const int MacrosCount = 0x80; private const int MacroIndexMask = MacrosCount - 1; - private NsGpu Gpu; + private NvGpu Gpu; - private ConcurrentQueue<(AMemory, NsGpuPBEntry)> BufferQueue; + private ConcurrentQueue<(NvGpuVmm, NvGpuPBEntry)> BufferQueue; private NvGpuEngine[] SubChannels; @@ -32,9 +31,9 @@ namespace Ryujinx.Graphics.Gpu Interpreter?.Fifo.Enqueue(Param); } - public void Execute(AMemory Memory, int Param) + public void Execute(NvGpuVmm Vmm, int Param) { - Interpreter?.Execute(Memory, Position, Param); + Interpreter?.Execute(Vmm, Position, Param); } } @@ -43,22 +42,22 @@ namespace Ryujinx.Graphics.Gpu private CachedMacro[] Macros; - public NvGpuFifo(NsGpu Gpu) + public NvGpuFifo(NvGpu Gpu) { this.Gpu = Gpu; - BufferQueue = new ConcurrentQueue<(AMemory, NsGpuPBEntry)>(); + BufferQueue = new ConcurrentQueue<(NvGpuVmm, NvGpuPBEntry)>(); SubChannels = new NvGpuEngine[8]; Macros = new CachedMacro[MacrosCount]; } - public void PushBuffer(AMemory Memory, NsGpuPBEntry[] Buffer) + public void PushBuffer(NvGpuVmm Vmm, NvGpuPBEntry[] Buffer) { - foreach (NsGpuPBEntry PBEntry in Buffer) + foreach (NvGpuPBEntry PBEntry in Buffer) { - BufferQueue.Enqueue((Memory, PBEntry)); + BufferQueue.Enqueue((Vmm, PBEntry)); } } @@ -69,9 +68,9 @@ namespace Ryujinx.Graphics.Gpu public bool Step() { - if (BufferQueue.TryDequeue(out (AMemory Memory, NsGpuPBEntry PBEntry) Tuple)) + if (BufferQueue.TryDequeue(out (NvGpuVmm Vmm, NvGpuPBEntry PBEntry) Tuple)) { - CallMethod(Tuple.Memory, Tuple.PBEntry); + CallMethod(Tuple.Vmm, Tuple.PBEntry); return true; } @@ -79,7 +78,7 @@ namespace Ryujinx.Graphics.Gpu return false; } - private void CallMethod(AMemory Memory, NsGpuPBEntry PBEntry) + private void CallMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) { if (PBEntry.Method < 0x80) { @@ -103,11 +102,11 @@ namespace Ryujinx.Graphics.Gpu case NvGpuFifoMeth.SendMacroCodeData: { - long Position = Gpu.GetCpuAddr(CurrMacroPosition); + long Position = CurrMacroPosition; foreach (int Arg in PBEntry.Arguments) { - Memory.WriteInt32(Position, Arg); + Vmm.WriteInt32(Position, Arg); CurrMacroPosition += 4; @@ -127,8 +126,6 @@ namespace Ryujinx.Graphics.Gpu { long Position = (long)((ulong)PBEntry.Arguments[0] << 2); - Position = Gpu.GetCpuAddr(Position); - Macros[CurrMacroBindIndex] = new CachedMacro(this, Gpu.Engine3d, Position); break; @@ -139,22 +136,22 @@ namespace Ryujinx.Graphics.Gpu { switch (SubChannels[PBEntry.SubChannel]) { - case NvGpuEngine._2d: Call2dMethod(Memory, PBEntry); break; - case NvGpuEngine._3d: Call3dMethod(Memory, PBEntry); break; + case NvGpuEngine._2d: Call2dMethod(Vmm, PBEntry); break; + case NvGpuEngine._3d: Call3dMethod(Vmm, PBEntry); break; } } } - private void Call2dMethod(AMemory Memory, NsGpuPBEntry PBEntry) + private void Call2dMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) { - Gpu.Engine2d.CallMethod(Memory, PBEntry); + Gpu.Engine2d.CallMethod(Vmm, PBEntry); } - private void Call3dMethod(AMemory Memory, NsGpuPBEntry PBEntry) + private void Call3dMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) { if (PBEntry.Method < 0xe00) { - Gpu.Engine3d.CallMethod(Memory, PBEntry); + Gpu.Engine3d.CallMethod(Vmm, PBEntry); } else { @@ -169,7 +166,7 @@ namespace Ryujinx.Graphics.Gpu } else { - Macros[MacroIndex].Execute(Memory, PBEntry.Arguments[0]); + Macros[MacroIndex].Execute(Vmm, PBEntry.Arguments[0]); } } } diff --git a/Ryujinx.Graphics/Gpu/NvGpuFifoMeth.cs b/Ryujinx.Core/Gpu/NvGpuFifoMeth.cs similarity index 87% rename from Ryujinx.Graphics/Gpu/NvGpuFifoMeth.cs rename to Ryujinx.Core/Gpu/NvGpuFifoMeth.cs index 4287e250..78ec9080 100644 --- a/Ryujinx.Graphics/Gpu/NvGpuFifoMeth.cs +++ b/Ryujinx.Core/Gpu/NvGpuFifoMeth.cs @@ -1,4 +1,4 @@ -namespace Ryujinx.Graphics.Gpu +namespace Ryujinx.Core.Gpu { enum NvGpuFifoMeth { diff --git a/Ryujinx.Core/Gpu/NvGpuMethod.cs b/Ryujinx.Core/Gpu/NvGpuMethod.cs new file mode 100644 index 00000000..42e1b553 --- /dev/null +++ b/Ryujinx.Core/Gpu/NvGpuMethod.cs @@ -0,0 +1,4 @@ +namespace Ryujinx.Core.Gpu +{ + delegate void NvGpuMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry); +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gpu/NsGpuPBEntry.cs b/Ryujinx.Core/Gpu/NvGpuPBEntry.cs similarity index 78% rename from Ryujinx.Graphics/Gpu/NsGpuPBEntry.cs rename to Ryujinx.Core/Gpu/NvGpuPBEntry.cs index d405a93c..ebf35b9e 100644 --- a/Ryujinx.Graphics/Gpu/NsGpuPBEntry.cs +++ b/Ryujinx.Core/Gpu/NvGpuPBEntry.cs @@ -1,9 +1,9 @@ using System; using System.Collections.ObjectModel; -namespace Ryujinx.Graphics.Gpu +namespace Ryujinx.Core.Gpu { - public struct NsGpuPBEntry + public struct NvGpuPBEntry { public int Method { get; private set; } @@ -13,7 +13,7 @@ namespace Ryujinx.Graphics.Gpu public ReadOnlyCollection Arguments => Array.AsReadOnly(m_Arguments); - public NsGpuPBEntry(int Method, int SubChannel, params int[] Arguments) + public NvGpuPBEntry(int Method, int SubChannel, params int[] Arguments) { this.Method = Method; this.SubChannel = SubChannel; diff --git a/Ryujinx.Graphics/Gpu/NvGpuPushBuffer.cs b/Ryujinx.Core/Gpu/NvGpuPushBuffer.cs similarity index 85% rename from Ryujinx.Graphics/Gpu/NvGpuPushBuffer.cs rename to Ryujinx.Core/Gpu/NvGpuPushBuffer.cs index 8cbb3288..d5588655 100644 --- a/Ryujinx.Graphics/Gpu/NvGpuPushBuffer.cs +++ b/Ryujinx.Core/Gpu/NvGpuPushBuffer.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using System.IO; -namespace Ryujinx.Graphics.Gpu +namespace Ryujinx.Core.Gpu { public static class NvGpuPushBuffer { @@ -13,13 +13,13 @@ namespace Ryujinx.Graphics.Gpu IncrementOnce = 5 } - public static NsGpuPBEntry[] Decode(byte[] Data) + public static NvGpuPBEntry[] Decode(byte[] Data) { using (MemoryStream MS = new MemoryStream(Data)) { BinaryReader Reader = new BinaryReader(MS); - List PushBuffer = new List(); + List PushBuffer = new List(); bool CanRead() => MS.Position + 4 <= MS.Length; @@ -38,7 +38,7 @@ namespace Ryujinx.Graphics.Gpu { for (int Index = 0; Index < Args && CanRead(); Index++, Meth++) { - PushBuffer.Add(new NsGpuPBEntry(Meth, SubC, Reader.ReadInt32())); + PushBuffer.Add(new NvGpuPBEntry(Meth, SubC, Reader.ReadInt32())); } break; @@ -58,14 +58,14 @@ namespace Ryujinx.Graphics.Gpu Arguments[Index] = Reader.ReadInt32(); } - PushBuffer.Add(new NsGpuPBEntry(Meth, SubC, Arguments)); + PushBuffer.Add(new NvGpuPBEntry(Meth, SubC, Arguments)); break; } case SubmissionMode.Immediate: { - PushBuffer.Add(new NsGpuPBEntry(Meth, SubC, Args)); + PushBuffer.Add(new NvGpuPBEntry(Meth, SubC, Args)); break; } @@ -74,9 +74,9 @@ namespace Ryujinx.Graphics.Gpu { if (CanRead()) { - PushBuffer.Add(new NsGpuPBEntry(Meth, SubC, Reader.ReadInt32())); + PushBuffer.Add(new NvGpuPBEntry(Meth, SubC, Reader.ReadInt32())); } - + if (CanRead() && Args > 1) { int[] Arguments = new int[Args - 1]; @@ -86,7 +86,7 @@ namespace Ryujinx.Graphics.Gpu Arguments[Index] = Reader.ReadInt32(); } - PushBuffer.Add(new NsGpuPBEntry(Meth + 1, SubC, Arguments)); + PushBuffer.Add(new NvGpuPBEntry(Meth + 1, SubC, Arguments)); } break; diff --git a/Ryujinx.Core/Gpu/NvGpuVmm.cs b/Ryujinx.Core/Gpu/NvGpuVmm.cs new file mode 100644 index 00000000..cf94a6c0 --- /dev/null +++ b/Ryujinx.Core/Gpu/NvGpuVmm.cs @@ -0,0 +1,398 @@ +using ChocolArm64.Memory; +using System.Collections.Concurrent; + +namespace Ryujinx.Core.Gpu +{ + public class NvGpuVmm : IAMemory + { + public const long AddrSize = 1L << 40; + + private const int PTLvl0Bits = 14; + private const int PTLvl1Bits = 14; + private const int PTPageBits = 12; + + private const int PTLvl0Size = 1 << PTLvl0Bits; + private const int PTLvl1Size = 1 << PTLvl1Bits; + public const int PageSize = 1 << PTPageBits; + + private const int PTLvl0Mask = PTLvl0Size - 1; + private const int PTLvl1Mask = PTLvl1Size - 1; + public const int PageMask = PageSize - 1; + + private const int PTLvl0Bit = PTPageBits + PTLvl1Bits; + private const int PTLvl1Bit = PTPageBits; + + public AMemory Memory { get; private set; } + + private struct MappedMemory + { + public long Size; + + public MappedMemory(long Size) + { + this.Size = Size; + } + } + + private ConcurrentDictionary Maps; + + private const long PteUnmapped = -1; + private const long PteReserved = -2; + + private long[][] PageTable; + + public NvGpuVmm(AMemory Memory) + { + this.Memory = Memory; + + Maps = new ConcurrentDictionary(); + + PageTable = new long[PTLvl0Size][]; + } + + public long Map(long PA, long VA, long Size) + { + lock (PageTable) + { + for (long Offset = 0; Offset < Size; Offset += PageSize) + { + if (GetPte(VA + Offset) != PteReserved) + { + return Map(PA, Size); + } + } + + for (long Offset = 0; Offset < Size; Offset += PageSize) + { + SetPte(VA + Offset, PA + Offset); + } + } + + return VA; + } + + public long Map(long PA, long Size) + { + lock (PageTable) + { + long VA = GetFreePosition(Size); + + if (VA != -1) + { + MappedMemory Map = new MappedMemory(Size); + + Maps.AddOrUpdate(VA, Map, (Key, Old) => Map); + + for (long Offset = 0; Offset < Size; Offset += PageSize) + { + SetPte(VA + Offset, PA + Offset); + } + } + + return VA; + } + } + + public bool Unmap(long VA) + { + if (Maps.TryRemove(VA, out MappedMemory Map)) + { + Free(VA, Map.Size); + + return true; + } + + return false; + } + + public long Reserve(long VA, long Size, long Align) + { + lock (PageTable) + { + for (long Offset = 0; Offset < Size; Offset += PageSize) + { + if (IsPageInUse(VA + Offset)) + { + return Reserve(Size, Align); + } + } + + for (long Offset = 0; Offset < Size; Offset += PageSize) + { + SetPte(VA + Offset, PteReserved); + } + } + + return VA; + } + + public long Reserve(long Size, long Align) + { + lock (PageTable) + { + long Position = GetFreePosition(Size, Align); + + if (Position != -1) + { + for (long Offset = 0; Offset < Size; Offset += PageSize) + { + SetPte(Position + Offset, PteReserved); + } + } + + return Position; + } + } + + public void Free(long VA, long Size) + { + lock (PageTable) + { + for (long Offset = 0; Offset < Size; Offset += PageSize) + { + SetPte(VA + Offset, PteUnmapped); + } + } + } + + private long GetFreePosition(long Size, long Align = 1) + { + long Position = 0; + long FreeSize = 0; + + if (Align < 1) + { + Align = 1; + } + + Align = (Align + PageMask) & ~PageMask; + + while (Position + FreeSize < AddrSize) + { + if (!IsPageInUse(Position + FreeSize)) + { + FreeSize += PageSize; + + if (FreeSize >= Size) + { + return Position; + } + } + else + { + Position += FreeSize + PageSize; + FreeSize = 0; + + long Remainder = Position % Align; + + if (Remainder != 0) + { + Position = (Position - Remainder) + Align; + } + } + } + + return -1; + } + + public long GetPhysicalAddress(long VA) + { + long BasePos = GetPte(VA); + + if (BasePos < 0) + { + return -1; + } + + return BasePos + (VA & PageMask); + } + + public bool IsRegionFree(long VA, long Size) + { + for (long Offset = 0; Offset < Size; Offset += PageSize) + { + if (IsPageInUse(VA + Offset)) + { + return false; + } + } + + return true; + } + + private bool IsPageInUse(long VA) + { + if (VA >> PTLvl0Bits + PTLvl1Bits + PTPageBits != 0) + { + return false; + } + + long L0 = (VA >> PTLvl0Bit) & PTLvl0Mask; + long L1 = (VA >> PTLvl1Bit) & PTLvl1Mask; + + if (PageTable[L0] == null) + { + return false; + } + + return PageTable[L0][L1] != PteUnmapped; + } + + private long GetPte(long Position) + { + long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask; + long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask; + + if (PageTable[L0] == null) + { + return -1; + } + + return PageTable[L0][L1]; + } + + private void SetPte(long Position, long TgtAddr) + { + long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask; + long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask; + + if (PageTable[L0] == null) + { + PageTable[L0] = new long[PTLvl1Size]; + + for (int Index = 0; Index < PTLvl1Size; Index++) + { + PageTable[L0][Index] = PteUnmapped; + } + } + + PageTable[L0][L1] = TgtAddr; + } + + public byte ReadByte(long Position) + { + Position = GetPhysicalAddress(Position); + + return Memory.ReadByte(Position); + } + + public ushort ReadUInt16(long Position) + { + Position = GetPhysicalAddress(Position); + + return Memory.ReadUInt16(Position); + } + + public uint ReadUInt32(long Position) + { + Position = GetPhysicalAddress(Position); + + return Memory.ReadUInt32(Position); + } + + public ulong ReadUInt64(long Position) + { + Position = GetPhysicalAddress(Position); + + return Memory.ReadUInt64(Position); + } + + public sbyte ReadSByte(long Position) + { + Position = GetPhysicalAddress(Position); + + return Memory.ReadSByte(Position); + } + + public short ReadInt16(long Position) + { + Position = GetPhysicalAddress(Position); + + return Memory.ReadInt16(Position); + } + + public int ReadInt32(long Position) + { + Position = GetPhysicalAddress(Position); + + return Memory.ReadInt32(Position); + } + + public long ReadInt64(long Position) + { + Position = GetPhysicalAddress(Position); + + return Memory.ReadInt64(Position); + } + + public byte[] ReadBytes(long Position, long Size) + { + Position = GetPhysicalAddress(Position); + + return AMemoryHelper.ReadBytes(Memory, Position, Size); + } + + public void WriteByte(long Position, byte Value) + { + Position = GetPhysicalAddress(Position); + + Memory.WriteByte(Position, Value); + } + + public void WriteUInt16(long Position, ushort Value) + { + Position = GetPhysicalAddress(Position); + + Memory.WriteUInt16(Position, Value); + } + + public void WriteUInt32(long Position, uint Value) + { + Position = GetPhysicalAddress(Position); + + Memory.WriteUInt32(Position, Value); + } + + public void WriteUInt64(long Position, ulong Value) + { + Position = GetPhysicalAddress(Position); + + Memory.WriteUInt64(Position, Value); + } + + public void WriteSByte(long Position, sbyte Value) + { + Position = GetPhysicalAddress(Position); + + Memory.WriteSByte(Position, Value); + } + + public void WriteInt16(long Position, short Value) + { + Position = GetPhysicalAddress(Position); + + Memory.WriteInt16(Position, Value); + } + + public void WriteInt32(long Position, int Value) + { + Position = GetPhysicalAddress(Position); + + Memory.WriteInt32(Position, Value); + } + + public void WriteInt64(long Position, long Value) + { + Position = GetPhysicalAddress(Position); + + Memory.WriteInt64(Position, Value); + } + + public void WriteBytes(long Position, byte[] Data) + { + Position = GetPhysicalAddress(Position); + + AMemoryHelper.WriteBytes(Memory, Position, Data); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gpu/Texture.cs b/Ryujinx.Core/Gpu/Texture.cs similarity index 97% rename from Ryujinx.Graphics/Gpu/Texture.cs rename to Ryujinx.Core/Gpu/Texture.cs index cbfa683d..39a35059 100644 --- a/Ryujinx.Graphics/Gpu/Texture.cs +++ b/Ryujinx.Core/Gpu/Texture.cs @@ -1,6 +1,6 @@ using Ryujinx.Graphics.Gal; -namespace Ryujinx.Graphics.Gpu +namespace Ryujinx.Core.Gpu { public struct Texture { diff --git a/Ryujinx.Graphics/Gpu/TextureFactory.cs b/Ryujinx.Core/Gpu/TextureFactory.cs similarity index 77% rename from Ryujinx.Graphics/Gpu/TextureFactory.cs rename to Ryujinx.Core/Gpu/TextureFactory.cs index 7f8580d9..68b48a1f 100644 --- a/Ryujinx.Graphics/Gpu/TextureFactory.cs +++ b/Ryujinx.Core/Gpu/TextureFactory.cs @@ -1,14 +1,13 @@ -using ChocolArm64.Memory; using Ryujinx.Graphics.Gal; using System; -namespace Ryujinx.Graphics.Gpu +namespace Ryujinx.Core.Gpu { static class TextureFactory { - public static GalTexture MakeTexture(NsGpu Gpu, AMemory Memory, long TicPosition) + public static GalTexture MakeTexture(NvGpu Gpu, NvGpuVmm Vmm, long TicPosition) { - int[] Tic = ReadWords(Memory, TicPosition, 8); + int[] Tic = ReadWords(Vmm, TicPosition, 8); GalTextureFormat Format = (GalTextureFormat)(Tic[0] & 0x7f); @@ -16,8 +15,6 @@ namespace Ryujinx.Graphics.Gpu TextureAddress |= (long)((ushort)Tic[2]) << 32; - TextureAddress = Gpu.GetCpuAddr(TextureAddress); - TextureSwizzle Swizzle = (TextureSwizzle)((Tic[2] >> 21) & 7); int Pitch = (Tic[3] & 0xffff) << 5; @@ -38,14 +35,14 @@ namespace Ryujinx.Graphics.Gpu Swizzle, Format); - byte[] Data = TextureReader.Read(Memory, Texture); + byte[] Data = TextureReader.Read(Vmm, Texture); return new GalTexture(Data, Width, Height, Format); } - public static GalTextureSampler MakeSampler(NsGpu Gpu, AMemory Memory, long TscPosition) + public static GalTextureSampler MakeSampler(NvGpu Gpu, NvGpuVmm Vmm, long TscPosition) { - int[] Tsc = ReadWords(Memory, TscPosition, 8); + int[] Tsc = ReadWords(Vmm, TscPosition, 8); GalTextureWrap AddressU = (GalTextureWrap)((Tsc[0] >> 0) & 7); GalTextureWrap AddressV = (GalTextureWrap)((Tsc[0] >> 3) & 7); @@ -71,13 +68,13 @@ namespace Ryujinx.Graphics.Gpu BorderColor); } - private static int[] ReadWords(AMemory Memory, long Position, int Count) + private static int[] ReadWords(NvGpuVmm Vmm, long Position, int Count) { int[] Words = new int[Count]; for (int Index = 0; Index < Count; Index++, Position += 4) { - Words[Index] = Memory.ReadInt32(Position); + Words[Index] = Vmm.ReadInt32(Position); } return Words; diff --git a/Ryujinx.Graphics/Gpu/TextureHelper.cs b/Ryujinx.Core/Gpu/TextureHelper.cs similarity index 62% rename from Ryujinx.Graphics/Gpu/TextureHelper.cs rename to Ryujinx.Core/Gpu/TextureHelper.cs index d3c2ac14..f0ebc1f0 100644 --- a/Ryujinx.Graphics/Gpu/TextureHelper.cs +++ b/Ryujinx.Core/Gpu/TextureHelper.cs @@ -1,6 +1,7 @@ +using ChocolArm64.Memory; using System; -namespace Ryujinx.Graphics.Gpu +namespace Ryujinx.Core.Gpu { static class TextureHelper { @@ -19,5 +20,17 @@ namespace Ryujinx.Graphics.Gpu throw new NotImplementedException(Texture.Swizzle.ToString()); } + + public static (AMemory Memory, long Position) GetMemoryAndPosition( + IAMemory Memory, + long Position) + { + if (Memory is NvGpuVmm Vmm) + { + return (Vmm.Memory, Vmm.GetPhysicalAddress(Position)); + } + + return ((AMemory)Memory, Position); + } } } diff --git a/Ryujinx.Graphics/Gpu/TextureReader.cs b/Ryujinx.Core/Gpu/TextureReader.cs similarity index 73% rename from Ryujinx.Graphics/Gpu/TextureReader.cs rename to Ryujinx.Core/Gpu/TextureReader.cs index 17fd95c5..f3e41046 100644 --- a/Ryujinx.Graphics/Gpu/TextureReader.cs +++ b/Ryujinx.Core/Gpu/TextureReader.cs @@ -2,11 +2,11 @@ using ChocolArm64.Memory; using Ryujinx.Graphics.Gal; using System; -namespace Ryujinx.Graphics.Gpu +namespace Ryujinx.Core.Gpu { public static class TextureReader { - public static byte[] Read(AMemory Memory, Texture Texture) + public static byte[] Read(IAMemory Memory, Texture Texture) { switch (Texture.Format) { @@ -23,7 +23,7 @@ namespace Ryujinx.Graphics.Gpu throw new NotImplementedException(Texture.Format.ToString()); } - private unsafe static byte[] Read2Bpp(AMemory Memory, Texture Texture) + private unsafe static byte[] Read2Bpp(IAMemory Memory, Texture Texture) { int Width = Texture.Width; int Height = Texture.Height; @@ -32,6 +32,10 @@ namespace Ryujinx.Graphics.Gpu ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, Width, 2); + (AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition( + Memory, + Texture.Position); + fixed (byte* BuffPtr = Output) { long OutOffs = 0; @@ -41,7 +45,7 @@ namespace Ryujinx.Graphics.Gpu { long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y); - short Pixel = Memory.ReadInt16Unchecked(Texture.Position + Offset); + short Pixel = CpuMem.ReadInt16Unchecked(Position + Offset); *(short*)(BuffPtr + OutOffs) = Pixel; @@ -52,7 +56,7 @@ namespace Ryujinx.Graphics.Gpu return Output; } - private unsafe static byte[] Read4Bpp(AMemory Memory, Texture Texture) + private unsafe static byte[] Read4Bpp(IAMemory Memory, Texture Texture) { int Width = Texture.Width; int Height = Texture.Height; @@ -61,6 +65,10 @@ namespace Ryujinx.Graphics.Gpu ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, Width, 4); + (AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition( + Memory, + Texture.Position); + fixed (byte* BuffPtr = Output) { long OutOffs = 0; @@ -70,7 +78,7 @@ namespace Ryujinx.Graphics.Gpu { long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y); - int Pixel = Memory.ReadInt32Unchecked(Texture.Position + Offset); + int Pixel = CpuMem.ReadInt32Unchecked(Position + Offset); *(int*)(BuffPtr + OutOffs) = Pixel; @@ -81,7 +89,7 @@ namespace Ryujinx.Graphics.Gpu return Output; } - private unsafe static byte[] Read8Bpt4x4(AMemory Memory, Texture Texture) + private unsafe static byte[] Read8Bpt4x4(IAMemory Memory, Texture Texture) { int Width = (Texture.Width + 3) / 4; int Height = (Texture.Height + 3) / 4; @@ -90,6 +98,10 @@ namespace Ryujinx.Graphics.Gpu ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, Width, 8); + (AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition( + Memory, + Texture.Position); + fixed (byte* BuffPtr = Output) { long OutOffs = 0; @@ -99,7 +111,7 @@ namespace Ryujinx.Graphics.Gpu { long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y); - long Tile = Memory.ReadInt64Unchecked(Texture.Position + Offset); + long Tile = CpuMem.ReadInt64Unchecked(Position + Offset); *(long*)(BuffPtr + OutOffs) = Tile; @@ -110,7 +122,7 @@ namespace Ryujinx.Graphics.Gpu return Output; } - private unsafe static byte[] Read16Bpt4x4(AMemory Memory, Texture Texture) + private unsafe static byte[] Read16Bpt4x4(IAMemory Memory, Texture Texture) { int Width = (Texture.Width + 3) / 4; int Height = (Texture.Height + 3) / 4; @@ -119,6 +131,10 @@ namespace Ryujinx.Graphics.Gpu ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, Width, 16); + (AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition( + Memory, + Texture.Position); + fixed (byte* BuffPtr = Output) { long OutOffs = 0; @@ -128,8 +144,8 @@ namespace Ryujinx.Graphics.Gpu { long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y); - long Tile0 = Memory.ReadInt64Unchecked(Texture.Position + Offset + 0); - long Tile1 = Memory.ReadInt64Unchecked(Texture.Position + Offset + 8); + long Tile0 = CpuMem.ReadInt64Unchecked(Position + Offset + 0); + long Tile1 = CpuMem.ReadInt64Unchecked(Position + Offset + 8); *(long*)(BuffPtr + OutOffs + 0) = Tile0; *(long*)(BuffPtr + OutOffs + 8) = Tile1; diff --git a/Ryujinx.Graphics/Gpu/TextureSwizzle.cs b/Ryujinx.Core/Gpu/TextureSwizzle.cs similarity index 87% rename from Ryujinx.Graphics/Gpu/TextureSwizzle.cs rename to Ryujinx.Core/Gpu/TextureSwizzle.cs index 7d99279c..3214f45f 100644 --- a/Ryujinx.Graphics/Gpu/TextureSwizzle.cs +++ b/Ryujinx.Core/Gpu/TextureSwizzle.cs @@ -1,4 +1,4 @@ -namespace Ryujinx.Graphics.Gpu +namespace Ryujinx.Core.Gpu { public enum TextureSwizzle { diff --git a/Ryujinx.Graphics/Gpu/TextureWriter.cs b/Ryujinx.Core/Gpu/TextureWriter.cs similarity index 63% rename from Ryujinx.Graphics/Gpu/TextureWriter.cs rename to Ryujinx.Core/Gpu/TextureWriter.cs index 2f25de73..125bb8c4 100644 --- a/Ryujinx.Graphics/Gpu/TextureWriter.cs +++ b/Ryujinx.Core/Gpu/TextureWriter.cs @@ -2,28 +2,31 @@ using ChocolArm64.Memory; using Ryujinx.Graphics.Gal; using System; -namespace Ryujinx.Graphics.Gpu +namespace Ryujinx.Core.Gpu { public static class TextureWriter { - public static void Write(AMemory Memory, Texture Texture, byte[] Data) + public static void Write(IAMemory Memory, Texture Texture, byte[] Data) { switch (Texture.Format) { case GalTextureFormat.A8B8G8R8: Write4Bpp(Memory, Texture, Data); break; - default: - throw new NotImplementedException(Texture.Format.ToString()); + default: throw new NotImplementedException(Texture.Format.ToString()); } } - private unsafe static void Write4Bpp(AMemory Memory, Texture Texture, byte[] Data) + private unsafe static void Write4Bpp(IAMemory Memory, Texture Texture, byte[] Data) { int Width = Texture.Width; int Height = Texture.Height; ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, Width, 4); + (AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition( + Memory, + Texture.Position); + fixed (byte* BuffPtr = Data) { long InOffs = 0; @@ -35,7 +38,7 @@ namespace Ryujinx.Graphics.Gpu int Pixel = *(int*)(BuffPtr + InOffs); - Memory.WriteInt32Unchecked(Texture.Position + Offset, Pixel); + CpuMem.WriteInt32Unchecked(Position + Offset, Pixel); InOffs += 4; } diff --git a/Ryujinx.Core/Loaders/Executable.cs b/Ryujinx.Core/Loaders/Executable.cs index 39ee9618..ab7e1979 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/Horizon.cs b/Ryujinx.Core/OsHle/Horizon.cs index 0cc3d815..1d4098c7 100644 --- a/Ryujinx.Core/OsHle/Horizon.cs +++ b/Ryujinx.Core/OsHle/Horizon.cs @@ -20,6 +20,8 @@ namespace Ryujinx.Core.OsHle public SystemStateMgr SystemState { get; private set; } + internal MemoryAllocator Allocator { get; private set; } + internal HSharedMem HidSharedMem { get; private set; } internal HSharedMem FontSharedMem { get; private set; } @@ -35,6 +37,8 @@ namespace Ryujinx.Core.OsHle SystemState = new SystemStateMgr(); + Allocator = new MemoryAllocator(); + HidSharedMem = new HSharedMem(); FontSharedMem = new HSharedMem(); diff --git a/Ryujinx.Core/OsHle/Ipc/IpcHandler.cs b/Ryujinx.Core/OsHle/Ipc/IpcHandler.cs index 42322d41..420890eb 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/Ipc/IpcMessage.cs b/Ryujinx.Core/OsHle/Ipc/IpcMessage.cs index d81f44bd..afcbb3a4 100644 --- a/Ryujinx.Core/OsHle/Ipc/IpcMessage.cs +++ b/Ryujinx.Core/OsHle/Ipc/IpcMessage.cs @@ -174,31 +174,6 @@ namespace Ryujinx.Core.OsHle.Ipc return 0; } - public long GetSendBuffPtr() - { - if (SendBuff.Count > 0 && SendBuff[0].Size != 0) - { - return SendBuff[0].Position; - } - - if (PtrBuff.Count > 0 && PtrBuff[0].Size != 0) - { - return PtrBuff[0].Position; - } - - if (ReceiveBuff.Count > 0 && ReceiveBuff[0].Size != 0) - { - return ReceiveBuff[0].Position; - } - - if (RecvListBuff.Count > 0 && RecvListBuff[0].Size != 0) - { - return RecvListBuff[0].Position; - } - - return -1; - } - public long GetBufferType0x21Position() { if (PtrBuff.Count > 0 && PtrBuff[0].Position != 0) diff --git a/Ryujinx.Core/OsHle/Kernel/KernelErr.cs b/Ryujinx.Core/OsHle/Kernel/KernelErr.cs index b568405b..87f9cf3b 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 e855b77d..1874360b 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 601b211c..e5b080a8 100644 --- a/Ryujinx.Core/OsHle/Kernel/SvcSystem.cs +++ b/Ryujinx.Core/OsHle/Kernel/SvcSystem.cs @@ -40,7 +40,7 @@ namespace Ryujinx.Core.OsHle.Kernel if (Obj == null) { - Ns.Log.PrintWarning(LogClass.KernelSvc, $"Tried to CloseHandle on invalid handle 0x{Handle:x8}!"); + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid handle 0x{Handle:x8}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); @@ -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) @@ -190,13 +241,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 e382cf75..57608cda 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/MemoryAllocator.cs b/Ryujinx.Core/OsHle/MemoryAllocator.cs new file mode 100644 index 00000000..e57f5264 --- /dev/null +++ b/Ryujinx.Core/OsHle/MemoryAllocator.cs @@ -0,0 +1,12 @@ +using System; + +namespace Ryujinx.Core.OsHle +{ + class MemoryAllocator + { + public bool TryAllocate(long Size, out long Address) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Process.cs b/Ryujinx.Core/OsHle/Process.cs index b8c08856..3ccbc016 100644 --- a/Ryujinx.Core/OsHle/Process.cs +++ b/Ryujinx.Core/OsHle/Process.cs @@ -410,11 +410,7 @@ namespace Ryujinx.Core.OsHle } } - INvDrvServices.Fds.DeleteProcess(this); - - INvDrvServices.NvMaps .DeleteProcess(this); - INvDrvServices.NvMapsById.DeleteProcess(this); - INvDrvServices.NvMapsFb .DeleteProcess(this); + INvDrvServices.UnloadProcess(this); AppletState.Dispose(); diff --git a/Ryujinx.Core/OsHle/Services/Nv/INvDrvServices.cs b/Ryujinx.Core/OsHle/Services/Nv/INvDrvServices.cs index 2652724d..00209f9d 100644 --- a/Ryujinx.Core/OsHle/Services/Nv/INvDrvServices.cs +++ b/Ryujinx.Core/OsHle/Services/Nv/INvDrvServices.cs @@ -2,30 +2,36 @@ using ChocolArm64.Memory; using Ryujinx.Core.Logging; using Ryujinx.Core.OsHle.Handles; using Ryujinx.Core.OsHle.Ipc; -using Ryujinx.Core.OsHle.Utilities; -using Ryujinx.Graphics.Gpu; +using Ryujinx.Core.OsHle.Services.Nv.NvGpuAS; +using Ryujinx.Core.OsHle.Services.Nv.NvGpuGpu; +using Ryujinx.Core.OsHle.Services.Nv.NvHostChannel; +using Ryujinx.Core.OsHle.Services.Nv.NvHostCtrl; +using Ryujinx.Core.OsHle.Services.Nv.NvMap; using System; using System.Collections.Generic; -using System.IO; namespace Ryujinx.Core.OsHle.Services.Nv { class INvDrvServices : IpcService, IDisposable { - private delegate long ServiceProcessIoctl(ServiceCtx Context); + private delegate int IoctlProcessor(ServiceCtx Context, int Cmd); private Dictionary m_Commands; public override IReadOnlyDictionary Commands => m_Commands; - private Dictionary<(string, int), ServiceProcessIoctl> IoctlCmds; + private static Dictionary IoctlProcessors = + new Dictionary() + { + { "/dev/nvhost-as-gpu", ProcessIoctlNvGpuAS }, + { "/dev/nvhost-ctrl", ProcessIoctlNvHostCtrl }, + { "/dev/nvhost-ctrl-gpu", ProcessIoctlNvGpuGpu }, + { "/dev/nvhost-gpu", ProcessIoctlNvHostChannel }, + { "/dev/nvmap", ProcessIoctlNvMap } + }; public static GlobalStateTable Fds { get; private set; } - public static GlobalStateTable NvMaps { get; private set; } - public static GlobalStateTable NvMapsById { get; private set; } - public static GlobalStateTable NvMapsFb { get; private set; } - private KEvent Event; public INvDrvServices() @@ -37,42 +43,7 @@ namespace Ryujinx.Core.OsHle.Services.Nv { 2, Close }, { 3, Initialize }, { 4, QueryEvent }, - { 8, SetClientPid }, - }; - - IoctlCmds = new Dictionary<(string, int), ServiceProcessIoctl>() - { - { ("/dev/nvhost-as-gpu", 0x4101), NvGpuAsIoctlBindChannel }, - { ("/dev/nvhost-as-gpu", 0x4102), NvGpuAsIoctlAllocSpace }, - { ("/dev/nvhost-as-gpu", 0x4105), NvGpuAsIoctlUnmap }, - { ("/dev/nvhost-as-gpu", 0x4106), NvGpuAsIoctlMapBufferEx }, - { ("/dev/nvhost-as-gpu", 0x4108), NvGpuAsIoctlGetVaRegions }, - { ("/dev/nvhost-as-gpu", 0x4109), NvGpuAsIoctlInitializeEx }, - { ("/dev/nvhost-as-gpu", 0x4114), NvGpuAsIoctlRemap }, - { ("/dev/nvhost-ctrl", 0x001b), NvHostIoctlCtrlGetConfig }, - { ("/dev/nvhost-ctrl", 0x001d), NvHostIoctlCtrlEventWait }, - { ("/dev/nvhost-ctrl", 0x001e), NvHostIoctlCtrlEventWaitAsync }, - { ("/dev/nvhost-ctrl", 0x001f), NvHostIoctlCtrlEventRegister }, - { ("/dev/nvhost-ctrl-gpu", 0x4701), NvGpuIoctlZcullGetCtxSize }, - { ("/dev/nvhost-ctrl-gpu", 0x4702), NvGpuIoctlZcullGetInfo }, - { ("/dev/nvhost-ctrl-gpu", 0x4703), NvGpuIoctlZbcSetTable }, - { ("/dev/nvhost-ctrl-gpu", 0x4705), NvGpuIoctlGetCharacteristics }, - { ("/dev/nvhost-ctrl-gpu", 0x4706), NvGpuIoctlGetTpcMasks }, - { ("/dev/nvhost-ctrl-gpu", 0x4714), NvGpuIoctlZbcGetActiveSlotMask }, - { ("/dev/nvhost-gpu", 0x4714), NvMapIoctlChannelSetUserData }, - { ("/dev/nvhost-gpu", 0x4801), NvMapIoctlChannelSetNvMap }, - { ("/dev/nvhost-gpu", 0x4808), NvMapIoctlChannelSubmitGpFifo }, - { ("/dev/nvhost-gpu", 0x4809), NvMapIoctlChannelAllocObjCtx }, - { ("/dev/nvhost-gpu", 0x480b), NvMapIoctlChannelZcullBind }, - { ("/dev/nvhost-gpu", 0x480c), NvMapIoctlChannelSetErrorNotifier }, - { ("/dev/nvhost-gpu", 0x480d), NvMapIoctlChannelSetPriority }, - { ("/dev/nvhost-gpu", 0x481a), NvMapIoctlChannelAllocGpFifoEx2 }, - { ("/dev/nvmap", 0x0101), NvMapIocCreate }, - { ("/dev/nvmap", 0x0103), NvMapIocFromId }, - { ("/dev/nvmap", 0x0104), NvMapIocAlloc }, - { ("/dev/nvmap", 0x0105), NvMapIocFree }, - { ("/dev/nvmap", 0x0109), NvMapIocParam }, - { ("/dev/nvmap", 0x010e), NvMapIocGetId }, + { 8, SetClientPid } }; Event = new KEvent(); @@ -81,10 +52,6 @@ namespace Ryujinx.Core.OsHle.Services.Nv static INvDrvServices() { Fds = new GlobalStateTable(); - - NvMaps = new GlobalStateTable(); - NvMapsById = new GlobalStateTable(); - NvMapsFb = new GlobalStateTable(); } public long Open(ServiceCtx Context) @@ -104,22 +71,25 @@ namespace Ryujinx.Core.OsHle.Services.Nv public long Ioctl(ServiceCtx Context) { int Fd = Context.RequestData.ReadInt32(); - int Cmd = Context.RequestData.ReadInt32() & 0xffff; + int Cmd = Context.RequestData.ReadInt32(); NvFd FdData = Fds.GetData(Context.Process, Fd); - long Position = Context.Request.GetSendBuffPtr(); + int Result; - Context.ResponseData.Write(0); - - if (IoctlCmds.TryGetValue((FdData.Name, Cmd), out ServiceProcessIoctl ProcReq)) + if (IoctlProcessors.TryGetValue(FdData.Name, out IoctlProcessor Process)) { - return ProcReq(Context); + Result = Process(Context, Cmd); } else { throw new NotImplementedException($"{FdData.Name} {Cmd:x4}"); } + + //TODO: Verify if the error codes needs to be translated. + Context.ResponseData.Write(Result); + + return 0; } public long Close(ServiceCtx Context) @@ -138,9 +108,9 @@ namespace Ryujinx.Core.OsHle.Services.Nv long TransferMemSize = Context.RequestData.ReadInt64(); int TransferMemHandle = Context.Request.HandleDesc.ToCopy[0]; - Context.ResponseData.Write(0); + NvMapIoctl.InitializeNvMap(Context); - NvMapsFb.Add(Context.Process, 0, new NvMapFb()); + Context.ResponseData.Write(0); return 0; } @@ -169,659 +139,69 @@ namespace Ryujinx.Core.OsHle.Services.Nv return 0; } - private long NvGpuAsIoctlBindChannel(ServiceCtx Context) + private static int ProcessIoctlNvGpuAS(ServiceCtx Context, int Cmd) { - long Position = Context.Request.GetSendBuffPtr(); - - int Fd = Context.Memory.ReadInt32(Position); - - return 0; + return ProcessIoctl(Context, Cmd, NvGpuASIoctl.ProcessIoctl); } - private long NvGpuAsIoctlAllocSpace(ServiceCtx Context) + private static int ProcessIoctlNvHostCtrl(ServiceCtx Context, int Cmd) { - long Position = Context.Request.GetSendBuffPtr(); + return ProcessIoctl(Context, Cmd, NvHostCtrlIoctl.ProcessIoctl); + } - MemReader Reader = new MemReader(Context.Memory, Position); + private static int ProcessIoctlNvGpuGpu(ServiceCtx Context, int Cmd) + { + return ProcessIoctl(Context, Cmd, NvGpuGpuIoctl.ProcessIoctl); + } - int Pages = Reader.ReadInt32(); - int PageSize = Reader.ReadInt32(); - int Flags = Reader.ReadInt32(); - int Padding = Reader.ReadInt32(); - long Align = Reader.ReadInt64(); + private static int ProcessIoctlNvHostChannel(ServiceCtx Context, int Cmd) + { + return ProcessIoctl(Context, Cmd, NvHostChannelIoctl.ProcessIoctl); + } - if ((Flags & 1) != 0) + private static int ProcessIoctlNvMap(ServiceCtx Context, int Cmd) + { + return ProcessIoctl(Context, Cmd, NvMapIoctl.ProcessIoctl); + } + + private static int ProcessIoctl(ServiceCtx Context, int Cmd, IoctlProcessor Processor) + { + if (CmdIn(Cmd) && Context.Request.GetBufferType0x21Position() == 0) { - Align = Context.Ns.Gpu.ReserveMemory(Align, (long)Pages * PageSize, 1); - } - else - { - Align = Context.Ns.Gpu.ReserveMemory((long)Pages * PageSize, Align); + Context.Ns.Log.PrintError(LogClass.ServiceNv, "Input buffer is null!"); + + return NvResult.InvalidInput; } - Context.Memory.WriteInt64(Position + 0x10, Align); - - return 0; - } - - private long NvGpuAsIoctlUnmap(ServiceCtx Context) - { - long Position = Context.Request.GetSendBuffPtr(); - - MemReader Reader = new MemReader(Context.Memory, Position); - - long Offset = Reader.ReadInt64(); - - Context.Ns.Gpu.MemoryMgr.Unmap(Offset, 0x10000); - - return 0; - } - - private long NvGpuAsIoctlMapBufferEx(ServiceCtx Context) - { - long Position = Context.Request.GetSendBuffPtr(); - - MemReader Reader = new MemReader(Context.Memory, Position); - - int Flags = Reader.ReadInt32(); - int Kind = Reader.ReadInt32(); - int Handle = Reader.ReadInt32(); - int PageSize = Reader.ReadInt32(); - long BuffAddr = Reader.ReadInt64(); - long MapSize = Reader.ReadInt64(); - long Offset = Reader.ReadInt64(); - - if (Handle == 0) + if (CmdOut(Cmd) && Context.Request.GetBufferType0x22Position() == 0) { - //This is used to store offsets for the Framebuffer(s); - NvMapFb MapFb = (NvMapFb)NvMapsFb.GetData(Context.Process, 0); + Context.Ns.Log.PrintError(LogClass.ServiceNv, "Output buffer is null!"); - MapFb.AddBufferOffset(BuffAddr); - - return 0; + return NvResult.InvalidInput; } - NvMap Map = NvMaps.GetData(Context.Process, Handle); - - if (Map == null) - { - Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"invalid NvMap Handle {Handle}!"); - - return -1; //TODO: Corrent error code. - } - - if ((Flags & 1) != 0) - { - Offset = Context.Ns.Gpu.MapMemory(Map.CpuAddress, Offset, Map.Size); - } - else - { - Offset = Context.Ns.Gpu.MapMemory(Map.CpuAddress, Map.Size); - } - - Context.Memory.WriteInt64(Position + 0x20, Offset); - - Map.GpuAddress = Offset; - - return 0; + return Processor(Context, Cmd); } - private long NvGpuAsIoctlGetVaRegions(ServiceCtx Context) + private static bool CmdIn(int Cmd) { - long Position = Context.Request.GetSendBuffPtr(); - - MemReader Reader = new MemReader(Context.Memory, Position); - MemWriter Writer = new MemWriter(Context.Memory, Position); - - long Unused = Reader.ReadInt64(); - int BuffSize = Reader.ReadInt32(); - int Padding = Reader.ReadInt32(); - - BuffSize = 0x30; - - Writer.WriteInt64(Unused); - Writer.WriteInt32(BuffSize); - Writer.WriteInt32(Padding); - - Writer.WriteInt64(0); - Writer.WriteInt32(0); - Writer.WriteInt32(0); - Writer.WriteInt64(0); - - Writer.WriteInt64(0); - Writer.WriteInt32(0); - Writer.WriteInt32(0); - Writer.WriteInt64(0); - - return 0; + return ((Cmd >> 30) & 1) != 0; } - private long NvGpuAsIoctlInitializeEx(ServiceCtx Context) + private static bool CmdOut(int Cmd) { - long Position = Context.Request.GetSendBuffPtr(); - - MemReader Reader = new MemReader(Context.Memory, Position); - - int BigPageSize = Reader.ReadInt32(); - int AsFd = Reader.ReadInt32(); - int Flags = Reader.ReadInt32(); - int Reserved = Reader.ReadInt32(); - long Unknown10 = Reader.ReadInt64(); - long Unknown18 = Reader.ReadInt64(); - long Unknown20 = Reader.ReadInt64(); - - return 0; + return ((Cmd >> 31) & 1) != 0; } - private long NvGpuAsIoctlRemap(ServiceCtx Context) + public static void UnloadProcess(Process Process) { - Context.RequestData.BaseStream.Seek(-4, SeekOrigin.Current); + Fds.DeleteProcess(Process); - int Cmd = Context.RequestData.ReadInt32(); + NvGpuASIoctl.UnloadProcess(Process); - int Size = (Cmd >> 16) & 0xff; + NvHostCtrlIoctl.UnloadProcess(Process); - int Count = Size / 0x18; - - long Position = Context.Request.GetSendBuffPtr(); - - MemReader Reader = new MemReader(Context.Memory, Position); - - for (int Index = 0; Index < Count; Index++) - { - int Flags = Reader.ReadInt32(); - int Kind = Reader.ReadInt32(); - int Handle = Reader.ReadInt32(); - int Padding = Reader.ReadInt32(); - int Offset = Reader.ReadInt32(); - int Pages = Reader.ReadInt32(); - - NvMap Map = NvMaps.GetData(Context.Process, Handle); - - if (Map == null) - { - Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"invalid NvMap Handle {Handle}!"); - - return -1; //TODO: Corrent error code. - } - - Context.Ns.Gpu.MapMemory(Map.CpuAddress, - (long)(uint)Offset << 16, - (long)(uint)Pages << 16); - } - - //TODO - - return 0; - } - - private long NvHostIoctlCtrlGetConfig(ServiceCtx Context) - { - long Position = Context.Request.GetSendBuffPtr(); - - MemReader Reader = new MemReader(Context.Memory, Position); - MemWriter Writer = new MemWriter(Context.Memory, Position + 0x82); - - for (int Index = 0; Index < 0x101; Index++) - { - Writer.WriteByte(0); - } - - return 0; - } - - private long NvHostIoctlCtrlEventWait(ServiceCtx Context) - { - long Position = Context.Request.GetSendBuffPtr(); - - MemReader Reader = new MemReader(Context.Memory, Position); - - int SyncPtId = Reader.ReadInt32(); - int Threshold = Reader.ReadInt32(); - int Timeout = Reader.ReadInt32(); - int Value = Reader.ReadInt32(); - - Context.Memory.WriteInt32(Position + 0xc, 0xcafe); - - return 0; - } - - private long NvHostIoctlCtrlEventWaitAsync(ServiceCtx Context) - { - long Position = Context.Request.GetSendBuffPtr(); - - MemReader Reader = new MemReader(Context.Memory, Position); - - int SyncPtId = Reader.ReadInt32(); - int Threshold = Reader.ReadInt32(); - int Timeout = Reader.ReadInt32(); - int Value = Reader.ReadInt32(); - - Context.Memory.WriteInt32(Position + 0xc, 0xcafe); - - return 0; - } - - private long NvHostIoctlCtrlEventRegister(ServiceCtx Context) - { - long Position = Context.Request.GetSendBuffPtr(); - - MemReader Reader = new MemReader(Context.Memory, Position); - - int UserEventId = Reader.ReadInt32(); - - return 0; - } - - private long NvGpuIoctlZcullGetCtxSize(ServiceCtx Context) - { - long Position = Context.Request.GetSendBuffPtr(); - - Context.Memory.WriteInt32(Position, 1); - - return 0; - } - - private long NvGpuIoctlZcullGetInfo(ServiceCtx Context) - { - long Position = Context.Request.GetSendBuffPtr(); - - MemWriter Writer = new MemWriter(Context.Memory, Position); - - Writer.WriteInt32(0); - Writer.WriteInt32(0); - Writer.WriteInt32(0); - Writer.WriteInt32(0); - Writer.WriteInt32(0); - Writer.WriteInt32(0); - Writer.WriteInt32(0); - Writer.WriteInt32(0); - Writer.WriteInt32(0); - Writer.WriteInt32(0); - - return 0; - } - - private long NvGpuIoctlZbcSetTable(ServiceCtx Context) - { - long Position = Context.Request.GetSendBuffPtr(); - - MemReader Reader = new MemReader(Context.Memory, Position); - - int[] ColorDs = new int[4]; - int[] ColorL2 = new int[4]; - - ColorDs[0] = Reader.ReadInt32(); - ColorDs[1] = Reader.ReadInt32(); - ColorDs[2] = Reader.ReadInt32(); - ColorDs[3] = Reader.ReadInt32(); - - ColorL2[0] = Reader.ReadInt32(); - ColorL2[1] = Reader.ReadInt32(); - ColorL2[2] = Reader.ReadInt32(); - ColorL2[3] = Reader.ReadInt32(); - - int Depth = Reader.ReadInt32(); - int Format = Reader.ReadInt32(); - int Type = Reader.ReadInt32(); - - return 0; - } - - private long NvGpuIoctlGetCharacteristics(ServiceCtx Context) - { - long Position = Context.Request.GetSendBuffPtr(); - - MemReader Reader = new MemReader(Context.Memory, Position); - MemWriter Writer = new MemWriter(Context.Memory, Position); - - //Note: We should just ignore the BuffAddr, because official code - //does __memcpy_device from Position + 0x10 to BuffAddr. - long BuffSize = Reader.ReadInt64(); - long BuffAddr = Reader.ReadInt64(); - - BuffSize = 0xa0; - - Writer.WriteInt64(BuffSize); - Writer.WriteInt64(BuffAddr); - Writer.WriteInt32(0x120); //NVGPU_GPU_ARCH_GM200 - Writer.WriteInt32(0xb); //NVGPU_GPU_IMPL_GM20B - Writer.WriteInt32(0xa1); - Writer.WriteInt32(1); - Writer.WriteInt64(0x40000); - Writer.WriteInt64(0); - Writer.WriteInt32(2); - Writer.WriteInt32(0x20); //NVGPU_GPU_BUS_TYPE_AXI - Writer.WriteInt32(0x20000); - Writer.WriteInt32(0x20000); - Writer.WriteInt32(0x1b); - Writer.WriteInt32(0x30000); - Writer.WriteInt32(1); - Writer.WriteInt32(0x503); - Writer.WriteInt32(0x503); - Writer.WriteInt32(0x80); - Writer.WriteInt32(0x28); - Writer.WriteInt32(0); - Writer.WriteInt64(0x55); - Writer.WriteInt32(0x902d); //FERMI_TWOD_A - Writer.WriteInt32(0xb197); //MAXWELL_B - Writer.WriteInt32(0xb1c0); //MAXWELL_COMPUTE_B - Writer.WriteInt32(0xb06f); //MAXWELL_CHANNEL_GPFIFO_A - Writer.WriteInt32(0xa140); //KEPLER_INLINE_TO_MEMORY_B - Writer.WriteInt32(0xb0b5); //MAXWELL_DMA_COPY_A - Writer.WriteInt32(1); - Writer.WriteInt32(0); - Writer.WriteInt32(2); - Writer.WriteInt32(1); - Writer.WriteInt32(0); - Writer.WriteInt32(1); - Writer.WriteInt32(0x21d70); - Writer.WriteInt32(0); - Writer.WriteByte((byte)'g'); - Writer.WriteByte((byte)'m'); - Writer.WriteByte((byte)'2'); - Writer.WriteByte((byte)'0'); - Writer.WriteByte((byte)'b'); - Writer.WriteByte((byte)'\0'); - Writer.WriteByte((byte)'\0'); - Writer.WriteByte((byte)'\0'); - Writer.WriteInt64(0); - - return 0; - } - - private long NvGpuIoctlGetTpcMasks(ServiceCtx Context) - { - long Position = Context.Request.GetSendBuffPtr(); - - MemReader Reader = new MemReader(Context.Memory, Position); - - int MaskBuffSize = Reader.ReadInt32(); - int Reserved = Reader.ReadInt32(); - long MaskBuffAddr = Reader.ReadInt64(); - long Unknown = Reader.ReadInt64(); - - return 0; - } - - private long NvGpuIoctlZbcGetActiveSlotMask(ServiceCtx Context) - { - long Position = Context.Request.GetSendBuffPtr(); - - Context.Memory.WriteInt32(Position + 0, 7); - Context.Memory.WriteInt32(Position + 4, 1); - - return 0; - } - - private long NvMapIoctlChannelSetUserData(ServiceCtx Context) - { - long Position = Context.Request.GetSendBuffPtr(); - - return 0; - } - - private long NvMapIoctlChannelSetNvMap(ServiceCtx Context) - { - long Position = Context.Request.GetSendBuffPtr(); - - int Fd = Context.Memory.ReadInt32(Position); - - return 0; - } - - private long NvMapIoctlChannelSubmitGpFifo(ServiceCtx Context) - { - long Position = Context.Request.GetSendBuffPtr(); - - MemReader Reader = new MemReader(Context.Memory, Position); - MemWriter Writer = new MemWriter(Context.Memory, Position + 0x10); - - long GpFifo = Reader.ReadInt64(); - int Count = Reader.ReadInt32(); - int Flags = Reader.ReadInt32(); - int FenceId = Reader.ReadInt32(); - int FenceVal = Reader.ReadInt32(); - - for (int Index = 0; Index < Count; Index++) - { - long GpFifoHdr = Reader.ReadInt64(); - - long GpuAddr = GpFifoHdr & 0xffffffffff; - - int Size = (int)(GpFifoHdr >> 40) & 0x7ffffc; - - long CpuAddr = Context.Ns.Gpu.GetCpuAddr(GpuAddr); - - if (CpuAddr != -1) - { - byte[] Data = AMemoryHelper.ReadBytes(Context.Memory, CpuAddr, Size); - - NsGpuPBEntry[] PushBuffer = NvGpuPushBuffer.Decode(Data); - - Context.Ns.Gpu.Fifo.PushBuffer(Context.Memory, PushBuffer); - } - } - - Writer.WriteInt32(0); - Writer.WriteInt32(0); - - return 0; - } - - private long NvMapIoctlChannelAllocObjCtx(ServiceCtx Context) - { - long Position = Context.Request.GetSendBuffPtr(); - - int ClassNum = Context.Memory.ReadInt32(Position + 0); - int Flags = Context.Memory.ReadInt32(Position + 4); - - Context.Memory.WriteInt32(Position + 8, 0); - - return 0; - } - - private long NvMapIoctlChannelZcullBind(ServiceCtx Context) - { - long Position = Context.Request.GetSendBuffPtr(); - - MemReader Reader = new MemReader(Context.Memory, Position); - - long GpuVa = Reader.ReadInt64(); - int Mode = Reader.ReadInt32(); - int Padding = Reader.ReadInt32(); - - return 0; - } - - private long NvMapIoctlChannelSetErrorNotifier(ServiceCtx Context) - { - long Position = Context.Request.GetSendBuffPtr(); - - MemReader Reader = new MemReader(Context.Memory, Position); - - long Offset = Reader.ReadInt64(); - long Size = Reader.ReadInt64(); - int Mem = Reader.ReadInt32(); - int Padding = Reader.ReadInt32(); - - return 0; - } - - private long NvMapIoctlChannelSetPriority(ServiceCtx Context) - { - long Position = Context.Request.GetSendBuffPtr(); - - int Priority = Context.Memory.ReadInt32(Position); - - return 0; - } - - private long NvMapIoctlChannelAllocGpFifoEx2(ServiceCtx Context) - { - long Position = Context.Request.GetSendBuffPtr(); - - MemReader Reader = new MemReader(Context.Memory, Position); - MemWriter Writer = new MemWriter(Context.Memory, Position + 0xc); - - int Count = Reader.ReadInt32(); - int Flags = Reader.ReadInt32(); - int Unknown8 = Reader.ReadInt32(); - long Fence = Reader.ReadInt64(); - int Unknown14 = Reader.ReadInt32(); - int Unknown18 = Reader.ReadInt32(); - - Writer.WriteInt32(0); - Writer.WriteInt32(0); - - return 0; - } - - private long NvMapIocCreate(ServiceCtx Context) - { - long Position = Context.Request.GetSendBuffPtr(); - - int Size = Context.Memory.ReadInt32(Position); - - NvMap Map = new NvMap() { Size = Size }; - - Map.Handle = NvMaps.Add(Context.Process, Map); - - Map.Id = NvMapsById.Add(Context.Process, Map); - - Context.Memory.WriteInt32(Position + 4, Map.Handle); - - Context.Ns.Log.PrintInfo(LogClass.ServiceNv, $"NvMap {Map.Id} created with size {Size:x8}!"); - - return 0; - } - - private long NvMapIocFromId(ServiceCtx Context) - { - long Position = Context.Request.GetSendBuffPtr(); - - int Id = Context.Memory.ReadInt32(Position); - - NvMap Map = NvMapsById.GetData(Context.Process, Id); - - if (Map == null) - { - Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Invalid NvMap Id {Id}!"); - - return -1; //TODO: Corrent error code. - } - - Context.Memory.WriteInt32(Position + 4, Map.Handle); - - return 0; - } - - private long NvMapIocAlloc(ServiceCtx Context) - { - long Position = Context.Request.GetSendBuffPtr(); - - MemReader Reader = new MemReader(Context.Memory, Position); - - int Handle = Reader.ReadInt32(); - int HeapMask = Reader.ReadInt32(); - int Flags = Reader.ReadInt32(); - int Align = Reader.ReadInt32(); - byte Kind = (byte)Reader.ReadInt64(); - long Addr = Reader.ReadInt64(); - - NvMap Map = NvMaps.GetData(Context.Process, Handle); - - if (Map == null) - { - Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Invalid NvMap Handle {Handle}!"); - - return -1; //TODO: Corrent error code. - } - - Map.CpuAddress = Addr; - Map.Align = Align; - Map.Kind = Kind; - - return 0; - } - - private long NvMapIocFree(ServiceCtx Context) - { - long Position = Context.Request.GetSendBuffPtr(); - - MemReader Reader = new MemReader(Context.Memory, Position); - MemWriter Writer = new MemWriter(Context.Memory, Position + 8); - - int Handle = Reader.ReadInt32(); - int Padding = Reader.ReadInt32(); - - NvMap Map = NvMaps.GetData(Context.Process, Handle); - - if (Map == null) - { - Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Invalid NvMap Handle {Handle}!"); - - return -1; //TODO: Corrent error code. - } - - Writer.WriteInt64(0); - Writer.WriteInt32(Map.Size); - Writer.WriteInt32(0); - - return 0; - } - - private long NvMapIocParam(ServiceCtx Context) - { - long Position = Context.Request.GetSendBuffPtr(); - - MemReader Reader = new MemReader(Context.Memory, Position); - - int Handle = Reader.ReadInt32(); - int Param = Reader.ReadInt32(); - - NvMap Map = NvMaps.GetData(Context.Process, Handle); - - if (Map == null) - { - Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Invalid NvMap Handle {Handle}!"); - - return -1; //TODO: Corrent error code. - } - - int Response = 0; - - switch (Param) - { - case 1: Response = Map.Size; break; - case 2: Response = Map.Align; break; - case 4: Response = 0x40000000; break; - case 5: Response = Map.Kind; break; - } - - Context.Memory.WriteInt32(Position + 8, Response); - - return 0; - } - - private long NvMapIocGetId(ServiceCtx Context) - { - long Position = Context.Request.GetSendBuffPtr(); - - int Handle = Context.Memory.ReadInt32(Position + 4); - - NvMap Map = NvMaps.GetData(Context.Process, Handle); - - if (Map == null) - { - Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Invalid NvMap Handle {Handle}!"); - - return -1; //TODO: Corrent error code. - } - - Context.Memory.WriteInt32(Position, Map.Id); - - return 0; + NvMapIoctl.UnloadProcess(Process); } public void Dispose() diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvChNvMap.cs b/Ryujinx.Core/OsHle/Services/Nv/NvChNvMap.cs deleted file mode 100644 index 9ea3ae6e..00000000 --- a/Ryujinx.Core/OsHle/Services/Nv/NvChNvMap.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Collections.Concurrent; - -namespace Ryujinx.Core.OsHle.Services.Nv -{ - class NvChNvMap - { - private static ConcurrentDictionary NvMaps; - - public void Create(ServiceCtx Context) - { - long InputPosition = Context.Request.GetBufferType0x21Position(); - long OutputPosition = Context.Request.GetBufferType0x22Position(); - - int Size = Context.Memory.ReadInt32(InputPosition); - - int Handle = AddNvMap(Context, new NvMap(Size)); - - Context.Memory.WriteInt32(OutputPosition, Handle); - } - - private int AddNvMap(ServiceCtx Context, NvMap Map) - { - return NvMaps[Context.Process].Add(Map); - } - - public NvMap GetNvMap(ServiceCtx Context, int Handle) - { - return NvMaps[Context.Process].GetData(Handle); - } - } -} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvGpuAS/NvGpuASAllocSpace.cs b/Ryujinx.Core/OsHle/Services/Nv/NvGpuAS/NvGpuASAllocSpace.cs new file mode 100644 index 00000000..7e652d1f --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvGpuAS/NvGpuASAllocSpace.cs @@ -0,0 +1,11 @@ +namespace Ryujinx.Core.OsHle.Services.Nv.NvGpuAS +{ + struct NvGpuASAllocSpace + { + public int Pages; + public int PageSize; + public int Flags; + public int Padding; + public long Offset; + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvGpuAS/NvGpuASIoctl.cs b/Ryujinx.Core/OsHle/Services/Nv/NvGpuAS/NvGpuASIoctl.cs new file mode 100644 index 00000000..6be45d5c --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvGpuAS/NvGpuASIoctl.cs @@ -0,0 +1,245 @@ +using ChocolArm64.Memory; +using Ryujinx.Core.Gpu; +using Ryujinx.Core.Logging; +using Ryujinx.Core.OsHle.Services.Nv.NvMap; +using System; +using System.Collections.Concurrent; + +namespace Ryujinx.Core.OsHle.Services.Nv.NvGpuAS +{ + class NvGpuASIoctl + { + private const int FlagFixedOffset = 1; + + private static ConcurrentDictionary Vmms; + + static NvGpuASIoctl() + { + Vmms = new ConcurrentDictionary(); + } + + public static int ProcessIoctl(ServiceCtx Context, int Cmd) + { + switch (Cmd & 0xffff) + { + case 0x4101: return BindChannel (Context); + case 0x4102: return AllocSpace (Context); + case 0x4103: return FreeSpace (Context); + case 0x4105: return UnmapBuffer (Context); + case 0x4106: return MapBufferEx (Context); + case 0x4108: return GetVaRegions(Context); + case 0x4109: return InitializeEx(Context); + case 0x4114: return Remap (Context); + } + + throw new NotImplementedException(Cmd.ToString("x8")); + } + + private static int BindChannel(ServiceCtx Context) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + Context.Ns.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + + return NvResult.Success; + } + + private static int AllocSpace(ServiceCtx Context) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + NvGpuASAllocSpace Args = AMemoryHelper.Read(Context.Memory, InputPosition); + + NvGpuVmm Vmm = GetVmm(Context); + + ulong Size = (ulong)Args.Pages * + (ulong)Args.PageSize; + + if ((Args.Flags & FlagFixedOffset) != 0) + { + Args.Offset = Vmm.Reserve(Args.Offset, (long)Size, 1); + } + else + { + Args.Offset = Vmm.Reserve((long)Size, 1); + } + + int Result = NvResult.Success; + + if (Args.Offset < 0) + { + Args.Offset = 0; + + Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"No memory to allocate size {Size:x16}!"); + + Result = NvResult.OutOfMemory; + } + + AMemoryHelper.Write(Context.Memory, OutputPosition, Args); + + return Result; + } + + private static int FreeSpace(ServiceCtx Context) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + NvGpuASAllocSpace Args = AMemoryHelper.Read(Context.Memory, InputPosition); + + NvGpuVmm Vmm = GetVmm(Context); + + ulong Size = (ulong)Args.Pages * + (ulong)Args.PageSize; + + Vmm.Free(Args.Offset, (long)Size); + + return NvResult.Success; + } + + private static int UnmapBuffer(ServiceCtx Context) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + NvGpuASUnmapBuffer Args = AMemoryHelper.Read(Context.Memory, InputPosition); + + NvGpuVmm Vmm = GetVmm(Context); + + if (!Vmm.Unmap(Args.Offset)) + { + Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Invalid buffer offset {Args.Offset:x16}!"); + } + + return NvResult.Success; + } + + private static int MapBufferEx(ServiceCtx Context) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + NvGpuASMapBufferEx Args = AMemoryHelper.Read(Context.Memory, InputPosition); + + NvGpuVmm Vmm = GetVmm(Context); + + NvMapHandle Map = NvMapIoctl.GetNvMapWithFb(Context, Args.NvMapHandle); + + if (Map == null) + { + Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Invalid NvMap handle 0x{Args.NvMapHandle:x8}!"); + + return NvResult.InvalidInput; + } + + long PA = Map.Address + Args.BufferOffset; + + long Size = Args.MappingSize; + + if (Size == 0) + { + Size = Map.Size; + } + + Size = Map.Size; + + int Result = NvResult.Success; + + //Note: When the fixed offset flag is not set, + //the Offset field holds the alignment size instead. + if ((Args.Flags & FlagFixedOffset) != 0) + { + long MapEnd = Args.Offset + Args.MappingSize; + + if ((ulong)MapEnd <= (ulong)Args.Offset) + { + Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Offset 0x{Args.Offset:x16} and size 0x{Args.MappingSize:x16} results in a overflow!"); + + return NvResult.InvalidInput; + } + + if ((Args.Offset & NvGpuVmm.PageMask) != 0) + { + Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Offset 0x{Args.Offset:x16} is not page aligned!"); + + return NvResult.InvalidInput; + } + + Args.Offset = Vmm.Map(PA, Args.Offset, Size); + } + else + { + Args.Offset = Vmm.Map(PA, Size); + + if (Args.Offset < 0) + { + Args.Offset = 0; + + Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"No memory to map size {Args.MappingSize:x16}!"); + + Result = NvResult.InvalidInput; + } + } + + AMemoryHelper.Write(Context.Memory, OutputPosition, Args); + + return Result; + } + + private static int GetVaRegions(ServiceCtx Context) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + Context.Ns.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + + return NvResult.Success; + } + + private static int InitializeEx(ServiceCtx Context) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + Context.Ns.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + + return NvResult.Success; + } + + private static int Remap(ServiceCtx Context) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + + NvGpuASRemap Args = AMemoryHelper.Read(Context.Memory, InputPosition); + + NvGpuVmm Vmm = GetVmm(Context); + + NvMapHandle Map = NvMapIoctl.GetNvMapWithFb(Context, Args.NvMapHandle); + + if (Map == null) + { + Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Invalid NvMap handle 0x{Args.NvMapHandle:x8}!"); + + return NvResult.InvalidInput; + } + + //FIXME: This is most likely wrong... + Vmm.Map(Map.Address, (long)(uint)Args.Offset << 16, + (long)(uint)Args.Pages << 16); + + return NvResult.Success; + } + + public static NvGpuVmm GetVmm(ServiceCtx Context) + { + return Vmms.GetOrAdd(Context.Process, (Key) => new NvGpuVmm(Context.Memory)); + } + + public static void UnloadProcess(Process Process) + { + Vmms.TryRemove(Process, out _); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvGpuAS/NvGpuASMapBufferEx.cs b/Ryujinx.Core/OsHle/Services/Nv/NvGpuAS/NvGpuASMapBufferEx.cs new file mode 100644 index 00000000..b8bdd706 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvGpuAS/NvGpuASMapBufferEx.cs @@ -0,0 +1,13 @@ +namespace Ryujinx.Core.OsHle.Services.Nv.NvGpuAS +{ + struct NvGpuASMapBufferEx + { + public int Flags; + public int Kind; + public int NvMapHandle; + public int PageSize; + public long BufferOffset; + public long MappingSize; + public long Offset; + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvGpuAS/NvGpuASRemap.cs b/Ryujinx.Core/OsHle/Services/Nv/NvGpuAS/NvGpuASRemap.cs new file mode 100644 index 00000000..363ae687 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvGpuAS/NvGpuASRemap.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Core.OsHle.Services.Nv.NvGpuAS +{ + struct NvGpuASRemap + { + public short Flags; + public short Kind; + public int NvMapHandle; + public int Padding; + public int Offset; + public int Pages; + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvGpuAS/NvGpuASUnmapBuffer.cs b/Ryujinx.Core/OsHle/Services/Nv/NvGpuAS/NvGpuASUnmapBuffer.cs new file mode 100644 index 00000000..8b627511 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvGpuAS/NvGpuASUnmapBuffer.cs @@ -0,0 +1,7 @@ +namespace Ryujinx.Core.OsHle.Services.Nv.NvGpuAS +{ + struct NvGpuASUnmapBuffer + { + public long Offset; + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvGpuGpu/NvGpuGpuGetCharacteristics.cs b/Ryujinx.Core/OsHle/Services/Nv/NvGpuGpu/NvGpuGpuGetCharacteristics.cs new file mode 100644 index 00000000..5d92b508 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvGpuGpu/NvGpuGpuGetCharacteristics.cs @@ -0,0 +1,43 @@ +namespace Ryujinx.Core.OsHle.Services.Nv.NvGpuGpu +{ + struct NvGpuGpuGetCharacteristics + { + public long BufferSize; + public long BufferAddress; + public int Arch; + public int Impl; + public int Rev; + public int NumGpc; + public long L2CacheSize; + public long OnBoardVideoMemorySize; + public int NumTpcPerGpc; + public int BusType; + public int BigPageSize; + public int CompressionPageSize; + public int PdeCoverageBitCount; + public int AvailableBigPageSizes; + public int GpcMask; + public int SmArchSmVersion; + public int SmArchSpaVersion; + public int SmArchWarpCount; + public int GpuVaBitCount; + public int Reserved; + public long Flags; + public int TwodClass; + public int ThreedClass; + public int ComputeClass; + public int GpfifoClass; + public int InlineToMemoryClass; + public int DmaCopyClass; + public int MaxFbpsCount; + public int FbpEnMask; + public int MaxLtcPerFbp; + public int MaxLtsPerLtc; + public int MaxTexPerTpc; + public int MaxGpcCount; + public int RopL2EnMask0; + public int RopL2EnMask1; + public long ChipName; + public long GrCompbitStoreBaseHw; + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvGpuGpu/NvGpuGpuIoctl.cs b/Ryujinx.Core/OsHle/Services/Nv/NvGpuGpu/NvGpuGpuIoctl.cs new file mode 100644 index 00000000..772b6786 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvGpuGpu/NvGpuGpuIoctl.cs @@ -0,0 +1,155 @@ +using ChocolArm64.Memory; +using Ryujinx.Core.Logging; +using System; +using System.Diagnostics; + +namespace Ryujinx.Core.OsHle.Services.Nv.NvGpuGpu +{ + class NvGpuGpuIoctl + { + private static Stopwatch PTimer; + + private static double TicksToNs; + + static NvGpuGpuIoctl() + { + 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) + { + case 0x4701: return ZcullGetCtxSize (Context); + case 0x4702: return ZcullGetInfo (Context); + case 0x4703: return ZbcSetTable (Context); + 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")); + } + + private static int ZcullGetCtxSize(ServiceCtx Context) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + Context.Ns.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + + return NvResult.Success; + } + + private static int ZcullGetInfo(ServiceCtx Context) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + Context.Ns.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + + return NvResult.Success; + } + + private static int ZbcSetTable(ServiceCtx Context) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + Context.Ns.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + + return NvResult.Success; + } + + private static int GetCharacteristics(ServiceCtx Context) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + NvGpuGpuGetCharacteristics Args = AMemoryHelper.Read(Context.Memory, InputPosition); + + Args.BufferSize = 0xa0; + + Args.Arch = 0x120; + Args.Impl = 0xb; + Args.Rev = 0xa1; + Args.NumGpc = 0x1; + Args.L2CacheSize = 0x40000; + Args.OnBoardVideoMemorySize = 0x0; + Args.NumTpcPerGpc = 0x2; + Args.BusType = 0x20; + Args.BigPageSize = 0x20000; + Args.CompressionPageSize = 0x20000; + Args.PdeCoverageBitCount = 0x1b; + Args.AvailableBigPageSizes = 0x30000; + Args.GpcMask = 0x1; + Args.SmArchSmVersion = 0x503; + Args.SmArchSpaVersion = 0x503; + Args.SmArchWarpCount = 0x80; + Args.GpuVaBitCount = 0x28; + Args.Reserved = 0x0; + Args.Flags = 0x55; + Args.TwodClass = 0x902d; + Args.ThreedClass = 0xb197; + Args.ComputeClass = 0xb1c0; + Args.GpfifoClass = 0xb06f; + Args.InlineToMemoryClass = 0xa140; + Args.DmaCopyClass = 0xb0b5; + Args.MaxFbpsCount = 0x1; + Args.FbpEnMask = 0x0; + Args.MaxLtcPerFbp = 0x2; + Args.MaxLtsPerLtc = 0x1; + Args.MaxTexPerTpc = 0x0; + Args.MaxGpcCount = 0x1; + Args.RopL2EnMask0 = 0x21d70; + Args.RopL2EnMask1 = 0x0; + Args.ChipName = 0x6230326d67; + Args.GrCompbitStoreBaseHw = 0x0; + + AMemoryHelper.Write(Context.Memory, OutputPosition, Args); + + return NvResult.Success; + } + + private static int GetTpcMasks(ServiceCtx Context) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + Context.Ns.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + + return NvResult.Success; + } + + private static int GetActiveSlotMask(ServiceCtx Context) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + Context.Ns.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + + 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/Nv/NvHelper.cs b/Ryujinx.Core/OsHle/Services/Nv/NvHelper.cs new file mode 100644 index 00000000..c5cee361 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvHelper.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.Core.OsHle.Services.Nv +{ + static class NvHelper + { + public static void Crash() + { + + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs b/Ryujinx.Core/OsHle/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs new file mode 100644 index 00000000..85b47533 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs @@ -0,0 +1,130 @@ +using ChocolArm64.Memory; +using Ryujinx.Core.Logging; +using Ryujinx.Core.OsHle.Services.Nv.NvGpuAS; +using Ryujinx.Core.Gpu; +using System; + +namespace Ryujinx.Core.OsHle.Services.Nv.NvHostChannel +{ + class NvHostChannelIoctl + { + public static int ProcessIoctl(ServiceCtx Context, int Cmd) + { + switch (Cmd & 0xffff) + { + case 0x4714: return SetUserData (Context); + case 0x4801: return SetNvMap (Context); + case 0x4808: return SubmitGpfifo (Context); + case 0x4809: return AllocObjCtx (Context); + case 0x480b: return ZcullBind (Context); + case 0x480c: return SetErrorNotifier(Context); + case 0x480d: return SetPriority (Context); + case 0x481a: return AllocGpfifoEx2 (Context); + } + + throw new NotImplementedException(Cmd.ToString("x8")); + } + + private static int SetUserData(ServiceCtx Context) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + Context.Ns.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + + return NvResult.Success; + } + + private static int SetNvMap(ServiceCtx Context) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + Context.Ns.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + + return NvResult.Success; + } + + private static int SubmitGpfifo(ServiceCtx Context) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + NvHostChannelSubmitGpfifo Args = AMemoryHelper.Read(Context.Memory, InputPosition); + + NvGpuVmm Vmm = NvGpuASIoctl.GetVmm(Context); + + for (int Index = 0; Index < Args.NumEntries; Index++) + { + long Gpfifo = Context.Memory.ReadInt64(InputPosition + 0x18 + Index * 8); + + long VA = Gpfifo & 0xff_ffff_ffff; + + int Size = (int)(Gpfifo >> 40) & 0x7ffffc; + + byte[] Data = Vmm.ReadBytes(VA, Size); + + NvGpuPBEntry[] PushBuffer = NvGpuPushBuffer.Decode(Data); + + Context.Ns.Gpu.Fifo.PushBuffer(Vmm, PushBuffer); + } + + Args.SyncptId = 0; + Args.SyncptValue = 0; + + AMemoryHelper.Write(Context.Memory, OutputPosition, Args); + + return NvResult.Success; + } + + private static int AllocObjCtx(ServiceCtx Context) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + Context.Ns.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + + return NvResult.Success; + } + + private static int ZcullBind(ServiceCtx Context) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + Context.Ns.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + + return NvResult.Success; + } + + private static int SetErrorNotifier(ServiceCtx Context) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + Context.Ns.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + + return NvResult.Success; + } + + private static int SetPriority(ServiceCtx Context) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + Context.Ns.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + + return NvResult.Success; + } + + private static int AllocGpfifoEx2(ServiceCtx Context) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + Context.Ns.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + + return NvResult.Success; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvHostChannel/NvHostChannelSubmitGpfifo.cs b/Ryujinx.Core/OsHle/Services/Nv/NvHostChannel/NvHostChannelSubmitGpfifo.cs new file mode 100644 index 00000000..4698a3da --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvHostChannel/NvHostChannelSubmitGpfifo.cs @@ -0,0 +1,11 @@ +namespace Ryujinx.Core.OsHle.Services.Nv.NvHostChannel +{ + struct NvHostChannelSubmitGpfifo + { + public long Gpfifo; + public int NumEntries; + public int Flags; + public int SyncptId; + public int SyncptValue; + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlIoctl.cs b/Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlIoctl.cs new file mode 100644 index 00000000..8c705d7f --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlIoctl.cs @@ -0,0 +1,355 @@ +using ChocolArm64.Memory; +using Ryujinx.Core.Logging; +using System; +using System.Collections.Concurrent; +using System.Threading; + +namespace Ryujinx.Core.OsHle.Services.Nv.NvHostCtrl +{ + class NvHostCtrlIoctl + { + private static ConcurrentDictionary UserCtxs; + + static NvHostCtrlIoctl() + { + UserCtxs = new ConcurrentDictionary(); + } + + public static int ProcessIoctl(ServiceCtx Context, int Cmd) + { + switch (Cmd & 0xffff) + { + case 0x0014: return SyncptRead (Context); + case 0x0015: return SyncptIncr (Context); + case 0x0016: return SyncptWait (Context); + case 0x0019: return SyncptWaitEx (Context); + case 0x001a: return SyncptReadMax (Context); + case 0x001b: return GetConfig (Context); + case 0x001d: return EventWait (Context); + case 0x001e: return EventWaitAsync(Context); + case 0x001f: return EventRegister (Context); + } + + throw new NotImplementedException(Cmd.ToString("x8")); + } + + private static int SyncptRead(ServiceCtx Context) + { + return SyncptReadMinOrMax(Context, Max: false); + } + + private static int SyncptIncr(ServiceCtx Context) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + + int Id = Context.Memory.ReadInt32(InputPosition); + + if ((uint)Id >= NvHostSyncpt.SyncptsCount) + { + return NvResult.InvalidInput; + } + + GetUserCtx(Context).Syncpt.Increment(Id); + + return NvResult.Success; + } + + private static int SyncptWait(ServiceCtx Context) + { + return SyncptWait(Context, Extended: false); + } + + private static int SyncptWaitEx(ServiceCtx Context) + { + return SyncptWait(Context, Extended: true); + } + + private static int SyncptReadMax(ServiceCtx Context) + { + return SyncptReadMinOrMax(Context, Max: true); + } + + private static int GetConfig(ServiceCtx Context) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + string Nv = AMemoryHelper.ReadAsciiString(Context.Memory, InputPosition + 0, 0x41); + string Name = AMemoryHelper.ReadAsciiString(Context.Memory, InputPosition + 0x41, 0x41); + + Context.Memory.WriteByte(OutputPosition + 0x82, 0); + + Context.Ns.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + + return NvResult.Success; + } + + private static int EventWait(ServiceCtx Context) + { + return EventWait(Context, Async: false); + } + + private static int EventWaitAsync(ServiceCtx Context) + { + return EventWait(Context, Async: true); + } + + private static int EventRegister(ServiceCtx Context) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + int EventId = Context.Memory.ReadInt32(InputPosition); + + Context.Ns.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + + return NvResult.Success; + } + + private static int SyncptReadMinOrMax(ServiceCtx Context, bool Max) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + NvHostCtrlSyncptRead Args = AMemoryHelper.Read(Context.Memory, InputPosition); + + if ((uint)Args.Id >= NvHostSyncpt.SyncptsCount) + { + return NvResult.InvalidInput; + } + + if (Max) + { + Args.Value = GetUserCtx(Context).Syncpt.GetMax(Args.Id); + } + else + { + Args.Value = GetUserCtx(Context).Syncpt.GetMin(Args.Id); + } + + AMemoryHelper.Write(Context.Memory, OutputPosition, Args); + + return NvResult.Success; + } + + private static int SyncptWait(ServiceCtx Context, bool Extended) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + NvHostCtrlSyncptWait Args = AMemoryHelper.Read(Context.Memory, InputPosition); + + NvHostSyncpt Syncpt = GetUserCtx(Context).Syncpt; + + if ((uint)Args.Id >= NvHostSyncpt.SyncptsCount) + { + return NvResult.InvalidInput; + } + + int Result; + + if (Syncpt.MinCompare(Args.Id, Args.Thresh)) + { + Result = NvResult.Success; + } + else if (Args.Timeout == 0) + { + Result = NvResult.TryAgain; + } + else + { + Context.Ns.Log.PrintDebug(LogClass.ServiceNv, "Waiting syncpt with timeout of " + Args.Timeout + "ms..."); + + using (ManualResetEvent WaitEvent = new ManualResetEvent(false)) + { + Syncpt.AddWaiter(Args.Thresh, WaitEvent); + + //Note: Negative (> INT_MAX) timeouts aren't valid on .NET, + //in this case we just use the maximum timeout possible. + int Timeout = Args.Timeout; + + if (Timeout < -1) + { + Timeout = int.MaxValue; + } + + if (Timeout == -1) + { + WaitEvent.WaitOne(); + + Result = NvResult.Success; + } + else if (WaitEvent.WaitOne(Timeout)) + { + Result = NvResult.Success; + } + else + { + Result = NvResult.TimedOut; + } + } + + Context.Ns.Log.PrintDebug(LogClass.ServiceNv, "Resuming..."); + } + + if (Extended) + { + Context.Memory.WriteInt32(OutputPosition + 0xc, Syncpt.GetMin(Args.Id)); + } + + return Result; + } + + private static int EventWait(ServiceCtx Context, bool Async) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + NvHostCtrlSyncptWaitEx Args = AMemoryHelper.Read(Context.Memory, InputPosition); + + if ((uint)Args.Id >= NvHostSyncpt.SyncptsCount) + { + return NvResult.InvalidInput; + } + + void WriteArgs() + { + AMemoryHelper.Write(Context.Memory, OutputPosition, Args); + } + + NvHostSyncpt Syncpt = GetUserCtx(Context).Syncpt; + + if (Syncpt.MinCompare(Args.Id, Args.Thresh)) + { + Args.Value = Syncpt.GetMin(Args.Id); + + WriteArgs(); + + return NvResult.Success; + } + + if (!Async) + { + Args.Value = 0; + } + + if (Args.Timeout == 0) + { + WriteArgs(); + + return NvResult.TryAgain; + } + + NvHostEvent Event; + + int Result, EventIndex; + + if (Async) + { + EventIndex = Args.Value; + + if ((uint)EventIndex >= NvHostCtrlUserCtx.EventsCount) + { + return NvResult.InvalidInput; + } + + Event = GetUserCtx(Context).Events[EventIndex]; + } + else + { + Event = GetFreeEvent(Context, Syncpt, Args.Id, out EventIndex); + } + + if (Event != null && + (Event.State == NvHostEventState.Registered || + Event.State == NvHostEventState.Free)) + { + Event.Id = Args.Id; + Event.Thresh = Args.Thresh; + + Event.State = NvHostEventState.Waiting; + + if (!Async) + { + Args.Value = ((Args.Id & 0xfff) << 16) | 0x10000000; + } + else + { + Args.Value = Args.Id << 4; + } + + Args.Value |= EventIndex; + + Result = NvResult.TryAgain; + } + else + { + Result = NvResult.InvalidInput; + } + + WriteArgs(); + + return Result; + } + + private static NvHostEvent GetFreeEvent( + ServiceCtx Context, + NvHostSyncpt Syncpt, + int Id, + out int EventIndex) + { + NvHostEvent[] Events = GetUserCtx(Context).Events; + + EventIndex = NvHostCtrlUserCtx.EventsCount; + + int NullIndex = NvHostCtrlUserCtx.EventsCount; + + for (int Index = 0; Index < NvHostCtrlUserCtx.EventsCount; Index++) + { + NvHostEvent Event = Events[Index]; + + if (Event != null) + { + if (Event.State == NvHostEventState.Registered || + Event.State == NvHostEventState.Free) + { + EventIndex = Index; + + if (Event.Id == Id) + { + return Event; + } + } + } + else if (NullIndex == NvHostCtrlUserCtx.EventsCount) + { + NullIndex = Index; + } + } + + if (NullIndex < NvHostCtrlUserCtx.EventsCount) + { + EventIndex = NullIndex; + + return Events[NullIndex] = new NvHostEvent(); + } + + if (EventIndex < NvHostCtrlUserCtx.EventsCount) + { + return Events[EventIndex]; + } + + return null; + } + + public static NvHostCtrlUserCtx GetUserCtx(ServiceCtx Context) + { + return UserCtxs.GetOrAdd(Context.Process, (Key) => new NvHostCtrlUserCtx()); + } + + public static void UnloadProcess(Process Process) + { + UserCtxs.TryRemove(Process, out _); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlSyncPtRead.cs b/Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlSyncPtRead.cs new file mode 100644 index 00000000..0b05e63a --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlSyncPtRead.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.Core.OsHle.Services.Nv.NvHostCtrl +{ + struct NvHostCtrlSyncptRead + { + public int Id; + public int Value; + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlSyncPtWait.cs b/Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlSyncPtWait.cs new file mode 100644 index 00000000..6746090a --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlSyncPtWait.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.Core.OsHle.Services.Nv.NvHostCtrl +{ + struct NvHostCtrlSyncptWait + { + public int Id; + public int Thresh; + public int Timeout; + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlSyncPtWaitEx.cs b/Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlSyncPtWaitEx.cs new file mode 100644 index 00000000..21ba3783 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlSyncPtWaitEx.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.Core.OsHle.Services.Nv.NvHostCtrl +{ + struct NvHostCtrlSyncptWaitEx + { + public int Id; + public int Thresh; + public int Timeout; + public int Value; + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlUserCtx.cs b/Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlUserCtx.cs new file mode 100644 index 00000000..be71b225 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlUserCtx.cs @@ -0,0 +1,19 @@ +namespace Ryujinx.Core.OsHle.Services.Nv.NvHostCtrl +{ + class NvHostCtrlUserCtx + { + public const int LocksCount = 16; + public const int EventsCount = 64; + + public NvHostSyncpt Syncpt { get; private set; } + + public NvHostEvent[] Events { get; private set; } + + public NvHostCtrlUserCtx() + { + Syncpt = new NvHostSyncpt(); + + Events = new NvHostEvent[EventsCount]; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostEvent.cs b/Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostEvent.cs new file mode 100644 index 00000000..027e25b0 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostEvent.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.Core.OsHle.Services.Nv.NvHostCtrl +{ + class NvHostEvent + { + public int Id; + public int Thresh; + + public NvHostEventState State; + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostEventState.cs b/Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostEventState.cs new file mode 100644 index 00000000..a4bd2cb6 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostEventState.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.Core.OsHle.Services.Nv.NvHostCtrl +{ + enum NvHostEventState + { + Registered = 0, + Waiting = 1, + Busy = 2, + Free = 5 + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostSyncPt.cs b/Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostSyncPt.cs new file mode 100644 index 00000000..96ae16a3 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostSyncPt.cs @@ -0,0 +1,107 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Threading; + +namespace Ryujinx.Core.OsHle.Services.Nv.NvHostCtrl +{ + class NvHostSyncpt + { + public const int SyncptsCount = 192; + + private int[] CounterMin; + private int[] CounterMax; + + private long EventMask; + + private ConcurrentDictionary Waiters; + + public NvHostSyncpt() + { + CounterMin = new int[SyncptsCount]; + CounterMax = new int[SyncptsCount]; + + Waiters = new ConcurrentDictionary(); + } + + public int GetMin(int Id) + { + return CounterMin[Id]; + } + + public int GetMax(int Id) + { + return CounterMax[Id]; + } + + public int Increment(int Id) + { + if (((EventMask >> Id) & 1) != 0) + { + Interlocked.Increment(ref CounterMax[Id]); + } + + return IncrementMin(Id); + } + + public int IncrementMin(int Id) + { + int Value = Interlocked.Increment(ref CounterMin[Id]); + + WakeUpWaiters(Id, Value); + + return Value; + } + + public int IncrementMax(int Id) + { + return Interlocked.Increment(ref CounterMax[Id]); + } + + public void AddWaiter(int Threshold, EventWaitHandle WaitEvent) + { + if (!Waiters.TryAdd(WaitEvent, Threshold)) + { + throw new InvalidOperationException(); + } + } + + public bool RemoveWaiter(EventWaitHandle WaitEvent) + { + return Waiters.TryRemove(WaitEvent, out _); + } + + private void WakeUpWaiters(int Id, int NewValue) + { + foreach (KeyValuePair KV in Waiters) + { + if (MinCompare(Id, NewValue, CounterMax[Id], KV.Value)) + { + KV.Key.Set(); + + Waiters.TryRemove(KV.Key, out _); + } + } + } + + public bool MinCompare(int Id, int Threshold) + { + return MinCompare(Id, CounterMin[Id], CounterMax[Id], Threshold); + } + + private bool MinCompare(int Id, int Min, int Max, int Threshold) + { + int MinDiff = Min - Threshold; + int MaxDiff = Max - Threshold; + + if (((EventMask >> Id) & 1) != 0) + { + return MinDiff >= 0; + } + else + { + return (uint)MaxDiff >= (uint)MinDiff; + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvMap.cs b/Ryujinx.Core/OsHle/Services/Nv/NvMap.cs deleted file mode 100644 index 570cef68..00000000 --- a/Ryujinx.Core/OsHle/Services/Nv/NvMap.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace Ryujinx.Core.OsHle.Services.Nv -{ - class NvMap - { - public int Handle; - public int Id; - public int Size; - public int Align; - public int Kind; - public long CpuAddress; - public long GpuAddress; - - public NvMap() { } - - public NvMap(int Size) - { - this.Size = Size; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapAlloc.cs b/Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapAlloc.cs new file mode 100644 index 00000000..86e4c238 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapAlloc.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Core.OsHle.Services.Nv.NvMap +{ + struct NvMapAlloc + { + public int Handle; + public int HeapMask; + public int Flags; + public int Align; + public long Kind; + public long Address; + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapCreate.cs b/Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapCreate.cs new file mode 100644 index 00000000..7d35731c --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapCreate.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.Core.OsHle.Services.Nv.NvMap +{ + struct NvMapCreate + { + public int Size; + public int Handle; + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapFree.cs b/Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapFree.cs new file mode 100644 index 00000000..ee8bc618 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapFree.cs @@ -0,0 +1,11 @@ +namespace Ryujinx.Core.OsHle.Services.Nv.NvMap +{ + struct NvMapFree + { + public int Handle; + public int Padding; + public long RefCount; + public int Size; + public int Flags; + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapFromId.cs b/Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapFromId.cs new file mode 100644 index 00000000..377eaa7f --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapFromId.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.Core.OsHle.Services.Nv.NvMap +{ + struct NvMapFromId + { + public int Id; + public int Handle; + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapGetId.cs b/Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapGetId.cs new file mode 100644 index 00000000..639a5fb4 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapGetId.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.Core.OsHle.Services.Nv.NvMap +{ + struct NvMapGetId + { + public int Id; + public int Handle; + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapHandle.cs b/Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapHandle.cs new file mode 100644 index 00000000..4a021f6f --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapHandle.cs @@ -0,0 +1,37 @@ +using System.Threading; + +namespace Ryujinx.Core.OsHle.Services.Nv.NvMap +{ + class NvMapHandle + { + public int Handle; + public int Id; + public int Size; + public int Align; + public int Kind; + public long Address; + public bool Allocated; + + private long Dupes; + + public NvMapHandle() + { + Dupes = 1; + } + + public NvMapHandle(int Size) : this() + { + this.Size = Size; + } + + public long IncrementRefCount() + { + return Interlocked.Increment(ref Dupes); + } + + public long DecrementRefCount() + { + return Interlocked.Decrement(ref Dupes); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapHandleParam.cs b/Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapHandleParam.cs new file mode 100644 index 00000000..80ff4c03 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapHandleParam.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Core.OsHle.Services.Nv.NvMap +{ + enum NvMapHandleParam + { + Size = 1, + Align = 2, + Base = 3, + Heap = 4, + Kind = 5, + Compr = 6 + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapIoctl.cs b/Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapIoctl.cs new file mode 100644 index 00000000..f9c1564a --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapIoctl.cs @@ -0,0 +1,302 @@ +using ChocolArm64.Memory; +using Ryujinx.Core.Logging; +using Ryujinx.Core.OsHle.Utilities; +using Ryujinx.Core.Gpu; +using System.Collections.Concurrent; + +namespace Ryujinx.Core.OsHle.Services.Nv.NvMap +{ + class NvMapIoctl + { + private const int FlagNotFreedYet = 1; + + private static ConcurrentDictionary Maps; + + static NvMapIoctl() + { + Maps = new ConcurrentDictionary(); + } + + public static int ProcessIoctl(ServiceCtx Context, int Cmd) + { + switch (Cmd & 0xffff) + { + case 0x0101: return Create(Context); + case 0x0103: return FromId(Context); + case 0x0104: return Alloc (Context); + case 0x0105: return Free (Context); + case 0x0109: return Param (Context); + case 0x010e: return GetId (Context); + } + + Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Unsupported Ioctl command 0x{Cmd:x8}!"); + + return NvResult.NotSupported; + } + + private static int Create(ServiceCtx Context) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + NvMapCreate Args = AMemoryHelper.Read(Context.Memory, InputPosition); + + if (Args.Size == 0) + { + Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Invalid size 0x{Args.Size:x8}!"); + + return NvResult.InvalidInput; + } + + int Size = IntUtils.RoundUp(Args.Size, NvGpuVmm.PageSize); + + Args.Handle = AddNvMap(Context, new NvMapHandle(Size)); + + Context.Ns.Log.PrintInfo(LogClass.ServiceNv, $"Created map {Args.Handle} with size 0x{Size:x8}!"); + + AMemoryHelper.Write(Context.Memory, OutputPosition, Args); + + return NvResult.Success; + } + + private static int FromId(ServiceCtx Context) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + NvMapFromId Args = AMemoryHelper.Read(Context.Memory, InputPosition); + + NvMapHandle Map = GetNvMap(Context, Args.Id); + + if (Map == null) + { + Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Invalid handle 0x{Args.Handle:x8}!"); + + return NvResult.InvalidInput; + } + + Map.IncrementRefCount(); + + Args.Handle = Args.Id; + + AMemoryHelper.Write(Context.Memory, OutputPosition, Args); + + return NvResult.Success; + } + + private static int Alloc(ServiceCtx Context) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + NvMapAlloc Args = AMemoryHelper.Read(Context.Memory, InputPosition); + + NvMapHandle Map = GetNvMap(Context, Args.Handle); + + if (Map == null) + { + Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Invalid handle 0x{Args.Handle:x8}!"); + + return NvResult.InvalidInput; + } + + if ((Args.Align & (Args.Align - 1)) != 0) + { + Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Invalid alignment 0x{Args.Align:x8}!"); + + return NvResult.InvalidInput; + } + + if ((uint)Args.Align < NvGpuVmm.PageSize) + { + Args.Align = NvGpuVmm.PageSize; + } + + int Result = NvResult.Success; + + if (!Map.Allocated) + { + Map.Allocated = true; + + Map.Align = Args.Align; + Map.Kind = (byte)Args.Kind; + + int Size = IntUtils.RoundUp(Map.Size, NvGpuVmm.PageSize); + + long Address = Args.Address; + + if (Address == 0) + { + //When the address is zero, we need to allocate + //our own backing memory for the NvMap. + if (!Context.Ns.Os.Allocator.TryAllocate((uint)Size, out Address)) + { + Result = NvResult.OutOfMemory; + } + } + + if (Result == NvResult.Success) + { + Map.Size = Size; + Map.Address = Address; + } + } + + AMemoryHelper.Write(Context.Memory, OutputPosition, Args); + + return Result; + } + + private static int Free(ServiceCtx Context) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + NvMapFree Args = AMemoryHelper.Read(Context.Memory, InputPosition); + + NvMapHandle Map = GetNvMap(Context, Args.Handle); + + if (Map == null) + { + Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Invalid handle 0x{Args.Handle:x8}!"); + + return NvResult.InvalidInput; + } + + long RefCount = Map.DecrementRefCount(); + + if (RefCount <= 0) + { + DeleteNvMap(Context, Args.Handle); + + Context.Ns.Log.PrintInfo(LogClass.ServiceNv, $"Deleted map {Args.Handle}!"); + + Args.Flags = 0; + } + else + { + Args.Flags = FlagNotFreedYet; + } + + Args.RefCount = RefCount; + Args.Size = Map.Size; + + AMemoryHelper.Write(Context.Memory, OutputPosition, Args); + + return NvResult.Success; + } + + private static int Param(ServiceCtx Context) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + NvMapParam Args = AMemoryHelper.Read(Context.Memory, InputPosition); + + NvMapHandle Map = GetNvMap(Context, Args.Handle); + + if (Map == null) + { + Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Invalid handle 0x{Args.Handle:x8}!"); + + return NvResult.InvalidInput; + } + + switch ((NvMapHandleParam)Args.Param) + { + case NvMapHandleParam.Size: Args.Result = Map.Size; break; + case NvMapHandleParam.Align: Args.Result = Map.Align; break; + case NvMapHandleParam.Heap: Args.Result = 0x40000000; break; + case NvMapHandleParam.Kind: Args.Result = Map.Kind; break; + case NvMapHandleParam.Compr: Args.Result = 0; break; + + //Note: Base is not supported and returns an error. + //Any other value also returns an error. + default: return NvResult.InvalidInput; + } + + AMemoryHelper.Write(Context.Memory, OutputPosition, Args); + + return NvResult.Success; + } + + private static int GetId(ServiceCtx Context) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + NvMapGetId Args = AMemoryHelper.Read(Context.Memory, InputPosition); + + NvMapHandle Map = GetNvMap(Context, Args.Handle); + + if (Map == null) + { + Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Invalid handle 0x{Args.Handle:x8}!"); + + return NvResult.InvalidInput; + } + + Args.Id = Args.Handle; + + AMemoryHelper.Write(Context.Memory, OutputPosition, Args); + + return NvResult.Success; + } + + private static int AddNvMap(ServiceCtx Context, NvMapHandle Map) + { + IdDictionary Dict = Maps.GetOrAdd(Context.Process, (Key) => + { + IdDictionary NewDict = new IdDictionary(); + + NewDict.Add(0, new NvMapHandle()); + + return NewDict; + }); + + return Dict.Add(Map); + } + + private static bool DeleteNvMap(ServiceCtx Context, int Handle) + { + if (Maps.TryGetValue(Context.Process, out IdDictionary Dict)) + { + return Dict.Delete(Handle) != null; + } + + return false; + } + + public static void InitializeNvMap(ServiceCtx Context) + { + IdDictionary Dict = Maps.GetOrAdd(Context.Process, (Key) =>new IdDictionary()); + + Dict.Add(0, new NvMapHandle()); + } + + public static NvMapHandle GetNvMapWithFb(ServiceCtx Context, int Handle) + { + if (Maps.TryGetValue(Context.Process, out IdDictionary Dict)) + { + return Dict.GetData(Handle); + } + + return null; + } + + public static NvMapHandle GetNvMap(ServiceCtx Context, int Handle) + { + if (Handle != 0 && Maps.TryGetValue(Context.Process, out IdDictionary Dict)) + { + return Dict.GetData(Handle); + } + + return null; + } + + public static void UnloadProcess(Process Process) + { + Maps.TryRemove(Process, out _); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapParam.cs b/Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapParam.cs new file mode 100644 index 00000000..196ef6ab --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapParam.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.Core.OsHle.Services.Nv.NvMap +{ + struct NvMapParam + { + public int Handle; + public int Param; + public int Result; + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvMapFb.cs b/Ryujinx.Core/OsHle/Services/Nv/NvMapFb.cs deleted file mode 100644 index d8a47418..00000000 --- a/Ryujinx.Core/OsHle/Services/Nv/NvMapFb.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Ryujinx.Core.OsHle.Services.Nv -{ - class NvMapFb - { - private List BufferOffs; - - public NvMapFb() - { - BufferOffs = new List(); - } - - public void AddBufferOffset(long Offset) - { - BufferOffs.Add(Offset); - } - - public bool HasBufferOffset(int Index) - { - if ((uint)Index >= BufferOffs.Count) - { - return false; - } - - return true; - } - - public long GetBufferOffset(int Index) - { - if ((uint)Index >= BufferOffs.Count) - { - throw new ArgumentOutOfRangeException(nameof(Index)); - } - - return BufferOffs[Index]; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvResult.cs b/Ryujinx.Core/OsHle/Services/Nv/NvResult.cs new file mode 100644 index 00000000..5a419165 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvResult.cs @@ -0,0 +1,13 @@ +namespace Ryujinx.Core.OsHle.Services.Nv +{ + static class NvResult + { + public const int Success = 0; + public const int TryAgain = -11; + public const int OutOfMemory = -12; + public const int InvalidInput = -22; + public const int NotSupported = -25; + public const int Restart = -85; + public const int TimedOut = -110; + } +} \ 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 5aa3c3d5..db2f7fa2 100644 --- a/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs +++ b/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs @@ -1,9 +1,9 @@ using ChocolArm64.Memory; +using Ryujinx.Core.Gpu; using Ryujinx.Core.Logging; using Ryujinx.Core.OsHle.Handles; -using Ryujinx.Core.OsHle.Services.Nv; +using Ryujinx.Core.OsHle.Services.Nv.NvMap; using Ryujinx.Graphics.Gal; -using Ryujinx.Graphics.Gpu; using System; using System.Collections.Generic; using System.IO; @@ -282,20 +282,12 @@ namespace Ryujinx.Core.OsHle.Services.Android int FbWidth = 1280; int FbHeight = 720; - NvMap Map = GetNvMap(Context, Slot); + int NvMapHandle = BitConverter.ToInt32(BufferQueue[Slot].Data.RawData, 0x4c); + int BufferOffset = BitConverter.ToInt32(BufferQueue[Slot].Data.RawData, 0x50); - NvMapFb MapFb = (NvMapFb)INvDrvServices.NvMapsFb.GetData(Context.Process, 0); + NvMapHandle Map = NvMapIoctl.GetNvMap(Context, NvMapHandle);; - long CpuAddr = Map.CpuAddress; - long GpuAddr = Map.GpuAddress; - - if (MapFb.HasBufferOffset(Slot)) - { - CpuAddr += MapFb.GetBufferOffset(Slot); - - //TODO: Enable once the frame buffers problems are fixed. - //GpuAddr += MapFb.GetBufferOffset(Slot); - } + long FbAddr = Map.Address + BufferOffset; BufferQueue[Slot].State = BufferState.Acquired; @@ -352,17 +344,17 @@ namespace Ryujinx.Core.OsHle.Services.Android //TODO: Support double buffering here aswell, it is broken for GPU //frame buffers because it seems to be completely out of sync. - if (Context.Ns.Gpu.Engine3d.IsFrameBufferPosition(GpuAddr)) + if (Context.Ns.Gpu.Engine3d.IsFrameBufferPosition(FbAddr)) { //Frame buffer is rendered to by the GPU, we can just //bind the frame buffer texture, it's not necessary to read anything. - Renderer.SetFrameBuffer(GpuAddr); + Renderer.SetFrameBuffer(FbAddr); } else { //Frame buffer is not set on the GPU registers, in this case //assume that the app is manually writing to it. - Texture Texture = new Texture(CpuAddr, FbWidth, FbHeight); + Texture Texture = new Texture(FbAddr, FbWidth, FbHeight); byte[] Data = TextureReader.Read(Context.Memory, Texture); @@ -372,22 +364,6 @@ namespace Ryujinx.Core.OsHle.Services.Android Context.Ns.Gpu.Renderer.QueueAction(() => ReleaseBuffer(Slot)); } - private NvMap 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 INvDrvServices.NvMaps.GetData(Context.Process, NvMapHandle); - } - private void ReleaseBuffer(int Slot) { BufferQueue[Slot].State = BufferState.Free; diff --git a/Ryujinx.Core/OsHle/Utilities/IntUtils.cs b/Ryujinx.Core/OsHle/Utilities/IntUtils.cs new file mode 100644 index 00000000..4a522465 --- /dev/null +++ b/Ryujinx.Core/OsHle/Utilities/IntUtils.cs @@ -0,0 +1,15 @@ +namespace Ryujinx.Core.OsHle.Utilities +{ + static class IntUtils + { + public static int RoundUp(int Value, int Size) + { + return (Value + (Size - 1)) & ~(Size - 1); + } + + public static long RoundUp(long Value, int Size) + { + return (Value + (Size - 1)) & ~((long)Size - 1); + } + } +} diff --git a/Ryujinx.Core/OsHle/Utilities/MemReader.cs b/Ryujinx.Core/OsHle/Utilities/MemReader.cs deleted file mode 100644 index fe92f68f..00000000 --- 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 21b6a3b6..00000000 --- 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 diff --git a/Ryujinx.Core/Switch.cs b/Ryujinx.Core/Switch.cs index 02fdc8b6..a755ea0c 100644 --- a/Ryujinx.Core/Switch.cs +++ b/Ryujinx.Core/Switch.cs @@ -4,7 +4,7 @@ using Ryujinx.Core.Logging; using Ryujinx.Core.OsHle; using Ryujinx.Core.Settings; using Ryujinx.Graphics.Gal; -using Ryujinx.Graphics.Gpu; +using Ryujinx.Core.Gpu; using System; namespace Ryujinx.Core @@ -15,7 +15,7 @@ namespace Ryujinx.Core public Logger Log { get; private set; } - internal NsGpu Gpu { get; private set; } + internal NvGpu Gpu { get; private set; } internal VirtualFileSystem VFs { get; private set; } @@ -45,7 +45,7 @@ namespace Ryujinx.Core Log = new Logger(); - Gpu = new NsGpu(Renderer); + Gpu = new NvGpu(Renderer); VFs = new VirtualFileSystem(); diff --git a/Ryujinx.Graphics/Gpu/INvGpuEngine.cs b/Ryujinx.Graphics/Gpu/INvGpuEngine.cs deleted file mode 100644 index 17e9b435..00000000 --- a/Ryujinx.Graphics/Gpu/INvGpuEngine.cs +++ /dev/null @@ -1,11 +0,0 @@ -using ChocolArm64.Memory; - -namespace Ryujinx.Graphics.Gpu -{ - interface INvGpuEngine - { - int[] Registers { get; } - - void CallMethod(AMemory Memory, NsGpuPBEntry PBEntry); - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gpu/NsGpuMemoryMgr.cs b/Ryujinx.Graphics/Gpu/NsGpuMemoryMgr.cs deleted file mode 100644 index eff51783..00000000 --- a/Ryujinx.Graphics/Gpu/NsGpuMemoryMgr.cs +++ /dev/null @@ -1,212 +0,0 @@ -namespace Ryujinx.Graphics.Gpu -{ - public class NsGpuMemoryMgr - { - private const long AddrSize = 1L << 40; - - private const int PTLvl0Bits = 14; - private const int PTLvl1Bits = 14; - private const int PTPageBits = 12; - - private const int PTLvl0Size = 1 << PTLvl0Bits; - private const int PTLvl1Size = 1 << PTLvl1Bits; - private const int PageSize = 1 << PTPageBits; - - private const int PTLvl0Mask = PTLvl0Size - 1; - private const int PTLvl1Mask = PTLvl1Size - 1; - private const int PageMask = PageSize - 1; - - private const int PTLvl0Bit = PTPageBits + PTLvl1Bits; - private const int PTLvl1Bit = PTPageBits; - - private const long PteUnmapped = -1; - private const long PteReserved = -2; - - private long[][] PageTable; - - public NsGpuMemoryMgr() - { - PageTable = new long[PTLvl0Size][]; - } - - public long Map(long CpuAddr, long GpuAddr, long Size) - { - CpuAddr &= ~PageMask; - GpuAddr &= ~PageMask; - - for (long Offset = 0; Offset < Size; Offset += PageSize) - { - if (GetPTAddr(GpuAddr + Offset) != PteReserved) - { - return Map(CpuAddr, Size); - } - } - - for (long Offset = 0; Offset < Size; Offset += PageSize) - { - SetPTAddr(GpuAddr + Offset, CpuAddr + Offset); - } - - return GpuAddr; - } - - public void Unmap(long Position, long Size) - { - for (long Offset = 0; Offset < Size; Offset += PageSize) - { - SetPTAddr(Position + Offset, PteUnmapped); - } - } - - public long Map(long CpuAddr, long Size) - { - CpuAddr &= ~PageMask; - - long Position = GetFreePosition(Size); - - if (Position != -1) - { - for (long Offset = 0; Offset < Size; Offset += PageSize) - { - SetPTAddr(Position + Offset, CpuAddr + Offset); - } - } - - return Position; - } - - public long Reserve(long GpuAddr, long Size, long Align) - { - for (long Offset = 0; Offset < Size; Offset += PageSize) - { - if (HasPTAddr(GpuAddr + Offset)) - { - return Reserve(Size, Align); - } - } - - for (long Offset = 0; Offset < Size; Offset += PageSize) - { - SetPTAddr(GpuAddr + Offset, PteReserved); - } - - return GpuAddr; - } - - public long Reserve(long Size, long Align) - { - long Position = GetFreePosition(Size, Align); - - if (Position != -1) - { - for (long Offset = 0; Offset < Size; Offset += PageSize) - { - SetPTAddr(Position + Offset, PteReserved); - } - } - - return Position; - } - - private long GetFreePosition(long Size, long Align = 1) - { - long Position = 0; - long FreeSize = 0; - - if (Align < 1) - { - Align = 1; - } - - Align = (Align + PageMask) & ~PageMask; - - while (Position + FreeSize < AddrSize) - { - if (!HasPTAddr(Position + FreeSize)) - { - FreeSize += PageSize; - - if (FreeSize >= Size) - { - return Position; - } - } - else - { - Position += FreeSize + PageSize; - FreeSize = 0; - - long Remainder = Position % Align; - - if (Remainder != 0) - { - Position = (Position - Remainder) + Align; - } - } - } - - return -1; - } - - public long GetCpuAddr(long Position) - { - long BasePos = GetPTAddr(Position); - - if (BasePos < 0) - { - return -1; - } - - return BasePos + (Position & PageMask); - } - - private bool HasPTAddr(long Position) - { - if (Position >> PTLvl0Bits + PTLvl1Bits + PTPageBits != 0) - { - return false; - } - - long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask; - long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask; - - if (PageTable[L0] == null) - { - return false; - } - - return PageTable[L0][L1] != PteUnmapped; - } - - private long GetPTAddr(long Position) - { - long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask; - long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask; - - if (PageTable[L0] == null) - { - return -1; - } - - return PageTable[L0][L1]; - } - - private void SetPTAddr(long Position, long TgtAddr) - { - long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask; - long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask; - - if (PageTable[L0] == null) - { - PageTable[L0] = new long[PTLvl1Size]; - - for (int Index = 0; Index < PTLvl1Size; Index++) - { - PageTable[L0][Index] = PteUnmapped; - } - } - - PageTable[L0][L1] = TgtAddr; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gpu/NvGpuMethod.cs b/Ryujinx.Graphics/Gpu/NvGpuMethod.cs deleted file mode 100644 index 2923ddff..00000000 --- a/Ryujinx.Graphics/Gpu/NvGpuMethod.cs +++ /dev/null @@ -1,6 +0,0 @@ -using ChocolArm64.Memory; - -namespace Ryujinx.Graphics.Gpu -{ - delegate void NvGpuMethod(AMemory Memory, NsGpuPBEntry PBEntry); -} \ No newline at end of file