aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Core
diff options
context:
space:
mode:
authorgdkchan <gab.dark.100@gmail.com>2018-05-07 15:53:23 -0300
committerGitHub <noreply@github.com>2018-05-07 15:53:23 -0300
commit34037701c708cb70bbf44dea71ee0912f7b4102b (patch)
treeca4cf2bde85dea48af12033b8d0446f17b611f4f /Ryujinx.Core
parent4419e8d6b43432eae94a3a9304f7df22b34738a8 (diff)
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
Diffstat (limited to 'Ryujinx.Core')
-rw-r--r--Ryujinx.Core/Gpu/BlockLinearSwizzle.cs57
-rw-r--r--Ryujinx.Core/Gpu/INvGpuEngine.cs9
-rw-r--r--Ryujinx.Core/Gpu/ISwizzle.cs7
-rw-r--r--Ryujinx.Core/Gpu/LinearSwizzle.cs19
-rw-r--r--Ryujinx.Core/Gpu/MacroInterpreter.cs419
-rw-r--r--Ryujinx.Core/Gpu/NvGpu.cs45
-rw-r--r--Ryujinx.Core/Gpu/NvGpuEngine.cs11
-rw-r--r--Ryujinx.Core/Gpu/NvGpuEngine2d.cs148
-rw-r--r--Ryujinx.Core/Gpu/NvGpuEngine2dReg.cs25
-rw-r--r--Ryujinx.Core/Gpu/NvGpuEngine3d.cs558
-rw-r--r--Ryujinx.Core/Gpu/NvGpuEngine3dReg.cs61
-rw-r--r--Ryujinx.Core/Gpu/NvGpuFifo.cs174
-rw-r--r--Ryujinx.Core/Gpu/NvGpuFifoMeth.cs11
-rw-r--r--Ryujinx.Core/Gpu/NvGpuMethod.cs4
-rw-r--r--Ryujinx.Core/Gpu/NvGpuPBEntry.cs23
-rw-r--r--Ryujinx.Core/Gpu/NvGpuPushBuffer.cs101
-rw-r--r--Ryujinx.Core/Gpu/NvGpuVmm.cs398
-rw-r--r--Ryujinx.Core/Gpu/Texture.cs55
-rw-r--r--Ryujinx.Core/Gpu/TextureFactory.cs83
-rw-r--r--Ryujinx.Core/Gpu/TextureHelper.cs36
-rw-r--r--Ryujinx.Core/Gpu/TextureReader.cs160
-rw-r--r--Ryujinx.Core/Gpu/TextureSwizzle.cs11
-rw-r--r--Ryujinx.Core/Gpu/TextureWriter.cs48
-rw-r--r--Ryujinx.Core/Loaders/Executable.cs6
-rw-r--r--Ryujinx.Core/OsHle/Horizon.cs4
-rw-r--r--Ryujinx.Core/OsHle/Ipc/IpcHandler.cs4
-rw-r--r--Ryujinx.Core/OsHle/Ipc/IpcMessage.cs25
-rw-r--r--Ryujinx.Core/OsHle/Kernel/KernelErr.cs2
-rw-r--r--Ryujinx.Core/OsHle/Kernel/SvcHandler.cs21
-rw-r--r--Ryujinx.Core/OsHle/Kernel/SvcSystem.cs85
-rw-r--r--Ryujinx.Core/OsHle/Kernel/SvcThreadSync.cs6
-rw-r--r--Ryujinx.Core/OsHle/MemoryAllocator.cs12
-rw-r--r--Ryujinx.Core/OsHle/Process.cs6
-rw-r--r--Ryujinx.Core/OsHle/Services/Nv/INvDrvServices.cs728
-rw-r--r--Ryujinx.Core/OsHle/Services/Nv/NvChNvMap.cs31
-rw-r--r--Ryujinx.Core/OsHle/Services/Nv/NvGpuAS/NvGpuASAllocSpace.cs11
-rw-r--r--Ryujinx.Core/OsHle/Services/Nv/NvGpuAS/NvGpuASIoctl.cs245
-rw-r--r--Ryujinx.Core/OsHle/Services/Nv/NvGpuAS/NvGpuASMapBufferEx.cs13
-rw-r--r--Ryujinx.Core/OsHle/Services/Nv/NvGpuAS/NvGpuASRemap.cs12
-rw-r--r--Ryujinx.Core/OsHle/Services/Nv/NvGpuAS/NvGpuASUnmapBuffer.cs7
-rw-r--r--Ryujinx.Core/OsHle/Services/Nv/NvGpuGpu/NvGpuGpuGetCharacteristics.cs43
-rw-r--r--Ryujinx.Core/OsHle/Services/Nv/NvGpuGpu/NvGpuGpuIoctl.cs155
-rw-r--r--Ryujinx.Core/OsHle/Services/Nv/NvHelper.cs10
-rw-r--r--Ryujinx.Core/OsHle/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs130
-rw-r--r--Ryujinx.Core/OsHle/Services/Nv/NvHostChannel/NvHostChannelSubmitGpfifo.cs11
-rw-r--r--Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlIoctl.cs355
-rw-r--r--Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlSyncPtRead.cs8
-rw-r--r--Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlSyncPtWait.cs9
-rw-r--r--Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlSyncPtWaitEx.cs10
-rw-r--r--Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlUserCtx.cs19
-rw-r--r--Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostEvent.cs10
-rw-r--r--Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostEventState.cs10
-rw-r--r--Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostSyncPt.cs107
-rw-r--r--Ryujinx.Core/OsHle/Services/Nv/NvMap.cs20
-rw-r--r--Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapAlloc.cs12
-rw-r--r--Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapCreate.cs8
-rw-r--r--Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapFree.cs11
-rw-r--r--Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapFromId.cs8
-rw-r--r--Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapGetId.cs8
-rw-r--r--Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapHandle.cs37
-rw-r--r--Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapHandleParam.cs12
-rw-r--r--Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapIoctl.cs302
-rw-r--r--Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapParam.cs9
-rw-r--r--Ryujinx.Core/OsHle/Services/Nv/NvMapFb.cs40
-rw-r--r--Ryujinx.Core/OsHle/Services/Nv/NvResult.cs13
-rw-r--r--Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs42
-rw-r--r--Ryujinx.Core/OsHle/Utilities/IntUtils.cs15
-rw-r--r--Ryujinx.Core/OsHle/Utilities/MemReader.cs44
-rw-r--r--Ryujinx.Core/OsHle/Utilities/MemWriter.cs38
-rw-r--r--Ryujinx.Core/Switch.cs6
70 files changed, 4248 insertions, 935 deletions
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<int> 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<int>();
+
+ 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<int, NvGpuMethod> Methods;
+
+ public NvGpuEngine2d(NvGpu Gpu)
+ {
+ this.Gpu = Gpu;
+
+ Registers = new int[0xe00];
+
+ Methods = new Dictionary<int, NvGpuMethod>();
+
+ 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<int, NvGpuMethod> Methods;
+
+ private struct ConstBuffer
+ {
+ public bool Enabled;
+ public long Position;
+ public int Size;
+ }
+
+ private ConstBuffer[][] ConstBuffers;
+
+ private HashSet<long> FrameBuffers;
+
+ public NvGpuEngine3d(NvGpu Gpu)
+ {
+ this.Gpu = Gpu;
+
+ Registers = new int[0xe00];
+
+ Methods = new Dictionary<int, NvGpuMethod>();
+
+ 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<long>();
+ }
+
+ 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<GalVertexAttrib>[] Attribs = new List<GalVertexAttrib>[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<GalVertexAttrib>();
+ }
+
+ 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<int> 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<NvGpuPBEntry> PushBuffer = new List<NvGpuPBEntry>();
+
+ 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<long, MappedMemory> Maps;
+
+ private const long PteUnmapped = -1;
+ private const long PteReserved = -2;
+
+ private long[][] PageTable;
+
+ public NvGpuVmm(AMemory Memory)
+ {
+ this.Memory = Memory;
+
+ Maps = new ConcurrentDictionary<long, MappedMemory>();
+
+ 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<KThread, AutoResetEvent> 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<KThread, AutoResetEvent>();
+
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<KThread>(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<int, ServiceProcessRequest> m_Commands;
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
- private Dictionary<(string, int), ServiceProcessIoctl> IoctlCmds;
+ private static Dictionary<string, IoctlProcessor> IoctlProcessors =
+ new Dictionary<string, IoctlProcessor>()
+ {
+ { "/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<NvFd>(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<NvMap>(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<NvMap>(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<NvMap>(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<NvMap>(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<NvMap>(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<NvMap>(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<NvMap>(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<Process, IdDictionary> 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<NvMap>(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<Process, NvGpuVmm> Vmms;
+
+ static NvGpuASIoctl()
+ {
+ Vmms = new ConcurrentDictionary<Process, NvGpuVmm>();
+ }
+
+ 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<NvGpuASAllocSpace>(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<NvGpuASAllocSpace>(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<NvGpuASUnmapBuffer>(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<NvGpuASMapBufferEx>(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<NvGpuASRemap>(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<NvGpuGpuGetCharacteristics>(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<NvHostChannelSubmitGpfifo>(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<Process, NvHostCtrlUserCtx> UserCtxs;
+
+ static NvHostCtrlIoctl()
+ {
+ UserCtxs = new ConcurrentDictionary<Process, NvHostCtrlUserCtx>();
+ }
+
+ 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<NvHostCtrlSyncptRead>(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<NvHostCtrlSyncptWait>(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<NvHostCtrlSyncptWaitEx>(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<EventWaitHandle, int> Waiters;
+
+ public NvHostSyncpt()
+ {
+ CounterMin = new int[SyncptsCount];
+ CounterMax = new int[SyncptsCount];
+
+ Waiters = new ConcurrentDictionary<EventWaitHandle, int>();
+ }
+
+ 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<EventWaitHandle, int> 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<Process, IdDictionary> Maps;
+
+ static NvMapIoctl()
+ {
+ Maps = new ConcurrentDictionary<Process, IdDictionary>();
+ }
+
+ 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<NvMapCreate>(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<NvMapFromId>(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<NvMapAlloc>(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<NvMapFree>(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<NvMapParam>(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<NvMapGetId>(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<NvMapHandle>(Handle);
+ }
+
+ return null;
+ }
+
+ public static NvMapHandle GetNvMap(ServiceCtx Context, int Handle)
+ {
+ if (Handle != 0 && Maps.TryGetValue(Context.Process, out IdDictionary Dict))
+ {
+ return Dict.GetData<NvMapHandle>(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<long> BufferOffs;
-
- public NvMapFb()
- {
- BufferOffs = new List<long>();
- }
-
- 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<NvMap>(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();