From 34037701c708cb70bbf44dea71ee0912f7b4102b Mon Sep 17 00:00:00 2001 From: gdkchan Date: Mon, 7 May 2018 15:53:23 -0300 Subject: 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 ++ Ryujinx.Core/Gpu/BlockLinearSwizzle.cs | 57 ++ Ryujinx.Core/Gpu/INvGpuEngine.cs | 9 + Ryujinx.Core/Gpu/ISwizzle.cs | 7 + Ryujinx.Core/Gpu/LinearSwizzle.cs | 19 + Ryujinx.Core/Gpu/MacroInterpreter.cs | 419 ++++++++++++ Ryujinx.Core/Gpu/NvGpu.cs | 45 ++ Ryujinx.Core/Gpu/NvGpuEngine.cs | 11 + Ryujinx.Core/Gpu/NvGpuEngine2d.cs | 148 +++++ Ryujinx.Core/Gpu/NvGpuEngine2dReg.cs | 25 + Ryujinx.Core/Gpu/NvGpuEngine3d.cs | 558 ++++++++++++++++ Ryujinx.Core/Gpu/NvGpuEngine3dReg.cs | 61 ++ Ryujinx.Core/Gpu/NvGpuFifo.cs | 174 +++++ Ryujinx.Core/Gpu/NvGpuFifoMeth.cs | 11 + Ryujinx.Core/Gpu/NvGpuMethod.cs | 4 + Ryujinx.Core/Gpu/NvGpuPBEntry.cs | 23 + Ryujinx.Core/Gpu/NvGpuPushBuffer.cs | 101 +++ Ryujinx.Core/Gpu/NvGpuVmm.cs | 398 +++++++++++ Ryujinx.Core/Gpu/Texture.cs | 55 ++ Ryujinx.Core/Gpu/TextureFactory.cs | 83 +++ Ryujinx.Core/Gpu/TextureHelper.cs | 36 + Ryujinx.Core/Gpu/TextureReader.cs | 160 +++++ Ryujinx.Core/Gpu/TextureSwizzle.cs | 11 + Ryujinx.Core/Gpu/TextureWriter.cs | 48 ++ 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 | 85 ++- Ryujinx.Core/OsHle/Kernel/SvcThreadSync.cs | 6 +- Ryujinx.Core/OsHle/MemoryAllocator.cs | 12 + Ryujinx.Core/OsHle/Process.cs | 6 +- Ryujinx.Core/OsHle/Services/Nv/INvDrvServices.cs | 728 ++------------------- Ryujinx.Core/OsHle/Services/Nv/NvChNvMap.cs | 31 - .../OsHle/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 ++ .../OsHle/Services/Nv/NvGpuGpu/NvGpuGpuIoctl.cs | 155 +++++ Ryujinx.Core/OsHle/Services/Nv/NvHelper.cs | 10 + .../Nv/NvHostChannel/NvHostChannelIoctl.cs | 130 ++++ .../Nv/NvHostChannel/NvHostChannelSubmitGpfifo.cs | 11 + .../Services/Nv/NvHostCtrl/NvHostCtrlIoctl.cs | 355 ++++++++++ .../Services/Nv/NvHostCtrl/NvHostCtrlSyncPtRead.cs | 8 + .../Services/Nv/NvHostCtrl/NvHostCtrlSyncPtWait.cs | 9 + .../Nv/NvHostCtrl/NvHostCtrlSyncPtWaitEx.cs | 10 + .../Services/Nv/NvHostCtrl/NvHostCtrlUserCtx.cs | 19 + .../OsHle/Services/Nv/NvHostCtrl/NvHostEvent.cs | 10 + .../Services/Nv/NvHostCtrl/NvHostEventState.cs | 10 + .../OsHle/Services/Nv/NvHostCtrl/NvHostSyncPt.cs | 107 +++ Ryujinx.Core/OsHle/Services/Nv/NvMap.cs | 20 - Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapAlloc.cs | 12 + .../OsHle/Services/Nv/NvMap/NvMapCreate.cs | 8 + Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapFree.cs | 11 + .../OsHle/Services/Nv/NvMap/NvMapFromId.cs | 8 + Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapGetId.cs | 8 + .../OsHle/Services/Nv/NvMap/NvMapHandle.cs | 37 ++ .../OsHle/Services/Nv/NvMap/NvMapHandleParam.cs | 12 + Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapIoctl.cs | 302 +++++++++ Ryujinx.Core/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/BlockLinearSwizzle.cs | 57 -- Ryujinx.Graphics/Gpu/INvGpuEngine.cs | 11 - Ryujinx.Graphics/Gpu/ISwizzle.cs | 7 - Ryujinx.Graphics/Gpu/LinearSwizzle.cs | 19 - Ryujinx.Graphics/Gpu/MacroInterpreter.cs | 420 ------------ Ryujinx.Graphics/Gpu/NsGpu.cs | 74 --- Ryujinx.Graphics/Gpu/NsGpuMemoryMgr.cs | 212 ------ Ryujinx.Graphics/Gpu/NsGpuPBEntry.cs | 23 - Ryujinx.Graphics/Gpu/NvGpuEngine.cs | 11 - Ryujinx.Graphics/Gpu/NvGpuEngine2d.cs | 158 ----- Ryujinx.Graphics/Gpu/NvGpuEngine2dReg.cs | 25 - Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs | 581 ---------------- Ryujinx.Graphics/Gpu/NvGpuEngine3dReg.cs | 61 -- Ryujinx.Graphics/Gpu/NvGpuFifo.cs | 177 ----- Ryujinx.Graphics/Gpu/NvGpuFifoMeth.cs | 11 - Ryujinx.Graphics/Gpu/NvGpuMethod.cs | 6 - Ryujinx.Graphics/Gpu/NvGpuPushBuffer.cs | 101 --- Ryujinx.Graphics/Gpu/Texture.cs | 55 -- Ryujinx.Graphics/Gpu/TextureFactory.cs | 86 --- Ryujinx.Graphics/Gpu/TextureHelper.cs | 23 - Ryujinx.Graphics/Gpu/TextureReader.cs | 144 ---- Ryujinx.Graphics/Gpu/TextureSwizzle.cs | 11 - Ryujinx.Graphics/Gpu/TextureWriter.cs | 45 -- 95 files changed, 4286 insertions(+), 3254 deletions(-) create mode 100644 ChocolArm64/Memory/IAMemory.cs create mode 100644 Ryujinx.Core/Gpu/BlockLinearSwizzle.cs create mode 100644 Ryujinx.Core/Gpu/INvGpuEngine.cs create mode 100644 Ryujinx.Core/Gpu/ISwizzle.cs create mode 100644 Ryujinx.Core/Gpu/LinearSwizzle.cs create mode 100644 Ryujinx.Core/Gpu/MacroInterpreter.cs create mode 100644 Ryujinx.Core/Gpu/NvGpu.cs create mode 100644 Ryujinx.Core/Gpu/NvGpuEngine.cs create mode 100644 Ryujinx.Core/Gpu/NvGpuEngine2d.cs create mode 100644 Ryujinx.Core/Gpu/NvGpuEngine2dReg.cs create mode 100644 Ryujinx.Core/Gpu/NvGpuEngine3d.cs create mode 100644 Ryujinx.Core/Gpu/NvGpuEngine3dReg.cs create mode 100644 Ryujinx.Core/Gpu/NvGpuFifo.cs create mode 100644 Ryujinx.Core/Gpu/NvGpuFifoMeth.cs create mode 100644 Ryujinx.Core/Gpu/NvGpuMethod.cs create mode 100644 Ryujinx.Core/Gpu/NvGpuPBEntry.cs create mode 100644 Ryujinx.Core/Gpu/NvGpuPushBuffer.cs create mode 100644 Ryujinx.Core/Gpu/NvGpuVmm.cs create mode 100644 Ryujinx.Core/Gpu/Texture.cs create mode 100644 Ryujinx.Core/Gpu/TextureFactory.cs create mode 100644 Ryujinx.Core/Gpu/TextureHelper.cs create mode 100644 Ryujinx.Core/Gpu/TextureReader.cs create mode 100644 Ryujinx.Core/Gpu/TextureSwizzle.cs create mode 100644 Ryujinx.Core/Gpu/TextureWriter.cs 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/BlockLinearSwizzle.cs delete mode 100644 Ryujinx.Graphics/Gpu/INvGpuEngine.cs delete mode 100644 Ryujinx.Graphics/Gpu/ISwizzle.cs delete mode 100644 Ryujinx.Graphics/Gpu/LinearSwizzle.cs delete mode 100644 Ryujinx.Graphics/Gpu/MacroInterpreter.cs delete mode 100644 Ryujinx.Graphics/Gpu/NsGpu.cs delete mode 100644 Ryujinx.Graphics/Gpu/NsGpuMemoryMgr.cs delete mode 100644 Ryujinx.Graphics/Gpu/NsGpuPBEntry.cs delete mode 100644 Ryujinx.Graphics/Gpu/NvGpuEngine.cs delete mode 100644 Ryujinx.Graphics/Gpu/NvGpuEngine2d.cs delete mode 100644 Ryujinx.Graphics/Gpu/NvGpuEngine2dReg.cs delete mode 100644 Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs delete mode 100644 Ryujinx.Graphics/Gpu/NvGpuEngine3dReg.cs delete mode 100644 Ryujinx.Graphics/Gpu/NvGpuFifo.cs delete mode 100644 Ryujinx.Graphics/Gpu/NvGpuFifoMeth.cs delete mode 100644 Ryujinx.Graphics/Gpu/NvGpuMethod.cs delete mode 100644 Ryujinx.Graphics/Gpu/NvGpuPushBuffer.cs delete mode 100644 Ryujinx.Graphics/Gpu/Texture.cs delete mode 100644 Ryujinx.Graphics/Gpu/TextureFactory.cs delete mode 100644 Ryujinx.Graphics/Gpu/TextureHelper.cs delete mode 100644 Ryujinx.Graphics/Gpu/TextureReader.cs delete mode 100644 Ryujinx.Graphics/Gpu/TextureSwizzle.cs delete mode 100644 Ryujinx.Graphics/Gpu/TextureWriter.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.Core/Gpu/BlockLinearSwizzle.cs b/Ryujinx.Core/Gpu/BlockLinearSwizzle.cs new file mode 100644 index 00000000..eb41ea30 --- /dev/null +++ b/Ryujinx.Core/Gpu/BlockLinearSwizzle.cs @@ -0,0 +1,57 @@ +namespace Ryujinx.Core.Gpu +{ + class BlockLinearSwizzle : ISwizzle + { + private int BhShift; + private int BppShift; + private int BhMask; + + private int XShift; + private int GobStride; + + public BlockLinearSwizzle(int Width, int Bpp, int BlockHeight = 16) + { + BhMask = (BlockHeight * 8) - 1; + + BhShift = CountLsbZeros(BlockHeight * 8); + BppShift = CountLsbZeros(Bpp); + + int WidthInGobs = Width * Bpp / 64; + + GobStride = 512 * BlockHeight * WidthInGobs; + + XShift = CountLsbZeros(512 * BlockHeight); + } + + private int CountLsbZeros(int Value) + { + int Count = 0; + + while (((Value >> Count) & 1) == 0) + { + Count++; + } + + return Count; + } + + public int GetSwizzleOffset(int X, int Y) + { + X <<= BppShift; + + int Position = (Y >> BhShift) * GobStride; + + Position += (X >> 6) << XShift; + + Position += ((Y & BhMask) >> 3) << 9; + + Position += ((X & 0x3f) >> 5) << 8; + Position += ((Y & 0x07) >> 1) << 6; + Position += ((X & 0x1f) >> 4) << 5; + Position += ((Y & 0x01) >> 0) << 4; + Position += ((X & 0x0f) >> 0) << 0; + + return Position; + } + } +} \ No newline at end of file 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.Core/Gpu/ISwizzle.cs b/Ryujinx.Core/Gpu/ISwizzle.cs new file mode 100644 index 00000000..f475be6e --- /dev/null +++ b/Ryujinx.Core/Gpu/ISwizzle.cs @@ -0,0 +1,7 @@ +namespace Ryujinx.Core.Gpu +{ + interface ISwizzle + { + int GetSwizzleOffset(int X, int Y); + } +} \ No newline at end of file diff --git a/Ryujinx.Core/Gpu/LinearSwizzle.cs b/Ryujinx.Core/Gpu/LinearSwizzle.cs new file mode 100644 index 00000000..5f8dfdde --- /dev/null +++ b/Ryujinx.Core/Gpu/LinearSwizzle.cs @@ -0,0 +1,19 @@ +namespace Ryujinx.Core.Gpu +{ + class LinearSwizzle : ISwizzle + { + private int Pitch; + private int Bpp; + + public LinearSwizzle(int Pitch, int Bpp) + { + this.Pitch = Pitch; + this.Bpp = Bpp; + } + + public int GetSwizzleOffset(int X, int Y) + { + return X * Bpp + Y * Pitch; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/Gpu/MacroInterpreter.cs b/Ryujinx.Core/Gpu/MacroInterpreter.cs new file mode 100644 index 00000000..b799f98f --- /dev/null +++ b/Ryujinx.Core/Gpu/MacroInterpreter.cs @@ -0,0 +1,419 @@ +using System; +using System.Collections.Generic; + +namespace Ryujinx.Core.Gpu +{ + class MacroInterpreter + { + private enum AssignmentOperation + { + IgnoreAndFetch = 0, + Move = 1, + MoveAndSetMaddr = 2, + FetchAndSend = 3, + MoveAndSend = 4, + FetchAndSetMaddr = 5, + MoveAndSetMaddrThenFetchAndSend = 6, + MoveAndSetMaddrThenSendHigh = 7 + } + + private enum AluOperation + { + AluReg = 0, + AddImmediate = 1, + BitfieldReplace = 2, + BitfieldExtractLslImm = 3, + BitfieldExtractLslReg = 4, + ReadImmediate = 5 + } + + private enum AluRegOperation + { + Add = 0, + AddWithCarry = 1, + Subtract = 2, + SubtractWithBorrow = 3, + BitwiseExclusiveOr = 8, + BitwiseOr = 9, + BitwiseAnd = 10, + BitwiseAndNot = 11, + BitwiseNotAnd = 12 + } + + private NvGpuFifo PFifo; + private INvGpuEngine Engine; + + public Queue Fifo { get; private set; } + + private int[] Gprs; + + private int MethAddr; + private int MethIncr; + + private bool Carry; + + private int OpCode; + + private int PipeOp; + + private long Pc; + + public MacroInterpreter(NvGpuFifo PFifo, INvGpuEngine Engine) + { + this.PFifo = PFifo; + this.Engine = Engine; + + Fifo = new Queue(); + + Gprs = new int[8]; + } + + public void Execute(NvGpuVmm Vmm, long Position, int Param) + { + Reset(); + + Gprs[1] = Param; + + Pc = Position; + + FetchOpCode(Vmm); + + while (Step(Vmm)); + + //Due to the delay slot, we still need to execute + //one more instruction before we actually exit. + Step(Vmm); + } + + private void Reset() + { + for (int Index = 0; Index < Gprs.Length; Index++) + { + Gprs[Index] = 0; + } + + MethAddr = 0; + MethIncr = 0; + + Carry = false; + } + + private bool Step(NvGpuVmm Vmm) + { + long BaseAddr = Pc - 4; + + FetchOpCode(Vmm); + + if ((OpCode & 7) < 7) + { + //Operation produces a value. + AssignmentOperation AsgOp = (AssignmentOperation)((OpCode >> 4) & 7); + + int Result = GetAluResult(); + + switch (AsgOp) + { + //Fetch parameter and ignore result. + case AssignmentOperation.IgnoreAndFetch: + { + SetDstGpr(FetchParam()); + + break; + } + + //Move result. + case AssignmentOperation.Move: + { + SetDstGpr(Result); + + break; + } + + //Move result and use as Method Address. + case AssignmentOperation.MoveAndSetMaddr: + { + SetDstGpr(Result); + + SetMethAddr(Result); + + break; + } + + //Fetch parameter and send result. + case AssignmentOperation.FetchAndSend: + { + SetDstGpr(FetchParam()); + + Send(Vmm, Result); + + break; + } + + //Move and send result. + case AssignmentOperation.MoveAndSend: + { + SetDstGpr(Result); + + Send(Vmm, Result); + + break; + } + + //Fetch parameter and use result as Method Address. + case AssignmentOperation.FetchAndSetMaddr: + { + SetDstGpr(FetchParam()); + + SetMethAddr(Result); + + break; + } + + //Move result and use as Method Address, then fetch and send paramter. + case AssignmentOperation.MoveAndSetMaddrThenFetchAndSend: + { + SetDstGpr(Result); + + SetMethAddr(Result); + + Send(Vmm, FetchParam()); + + break; + } + + //Move result and use as Method Address, then send bits 17:12 of result. + case AssignmentOperation.MoveAndSetMaddrThenSendHigh: + { + SetDstGpr(Result); + + SetMethAddr(Result); + + Send(Vmm, (Result >> 12) & 0x3f); + + break; + } + } + } + else + { + //Branch. + bool OnNotZero = ((OpCode >> 4) & 1) != 0; + + bool Taken = OnNotZero + ? GetGprA() != 0 + : GetGprA() == 0; + + if (Taken) + { + Pc = BaseAddr + (GetImm() << 2); + + bool NoDelays = (OpCode & 0x20) != 0; + + if (NoDelays) + { + FetchOpCode(Vmm); + } + + return true; + } + } + + bool Exit = (OpCode & 0x80) != 0; + + return !Exit; + } + + private void FetchOpCode(NvGpuVmm Vmm) + { + OpCode = PipeOp; + + PipeOp = Vmm.ReadInt32(Pc); + + Pc += 4; + } + + private int GetAluResult() + { + AluOperation Op = (AluOperation)(OpCode & 7); + + switch (Op) + { + case AluOperation.AluReg: + { + AluRegOperation AluOp = (AluRegOperation)((OpCode >> 17) & 0x1f); + + return GetAluResult(AluOp, GetGprA(), GetGprB()); + } + + case AluOperation.AddImmediate: + { + return GetGprA() + GetImm(); + } + + case AluOperation.BitfieldReplace: + case AluOperation.BitfieldExtractLslImm: + case AluOperation.BitfieldExtractLslReg: + { + int BfSrcBit = (OpCode >> 17) & 0x1f; + int BfSize = (OpCode >> 22) & 0x1f; + int BfDstBit = (OpCode >> 27) & 0x1f; + + int BfMask = (1 << BfSize) - 1; + + int Dst = GetGprA(); + int Src = GetGprB(); + + switch (Op) + { + case AluOperation.BitfieldReplace: + { + Src = (int)((uint)Src >> BfSrcBit) & BfMask; + + Dst &= ~(BfMask << BfDstBit); + + Dst |= Src << BfDstBit; + + return Dst; + } + + case AluOperation.BitfieldExtractLslImm: + { + Src = (int)((uint)Src >> Dst) & BfMask; + + return Src << BfDstBit; + } + + case AluOperation.BitfieldExtractLslReg: + { + Src = (int)((uint)Src >> BfSrcBit) & BfMask; + + return Src << Dst; + } + } + + break; + } + + case AluOperation.ReadImmediate: + { + return Read(GetGprA() + GetImm()); + } + } + + throw new ArgumentException(nameof(OpCode)); + } + + private int GetAluResult(AluRegOperation AluOp, int A, int B) + { + switch (AluOp) + { + case AluRegOperation.Add: + { + ulong Result = (ulong)A + (ulong)B; + + Carry = Result > 0xffffffff; + + return (int)Result; + } + + case AluRegOperation.AddWithCarry: + { + ulong Result = (ulong)A + (ulong)B + (Carry ? 1UL : 0UL); + + Carry = Result > 0xffffffff; + + return (int)Result; + } + + case AluRegOperation.Subtract: + { + ulong Result = (ulong)A - (ulong)B; + + Carry = Result < 0x100000000; + + return (int)Result; + } + + case AluRegOperation.SubtractWithBorrow: + { + ulong Result = (ulong)A - (ulong)B - (Carry ? 0UL : 1UL); + + Carry = Result < 0x100000000; + + return (int)Result; + } + + case AluRegOperation.BitwiseExclusiveOr: return A ^ B; + case AluRegOperation.BitwiseOr: return A | B; + case AluRegOperation.BitwiseAnd: return A & B; + case AluRegOperation.BitwiseAndNot: return A & ~B; + case AluRegOperation.BitwiseNotAnd: return ~(A & B); + } + + throw new ArgumentOutOfRangeException(nameof(AluOp)); + } + + private int GetImm() + { + //Note: The immediate is signed, the sign-extension is intended here. + return OpCode >> 14; + } + + private void SetMethAddr(int Value) + { + MethAddr = (Value >> 0) & 0xfff; + MethIncr = (Value >> 12) & 0x3f; + } + + private void SetDstGpr(int Value) + { + Gprs[(OpCode >> 8) & 7] = Value; + } + + private int GetGprA() + { + return GetGprValue((OpCode >> 11) & 7); + } + + private int GetGprB() + { + return GetGprValue((OpCode >> 14) & 7); + } + + private int GetGprValue(int Index) + { + return Index != 0 ? Gprs[Index] : 0; + } + + private int FetchParam() + { + int Value; + + //If we don't have any parameters in the FIFO, + //keep running the PFIFO engine until it writes the parameters. + while (!Fifo.TryDequeue(out Value)) + { + if (!PFifo.Step()) + { + return 0; + } + } + + return Value; + } + + private int Read(int Reg) + { + return Engine.Registers[Reg]; + } + + private void Send(NvGpuVmm Vmm, int Value) + { + NvGpuPBEntry PBEntry = new NvGpuPBEntry(MethAddr, 0, Value); + + Engine.CallMethod(Vmm, PBEntry); + + MethAddr += MethIncr; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/Gpu/NvGpu.cs b/Ryujinx.Core/Gpu/NvGpu.cs new file mode 100644 index 00000000..71df76ff --- /dev/null +++ b/Ryujinx.Core/Gpu/NvGpu.cs @@ -0,0 +1,45 @@ +using Ryujinx.Graphics.Gal; +using System.Threading; + +namespace Ryujinx.Core.Gpu +{ + public class NvGpu + { + public IGalRenderer Renderer { get; private set; } + + public NvGpuFifo Fifo { get; private set; } + + public NvGpuEngine2d Engine2d { get; private set; } + public NvGpuEngine3d Engine3d { get; private set; } + + private Thread FifoProcessing; + + private bool KeepRunning; + + public NvGpu(IGalRenderer Renderer) + { + this.Renderer = Renderer; + + Fifo = new NvGpuFifo(this); + + Engine2d = new NvGpuEngine2d(this); + Engine3d = new NvGpuEngine3d(this); + + KeepRunning = true; + + FifoProcessing = new Thread(ProcessFifo); + + FifoProcessing.Start(); + } + + private void ProcessFifo() + { + while (KeepRunning) + { + Fifo.DispatchCalls(); + + Thread.Yield(); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/Gpu/NvGpuEngine.cs b/Ryujinx.Core/Gpu/NvGpuEngine.cs new file mode 100644 index 00000000..ee0420f7 --- /dev/null +++ b/Ryujinx.Core/Gpu/NvGpuEngine.cs @@ -0,0 +1,11 @@ +namespace Ryujinx.Core.Gpu +{ + enum NvGpuEngine + { + _2d = 0x902d, + _3d = 0xb197, + Compute = 0xb1c0, + Kepler = 0xa140, + Dma = 0xb0b5 + } +} \ No newline at end of file diff --git a/Ryujinx.Core/Gpu/NvGpuEngine2d.cs b/Ryujinx.Core/Gpu/NvGpuEngine2d.cs new file mode 100644 index 00000000..88395b7a --- /dev/null +++ b/Ryujinx.Core/Gpu/NvGpuEngine2d.cs @@ -0,0 +1,148 @@ +using Ryujinx.Graphics.Gal; +using System.Collections.Generic; + +namespace Ryujinx.Core.Gpu +{ + public class NvGpuEngine2d : INvGpuEngine + { + private enum CopyOperation + { + SrcCopyAnd, + RopAnd, + Blend, + SrcCopy, + Rop, + SrcCopyPremult, + BlendPremult + } + + public int[] Registers { get; private set; } + + private NvGpu Gpu; + + private Dictionary Methods; + + public NvGpuEngine2d(NvGpu Gpu) + { + this.Gpu = Gpu; + + Registers = new int[0xe00]; + + Methods = new Dictionary(); + + void AddMethod(int Meth, int Count, int Stride, NvGpuMethod Method) + { + while (Count-- > 0) + { + Methods.Add(Meth, Method); + + Meth += Stride; + } + } + + AddMethod(0xb5, 1, 1, TextureCopy); + } + + public void CallMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) + { + if (Methods.TryGetValue(PBEntry.Method, out NvGpuMethod Method)) + { + Method(Vmm, PBEntry); + } + else + { + WriteRegister(PBEntry); + } + } + + private void TextureCopy(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) + { + CopyOperation Operation = (CopyOperation)ReadRegister(NvGpuEngine2dReg.CopyOperation); + + bool SrcLinear = ReadRegister(NvGpuEngine2dReg.SrcLinear) != 0; + int SrcWidth = ReadRegister(NvGpuEngine2dReg.SrcWidth); + int SrcHeight = ReadRegister(NvGpuEngine2dReg.SrcHeight); + + bool DstLinear = ReadRegister(NvGpuEngine2dReg.DstLinear) != 0; + int DstWidth = ReadRegister(NvGpuEngine2dReg.DstWidth); + int DstHeight = ReadRegister(NvGpuEngine2dReg.DstHeight); + int DstPitch = ReadRegister(NvGpuEngine2dReg.DstPitch); + int DstBlkDim = ReadRegister(NvGpuEngine2dReg.DstBlockDimensions); + + TextureSwizzle DstSwizzle = DstLinear + ? TextureSwizzle.Pitch + : TextureSwizzle.BlockLinear; + + int DstBlockHeight = 1 << ((DstBlkDim >> 4) & 0xf); + + long Tag = Vmm.GetPhysicalAddress(MakeInt64From2xInt32(NvGpuEngine2dReg.SrcAddress)); + + long SrcAddress = MakeInt64From2xInt32(NvGpuEngine2dReg.SrcAddress); + long DstAddress = MakeInt64From2xInt32(NvGpuEngine2dReg.DstAddress); + + bool IsFbTexture = Gpu.Engine3d.IsFrameBufferPosition(Tag); + + if (IsFbTexture && DstLinear) + { + DstSwizzle = TextureSwizzle.BlockLinear; + } + + Texture DstTexture = new Texture( + DstAddress, + DstWidth, + DstHeight, + DstBlockHeight, + DstBlockHeight, + DstSwizzle, + GalTextureFormat.A8B8G8R8); + + if (IsFbTexture) + { + Gpu.Renderer.GetFrameBufferData(Tag, (byte[] Buffer) => + { + CopyTexture(Vmm, DstTexture, Buffer); + }); + } + else + { + long Size = SrcWidth * SrcHeight * 4; + + byte[] Buffer = Vmm.ReadBytes(SrcAddress, Size); + + CopyTexture(Vmm, DstTexture, Buffer); + } + } + + private void CopyTexture(NvGpuVmm Vmm, Texture Texture, byte[] Buffer) + { + TextureWriter.Write(Vmm, Texture, Buffer); + } + + private long MakeInt64From2xInt32(NvGpuEngine2dReg Reg) + { + return + (long)Registers[(int)Reg + 0] << 32 | + (uint)Registers[(int)Reg + 1]; + } + + private void WriteRegister(NvGpuPBEntry PBEntry) + { + int ArgsCount = PBEntry.Arguments.Count; + + if (ArgsCount > 0) + { + Registers[PBEntry.Method] = PBEntry.Arguments[ArgsCount - 1]; + } + } + + private int ReadRegister(NvGpuEngine2dReg Reg) + { + return Registers[(int)Reg]; + } + + private void WriteRegister(NvGpuEngine2dReg Reg, int Value) + { + Registers[(int)Reg] = Value; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/Gpu/NvGpuEngine2dReg.cs b/Ryujinx.Core/Gpu/NvGpuEngine2dReg.cs new file mode 100644 index 00000000..b4abad00 --- /dev/null +++ b/Ryujinx.Core/Gpu/NvGpuEngine2dReg.cs @@ -0,0 +1,25 @@ +namespace Ryujinx.Core.Gpu +{ + enum NvGpuEngine2dReg + { + DstFormat = 0x80, + DstLinear = 0x81, + DstBlockDimensions = 0x82, + DstDepth = 0x83, + DstLayer = 0x84, + DstPitch = 0x85, + DstWidth = 0x86, + DstHeight = 0x87, + DstAddress = 0x88, + SrcFormat = 0x8c, + SrcLinear = 0x8d, + SrcBlockDimensions = 0x8e, + SrcDepth = 0x8f, + SrcLayer = 0x90, + SrcPitch = 0x91, + SrcWidth = 0x92, + SrcHeight = 0x93, + SrcAddress = 0x94, + CopyOperation = 0xab + } +} \ No newline at end of file diff --git a/Ryujinx.Core/Gpu/NvGpuEngine3d.cs b/Ryujinx.Core/Gpu/NvGpuEngine3d.cs new file mode 100644 index 00000000..b08b9496 --- /dev/null +++ b/Ryujinx.Core/Gpu/NvGpuEngine3d.cs @@ -0,0 +1,558 @@ +using Ryujinx.Graphics.Gal; +using System; +using System.Collections.Generic; + +namespace Ryujinx.Core.Gpu +{ + public class NvGpuEngine3d : INvGpuEngine + { + public int[] Registers { get; private set; } + + private NvGpu Gpu; + + private Dictionary Methods; + + private struct ConstBuffer + { + public bool Enabled; + public long Position; + public int Size; + } + + private ConstBuffer[][] ConstBuffers; + + private HashSet FrameBuffers; + + public NvGpuEngine3d(NvGpu Gpu) + { + this.Gpu = Gpu; + + Registers = new int[0xe00]; + + Methods = new Dictionary(); + + void AddMethod(int Meth, int Count, int Stride, NvGpuMethod Method) + { + while (Count-- > 0) + { + Methods.Add(Meth, Method); + + Meth += Stride; + } + } + + AddMethod(0x585, 1, 1, VertexEndGl); + AddMethod(0x674, 1, 1, ClearBuffers); + AddMethod(0x6c3, 1, 1, QueryControl); + AddMethod(0x8e4, 16, 1, CbData); + AddMethod(0x904, 5, 8, CbBind); + + ConstBuffers = new ConstBuffer[6][]; + + for (int Index = 0; Index < ConstBuffers.Length; Index++) + { + ConstBuffers[Index] = new ConstBuffer[18]; + } + + FrameBuffers = new HashSet(); + } + + public void CallMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) + { + if (Methods.TryGetValue(PBEntry.Method, out NvGpuMethod Method)) + { + Method(Vmm, PBEntry); + } + else + { + WriteRegister(PBEntry); + } + } + + private void VertexEndGl(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) + { + SetFrameBuffer(Vmm, 0); + + long[] Tags = UploadShaders(Vmm); + + Gpu.Renderer.BindProgram(); + + SetAlphaBlending(); + + UploadTextures(Vmm, Tags); + UploadUniforms(Vmm); + UploadVertexArrays(Vmm); + } + + private void ClearBuffers(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) + { + int Arg0 = PBEntry.Arguments[0]; + + int FbIndex = (Arg0 >> 6) & 0xf; + + int Layer = (Arg0 >> 10) & 0x3ff; + + GalClearBufferFlags Flags = (GalClearBufferFlags)(Arg0 & 0x3f); + + SetFrameBuffer(Vmm, 0); + + //TODO: Enable this once the frame buffer problems are fixed. + //Gpu.Renderer.ClearBuffers(Layer, Flags); + } + + private void SetFrameBuffer(NvGpuVmm Vmm, int FbIndex) + { + long VA = MakeInt64From2xInt32(NvGpuEngine3dReg.FrameBufferNAddress + FbIndex * 0x10); + + 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(PA, 1280, 720); + Gpu.Renderer.BindFrameBuffer(PA); + } + + private long[] UploadShaders(NvGpuVmm Vmm) + { + long[] Tags = new long[5]; + + long BasePosition = MakeInt64From2xInt32(NvGpuEngine3dReg.ShaderAddress); + + for (int Index = 0; Index < 6; Index++) + { + int Control = ReadRegister(NvGpuEngine3dReg.ShaderNControl + Index * 0x10); + int Offset = ReadRegister(NvGpuEngine3dReg.ShaderNOffset + Index * 0x10); + + //Note: Vertex Program (B) is always enabled. + bool Enable = (Control & 1) != 0 || Index == 1; + + if (!Enable) + { + continue; + } + + long Tag = BasePosition + (uint)Offset; + + //TODO: Find a better way to calculate the size. + int Size = 0x20000; + + byte[] Code = Vmm.ReadBytes(Tag, Size); + + GalShaderType ShaderType = GetTypeFromProgram(Index); + + Tags[(int)ShaderType] = Tag; + + Gpu.Renderer.CreateShader(Tag, ShaderType, Code); + Gpu.Renderer.BindShader(Tag); + } + + int RawSX = ReadRegister(NvGpuEngine3dReg.ViewportScaleX); + int RawSY = ReadRegister(NvGpuEngine3dReg.ViewportScaleY); + + float SX = BitConverter.Int32BitsToSingle(RawSX); + float SY = BitConverter.Int32BitsToSingle(RawSY); + + float SignX = MathF.Sign(SX); + float SignY = MathF.Sign(SY); + + Gpu.Renderer.SetUniform2F(GalConsts.FlipUniformName, SignX, SignY); + + return Tags; + } + + private static GalShaderType GetTypeFromProgram(int Program) + { + switch (Program) + { + case 0: + case 1: return GalShaderType.Vertex; + case 2: return GalShaderType.TessControl; + case 3: return GalShaderType.TessEvaluation; + case 4: return GalShaderType.Geometry; + case 5: return GalShaderType.Fragment; + } + + throw new ArgumentOutOfRangeException(nameof(Program)); + } + + private void SetAlphaBlending() + { + //TODO: Support independent blend properly. + bool Enable = (ReadRegister(NvGpuEngine3dReg.IBlendNEnable) & 1) != 0; + + Gpu.Renderer.SetBlendEnable(Enable); + + bool BlendSeparateAlpha = (ReadRegister(NvGpuEngine3dReg.IBlendNSeparateAlpha) & 1) != 0; + + GalBlendEquation EquationRgb = (GalBlendEquation)ReadRegister(NvGpuEngine3dReg.IBlendNEquationRgb); + + GalBlendFactor FuncSrcRgb = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.IBlendNFuncSrcRgb); + GalBlendFactor FuncDstRgb = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.IBlendNFuncDstRgb); + + if (BlendSeparateAlpha) + { + GalBlendEquation EquationAlpha = (GalBlendEquation)ReadRegister(NvGpuEngine3dReg.IBlendNEquationAlpha); + + GalBlendFactor FuncSrcAlpha = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.IBlendNFuncSrcAlpha); + GalBlendFactor FuncDstAlpha = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.IBlendNFuncDstAlpha); + + Gpu.Renderer.SetBlendSeparate( + EquationRgb, + EquationAlpha, + FuncSrcRgb, + FuncDstRgb, + FuncSrcAlpha, + FuncDstAlpha); + } + else + { + Gpu.Renderer.SetBlend(EquationRgb, FuncSrcRgb, FuncDstRgb); + } + } + + private void UploadTextures(NvGpuVmm Vmm, long[] Tags) + { + long BaseShPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.ShaderAddress); + + int TextureCbIndex = ReadRegister(NvGpuEngine3dReg.TextureCbIndex); + + //Note: On the emulator renderer, Texture Unit 0 is + //reserved for drawing the frame buffer. + int TexIndex = 1; + + for (int Index = 0; Index < Tags.Length; Index++) + { + foreach (ShaderDeclInfo DeclInfo in Gpu.Renderer.GetTextureUsage(Tags[Index])) + { + long Position = ConstBuffers[Index][TextureCbIndex].Position; + + UploadTexture(Vmm, Position, TexIndex, DeclInfo.Index); + + Gpu.Renderer.SetUniform1(DeclInfo.Name, TexIndex); + + TexIndex++; + } + } + } + + private void UploadTexture(NvGpuVmm Vmm, long BasePosition, int TexIndex, int HndIndex) + { + long Position = BasePosition + HndIndex * 4; + + int TextureHandle = Vmm.ReadInt32(Position); + + int TicIndex = (TextureHandle >> 0) & 0xfffff; + int TscIndex = (TextureHandle >> 20) & 0xfff; + + long TicPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.TexHeaderPoolOffset); + long TscPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.TexSamplerPoolOffset); + + TicPosition += TicIndex * 0x20; + TscPosition += TscIndex * 0x20; + + GalTextureSampler Sampler = TextureFactory.MakeSampler(Gpu, Vmm, TscPosition); + + long TextureAddress = Vmm.ReadInt64(TicPosition + 4) & 0xffffffffffff; + + TextureAddress = Vmm.GetPhysicalAddress(TextureAddress); + + if (IsFrameBufferPosition(TextureAddress)) + { + //This texture is a frame buffer texture, + //we shouldn't read anything from memory and bind + //the frame buffer texture instead, since we're not + //really writing anything to memory. + Gpu.Renderer.BindFrameBufferTexture(TextureAddress, TexIndex, Sampler); + } + else + { + GalTexture Texture = TextureFactory.MakeTexture(Gpu, Vmm, TicPosition); + + Gpu.Renderer.SetTextureAndSampler(TexIndex, Texture, Sampler); + Gpu.Renderer.BindTexture(TexIndex); + } + } + + private void UploadUniforms(NvGpuVmm Vmm) + { + long BasePosition = MakeInt64From2xInt32(NvGpuEngine3dReg.ShaderAddress); + + for (int Index = 0; Index < 5; Index++) + { + int Control = ReadRegister(NvGpuEngine3dReg.ShaderNControl + (Index + 1) * 0x10); + int Offset = ReadRegister(NvGpuEngine3dReg.ShaderNOffset + (Index + 1) * 0x10); + + //Note: Vertex Program (B) is always enabled. + bool Enable = (Control & 1) != 0 || Index == 0; + + if (!Enable) + { + continue; + } + + for (int Cbuf = 0; Cbuf < ConstBuffers.Length; Cbuf++) + { + ConstBuffer Cb = ConstBuffers[Index][Cbuf]; + + if (Cb.Enabled) + { + byte[] Data = Vmm.ReadBytes(Cb.Position, (uint)Cb.Size); + + Gpu.Renderer.SetConstBuffer(BasePosition + (uint)Offset, Cbuf, Data); + } + } + } + } + + private void UploadVertexArrays(NvGpuVmm Vmm) + { + long IndexPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.IndexArrayAddress); + + int IndexSize = ReadRegister(NvGpuEngine3dReg.IndexArrayFormat); + int IndexFirst = ReadRegister(NvGpuEngine3dReg.IndexBatchFirst); + int IndexCount = ReadRegister(NvGpuEngine3dReg.IndexBatchCount); + + GalIndexFormat IndexFormat = (GalIndexFormat)IndexSize; + + IndexSize = 1 << IndexSize; + + if (IndexSize > 4) + { + throw new InvalidOperationException(); + } + + if (IndexSize != 0) + { + int BufferSize = IndexCount * IndexSize; + + byte[] Data = Vmm.ReadBytes(IndexPosition, BufferSize); + + Gpu.Renderer.SetIndexArray(Data, IndexFormat); + } + + List[] Attribs = new List[32]; + + for (int Attr = 0; Attr < 16; Attr++) + { + int Packed = ReadRegister(NvGpuEngine3dReg.VertexAttribNFormat + Attr); + + int ArrayIndex = Packed & 0x1f; + + if (Attribs[ArrayIndex] == null) + { + Attribs[ArrayIndex] = new List(); + } + + Attribs[ArrayIndex].Add(new GalVertexAttrib( + Attr, + ((Packed >> 6) & 0x1) != 0, + (Packed >> 7) & 0x3fff, + (GalVertexAttribSize)((Packed >> 21) & 0x3f), + (GalVertexAttribType)((Packed >> 27) & 0x7), + ((Packed >> 31) & 0x1) != 0)); + } + + for (int Index = 0; Index < 32; Index++) + { + int VertexFirst = ReadRegister(NvGpuEngine3dReg.VertexArrayFirst); + int VertexCount = ReadRegister(NvGpuEngine3dReg.VertexArrayCount); + + int Control = ReadRegister(NvGpuEngine3dReg.VertexArrayNControl + Index * 4); + + bool Enable = (Control & 0x1000) != 0; + + long VertexPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNAddress + Index * 4); + + if (!Enable) + { + continue; + } + + int Stride = Control & 0xfff; + + long Size = 0; + + if (IndexCount != 0) + { + Size = GetVertexCountFromIndexBuffer( + Vmm, + IndexPosition, + IndexCount, + IndexSize); + } + else + { + Size = VertexCount; + } + + //TODO: Support cases where the Stride is 0. + //In this case, we need to use the size of the attribute. + Size *= Stride; + + byte[] Data = Vmm.ReadBytes(VertexPosition, Size); + + GalVertexAttrib[] AttribArray = Attribs[Index]?.ToArray() ?? new GalVertexAttrib[0]; + + Gpu.Renderer.SetVertexArray(Index, Stride, Data, AttribArray); + + int PrimCtrl = ReadRegister(NvGpuEngine3dReg.VertexBeginGl); + + GalPrimitiveType PrimType = (GalPrimitiveType)(PrimCtrl & 0xffff); + + if (IndexCount != 0) + { + Gpu.Renderer.DrawElements(Index, IndexFirst, PrimType); + } + else + { + Gpu.Renderer.DrawArrays(Index, VertexFirst, VertexCount, PrimType); + } + } + } + + private int GetVertexCountFromIndexBuffer( + NvGpuVmm Vmm, + long IndexPosition, + int IndexCount, + int IndexSize) + { + int MaxIndex = -1; + + if (IndexSize == 2) + { + while (IndexCount -- > 0) + { + ushort Value = Vmm.ReadUInt16(IndexPosition); + + IndexPosition += 2; + + if (MaxIndex < Value) + { + MaxIndex = Value; + } + } + } + else if (IndexSize == 1) + { + while (IndexCount -- > 0) + { + byte Value = Vmm.ReadByte(IndexPosition++); + + if (MaxIndex < Value) + { + MaxIndex = Value; + } + } + } + else if (IndexSize == 4) + { + while (IndexCount -- > 0) + { + uint Value = Vmm.ReadUInt32(IndexPosition); + + IndexPosition += 2; + + if (MaxIndex < Value) + { + MaxIndex = (int)Value; + } + } + } + else + { + throw new ArgumentOutOfRangeException(nameof(IndexSize)); + } + + return MaxIndex + 1; + } + + private void QueryControl(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) + { + long Position = MakeInt64From2xInt32(NvGpuEngine3dReg.QueryAddress); + + int Seq = Registers[(int)NvGpuEngine3dReg.QuerySequence]; + int Ctrl = Registers[(int)NvGpuEngine3dReg.QueryControl]; + + int Mode = Ctrl & 3; + + if (Mode == 0) + { + //Write mode. + Vmm.WriteInt32(Position, Seq); + } + + WriteRegister(PBEntry); + } + + private void CbData(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) + { + long Position = MakeInt64From2xInt32(NvGpuEngine3dReg.ConstBufferAddress); + + int Offset = ReadRegister(NvGpuEngine3dReg.ConstBufferOffset); + + foreach (int Arg in PBEntry.Arguments) + { + Vmm.WriteInt32(Position + Offset, Arg); + + Offset += 4; + } + + WriteRegister(NvGpuEngine3dReg.ConstBufferOffset, Offset); + } + + 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; + + long Position = MakeInt64From2xInt32(NvGpuEngine3dReg.ConstBufferAddress); + + ConstBuffers[Stage][Index].Position = Position; + ConstBuffers[Stage][Index].Enabled = Enabled; + + ConstBuffers[Stage][Index].Size = ReadRegister(NvGpuEngine3dReg.ConstBufferSize); + } + + private long MakeInt64From2xInt32(NvGpuEngine3dReg Reg) + { + return + (long)Registers[(int)Reg + 0] << 32 | + (uint)Registers[(int)Reg + 1]; + } + + private void WriteRegister(NvGpuPBEntry PBEntry) + { + int ArgsCount = PBEntry.Arguments.Count; + + if (ArgsCount > 0) + { + Registers[PBEntry.Method] = PBEntry.Arguments[ArgsCount - 1]; + } + } + + private int ReadRegister(NvGpuEngine3dReg Reg) + { + return Registers[(int)Reg]; + } + + private void WriteRegister(NvGpuEngine3dReg Reg, int Value) + { + Registers[(int)Reg] = Value; + } + + public bool IsFrameBufferPosition(long Position) + { + return FrameBuffers.Contains(Position); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/Gpu/NvGpuEngine3dReg.cs b/Ryujinx.Core/Gpu/NvGpuEngine3dReg.cs new file mode 100644 index 00000000..823885ff --- /dev/null +++ b/Ryujinx.Core/Gpu/NvGpuEngine3dReg.cs @@ -0,0 +1,61 @@ +namespace Ryujinx.Core.Gpu +{ + enum NvGpuEngine3dReg + { + FrameBufferNAddress = 0x200, + FrameBufferNWidth = 0x202, + FrameBufferNHeight = 0x203, + FrameBufferNFormat = 0x204, + ViewportScaleX = 0x280, + ViewportScaleY = 0x281, + ViewportScaleZ = 0x282, + ViewportTranslateX = 0x283, + ViewportTranslateY = 0x284, + ViewportTranslateZ = 0x285, + VertexArrayFirst = 0x35d, + VertexArrayCount = 0x35e, + VertexAttribNFormat = 0x458, + IBlendEnable = 0x4b9, + BlendSeparateAlpha = 0x4cf, + BlendEquationRgb = 0x4d0, + BlendFuncSrcRgb = 0x4d1, + BlendFuncDstRgb = 0x4d2, + BlendEquationAlpha = 0x4d3, + BlendFuncSrcAlpha = 0x4d4, + BlendFuncDstAlpha = 0x4d6, + BlendEnableMaster = 0x4d7, + IBlendNEnable = 0x4d8, + VertexArrayElemBase = 0x50d, + TexHeaderPoolOffset = 0x55d, + TexSamplerPoolOffset = 0x557, + ShaderAddress = 0x582, + VertexBeginGl = 0x586, + IndexArrayAddress = 0x5f2, + IndexArrayEndAddr = 0x5f4, + IndexArrayFormat = 0x5f6, + IndexBatchFirst = 0x5f7, + IndexBatchCount = 0x5f8, + QueryAddress = 0x6c0, + QuerySequence = 0x6c2, + QueryControl = 0x6c3, + VertexArrayNControl = 0x700, + VertexArrayNAddress = 0x701, + VertexArrayNDivisor = 0x703, + IBlendNSeparateAlpha = 0x780, + IBlendNEquationRgb = 0x781, + IBlendNFuncSrcRgb = 0x782, + IBlendNFuncDstRgb = 0x783, + IBlendNEquationAlpha = 0x784, + IBlendNFuncSrcAlpha = 0x785, + IBlendNFuncDstAlpha = 0x786, + VertexArrayNEndAddr = 0x7c0, + ShaderNControl = 0x800, + ShaderNOffset = 0x801, + ShaderNMaxGprs = 0x803, + ShaderNType = 0x804, + ConstBufferSize = 0x8e0, + ConstBufferAddress = 0x8e1, + ConstBufferOffset = 0x8e3, + TextureCbIndex = 0x982 + } +} \ No newline at end of file diff --git a/Ryujinx.Core/Gpu/NvGpuFifo.cs b/Ryujinx.Core/Gpu/NvGpuFifo.cs new file mode 100644 index 00000000..d0e6fc14 --- /dev/null +++ b/Ryujinx.Core/Gpu/NvGpuFifo.cs @@ -0,0 +1,174 @@ +using System.Collections.Concurrent; + +namespace Ryujinx.Core.Gpu +{ + public class NvGpuFifo + { + private const int MacrosCount = 0x80; + private const int MacroIndexMask = MacrosCount - 1; + + private NvGpu Gpu; + + private ConcurrentQueue<(NvGpuVmm, NvGpuPBEntry)> BufferQueue; + + private NvGpuEngine[] SubChannels; + + private struct CachedMacro + { + public long Position { get; private set; } + + private MacroInterpreter Interpreter; + + public CachedMacro(NvGpuFifo PFifo, INvGpuEngine Engine, long Position) + { + this.Position = Position; + + Interpreter = new MacroInterpreter(PFifo, Engine); + } + + public void PushParam(int Param) + { + Interpreter?.Fifo.Enqueue(Param); + } + + public void Execute(NvGpuVmm Vmm, int Param) + { + Interpreter?.Execute(Vmm, Position, Param); + } + } + + private long CurrMacroPosition; + private int CurrMacroBindIndex; + + private CachedMacro[] Macros; + + public NvGpuFifo(NvGpu Gpu) + { + this.Gpu = Gpu; + + BufferQueue = new ConcurrentQueue<(NvGpuVmm, NvGpuPBEntry)>(); + + SubChannels = new NvGpuEngine[8]; + + Macros = new CachedMacro[MacrosCount]; + } + + public void PushBuffer(NvGpuVmm Vmm, NvGpuPBEntry[] Buffer) + { + foreach (NvGpuPBEntry PBEntry in Buffer) + { + BufferQueue.Enqueue((Vmm, PBEntry)); + } + } + + public void DispatchCalls() + { + while (Step()); + } + + public bool Step() + { + if (BufferQueue.TryDequeue(out (NvGpuVmm Vmm, NvGpuPBEntry PBEntry) Tuple)) + { + CallMethod(Tuple.Vmm, Tuple.PBEntry); + + return true; + } + + return false; + } + + private void CallMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) + { + if (PBEntry.Method < 0x80) + { + switch ((NvGpuFifoMeth)PBEntry.Method) + { + case NvGpuFifoMeth.BindChannel: + { + NvGpuEngine Engine = (NvGpuEngine)PBEntry.Arguments[0]; + + SubChannels[PBEntry.SubChannel] = Engine; + + break; + } + + case NvGpuFifoMeth.SetMacroUploadAddress: + { + CurrMacroPosition = (long)((ulong)PBEntry.Arguments[0] << 2); + + break; + } + + case NvGpuFifoMeth.SendMacroCodeData: + { + long Position = CurrMacroPosition; + + foreach (int Arg in PBEntry.Arguments) + { + Vmm.WriteInt32(Position, Arg); + + CurrMacroPosition += 4; + + Position += 4; + } + break; + } + + case NvGpuFifoMeth.SetMacroBindingIndex: + { + CurrMacroBindIndex = PBEntry.Arguments[0]; + + break; + } + + case NvGpuFifoMeth.BindMacro: + { + long Position = (long)((ulong)PBEntry.Arguments[0] << 2); + + Macros[CurrMacroBindIndex] = new CachedMacro(this, Gpu.Engine3d, Position); + + break; + } + } + } + else + { + switch (SubChannels[PBEntry.SubChannel]) + { + case NvGpuEngine._2d: Call2dMethod(Vmm, PBEntry); break; + case NvGpuEngine._3d: Call3dMethod(Vmm, PBEntry); break; + } + } + } + + private void Call2dMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) + { + Gpu.Engine2d.CallMethod(Vmm, PBEntry); + } + + private void Call3dMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) + { + if (PBEntry.Method < 0xe00) + { + Gpu.Engine3d.CallMethod(Vmm, PBEntry); + } + else + { + int MacroIndex = (PBEntry.Method >> 1) & MacroIndexMask; + + if ((PBEntry.Method & 1) != 0) + { + foreach (int Arg in PBEntry.Arguments) + { + Macros[MacroIndex].PushParam(Arg); + } + } + else + { + Macros[MacroIndex].Execute(Vmm, PBEntry.Arguments[0]); + } + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/Gpu/NvGpuFifoMeth.cs b/Ryujinx.Core/Gpu/NvGpuFifoMeth.cs new file mode 100644 index 00000000..78ec9080 --- /dev/null +++ b/Ryujinx.Core/Gpu/NvGpuFifoMeth.cs @@ -0,0 +1,11 @@ +namespace Ryujinx.Core.Gpu +{ + enum NvGpuFifoMeth + { + BindChannel = 0, + SetMacroUploadAddress = 0x45, + SendMacroCodeData = 0x46, + SetMacroBindingIndex = 0x47, + BindMacro = 0x48 + } +} \ No newline at end of file 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.Core/Gpu/NvGpuPBEntry.cs b/Ryujinx.Core/Gpu/NvGpuPBEntry.cs new file mode 100644 index 00000000..ebf35b9e --- /dev/null +++ b/Ryujinx.Core/Gpu/NvGpuPBEntry.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.ObjectModel; + +namespace Ryujinx.Core.Gpu +{ + public struct NvGpuPBEntry + { + public int Method { get; private set; } + + public int SubChannel { get; private set; } + + private int[] m_Arguments; + + public ReadOnlyCollection Arguments => Array.AsReadOnly(m_Arguments); + + public NvGpuPBEntry(int Method, int SubChannel, params int[] Arguments) + { + this.Method = Method; + this.SubChannel = SubChannel; + this.m_Arguments = Arguments; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/Gpu/NvGpuPushBuffer.cs b/Ryujinx.Core/Gpu/NvGpuPushBuffer.cs new file mode 100644 index 00000000..d5588655 --- /dev/null +++ b/Ryujinx.Core/Gpu/NvGpuPushBuffer.cs @@ -0,0 +1,101 @@ +using System.Collections.Generic; +using System.IO; + +namespace Ryujinx.Core.Gpu +{ + public static class NvGpuPushBuffer + { + private enum SubmissionMode + { + Incrementing = 1, + NonIncrementing = 3, + Immediate = 4, + IncrementOnce = 5 + } + + public static NvGpuPBEntry[] Decode(byte[] Data) + { + using (MemoryStream MS = new MemoryStream(Data)) + { + BinaryReader Reader = new BinaryReader(MS); + + List PushBuffer = new List(); + + bool CanRead() => MS.Position + 4 <= MS.Length; + + while (CanRead()) + { + int Packed = Reader.ReadInt32(); + + int Meth = (Packed >> 0) & 0x1fff; + int SubC = (Packed >> 13) & 7; + int Args = (Packed >> 16) & 0x1fff; + int Mode = (Packed >> 29) & 7; + + switch ((SubmissionMode)Mode) + { + case SubmissionMode.Incrementing: + { + for (int Index = 0; Index < Args && CanRead(); Index++, Meth++) + { + PushBuffer.Add(new NvGpuPBEntry(Meth, SubC, Reader.ReadInt32())); + } + + break; + } + + case SubmissionMode.NonIncrementing: + { + int[] Arguments = new int[Args]; + + for (int Index = 0; Index < Arguments.Length; Index++) + { + if (!CanRead()) + { + break; + } + + Arguments[Index] = Reader.ReadInt32(); + } + + PushBuffer.Add(new NvGpuPBEntry(Meth, SubC, Arguments)); + + break; + } + + case SubmissionMode.Immediate: + { + PushBuffer.Add(new NvGpuPBEntry(Meth, SubC, Args)); + + break; + } + + case SubmissionMode.IncrementOnce: + { + if (CanRead()) + { + PushBuffer.Add(new NvGpuPBEntry(Meth, SubC, Reader.ReadInt32())); + } + + if (CanRead() && Args > 1) + { + int[] Arguments = new int[Args - 1]; + + for (int Index = 0; Index < Arguments.Length && CanRead(); Index++) + { + Arguments[Index] = Reader.ReadInt32(); + } + + PushBuffer.Add(new NvGpuPBEntry(Meth + 1, SubC, Arguments)); + } + + break; + } + } + } + + return PushBuffer.ToArray(); + } + } + } +} \ No newline at end of file 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.Core/Gpu/Texture.cs b/Ryujinx.Core/Gpu/Texture.cs new file mode 100644 index 00000000..39a35059 --- /dev/null +++ b/Ryujinx.Core/Gpu/Texture.cs @@ -0,0 +1,55 @@ +using Ryujinx.Graphics.Gal; + +namespace Ryujinx.Core.Gpu +{ + public struct Texture + { + public long Position { get; private set; } + + public int Width { get; private set; } + public int Height { get; private set; } + public int Pitch { get; private set; } + + public int BlockHeight { get; private set; } + + public TextureSwizzle Swizzle { get; private set; } + + public GalTextureFormat Format { get; private set; } + + public Texture( + long Position, + int Width, + int Height) + { + this.Position = Position; + this.Width = Width; + this.Height = Height; + + Pitch = 0; + + BlockHeight = 16; + + Swizzle = TextureSwizzle.BlockLinear; + + Format = GalTextureFormat.A8B8G8R8; + } + + public Texture( + long Position, + int Width, + int Height, + int Pitch, + int BlockHeight, + TextureSwizzle Swizzle, + GalTextureFormat Format) + { + this.Position = Position; + this.Width = Width; + this.Height = Height; + this.Pitch = Pitch; + this.BlockHeight = BlockHeight; + this.Swizzle = Swizzle; + this.Format = Format; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/Gpu/TextureFactory.cs b/Ryujinx.Core/Gpu/TextureFactory.cs new file mode 100644 index 00000000..68b48a1f --- /dev/null +++ b/Ryujinx.Core/Gpu/TextureFactory.cs @@ -0,0 +1,83 @@ +using Ryujinx.Graphics.Gal; +using System; + +namespace Ryujinx.Core.Gpu +{ + static class TextureFactory + { + public static GalTexture MakeTexture(NvGpu Gpu, NvGpuVmm Vmm, long TicPosition) + { + int[] Tic = ReadWords(Vmm, TicPosition, 8); + + GalTextureFormat Format = (GalTextureFormat)(Tic[0] & 0x7f); + + long TextureAddress = (uint)Tic[1]; + + TextureAddress |= (long)((ushort)Tic[2]) << 32; + + TextureSwizzle Swizzle = (TextureSwizzle)((Tic[2] >> 21) & 7); + + int Pitch = (Tic[3] & 0xffff) << 5; + + int BlockHeightLog2 = (Tic[3] >> 3) & 7; + + int BlockHeight = 1 << BlockHeightLog2; + + int Width = (Tic[4] & 0xffff) + 1; + int Height = (Tic[5] & 0xffff) + 1; + + Texture Texture = new Texture( + TextureAddress, + Width, + Height, + Pitch, + BlockHeight, + Swizzle, + Format); + + byte[] Data = TextureReader.Read(Vmm, Texture); + + return new GalTexture(Data, Width, Height, Format); + } + + public static GalTextureSampler MakeSampler(NvGpu Gpu, NvGpuVmm Vmm, long TscPosition) + { + int[] Tsc = ReadWords(Vmm, TscPosition, 8); + + GalTextureWrap AddressU = (GalTextureWrap)((Tsc[0] >> 0) & 7); + GalTextureWrap AddressV = (GalTextureWrap)((Tsc[0] >> 3) & 7); + GalTextureWrap AddressP = (GalTextureWrap)((Tsc[0] >> 6) & 7); + + GalTextureFilter MagFilter = (GalTextureFilter) ((Tsc[1] >> 0) & 3); + GalTextureFilter MinFilter = (GalTextureFilter) ((Tsc[1] >> 4) & 3); + GalTextureMipFilter MipFilter = (GalTextureMipFilter)((Tsc[1] >> 6) & 3); + + GalColorF BorderColor = new GalColorF( + BitConverter.Int32BitsToSingle(Tsc[4]), + BitConverter.Int32BitsToSingle(Tsc[5]), + BitConverter.Int32BitsToSingle(Tsc[6]), + BitConverter.Int32BitsToSingle(Tsc[7])); + + return new GalTextureSampler( + AddressU, + AddressV, + AddressP, + MinFilter, + MagFilter, + MipFilter, + BorderColor); + } + + 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] = Vmm.ReadInt32(Position); + } + + return Words; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/Gpu/TextureHelper.cs b/Ryujinx.Core/Gpu/TextureHelper.cs new file mode 100644 index 00000000..f0ebc1f0 --- /dev/null +++ b/Ryujinx.Core/Gpu/TextureHelper.cs @@ -0,0 +1,36 @@ +using ChocolArm64.Memory; +using System; + +namespace Ryujinx.Core.Gpu +{ + static class TextureHelper + { + public static ISwizzle GetSwizzle(Texture Texture, int Width, int Bpp) + { + switch (Texture.Swizzle) + { + case TextureSwizzle.Pitch: + case TextureSwizzle.PitchColorKey: + return new LinearSwizzle(Texture.Pitch, Bpp); + + case TextureSwizzle.BlockLinear: + case TextureSwizzle.BlockLinearColorKey: + return new BlockLinearSwizzle(Width, Bpp, Texture.BlockHeight); + } + + 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.Core/Gpu/TextureReader.cs b/Ryujinx.Core/Gpu/TextureReader.cs new file mode 100644 index 00000000..f3e41046 --- /dev/null +++ b/Ryujinx.Core/Gpu/TextureReader.cs @@ -0,0 +1,160 @@ +using ChocolArm64.Memory; +using Ryujinx.Graphics.Gal; +using System; + +namespace Ryujinx.Core.Gpu +{ + public static class TextureReader + { + public static byte[] Read(IAMemory Memory, Texture Texture) + { + switch (Texture.Format) + { + case GalTextureFormat.A8B8G8R8: return Read4Bpp (Memory, Texture); + case GalTextureFormat.A1B5G5R5: return Read2Bpp (Memory, Texture); + case GalTextureFormat.B5G6R5: return Read2Bpp (Memory, Texture); + case GalTextureFormat.BC1: return Read8Bpt4x4 (Memory, Texture); + case GalTextureFormat.BC2: return Read16Bpt4x4(Memory, Texture); + case GalTextureFormat.BC3: return Read16Bpt4x4(Memory, Texture); + case GalTextureFormat.BC4: return Read8Bpt4x4 (Memory, Texture); + case GalTextureFormat.BC5: return Read16Bpt4x4(Memory, Texture); + } + + throw new NotImplementedException(Texture.Format.ToString()); + } + + private unsafe static byte[] Read2Bpp(IAMemory Memory, Texture Texture) + { + int Width = Texture.Width; + int Height = Texture.Height; + + byte[] Output = new byte[Width * Height * 2]; + + ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, Width, 2); + + (AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition( + Memory, + Texture.Position); + + fixed (byte* BuffPtr = Output) + { + long OutOffs = 0; + + for (int Y = 0; Y < Height; Y++) + for (int X = 0; X < Width; X++) + { + long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y); + + short Pixel = CpuMem.ReadInt16Unchecked(Position + Offset); + + *(short*)(BuffPtr + OutOffs) = Pixel; + + OutOffs += 2; + } + } + + return Output; + } + + private unsafe static byte[] Read4Bpp(IAMemory Memory, Texture Texture) + { + int Width = Texture.Width; + int Height = Texture.Height; + + byte[] Output = new byte[Width * Height * 4]; + + ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, Width, 4); + + (AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition( + Memory, + Texture.Position); + + fixed (byte* BuffPtr = Output) + { + long OutOffs = 0; + + for (int Y = 0; Y < Height; Y++) + for (int X = 0; X < Width; X++) + { + long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y); + + int Pixel = CpuMem.ReadInt32Unchecked(Position + Offset); + + *(int*)(BuffPtr + OutOffs) = Pixel; + + OutOffs += 4; + } + } + + return Output; + } + + private unsafe static byte[] Read8Bpt4x4(IAMemory Memory, Texture Texture) + { + int Width = (Texture.Width + 3) / 4; + int Height = (Texture.Height + 3) / 4; + + byte[] Output = new byte[Width * Height * 8]; + + ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, Width, 8); + + (AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition( + Memory, + Texture.Position); + + fixed (byte* BuffPtr = Output) + { + long OutOffs = 0; + + for (int Y = 0; Y < Height; Y++) + for (int X = 0; X < Width; X++) + { + long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y); + + long Tile = CpuMem.ReadInt64Unchecked(Position + Offset); + + *(long*)(BuffPtr + OutOffs) = Tile; + + OutOffs += 8; + } + } + + return Output; + } + + private unsafe static byte[] Read16Bpt4x4(IAMemory Memory, Texture Texture) + { + int Width = (Texture.Width + 3) / 4; + int Height = (Texture.Height + 3) / 4; + + byte[] Output = new byte[Width * Height * 16]; + + ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, Width, 16); + + (AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition( + Memory, + Texture.Position); + + fixed (byte* BuffPtr = Output) + { + long OutOffs = 0; + + for (int Y = 0; Y < Height; Y++) + for (int X = 0; X < Width; X++) + { + long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y); + + long Tile0 = CpuMem.ReadInt64Unchecked(Position + Offset + 0); + long Tile1 = CpuMem.ReadInt64Unchecked(Position + Offset + 8); + + *(long*)(BuffPtr + OutOffs + 0) = Tile0; + *(long*)(BuffPtr + OutOffs + 8) = Tile1; + + OutOffs += 16; + } + } + + return Output; + } + } +} diff --git a/Ryujinx.Core/Gpu/TextureSwizzle.cs b/Ryujinx.Core/Gpu/TextureSwizzle.cs new file mode 100644 index 00000000..3214f45f --- /dev/null +++ b/Ryujinx.Core/Gpu/TextureSwizzle.cs @@ -0,0 +1,11 @@ +namespace Ryujinx.Core.Gpu +{ + public enum TextureSwizzle + { + _1dBuffer = 0, + PitchColorKey = 1, + Pitch = 2, + BlockLinear = 3, + BlockLinearColorKey = 4 + } +} \ No newline at end of file diff --git a/Ryujinx.Core/Gpu/TextureWriter.cs b/Ryujinx.Core/Gpu/TextureWriter.cs new file mode 100644 index 00000000..125bb8c4 --- /dev/null +++ b/Ryujinx.Core/Gpu/TextureWriter.cs @@ -0,0 +1,48 @@ +using ChocolArm64.Memory; +using Ryujinx.Graphics.Gal; +using System; + +namespace Ryujinx.Core.Gpu +{ + public static class TextureWriter + { + 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()); + } + } + + 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; + + for (int Y = 0; Y < Height; Y++) + for (int X = 0; X < Width; X++) + { + long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y); + + int Pixel = *(int*)(BuffPtr + InOffs); + + 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); + using (AutoResetEvent WaitEvent = new AutoResetEvent(false)) + { + if (!SyncWaits.TryAdd(CurrThread, WaitEvent)) + { + throw new InvalidOperationException(); + } + + Handles[HandlesCount] = WaitEvent; - int HandleIndex; + Process.Scheduler.Suspend(CurrThread.ProcessorId); - ulong Result = 0; + int HandleIndex; - if (Timeout != ulong.MaxValue) - { - HandleIndex = WaitHandle.WaitAny(Handles, NsTimeConverter.GetTimeMs(Timeout)); + 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}!"); - Process.Scheduler.Resume(CurrThread); + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); - ThreadState.X0 = Result; + return; + } - 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(); - - Context.ResponseData.Write(0); + int Result; - 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) - { - long Position = Context.Request.GetSendBuffPtr(); - - int Fd = Context.Memory.ReadInt32(Position); - - return 0; - } - - private long NvGpuAsIoctlAllocSpace(ServiceCtx Context) - { - long Position = Context.Request.GetSendBuffPtr(); - - MemReader Reader = new MemReader(Context.Memory, Position); - - int Pages = Reader.ReadInt32(); - int PageSize = Reader.ReadInt32(); - int Flags = Reader.ReadInt32(); - int Padding = Reader.ReadInt32(); - long Align = Reader.ReadInt64(); - - if ((Flags & 1) != 0) - { - Align = Context.Ns.Gpu.ReserveMemory(Align, (long)Pages * PageSize, 1); - } - else - { - Align = Context.Ns.Gpu.ReserveMemory((long)Pages * PageSize, Align); - } - - 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) - { - //This is used to store offsets for the Framebuffer(s); - NvMapFb MapFb = (NvMapFb)NvMapsFb.GetData(Context.Process, 0); - - MapFb.AddBufferOffset(BuffAddr); - - return 0; - } - - 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; - } - - private long NvGpuAsIoctlGetVaRegions(ServiceCtx Context) - { - 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; - } - - private long NvGpuAsIoctlInitializeEx(ServiceCtx Context) - { - 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; - } - - private long NvGpuAsIoctlRemap(ServiceCtx Context) - { - Context.RequestData.BaseStream.Seek(-4, SeekOrigin.Current); - - int Cmd = Context.RequestData.ReadInt32(); - - int Size = (Cmd >> 16) & 0xff; - - 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) + private static int ProcessIoctlNvGpuAS(ServiceCtx Context, int Cmd) { - 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; + return ProcessIoctl(Context, Cmd, NvGpuASIoctl.ProcessIoctl); } - private long NvMapIoctlChannelSetErrorNotifier(ServiceCtx Context) + private static int ProcessIoctlNvHostCtrl(ServiceCtx Context, int Cmd) { - 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; + return ProcessIoctl(Context, Cmd, NvHostCtrlIoctl.ProcessIoctl); } - private long NvMapIoctlChannelSetPriority(ServiceCtx Context) + private static int ProcessIoctlNvGpuGpu(ServiceCtx Context, int Cmd) { - long Position = Context.Request.GetSendBuffPtr(); - - int Priority = Context.Memory.ReadInt32(Position); - - return 0; + return ProcessIoctl(Context, Cmd, NvGpuGpuIoctl.ProcessIoctl); } - private long NvMapIoctlChannelAllocGpFifoEx2(ServiceCtx Context) + private static int ProcessIoctlNvHostChannel(ServiceCtx Context, int Cmd) { - 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; + return ProcessIoctl(Context, Cmd, NvHostChannelIoctl.ProcessIoctl); } - private long NvMapIocCreate(ServiceCtx Context) + private static int ProcessIoctlNvMap(ServiceCtx Context, int Cmd) { - 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; + return ProcessIoctl(Context, Cmd, NvMapIoctl.ProcessIoctl); } - private long NvMapIocFromId(ServiceCtx Context) + private static int ProcessIoctl(ServiceCtx Context, int Cmd, IoctlProcessor Processor) { - long Position = Context.Request.GetSendBuffPtr(); - - int Id = Context.Memory.ReadInt32(Position); - - NvMap Map = NvMapsById.GetData(Context.Process, Id); - - if (Map == null) + if (CmdIn(Cmd) && Context.Request.GetBufferType0x21Position() == 0) { - Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Invalid NvMap Id {Id}!"); + Context.Ns.Log.PrintError(LogClass.ServiceNv, "Input buffer is null!"); - return -1; //TODO: Corrent error code. + return NvResult.InvalidInput; } - 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) + if (CmdOut(Cmd) && Context.Request.GetBufferType0x22Position() == 0) { - Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Invalid NvMap Handle {Handle}!"); + Context.Ns.Log.PrintError(LogClass.ServiceNv, "Output buffer is null!"); - return -1; //TODO: Corrent error code. + return NvResult.InvalidInput; } - Map.CpuAddress = Addr; - Map.Align = Align; - Map.Kind = Kind; - - return 0; + return Processor(Context, Cmd); } - private long NvMapIocFree(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 + 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; + return ((Cmd >> 30) & 1) != 0; } - private long NvMapIocParam(ServiceCtx Context) + private static bool CmdOut(int Cmd) { - 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; + return ((Cmd >> 31) & 1) != 0; } - private long NvMapIocGetId(ServiceCtx Context) + public static void UnloadProcess(Process Process) { - long Position = Context.Request.GetSendBuffPtr(); - - int Handle = Context.Memory.ReadInt32(Position + 4); - - NvMap Map = NvMaps.GetData(Context.Process, Handle); + Fds.DeleteProcess(Process); - if (Map == null) - { - Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Invalid NvMap Handle {Handle}!"); - - return -1; //TODO: Corrent error code. - } + NvGpuASIoctl.UnloadProcess(Process); - Context.Memory.WriteInt32(Position, Map.Id); + NvHostCtrlIoctl.UnloadProcess(Process); - 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/BlockLinearSwizzle.cs b/Ryujinx.Graphics/Gpu/BlockLinearSwizzle.cs deleted file mode 100644 index d2cbb144..00000000 --- a/Ryujinx.Graphics/Gpu/BlockLinearSwizzle.cs +++ /dev/null @@ -1,57 +0,0 @@ -namespace Ryujinx.Graphics.Gpu -{ - class BlockLinearSwizzle : ISwizzle - { - private int BhShift; - private int BppShift; - private int BhMask; - - private int XShift; - private int GobStride; - - public BlockLinearSwizzle(int Width, int Bpp, int BlockHeight = 16) - { - BhMask = (BlockHeight * 8) - 1; - - BhShift = CountLsbZeros(BlockHeight * 8); - BppShift = CountLsbZeros(Bpp); - - int WidthInGobs = Width * Bpp / 64; - - GobStride = 512 * BlockHeight * WidthInGobs; - - XShift = CountLsbZeros(512 * BlockHeight); - } - - private int CountLsbZeros(int Value) - { - int Count = 0; - - while (((Value >> Count) & 1) == 0) - { - Count++; - } - - return Count; - } - - public int GetSwizzleOffset(int X, int Y) - { - X <<= BppShift; - - int Position = (Y >> BhShift) * GobStride; - - Position += (X >> 6) << XShift; - - Position += ((Y & BhMask) >> 3) << 9; - - Position += ((X & 0x3f) >> 5) << 8; - Position += ((Y & 0x07) >> 1) << 6; - Position += ((X & 0x1f) >> 4) << 5; - Position += ((Y & 0x01) >> 0) << 4; - Position += ((X & 0x0f) >> 0) << 0; - - return Position; - } - } -} \ No newline at end of file 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/ISwizzle.cs b/Ryujinx.Graphics/Gpu/ISwizzle.cs deleted file mode 100644 index 755051d0..00000000 --- a/Ryujinx.Graphics/Gpu/ISwizzle.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Ryujinx.Graphics.Gpu -{ - interface ISwizzle - { - int GetSwizzleOffset(int X, int Y); - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gpu/LinearSwizzle.cs b/Ryujinx.Graphics/Gpu/LinearSwizzle.cs deleted file mode 100644 index c7a6b304..00000000 --- a/Ryujinx.Graphics/Gpu/LinearSwizzle.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Ryujinx.Graphics.Gpu -{ - class LinearSwizzle : ISwizzle - { - private int Pitch; - private int Bpp; - - public LinearSwizzle(int Pitch, int Bpp) - { - this.Pitch = Pitch; - this.Bpp = Bpp; - } - - public int GetSwizzleOffset(int X, int Y) - { - return X * Bpp + Y * Pitch; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gpu/MacroInterpreter.cs b/Ryujinx.Graphics/Gpu/MacroInterpreter.cs deleted file mode 100644 index 233baac8..00000000 --- a/Ryujinx.Graphics/Gpu/MacroInterpreter.cs +++ /dev/null @@ -1,420 +0,0 @@ -using ChocolArm64.Memory; -using System; -using System.Collections.Generic; - -namespace Ryujinx.Graphics.Gpu -{ - class MacroInterpreter - { - private enum AssignmentOperation - { - IgnoreAndFetch = 0, - Move = 1, - MoveAndSetMaddr = 2, - FetchAndSend = 3, - MoveAndSend = 4, - FetchAndSetMaddr = 5, - MoveAndSetMaddrThenFetchAndSend = 6, - MoveAndSetMaddrThenSendHigh = 7 - } - - private enum AluOperation - { - AluReg = 0, - AddImmediate = 1, - BitfieldReplace = 2, - BitfieldExtractLslImm = 3, - BitfieldExtractLslReg = 4, - ReadImmediate = 5 - } - - private enum AluRegOperation - { - Add = 0, - AddWithCarry = 1, - Subtract = 2, - SubtractWithBorrow = 3, - BitwiseExclusiveOr = 8, - BitwiseOr = 9, - BitwiseAnd = 10, - BitwiseAndNot = 11, - BitwiseNotAnd = 12 - } - - private NvGpuFifo PFifo; - private INvGpuEngine Engine; - - public Queue Fifo { get; private set; } - - private int[] Gprs; - - private int MethAddr; - private int MethIncr; - - private bool Carry; - - private int OpCode; - - private int PipeOp; - - private long Pc; - - public MacroInterpreter(NvGpuFifo PFifo, INvGpuEngine Engine) - { - this.PFifo = PFifo; - this.Engine = Engine; - - Fifo = new Queue(); - - Gprs = new int[8]; - } - - public void Execute(AMemory Memory, long Position, int Param) - { - Reset(); - - Gprs[1] = Param; - - Pc = Position; - - FetchOpCode(Memory); - - while (Step(Memory)); - - //Due to the delay slot, we still need to execute - //one more instruction before we actually exit. - Step(Memory); - } - - private void Reset() - { - for (int Index = 0; Index < Gprs.Length; Index++) - { - Gprs[Index] = 0; - } - - MethAddr = 0; - MethIncr = 0; - - Carry = false; - } - - private bool Step(AMemory Memory) - { - long BaseAddr = Pc - 4; - - FetchOpCode(Memory); - - if ((OpCode & 7) < 7) - { - //Operation produces a value. - AssignmentOperation AsgOp = (AssignmentOperation)((OpCode >> 4) & 7); - - int Result = GetAluResult(); - - switch (AsgOp) - { - //Fetch parameter and ignore result. - case AssignmentOperation.IgnoreAndFetch: - { - SetDstGpr(FetchParam()); - - break; - } - - //Move result. - case AssignmentOperation.Move: - { - SetDstGpr(Result); - - break; - } - - //Move result and use as Method Address. - case AssignmentOperation.MoveAndSetMaddr: - { - SetDstGpr(Result); - - SetMethAddr(Result); - - break; - } - - //Fetch parameter and send result. - case AssignmentOperation.FetchAndSend: - { - SetDstGpr(FetchParam()); - - Send(Memory, Result); - - break; - } - - //Move and send result. - case AssignmentOperation.MoveAndSend: - { - SetDstGpr(Result); - - Send(Memory, Result); - - break; - } - - //Fetch parameter and use result as Method Address. - case AssignmentOperation.FetchAndSetMaddr: - { - SetDstGpr(FetchParam()); - - SetMethAddr(Result); - - break; - } - - //Move result and use as Method Address, then fetch and send paramter. - case AssignmentOperation.MoveAndSetMaddrThenFetchAndSend: - { - SetDstGpr(Result); - - SetMethAddr(Result); - - Send(Memory, FetchParam()); - - break; - } - - //Move result and use as Method Address, then send bits 17:12 of result. - case AssignmentOperation.MoveAndSetMaddrThenSendHigh: - { - SetDstGpr(Result); - - SetMethAddr(Result); - - Send(Memory, (Result >> 12) & 0x3f); - - break; - } - } - } - else - { - //Branch. - bool OnNotZero = ((OpCode >> 4) & 1) != 0; - - bool Taken = OnNotZero - ? GetGprA() != 0 - : GetGprA() == 0; - - if (Taken) - { - Pc = BaseAddr + (GetImm() << 2); - - bool NoDelays = (OpCode & 0x20) != 0; - - if (NoDelays) - { - FetchOpCode(Memory); - } - - return true; - } - } - - bool Exit = (OpCode & 0x80) != 0; - - return !Exit; - } - - private void FetchOpCode(AMemory Memory) - { - OpCode = PipeOp; - - PipeOp = Memory.ReadInt32(Pc); - - Pc += 4; - } - - private int GetAluResult() - { - AluOperation Op = (AluOperation)(OpCode & 7); - - switch (Op) - { - case AluOperation.AluReg: - { - AluRegOperation AluOp = (AluRegOperation)((OpCode >> 17) & 0x1f); - - return GetAluResult(AluOp, GetGprA(), GetGprB()); - } - - case AluOperation.AddImmediate: - { - return GetGprA() + GetImm(); - } - - case AluOperation.BitfieldReplace: - case AluOperation.BitfieldExtractLslImm: - case AluOperation.BitfieldExtractLslReg: - { - int BfSrcBit = (OpCode >> 17) & 0x1f; - int BfSize = (OpCode >> 22) & 0x1f; - int BfDstBit = (OpCode >> 27) & 0x1f; - - int BfMask = (1 << BfSize) - 1; - - int Dst = GetGprA(); - int Src = GetGprB(); - - switch (Op) - { - case AluOperation.BitfieldReplace: - { - Src = (int)((uint)Src >> BfSrcBit) & BfMask; - - Dst &= ~(BfMask << BfDstBit); - - Dst |= Src << BfDstBit; - - return Dst; - } - - case AluOperation.BitfieldExtractLslImm: - { - Src = (int)((uint)Src >> Dst) & BfMask; - - return Src << BfDstBit; - } - - case AluOperation.BitfieldExtractLslReg: - { - Src = (int)((uint)Src >> BfSrcBit) & BfMask; - - return Src << Dst; - } - } - - break; - } - - case AluOperation.ReadImmediate: - { - return Read(GetGprA() + GetImm()); - } - } - - throw new ArgumentException(nameof(OpCode)); - } - - private int GetAluResult(AluRegOperation AluOp, int A, int B) - { - switch (AluOp) - { - case AluRegOperation.Add: - { - ulong Result = (ulong)A + (ulong)B; - - Carry = Result > 0xffffffff; - - return (int)Result; - } - - case AluRegOperation.AddWithCarry: - { - ulong Result = (ulong)A + (ulong)B + (Carry ? 1UL : 0UL); - - Carry = Result > 0xffffffff; - - return (int)Result; - } - - case AluRegOperation.Subtract: - { - ulong Result = (ulong)A - (ulong)B; - - Carry = Result < 0x100000000; - - return (int)Result; - } - - case AluRegOperation.SubtractWithBorrow: - { - ulong Result = (ulong)A - (ulong)B - (Carry ? 0UL : 1UL); - - Carry = Result < 0x100000000; - - return (int)Result; - } - - case AluRegOperation.BitwiseExclusiveOr: return A ^ B; - case AluRegOperation.BitwiseOr: return A | B; - case AluRegOperation.BitwiseAnd: return A & B; - case AluRegOperation.BitwiseAndNot: return A & ~B; - case AluRegOperation.BitwiseNotAnd: return ~(A & B); - } - - throw new ArgumentOutOfRangeException(nameof(AluOp)); - } - - private int GetImm() - { - //Note: The immediate is signed, the sign-extension is intended here. - return OpCode >> 14; - } - - private void SetMethAddr(int Value) - { - MethAddr = (Value >> 0) & 0xfff; - MethIncr = (Value >> 12) & 0x3f; - } - - private void SetDstGpr(int Value) - { - Gprs[(OpCode >> 8) & 7] = Value; - } - - private int GetGprA() - { - return GetGprValue((OpCode >> 11) & 7); - } - - private int GetGprB() - { - return GetGprValue((OpCode >> 14) & 7); - } - - private int GetGprValue(int Index) - { - return Index != 0 ? Gprs[Index] : 0; - } - - private int FetchParam() - { - int Value; - - //If we don't have any parameters in the FIFO, - //keep running the PFIFO engine until it writes the parameters. - while (!Fifo.TryDequeue(out Value)) - { - if (!PFifo.Step()) - { - return 0; - } - } - - return Value; - } - - private int Read(int Reg) - { - return Engine.Registers[Reg]; - } - - private void Send(AMemory Memory, int Value) - { - NsGpuPBEntry PBEntry = new NsGpuPBEntry(MethAddr, 0, Value); - - Engine.CallMethod(Memory, PBEntry); - - MethAddr += MethIncr; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gpu/NsGpu.cs b/Ryujinx.Graphics/Gpu/NsGpu.cs deleted file mode 100644 index e1088982..00000000 --- a/Ryujinx.Graphics/Gpu/NsGpu.cs +++ /dev/null @@ -1,74 +0,0 @@ -using Ryujinx.Graphics.Gal; -using System.Threading; - -namespace Ryujinx.Graphics.Gpu -{ - public class NsGpu - { - public IGalRenderer Renderer { get; private set; } - - public NsGpuMemoryMgr MemoryMgr { get; private set; } - - public NvGpuFifo Fifo { get; private set; } - - public NvGpuEngine2d Engine2d { get; private set; } - public NvGpuEngine3d Engine3d { get; private set; } - - private Thread FifoProcessing; - - private bool KeepRunning; - - public NsGpu(IGalRenderer Renderer) - { - this.Renderer = Renderer; - - MemoryMgr = new NsGpuMemoryMgr(); - - Fifo = new NvGpuFifo(this); - - Engine2d = new NvGpuEngine2d(this); - Engine3d = new NvGpuEngine3d(this); - - KeepRunning = true; - - FifoProcessing = new Thread(ProcessFifo); - - 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) - { - Fifo.DispatchCalls(); - - Thread.Yield(); - } - } - } -} \ 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/NsGpuPBEntry.cs b/Ryujinx.Graphics/Gpu/NsGpuPBEntry.cs deleted file mode 100644 index d405a93c..00000000 --- a/Ryujinx.Graphics/Gpu/NsGpuPBEntry.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using System.Collections.ObjectModel; - -namespace Ryujinx.Graphics.Gpu -{ - public struct NsGpuPBEntry - { - public int Method { get; private set; } - - public int SubChannel { get; private set; } - - private int[] m_Arguments; - - public ReadOnlyCollection Arguments => Array.AsReadOnly(m_Arguments); - - public NsGpuPBEntry(int Method, int SubChannel, params int[] Arguments) - { - this.Method = Method; - this.SubChannel = SubChannel; - this.m_Arguments = Arguments; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gpu/NvGpuEngine.cs b/Ryujinx.Graphics/Gpu/NvGpuEngine.cs deleted file mode 100644 index 624915d0..00000000 --- a/Ryujinx.Graphics/Gpu/NvGpuEngine.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Ryujinx.Graphics.Gpu -{ - enum NvGpuEngine - { - _2d = 0x902d, - _3d = 0xb197, - Compute = 0xb1c0, - Kepler = 0xa140, - Dma = 0xb0b5 - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gpu/NvGpuEngine2d.cs b/Ryujinx.Graphics/Gpu/NvGpuEngine2d.cs deleted file mode 100644 index c2bee167..00000000 --- a/Ryujinx.Graphics/Gpu/NvGpuEngine2d.cs +++ /dev/null @@ -1,158 +0,0 @@ -using ChocolArm64.Memory; -using Ryujinx.Graphics.Gal; -using System.Collections.Generic; - -namespace Ryujinx.Graphics.Gpu -{ - public class NvGpuEngine2d : INvGpuEngine - { - private enum CopyOperation - { - SrcCopyAnd, - RopAnd, - Blend, - SrcCopy, - Rop, - SrcCopyPremult, - BlendPremult - } - - public int[] Registers { get; private set; } - - private NsGpu Gpu; - - private Dictionary Methods; - - public NvGpuEngine2d(NsGpu Gpu) - { - this.Gpu = Gpu; - - Registers = new int[0xe00]; - - Methods = new Dictionary(); - - void AddMethod(int Meth, int Count, int Stride, NvGpuMethod Method) - { - while (Count-- > 0) - { - Methods.Add(Meth, Method); - - Meth += Stride; - } - } - - AddMethod(0xb5, 1, 1, TextureCopy); - } - - public void CallMethod(AMemory Memory, NsGpuPBEntry PBEntry) - { - if (Methods.TryGetValue(PBEntry.Method, out NvGpuMethod Method)) - { - Method(Memory, PBEntry); - } - else - { - WriteRegister(PBEntry); - } - } - - private void TextureCopy(AMemory Memory, NsGpuPBEntry PBEntry) - { - CopyOperation Operation = (CopyOperation)ReadRegister(NvGpuEngine2dReg.CopyOperation); - - bool SrcLinear = ReadRegister(NvGpuEngine2dReg.SrcLinear) != 0; - int SrcWidth = ReadRegister(NvGpuEngine2dReg.SrcWidth); - int SrcHeight = ReadRegister(NvGpuEngine2dReg.SrcHeight); - - bool DstLinear = ReadRegister(NvGpuEngine2dReg.DstLinear) != 0; - int DstWidth = ReadRegister(NvGpuEngine2dReg.DstWidth); - int DstHeight = ReadRegister(NvGpuEngine2dReg.DstHeight); - int DstPitch = ReadRegister(NvGpuEngine2dReg.DstPitch); - int DstBlkDim = ReadRegister(NvGpuEngine2dReg.DstBlockDimensions); - - TextureSwizzle DstSwizzle = DstLinear - ? TextureSwizzle.Pitch - : TextureSwizzle.BlockLinear; - - int DstBlockHeight = 1 << ((DstBlkDim >> 4) & 0xf); - - long Tag = MakeInt64From2xInt32(NvGpuEngine2dReg.SrcAddress); - - TryGetCpuAddr(NvGpuEngine2dReg.SrcAddress, out long SrcAddress); - TryGetCpuAddr(NvGpuEngine2dReg.DstAddress, out long DstAddress); - - bool IsFbTexture = Gpu.Engine3d.IsFrameBufferPosition(Tag); - - if (IsFbTexture && DstLinear) - { - DstSwizzle = TextureSwizzle.BlockLinear; - } - - Texture DstTexture = new Texture( - DstAddress, - DstWidth, - DstHeight, - DstBlockHeight, - DstBlockHeight, - DstSwizzle, - GalTextureFormat.A8B8G8R8); - - if (IsFbTexture) - { - Gpu.Renderer.GetFrameBufferData(Tag, (byte[] Buffer) => - { - CopyTexture(Memory, DstTexture, Buffer); - }); - } - else - { - long Size = SrcWidth * SrcHeight * 4; - - byte[] Buffer = AMemoryHelper.ReadBytes(Memory, SrcAddress, Size); - - CopyTexture(Memory, DstTexture, Buffer); - } - } - - private void CopyTexture(AMemory Memory, 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; - } - - private long MakeInt64From2xInt32(NvGpuEngine2dReg Reg) - { - return - (long)Registers[(int)Reg + 0] << 32 | - (uint)Registers[(int)Reg + 1]; - } - - private void WriteRegister(NsGpuPBEntry PBEntry) - { - int ArgsCount = PBEntry.Arguments.Count; - - if (ArgsCount > 0) - { - Registers[PBEntry.Method] = PBEntry.Arguments[ArgsCount - 1]; - } - } - - private int ReadRegister(NvGpuEngine2dReg Reg) - { - return Registers[(int)Reg]; - } - - private void WriteRegister(NvGpuEngine2dReg Reg, int Value) - { - Registers[(int)Reg] = Value; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gpu/NvGpuEngine2dReg.cs b/Ryujinx.Graphics/Gpu/NvGpuEngine2dReg.cs deleted file mode 100644 index 903baca8..00000000 --- a/Ryujinx.Graphics/Gpu/NvGpuEngine2dReg.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace Ryujinx.Graphics.Gpu -{ - enum NvGpuEngine2dReg - { - DstFormat = 0x80, - DstLinear = 0x81, - DstBlockDimensions = 0x82, - DstDepth = 0x83, - DstLayer = 0x84, - DstPitch = 0x85, - DstWidth = 0x86, - DstHeight = 0x87, - DstAddress = 0x88, - SrcFormat = 0x8c, - SrcLinear = 0x8d, - SrcBlockDimensions = 0x8e, - SrcDepth = 0x8f, - SrcLayer = 0x90, - SrcPitch = 0x91, - SrcWidth = 0x92, - SrcHeight = 0x93, - SrcAddress = 0x94, - CopyOperation = 0xab - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs b/Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs deleted file mode 100644 index a6696650..00000000 --- a/Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs +++ /dev/null @@ -1,581 +0,0 @@ -using ChocolArm64.Memory; -using Ryujinx.Graphics.Gal; -using System; -using System.Collections.Generic; - -namespace Ryujinx.Graphics.Gpu -{ - public class NvGpuEngine3d : INvGpuEngine - { - public int[] Registers { get; private set; } - - private NsGpu Gpu; - - private Dictionary Methods; - - private struct ConstBuffer - { - public bool Enabled; - public long Position; - public int Size; - } - - private ConstBuffer[] ConstBuffers; - - private HashSet FrameBuffers; - - public NvGpuEngine3d(NsGpu Gpu) - { - this.Gpu = Gpu; - - Registers = new int[0xe00]; - - Methods = new Dictionary(); - - void AddMethod(int Meth, int Count, int Stride, NvGpuMethod Method) - { - while (Count-- > 0) - { - Methods.Add(Meth, Method); - - Meth += Stride; - } - } - - AddMethod(0x585, 1, 1, VertexEndGl); - AddMethod(0x674, 1, 1, ClearBuffers); - AddMethod(0x6c3, 1, 1, QueryControl); - AddMethod(0x8e4, 16, 1, CbData); - AddMethod(0x904, 1, 1, CbBind); - - ConstBuffers = new ConstBuffer[18]; - - FrameBuffers = new HashSet(); - } - - public void CallMethod(AMemory Memory, NsGpuPBEntry PBEntry) - { - if (Methods.TryGetValue(PBEntry.Method, out NvGpuMethod Method)) - { - Method(Memory, PBEntry); - } - else - { - WriteRegister(PBEntry); - } - } - - private void VertexEndGl(AMemory Memory, NsGpuPBEntry PBEntry) - { - SetFrameBuffer(0); - - long[] Tags = UploadShaders(Memory); - - Gpu.Renderer.BindProgram(); - - SetAlphaBlending(); - - UploadTextures(Memory, Tags); - UploadUniforms(Memory); - UploadVertexArrays(Memory); - } - - private void ClearBuffers(AMemory Memory, NsGpuPBEntry PBEntry) - { - int Arg0 = PBEntry.Arguments[0]; - - int FbIndex = (Arg0 >> 6) & 0xf; - - int Layer = (Arg0 >> 10) & 0x3ff; - - GalClearBufferFlags Flags = (GalClearBufferFlags)(Arg0 & 0x3f); - - SetFrameBuffer(0); - - //TODO: Enable this once the frame buffer problems are fixed. - //Gpu.Renderer.ClearBuffers(Layer, Flags); - } - - private void SetFrameBuffer(int FbIndex) - { - long Address = MakeInt64From2xInt32(NvGpuEngine3dReg.FrameBufferNAddress + FbIndex * 0x10); - - FrameBuffers.Add(Address); - - 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); - } - - private long[] UploadShaders(AMemory Memory) - { - long[] Tags = new long[5]; - - long BasePosition = MakeInt64From2xInt32(NvGpuEngine3dReg.ShaderAddress); - - for (int Index = 0; Index < 6; Index++) - { - int Control = ReadRegister(NvGpuEngine3dReg.ShaderNControl + Index * 0x10); - int Offset = ReadRegister(NvGpuEngine3dReg.ShaderNOffset + Index * 0x10); - - //Note: Vertex Program (B) is always enabled. - bool Enable = (Control & 1) != 0 || Index == 1; - - if (!Enable) - { - continue; - } - - 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); - - GalShaderType ShaderType = GetTypeFromProgram(Index); - - Tags[(int)ShaderType] = Tag; - - Gpu.Renderer.CreateShader(Tag, ShaderType, Code); - Gpu.Renderer.BindShader(Tag); - } - - int RawSX = ReadRegister(NvGpuEngine3dReg.ViewportScaleX); - int RawSY = ReadRegister(NvGpuEngine3dReg.ViewportScaleY); - - float SX = BitConverter.Int32BitsToSingle(RawSX); - float SY = BitConverter.Int32BitsToSingle(RawSY); - - float SignX = MathF.Sign(SX); - float SignY = MathF.Sign(SY); - - Gpu.Renderer.SetUniform2F(GalConsts.FlipUniformName, SignX, SignY); - - return Tags; - } - - private static GalShaderType GetTypeFromProgram(int Program) - { - switch (Program) - { - case 0: - case 1: return GalShaderType.Vertex; - case 2: return GalShaderType.TessControl; - case 3: return GalShaderType.TessEvaluation; - case 4: return GalShaderType.Geometry; - case 5: return GalShaderType.Fragment; - } - - throw new ArgumentOutOfRangeException(nameof(Program)); - } - - private void SetAlphaBlending() - { - //TODO: Support independent blend properly. - bool Enable = (ReadRegister(NvGpuEngine3dReg.IBlendNEnable) & 1) != 0; - - Gpu.Renderer.SetBlendEnable(Enable); - - bool BlendSeparateAlpha = (ReadRegister(NvGpuEngine3dReg.IBlendNSeparateAlpha) & 1) != 0; - - GalBlendEquation EquationRgb = (GalBlendEquation)ReadRegister(NvGpuEngine3dReg.IBlendNEquationRgb); - - GalBlendFactor FuncSrcRgb = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.IBlendNFuncSrcRgb); - GalBlendFactor FuncDstRgb = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.IBlendNFuncDstRgb); - - if (BlendSeparateAlpha) - { - GalBlendEquation EquationAlpha = (GalBlendEquation)ReadRegister(NvGpuEngine3dReg.IBlendNEquationAlpha); - - GalBlendFactor FuncSrcAlpha = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.IBlendNFuncSrcAlpha); - GalBlendFactor FuncDstAlpha = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.IBlendNFuncDstAlpha); - - Gpu.Renderer.SetBlendSeparate( - EquationRgb, - EquationAlpha, - FuncSrcRgb, - FuncDstRgb, - FuncSrcAlpha, - FuncDstAlpha); - } - else - { - Gpu.Renderer.SetBlend(EquationRgb, FuncSrcRgb, FuncDstRgb); - } - } - - private void UploadTextures(AMemory Memory, 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; - - for (int Index = 0; Index < Tags.Length; Index++) - { - foreach (ShaderDeclInfo DeclInfo in Gpu.Renderer.GetTextureUsage(Tags[Index])) - { - long Position = BasePosition + Index * Size; - - UploadTexture(Memory, Position, TexIndex, DeclInfo.Index); - - Gpu.Renderer.SetUniform1(DeclInfo.Name, TexIndex); - - TexIndex++; - } - } - } - - private void UploadTexture(AMemory Memory, long BasePosition, int TexIndex, int HndIndex) - { - long Position = BasePosition + HndIndex * 4; - - int TextureHandle = Memory.ReadInt32(Position); - - int TicIndex = (TextureHandle >> 0) & 0xfffff; - int TscIndex = (TextureHandle >> 20) & 0xfff; - - TryGetCpuAddr(NvGpuEngine3dReg.TexHeaderPoolOffset, out long TicPosition); - TryGetCpuAddr(NvGpuEngine3dReg.TexSamplerPoolOffset, out long TscPosition); - - TicPosition += TicIndex * 0x20; - TscPosition += TscIndex * 0x20; - - GalTextureSampler Sampler = TextureFactory.MakeSampler(Gpu, Memory, TscPosition); - - long TextureAddress = Memory.ReadInt64(TicPosition + 4) & 0xffffffffffff; - - if (FrameBuffers.Contains(TextureAddress)) - { - //This texture is a frame buffer texture, - //we shouldn't read anything from memory and bind - //the frame buffer texture instead, since we're not - //really writing anything to memory. - Gpu.Renderer.BindFrameBufferTexture(TextureAddress, TexIndex, Sampler); - } - else - { - GalTexture Texture = TextureFactory.MakeTexture(Gpu, Memory, TicPosition); - - Gpu.Renderer.SetTextureAndSampler(TexIndex, Texture, Sampler); - Gpu.Renderer.BindTexture(TexIndex); - } - } - - private void UploadUniforms(AMemory Memory) - { - long BasePosition = MakeInt64From2xInt32(NvGpuEngine3dReg.ShaderAddress); - - for (int Index = 0; Index < 5; Index++) - { - int Control = ReadRegister(NvGpuEngine3dReg.ShaderNControl + (Index + 1) * 0x10); - int Offset = ReadRegister(NvGpuEngine3dReg.ShaderNOffset + (Index + 1) * 0x10); - - //Note: Vertex Program (B) is always enabled. - bool Enable = (Control & 1) != 0 || Index == 0; - - if (!Enable) - { - continue; - } - - for (int Cbuf = 0; Cbuf < ConstBuffers.Length; Cbuf++) - { - ConstBuffer Cb = ConstBuffers[Cbuf]; - - if (Cb.Enabled) - { - long CbPosition = Cb.Position + Index * Cb.Size; - - byte[] Data = AMemoryHelper.ReadBytes(Memory, CbPosition, (uint)Cb.Size); - - Gpu.Renderer.SetConstBuffer(BasePosition + (uint)Offset, Cbuf, Data); - } - } - } - } - - private void UploadVertexArrays(AMemory Memory) - { - long IndexPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.IndexArrayAddress); - - int IndexSize = ReadRegister(NvGpuEngine3dReg.IndexArrayFormat); - int IndexFirst = ReadRegister(NvGpuEngine3dReg.IndexBatchFirst); - int IndexCount = ReadRegister(NvGpuEngine3dReg.IndexBatchCount); - - GalIndexFormat IndexFormat = (GalIndexFormat)IndexSize; - - IndexSize = 1 << IndexSize; - - if (IndexSize > 4) - { - throw new InvalidOperationException(); - } - - if (IndexSize != 0) - { - IndexPosition = Gpu.GetCpuAddr(IndexPosition); - - int BufferSize = IndexCount * IndexSize; - - byte[] Data = AMemoryHelper.ReadBytes(Memory, IndexPosition, BufferSize); - - Gpu.Renderer.SetIndexArray(Data, IndexFormat); - } - - List[] Attribs = new List[32]; - - for (int Attr = 0; Attr < 16; Attr++) - { - int Packed = ReadRegister(NvGpuEngine3dReg.VertexAttribNFormat + Attr); - - int ArrayIndex = Packed & 0x1f; - - if (Attribs[ArrayIndex] == null) - { - Attribs[ArrayIndex] = new List(); - } - - Attribs[ArrayIndex].Add(new GalVertexAttrib( - Attr, - ((Packed >> 6) & 0x1) != 0, - (Packed >> 7) & 0x3fff, - (GalVertexAttribSize)((Packed >> 21) & 0x3f), - (GalVertexAttribType)((Packed >> 27) & 0x7), - ((Packed >> 31) & 0x1) != 0)); - } - - for (int Index = 0; Index < 32; Index++) - { - int VertexFirst = ReadRegister(NvGpuEngine3dReg.VertexArrayFirst); - int VertexCount = ReadRegister(NvGpuEngine3dReg.VertexArrayCount); - - int Control = ReadRegister(NvGpuEngine3dReg.VertexArrayNControl + Index * 4); - - bool Enable = (Control & 0x1000) != 0; - - long VertexPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNAddress + Index * 4); - - if (!Enable) - { - continue; - } - - int Stride = Control & 0xfff; - - long Size = 0; - - if (IndexCount != 0) - { - Size = GetVertexCountFromIndexBuffer( - Memory, - IndexPosition, - IndexCount, - IndexSize); - } - else - { - Size = VertexCount; - } - - //TODO: Support cases where the Stride is 0. - //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); - - GalVertexAttrib[] AttribArray = Attribs[Index]?.ToArray() ?? new GalVertexAttrib[0]; - - Gpu.Renderer.SetVertexArray(Index, Stride, Data, AttribArray); - - int PrimCtrl = ReadRegister(NvGpuEngine3dReg.VertexBeginGl); - - GalPrimitiveType PrimType = (GalPrimitiveType)(PrimCtrl & 0xffff); - - if (IndexCount != 0) - { - Gpu.Renderer.DrawElements(Index, IndexFirst, PrimType); - } - else - { - Gpu.Renderer.DrawArrays(Index, VertexFirst, VertexCount, PrimType); - } - } - } - - private int GetVertexCountFromIndexBuffer( - AMemory Memory, - long IndexPosition, - int IndexCount, - int IndexSize) - { - int MaxIndex = -1; - - if (IndexSize == 2) - { - while (IndexCount -- > 0) - { - ushort Value = Memory.ReadUInt16(IndexPosition); - - IndexPosition += 2; - - if (MaxIndex < Value) - { - MaxIndex = Value; - } - } - } - else if (IndexSize == 1) - { - while (IndexCount -- > 0) - { - byte Value = Memory.ReadByte(IndexPosition++); - - if (MaxIndex < Value) - { - MaxIndex = Value; - } - } - } - else if (IndexSize == 4) - { - while (IndexCount -- > 0) - { - uint Value = Memory.ReadUInt32(IndexPosition); - - IndexPosition += 2; - - if (MaxIndex < Value) - { - MaxIndex = (int)Value; - } - } - } - else - { - throw new ArgumentOutOfRangeException(nameof(IndexSize)); - } - - return MaxIndex + 1; - } - - private void QueryControl(AMemory Memory, NsGpuPBEntry PBEntry) - { - if (TryGetCpuAddr(NvGpuEngine3dReg.QueryAddress, out long Position)) - { - 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); - } - } - - WriteRegister(PBEntry); - } - - private void CbData(AMemory Memory, NsGpuPBEntry PBEntry) - { - if (TryGetCpuAddr(NvGpuEngine3dReg.ConstBufferNAddress, out long Position)) - { - int Offset = ReadRegister(NvGpuEngine3dReg.ConstBufferNOffset); - - foreach (int Arg in PBEntry.Arguments) - { - Memory.WriteInt32(Position + Offset, Arg); - - Offset += 4; - } - - WriteRegister(NvGpuEngine3dReg.ConstBufferNOffset, Offset); - } - } - - private void CbBind(AMemory Memory, NsGpuPBEntry PBEntry) - { - 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; - - ConstBuffers[Index].Size = ReadRegister(NvGpuEngine3dReg.ConstBufferNSize); - } - } - - 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; - } - - private long MakeInt64From2xInt32(NvGpuEngine3dReg Reg) - { - return - (long)Registers[(int)Reg + 0] << 32 | - (uint)Registers[(int)Reg + 1]; - } - - private void WriteRegister(NsGpuPBEntry PBEntry) - { - int ArgsCount = PBEntry.Arguments.Count; - - if (ArgsCount > 0) - { - Registers[PBEntry.Method] = PBEntry.Arguments[ArgsCount - 1]; - } - } - - private int ReadRegister(NvGpuEngine3dReg Reg) - { - return Registers[(int)Reg]; - } - - private void WriteRegister(NvGpuEngine3dReg Reg, int Value) - { - Registers[(int)Reg] = Value; - } - - public bool IsFrameBufferPosition(long Position) - { - return FrameBuffers.Contains(Position); - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gpu/NvGpuEngine3dReg.cs b/Ryujinx.Graphics/Gpu/NvGpuEngine3dReg.cs deleted file mode 100644 index 0d995619..00000000 --- a/Ryujinx.Graphics/Gpu/NvGpuEngine3dReg.cs +++ /dev/null @@ -1,61 +0,0 @@ -namespace Ryujinx.Graphics.Gpu -{ - enum NvGpuEngine3dReg - { - FrameBufferNAddress = 0x200, - FrameBufferNWidth = 0x202, - FrameBufferNHeight = 0x203, - FrameBufferNFormat = 0x204, - ViewportScaleX = 0x280, - ViewportScaleY = 0x281, - ViewportScaleZ = 0x282, - ViewportTranslateX = 0x283, - ViewportTranslateY = 0x284, - ViewportTranslateZ = 0x285, - VertexArrayFirst = 0x35d, - VertexArrayCount = 0x35e, - VertexAttribNFormat = 0x458, - IBlendEnable = 0x4b9, - BlendSeparateAlpha = 0x4cf, - BlendEquationRgb = 0x4d0, - BlendFuncSrcRgb = 0x4d1, - BlendFuncDstRgb = 0x4d2, - BlendEquationAlpha = 0x4d3, - BlendFuncSrcAlpha = 0x4d4, - BlendFuncDstAlpha = 0x4d6, - BlendEnableMaster = 0x4d7, - IBlendNEnable = 0x4d8, - VertexArrayElemBase = 0x50d, - TexHeaderPoolOffset = 0x55d, - TexSamplerPoolOffset = 0x557, - ShaderAddress = 0x582, - VertexBeginGl = 0x586, - IndexArrayAddress = 0x5f2, - IndexArrayEndAddr = 0x5f4, - IndexArrayFormat = 0x5f6, - IndexBatchFirst = 0x5f7, - IndexBatchCount = 0x5f8, - QueryAddress = 0x6c0, - QuerySequence = 0x6c2, - QueryControl = 0x6c3, - VertexArrayNControl = 0x700, - VertexArrayNAddress = 0x701, - VertexArrayNDivisor = 0x703, - IBlendNSeparateAlpha = 0x780, - IBlendNEquationRgb = 0x781, - IBlendNFuncSrcRgb = 0x782, - IBlendNFuncDstRgb = 0x783, - IBlendNEquationAlpha = 0x784, - IBlendNFuncSrcAlpha = 0x785, - IBlendNFuncDstAlpha = 0x786, - VertexArrayNEndAddr = 0x7c0, - ShaderNControl = 0x800, - ShaderNOffset = 0x801, - ShaderNMaxGprs = 0x803, - ShaderNType = 0x804, - ConstBufferNSize = 0x8e0, - ConstBufferNAddress = 0x8e1, - ConstBufferNOffset = 0x8e3, - TextureCbIndex = 0x982 - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gpu/NvGpuFifo.cs b/Ryujinx.Graphics/Gpu/NvGpuFifo.cs deleted file mode 100644 index 68c2902a..00000000 --- a/Ryujinx.Graphics/Gpu/NvGpuFifo.cs +++ /dev/null @@ -1,177 +0,0 @@ -using ChocolArm64.Memory; -using System.Collections.Concurrent; - -namespace Ryujinx.Graphics.Gpu -{ - public class NvGpuFifo - { - private const int MacrosCount = 0x80; - private const int MacroIndexMask = MacrosCount - 1; - - private NsGpu Gpu; - - private ConcurrentQueue<(AMemory, NsGpuPBEntry)> BufferQueue; - - private NvGpuEngine[] SubChannels; - - private struct CachedMacro - { - public long Position { get; private set; } - - private MacroInterpreter Interpreter; - - public CachedMacro(NvGpuFifo PFifo, INvGpuEngine Engine, long Position) - { - this.Position = Position; - - Interpreter = new MacroInterpreter(PFifo, Engine); - } - - public void PushParam(int Param) - { - Interpreter?.Fifo.Enqueue(Param); - } - - public void Execute(AMemory Memory, int Param) - { - Interpreter?.Execute(Memory, Position, Param); - } - } - - private long CurrMacroPosition; - private int CurrMacroBindIndex; - - private CachedMacro[] Macros; - - public NvGpuFifo(NsGpu Gpu) - { - this.Gpu = Gpu; - - BufferQueue = new ConcurrentQueue<(AMemory, NsGpuPBEntry)>(); - - SubChannels = new NvGpuEngine[8]; - - Macros = new CachedMacro[MacrosCount]; - } - - public void PushBuffer(AMemory Memory, NsGpuPBEntry[] Buffer) - { - foreach (NsGpuPBEntry PBEntry in Buffer) - { - BufferQueue.Enqueue((Memory, PBEntry)); - } - } - - public void DispatchCalls() - { - while (Step()); - } - - public bool Step() - { - if (BufferQueue.TryDequeue(out (AMemory Memory, NsGpuPBEntry PBEntry) Tuple)) - { - CallMethod(Tuple.Memory, Tuple.PBEntry); - - return true; - } - - return false; - } - - private void CallMethod(AMemory Memory, NsGpuPBEntry PBEntry) - { - if (PBEntry.Method < 0x80) - { - switch ((NvGpuFifoMeth)PBEntry.Method) - { - case NvGpuFifoMeth.BindChannel: - { - NvGpuEngine Engine = (NvGpuEngine)PBEntry.Arguments[0]; - - SubChannels[PBEntry.SubChannel] = Engine; - - break; - } - - case NvGpuFifoMeth.SetMacroUploadAddress: - { - CurrMacroPosition = (long)((ulong)PBEntry.Arguments[0] << 2); - - break; - } - - case NvGpuFifoMeth.SendMacroCodeData: - { - long Position = Gpu.GetCpuAddr(CurrMacroPosition); - - foreach (int Arg in PBEntry.Arguments) - { - Memory.WriteInt32(Position, Arg); - - CurrMacroPosition += 4; - - Position += 4; - } - break; - } - - case NvGpuFifoMeth.SetMacroBindingIndex: - { - CurrMacroBindIndex = PBEntry.Arguments[0]; - - break; - } - - case NvGpuFifoMeth.BindMacro: - { - long Position = (long)((ulong)PBEntry.Arguments[0] << 2); - - Position = Gpu.GetCpuAddr(Position); - - Macros[CurrMacroBindIndex] = new CachedMacro(this, Gpu.Engine3d, Position); - - break; - } - } - } - else - { - switch (SubChannels[PBEntry.SubChannel]) - { - case NvGpuEngine._2d: Call2dMethod(Memory, PBEntry); break; - case NvGpuEngine._3d: Call3dMethod(Memory, PBEntry); break; - } - } - } - - private void Call2dMethod(AMemory Memory, NsGpuPBEntry PBEntry) - { - Gpu.Engine2d.CallMethod(Memory, PBEntry); - } - - private void Call3dMethod(AMemory Memory, NsGpuPBEntry PBEntry) - { - if (PBEntry.Method < 0xe00) - { - Gpu.Engine3d.CallMethod(Memory, PBEntry); - } - else - { - int MacroIndex = (PBEntry.Method >> 1) & MacroIndexMask; - - if ((PBEntry.Method & 1) != 0) - { - foreach (int Arg in PBEntry.Arguments) - { - Macros[MacroIndex].PushParam(Arg); - } - } - else - { - Macros[MacroIndex].Execute(Memory, PBEntry.Arguments[0]); - } - } - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gpu/NvGpuFifoMeth.cs b/Ryujinx.Graphics/Gpu/NvGpuFifoMeth.cs deleted file mode 100644 index 4287e250..00000000 --- a/Ryujinx.Graphics/Gpu/NvGpuFifoMeth.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Ryujinx.Graphics.Gpu -{ - enum NvGpuFifoMeth - { - BindChannel = 0, - SetMacroUploadAddress = 0x45, - SendMacroCodeData = 0x46, - SetMacroBindingIndex = 0x47, - BindMacro = 0x48 - } -} \ 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 diff --git a/Ryujinx.Graphics/Gpu/NvGpuPushBuffer.cs b/Ryujinx.Graphics/Gpu/NvGpuPushBuffer.cs deleted file mode 100644 index 8cbb3288..00000000 --- a/Ryujinx.Graphics/Gpu/NvGpuPushBuffer.cs +++ /dev/null @@ -1,101 +0,0 @@ -using System.Collections.Generic; -using System.IO; - -namespace Ryujinx.Graphics.Gpu -{ - public static class NvGpuPushBuffer - { - private enum SubmissionMode - { - Incrementing = 1, - NonIncrementing = 3, - Immediate = 4, - IncrementOnce = 5 - } - - public static NsGpuPBEntry[] Decode(byte[] Data) - { - using (MemoryStream MS = new MemoryStream(Data)) - { - BinaryReader Reader = new BinaryReader(MS); - - List PushBuffer = new List(); - - bool CanRead() => MS.Position + 4 <= MS.Length; - - while (CanRead()) - { - int Packed = Reader.ReadInt32(); - - int Meth = (Packed >> 0) & 0x1fff; - int SubC = (Packed >> 13) & 7; - int Args = (Packed >> 16) & 0x1fff; - int Mode = (Packed >> 29) & 7; - - switch ((SubmissionMode)Mode) - { - case SubmissionMode.Incrementing: - { - for (int Index = 0; Index < Args && CanRead(); Index++, Meth++) - { - PushBuffer.Add(new NsGpuPBEntry(Meth, SubC, Reader.ReadInt32())); - } - - break; - } - - case SubmissionMode.NonIncrementing: - { - int[] Arguments = new int[Args]; - - for (int Index = 0; Index < Arguments.Length; Index++) - { - if (!CanRead()) - { - break; - } - - Arguments[Index] = Reader.ReadInt32(); - } - - PushBuffer.Add(new NsGpuPBEntry(Meth, SubC, Arguments)); - - break; - } - - case SubmissionMode.Immediate: - { - PushBuffer.Add(new NsGpuPBEntry(Meth, SubC, Args)); - - break; - } - - case SubmissionMode.IncrementOnce: - { - if (CanRead()) - { - PushBuffer.Add(new NsGpuPBEntry(Meth, SubC, Reader.ReadInt32())); - } - - if (CanRead() && Args > 1) - { - int[] Arguments = new int[Args - 1]; - - for (int Index = 0; Index < Arguments.Length && CanRead(); Index++) - { - Arguments[Index] = Reader.ReadInt32(); - } - - PushBuffer.Add(new NsGpuPBEntry(Meth + 1, SubC, Arguments)); - } - - break; - } - } - } - - return PushBuffer.ToArray(); - } - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gpu/Texture.cs b/Ryujinx.Graphics/Gpu/Texture.cs deleted file mode 100644 index cbfa683d..00000000 --- a/Ryujinx.Graphics/Gpu/Texture.cs +++ /dev/null @@ -1,55 +0,0 @@ -using Ryujinx.Graphics.Gal; - -namespace Ryujinx.Graphics.Gpu -{ - public struct Texture - { - public long Position { get; private set; } - - public int Width { get; private set; } - public int Height { get; private set; } - public int Pitch { get; private set; } - - public int BlockHeight { get; private set; } - - public TextureSwizzle Swizzle { get; private set; } - - public GalTextureFormat Format { get; private set; } - - public Texture( - long Position, - int Width, - int Height) - { - this.Position = Position; - this.Width = Width; - this.Height = Height; - - Pitch = 0; - - BlockHeight = 16; - - Swizzle = TextureSwizzle.BlockLinear; - - Format = GalTextureFormat.A8B8G8R8; - } - - public Texture( - long Position, - int Width, - int Height, - int Pitch, - int BlockHeight, - TextureSwizzle Swizzle, - GalTextureFormat Format) - { - this.Position = Position; - this.Width = Width; - this.Height = Height; - this.Pitch = Pitch; - this.BlockHeight = BlockHeight; - this.Swizzle = Swizzle; - this.Format = Format; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gpu/TextureFactory.cs b/Ryujinx.Graphics/Gpu/TextureFactory.cs deleted file mode 100644 index 7f8580d9..00000000 --- a/Ryujinx.Graphics/Gpu/TextureFactory.cs +++ /dev/null @@ -1,86 +0,0 @@ -using ChocolArm64.Memory; -using Ryujinx.Graphics.Gal; -using System; - -namespace Ryujinx.Graphics.Gpu -{ - static class TextureFactory - { - public static GalTexture MakeTexture(NsGpu Gpu, AMemory Memory, long TicPosition) - { - int[] Tic = ReadWords(Memory, TicPosition, 8); - - GalTextureFormat Format = (GalTextureFormat)(Tic[0] & 0x7f); - - long TextureAddress = (uint)Tic[1]; - - TextureAddress |= (long)((ushort)Tic[2]) << 32; - - TextureAddress = Gpu.GetCpuAddr(TextureAddress); - - TextureSwizzle Swizzle = (TextureSwizzle)((Tic[2] >> 21) & 7); - - int Pitch = (Tic[3] & 0xffff) << 5; - - int BlockHeightLog2 = (Tic[3] >> 3) & 7; - - int BlockHeight = 1 << BlockHeightLog2; - - int Width = (Tic[4] & 0xffff) + 1; - int Height = (Tic[5] & 0xffff) + 1; - - Texture Texture = new Texture( - TextureAddress, - Width, - Height, - Pitch, - BlockHeight, - Swizzle, - Format); - - byte[] Data = TextureReader.Read(Memory, Texture); - - return new GalTexture(Data, Width, Height, Format); - } - - public static GalTextureSampler MakeSampler(NsGpu Gpu, AMemory Memory, long TscPosition) - { - int[] Tsc = ReadWords(Memory, TscPosition, 8); - - GalTextureWrap AddressU = (GalTextureWrap)((Tsc[0] >> 0) & 7); - GalTextureWrap AddressV = (GalTextureWrap)((Tsc[0] >> 3) & 7); - GalTextureWrap AddressP = (GalTextureWrap)((Tsc[0] >> 6) & 7); - - GalTextureFilter MagFilter = (GalTextureFilter) ((Tsc[1] >> 0) & 3); - GalTextureFilter MinFilter = (GalTextureFilter) ((Tsc[1] >> 4) & 3); - GalTextureMipFilter MipFilter = (GalTextureMipFilter)((Tsc[1] >> 6) & 3); - - GalColorF BorderColor = new GalColorF( - BitConverter.Int32BitsToSingle(Tsc[4]), - BitConverter.Int32BitsToSingle(Tsc[5]), - BitConverter.Int32BitsToSingle(Tsc[6]), - BitConverter.Int32BitsToSingle(Tsc[7])); - - return new GalTextureSampler( - AddressU, - AddressV, - AddressP, - MinFilter, - MagFilter, - MipFilter, - BorderColor); - } - - private static int[] ReadWords(AMemory Memory, long Position, int Count) - { - int[] Words = new int[Count]; - - for (int Index = 0; Index < Count; Index++, Position += 4) - { - Words[Index] = Memory.ReadInt32(Position); - } - - return Words; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gpu/TextureHelper.cs b/Ryujinx.Graphics/Gpu/TextureHelper.cs deleted file mode 100644 index d3c2ac14..00000000 --- a/Ryujinx.Graphics/Gpu/TextureHelper.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; - -namespace Ryujinx.Graphics.Gpu -{ - static class TextureHelper - { - public static ISwizzle GetSwizzle(Texture Texture, int Width, int Bpp) - { - switch (Texture.Swizzle) - { - case TextureSwizzle.Pitch: - case TextureSwizzle.PitchColorKey: - return new LinearSwizzle(Texture.Pitch, Bpp); - - case TextureSwizzle.BlockLinear: - case TextureSwizzle.BlockLinearColorKey: - return new BlockLinearSwizzle(Width, Bpp, Texture.BlockHeight); - } - - throw new NotImplementedException(Texture.Swizzle.ToString()); - } - } -} diff --git a/Ryujinx.Graphics/Gpu/TextureReader.cs b/Ryujinx.Graphics/Gpu/TextureReader.cs deleted file mode 100644 index 17fd95c5..00000000 --- a/Ryujinx.Graphics/Gpu/TextureReader.cs +++ /dev/null @@ -1,144 +0,0 @@ -using ChocolArm64.Memory; -using Ryujinx.Graphics.Gal; -using System; - -namespace Ryujinx.Graphics.Gpu -{ - public static class TextureReader - { - public static byte[] Read(AMemory Memory, Texture Texture) - { - switch (Texture.Format) - { - case GalTextureFormat.A8B8G8R8: return Read4Bpp (Memory, Texture); - case GalTextureFormat.A1B5G5R5: return Read2Bpp (Memory, Texture); - case GalTextureFormat.B5G6R5: return Read2Bpp (Memory, Texture); - case GalTextureFormat.BC1: return Read8Bpt4x4 (Memory, Texture); - case GalTextureFormat.BC2: return Read16Bpt4x4(Memory, Texture); - case GalTextureFormat.BC3: return Read16Bpt4x4(Memory, Texture); - case GalTextureFormat.BC4: return Read8Bpt4x4 (Memory, Texture); - case GalTextureFormat.BC5: return Read16Bpt4x4(Memory, Texture); - } - - throw new NotImplementedException(Texture.Format.ToString()); - } - - private unsafe static byte[] Read2Bpp(AMemory Memory, Texture Texture) - { - int Width = Texture.Width; - int Height = Texture.Height; - - byte[] Output = new byte[Width * Height * 2]; - - ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, Width, 2); - - fixed (byte* BuffPtr = Output) - { - long OutOffs = 0; - - for (int Y = 0; Y < Height; Y++) - for (int X = 0; X < Width; X++) - { - long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y); - - short Pixel = Memory.ReadInt16Unchecked(Texture.Position + Offset); - - *(short*)(BuffPtr + OutOffs) = Pixel; - - OutOffs += 2; - } - } - - return Output; - } - - private unsafe static byte[] Read4Bpp(AMemory Memory, Texture Texture) - { - int Width = Texture.Width; - int Height = Texture.Height; - - byte[] Output = new byte[Width * Height * 4]; - - ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, Width, 4); - - fixed (byte* BuffPtr = Output) - { - long OutOffs = 0; - - for (int Y = 0; Y < Height; Y++) - for (int X = 0; X < Width; X++) - { - long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y); - - int Pixel = Memory.ReadInt32Unchecked(Texture.Position + Offset); - - *(int*)(BuffPtr + OutOffs) = Pixel; - - OutOffs += 4; - } - } - - return Output; - } - - private unsafe static byte[] Read8Bpt4x4(AMemory Memory, Texture Texture) - { - int Width = (Texture.Width + 3) / 4; - int Height = (Texture.Height + 3) / 4; - - byte[] Output = new byte[Width * Height * 8]; - - ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, Width, 8); - - fixed (byte* BuffPtr = Output) - { - long OutOffs = 0; - - for (int Y = 0; Y < Height; Y++) - for (int X = 0; X < Width; X++) - { - long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y); - - long Tile = Memory.ReadInt64Unchecked(Texture.Position + Offset); - - *(long*)(BuffPtr + OutOffs) = Tile; - - OutOffs += 8; - } - } - - return Output; - } - - private unsafe static byte[] Read16Bpt4x4(AMemory Memory, Texture Texture) - { - int Width = (Texture.Width + 3) / 4; - int Height = (Texture.Height + 3) / 4; - - byte[] Output = new byte[Width * Height * 16]; - - ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, Width, 16); - - fixed (byte* BuffPtr = Output) - { - long OutOffs = 0; - - for (int Y = 0; Y < Height; Y++) - for (int X = 0; X < Width; X++) - { - long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y); - - long Tile0 = Memory.ReadInt64Unchecked(Texture.Position + Offset + 0); - long Tile1 = Memory.ReadInt64Unchecked(Texture.Position + Offset + 8); - - *(long*)(BuffPtr + OutOffs + 0) = Tile0; - *(long*)(BuffPtr + OutOffs + 8) = Tile1; - - OutOffs += 16; - } - } - - return Output; - } - } -} diff --git a/Ryujinx.Graphics/Gpu/TextureSwizzle.cs b/Ryujinx.Graphics/Gpu/TextureSwizzle.cs deleted file mode 100644 index 7d99279c..00000000 --- a/Ryujinx.Graphics/Gpu/TextureSwizzle.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Ryujinx.Graphics.Gpu -{ - public enum TextureSwizzle - { - _1dBuffer = 0, - PitchColorKey = 1, - Pitch = 2, - BlockLinear = 3, - BlockLinearColorKey = 4 - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gpu/TextureWriter.cs b/Ryujinx.Graphics/Gpu/TextureWriter.cs deleted file mode 100644 index 2f25de73..00000000 --- a/Ryujinx.Graphics/Gpu/TextureWriter.cs +++ /dev/null @@ -1,45 +0,0 @@ -using ChocolArm64.Memory; -using Ryujinx.Graphics.Gal; -using System; - -namespace Ryujinx.Graphics.Gpu -{ - public static class TextureWriter - { - public static void Write(AMemory Memory, Texture Texture, byte[] Data) - { - switch (Texture.Format) - { - case GalTextureFormat.A8B8G8R8: Write4Bpp(Memory, Texture, Data); break; - - default: - throw new NotImplementedException(Texture.Format.ToString()); - } - } - - private unsafe static void Write4Bpp(AMemory Memory, Texture Texture, byte[] Data) - { - int Width = Texture.Width; - int Height = Texture.Height; - - ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, Width, 4); - - fixed (byte* BuffPtr = Data) - { - long InOffs = 0; - - for (int Y = 0; Y < Height; Y++) - for (int X = 0; X < Width; X++) - { - long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y); - - int Pixel = *(int*)(BuffPtr + InOffs); - - Memory.WriteInt32Unchecked(Texture.Position + Offset, Pixel); - - InOffs += 4; - } - } - } - } -} -- cgit v1.2.3