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
This commit is contained in:
gdkchan 2018-05-07 15:53:23 -03:00 committed by GitHub
parent 4419e8d6b4
commit 34037701c7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
75 changed files with 2472 additions and 1440 deletions

View file

@ -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;
}
}
}

View file

@ -0,0 +1,9 @@
namespace Ryujinx.Core.Gpu
{
interface INvGpuEngine
{
int[] Registers { get; }
void CallMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry);
}
}

View file

@ -0,0 +1,7 @@
namespace Ryujinx.Core.Gpu
{
interface ISwizzle
{
int GetSwizzleOffset(int X, int Y);
}
}

View file

@ -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;
}
}
}

View file

@ -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;
}
}
}

45
Ryujinx.Core/Gpu/NvGpu.cs Normal file
View file

@ -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();
}
}
}
}

View file

@ -0,0 +1,11 @@
namespace Ryujinx.Core.Gpu
{
enum NvGpuEngine
{
_2d = 0x902d,
_3d = 0xb197,
Compute = 0xb1c0,
Kepler = 0xa140,
Dma = 0xb0b5
}
}

View file

@ -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;
}
}
}

View file

@ -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
}
}

View file

@ -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);
}
}
}

View file

@ -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
}
}

View file

@ -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]);
}
}
}
}
}

View file

@ -0,0 +1,11 @@
namespace Ryujinx.Core.Gpu
{
enum NvGpuFifoMeth
{
BindChannel = 0,
SetMacroUploadAddress = 0x45,
SendMacroCodeData = 0x46,
SetMacroBindingIndex = 0x47,
BindMacro = 0x48
}
}

View file

@ -0,0 +1,4 @@
namespace Ryujinx.Core.Gpu
{
delegate void NvGpuMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry);
}

View file

@ -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;
}
}
}

View file

@ -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();
}
}
}
}

View file

@ -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);
}
}
}

View file

@ -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;
}
}
}

View file

@ -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;
}
}
}

View file

@ -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);
}
}
}

View file

@ -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;
}
}
}

View file

@ -0,0 +1,11 @@
namespace Ryujinx.Core.Gpu
{
public enum TextureSwizzle
{
_1dBuffer = 0,
PitchColorKey = 1,
Pitch = 2,
BlockLinear = 3,
BlockLinearColorKey = 4
}
}

View file

@ -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;
}
}
}
}
}

View file

@ -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)
{

View file

@ -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();

View file

@ -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)

View file

@ -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)

View file

@ -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;
}
}

View file

@ -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);

View file

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

View file

@ -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();
}

View file

@ -0,0 +1,12 @@
using System;
namespace Ryujinx.Core.OsHle
{
class MemoryAllocator
{
public bool TryAllocate(long Size, out long Address)
{
throw new NotImplementedException();
}
}
}

View file

@ -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();

View file

@ -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();
int Result;
Context.ResponseData.Write(0);
if (IoctlCmds.TryGetValue((FdData.Name, Cmd), out ServiceProcessIoctl ProcReq))
if (IoctlProcessors.TryGetValue(FdData.Name, out IoctlProcessor Process))
{
return ProcReq(Context);
Result = Process(Context, Cmd);
}
else
{
throw new NotImplementedException($"{FdData.Name} {Cmd:x4}");
}
//TODO: Verify if the error codes needs to be translated.
Context.ResponseData.Write(Result);
return 0;
}
public long Close(ServiceCtx Context)
@ -138,9 +108,9 @@ namespace Ryujinx.Core.OsHle.Services.Nv
long TransferMemSize = Context.RequestData.ReadInt64();
int TransferMemHandle = Context.Request.HandleDesc.ToCopy[0];
Context.ResponseData.Write(0);
NvMapIoctl.InitializeNvMap(Context);
NvMapsFb.Add(Context.Process, 0, new NvMapFb());
Context.ResponseData.Write(0);
return 0;
}
@ -169,659 +139,69 @@ namespace Ryujinx.Core.OsHle.Services.Nv
return 0;
}
private long NvGpuAsIoctlBindChannel(ServiceCtx Context)
private static int ProcessIoctlNvGpuAS(ServiceCtx Context, int Cmd)
{
long Position = Context.Request.GetSendBuffPtr();
int Fd = Context.Memory.ReadInt32(Position);
return 0;
return ProcessIoctl(Context, Cmd, NvGpuASIoctl.ProcessIoctl);
}
private long NvGpuAsIoctlAllocSpace(ServiceCtx Context)
private static int ProcessIoctlNvHostCtrl(ServiceCtx Context, int Cmd)
{
long Position = Context.Request.GetSendBuffPtr();
return ProcessIoctl(Context, Cmd, NvHostCtrlIoctl.ProcessIoctl);
}
MemReader Reader = new MemReader(Context.Memory, Position);
private static int ProcessIoctlNvGpuGpu(ServiceCtx Context, int Cmd)
{
return ProcessIoctl(Context, Cmd, NvGpuGpuIoctl.ProcessIoctl);
}
int Pages = Reader.ReadInt32();
int PageSize = Reader.ReadInt32();
int Flags = Reader.ReadInt32();
int Padding = Reader.ReadInt32();
long Align = Reader.ReadInt64();
private static int ProcessIoctlNvHostChannel(ServiceCtx Context, int Cmd)
{
return ProcessIoctl(Context, Cmd, NvHostChannelIoctl.ProcessIoctl);
}
if ((Flags & 1) != 0)
private static int ProcessIoctlNvMap(ServiceCtx Context, int Cmd)
{
return ProcessIoctl(Context, Cmd, NvMapIoctl.ProcessIoctl);
}
private static int ProcessIoctl(ServiceCtx Context, int Cmd, IoctlProcessor Processor)
{
if (CmdIn(Cmd) && Context.Request.GetBufferType0x21Position() == 0)
{
Align = Context.Ns.Gpu.ReserveMemory(Align, (long)Pages * PageSize, 1);
}
else
{
Align = Context.Ns.Gpu.ReserveMemory((long)Pages * PageSize, Align);
Context.Ns.Log.PrintError(LogClass.ServiceNv, "Input buffer is null!");
return NvResult.InvalidInput;
}
Context.Memory.WriteInt64(Position + 0x10, Align);
return 0;
}
private long NvGpuAsIoctlUnmap(ServiceCtx Context)
{
long Position = Context.Request.GetSendBuffPtr();
MemReader Reader = new MemReader(Context.Memory, Position);
long Offset = Reader.ReadInt64();
Context.Ns.Gpu.MemoryMgr.Unmap(Offset, 0x10000);
return 0;
}
private long NvGpuAsIoctlMapBufferEx(ServiceCtx Context)
{
long Position = Context.Request.GetSendBuffPtr();
MemReader Reader = new MemReader(Context.Memory, Position);
int Flags = Reader.ReadInt32();
int Kind = Reader.ReadInt32();
int Handle = Reader.ReadInt32();
int PageSize = Reader.ReadInt32();
long BuffAddr = Reader.ReadInt64();
long MapSize = Reader.ReadInt64();
long Offset = Reader.ReadInt64();
if (Handle == 0)
if (CmdOut(Cmd) && Context.Request.GetBufferType0x22Position() == 0)
{
//This is used to store offsets for the Framebuffer(s);
NvMapFb MapFb = (NvMapFb)NvMapsFb.GetData(Context.Process, 0);
Context.Ns.Log.PrintError(LogClass.ServiceNv, "Output buffer is null!");
MapFb.AddBufferOffset(BuffAddr);
return 0;
return NvResult.InvalidInput;
}
NvMap Map = NvMaps.GetData<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;
return Processor(Context, Cmd);
}
private long NvGpuAsIoctlGetVaRegions(ServiceCtx Context)
private static bool CmdIn(int Cmd)
{
long Position = Context.Request.GetSendBuffPtr();
MemReader Reader = new MemReader(Context.Memory, Position);
MemWriter Writer = new MemWriter(Context.Memory, Position);
long Unused = Reader.ReadInt64();
int BuffSize = Reader.ReadInt32();
int Padding = Reader.ReadInt32();
BuffSize = 0x30;
Writer.WriteInt64(Unused);
Writer.WriteInt32(BuffSize);
Writer.WriteInt32(Padding);
Writer.WriteInt64(0);
Writer.WriteInt32(0);
Writer.WriteInt32(0);
Writer.WriteInt64(0);
Writer.WriteInt64(0);
Writer.WriteInt32(0);
Writer.WriteInt32(0);
Writer.WriteInt64(0);
return 0;
return ((Cmd >> 30) & 1) != 0;
}
private long NvGpuAsIoctlInitializeEx(ServiceCtx Context)
private static bool CmdOut(int Cmd)
{
long Position = Context.Request.GetSendBuffPtr();
MemReader Reader = new MemReader(Context.Memory, Position);
int BigPageSize = Reader.ReadInt32();
int AsFd = Reader.ReadInt32();
int Flags = Reader.ReadInt32();
int Reserved = Reader.ReadInt32();
long Unknown10 = Reader.ReadInt64();
long Unknown18 = Reader.ReadInt64();
long Unknown20 = Reader.ReadInt64();
return 0;
return ((Cmd >> 31) & 1) != 0;
}
private long NvGpuAsIoctlRemap(ServiceCtx Context)
public static void UnloadProcess(Process Process)
{
Context.RequestData.BaseStream.Seek(-4, SeekOrigin.Current);
Fds.DeleteProcess(Process);
int Cmd = Context.RequestData.ReadInt32();
NvGpuASIoctl.UnloadProcess(Process);
int Size = (Cmd >> 16) & 0xff;
NvHostCtrlIoctl.UnloadProcess(Process);
int Count = Size / 0x18;
long Position = Context.Request.GetSendBuffPtr();
MemReader Reader = new MemReader(Context.Memory, Position);
for (int Index = 0; Index < Count; Index++)
{
int Flags = Reader.ReadInt32();
int Kind = Reader.ReadInt32();
int Handle = Reader.ReadInt32();
int Padding = Reader.ReadInt32();
int Offset = Reader.ReadInt32();
int Pages = Reader.ReadInt32();
NvMap Map = NvMaps.GetData<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)
{
long Position = Context.Request.GetSendBuffPtr();
MemReader Reader = new MemReader(Context.Memory, Position);
long GpuVa = Reader.ReadInt64();
int Mode = Reader.ReadInt32();
int Padding = Reader.ReadInt32();
return 0;
}
private long NvMapIoctlChannelSetErrorNotifier(ServiceCtx Context)
{
long Position = Context.Request.GetSendBuffPtr();
MemReader Reader = new MemReader(Context.Memory, Position);
long Offset = Reader.ReadInt64();
long Size = Reader.ReadInt64();
int Mem = Reader.ReadInt32();
int Padding = Reader.ReadInt32();
return 0;
}
private long NvMapIoctlChannelSetPriority(ServiceCtx Context)
{
long Position = Context.Request.GetSendBuffPtr();
int Priority = Context.Memory.ReadInt32(Position);
return 0;
}
private long NvMapIoctlChannelAllocGpFifoEx2(ServiceCtx Context)
{
long Position = Context.Request.GetSendBuffPtr();
MemReader Reader = new MemReader(Context.Memory, Position);
MemWriter Writer = new MemWriter(Context.Memory, Position + 0xc);
int Count = Reader.ReadInt32();
int Flags = Reader.ReadInt32();
int Unknown8 = Reader.ReadInt32();
long Fence = Reader.ReadInt64();
int Unknown14 = Reader.ReadInt32();
int Unknown18 = Reader.ReadInt32();
Writer.WriteInt32(0);
Writer.WriteInt32(0);
return 0;
}
private long NvMapIocCreate(ServiceCtx Context)
{
long Position = Context.Request.GetSendBuffPtr();
int Size = Context.Memory.ReadInt32(Position);
NvMap Map = new NvMap() { Size = Size };
Map.Handle = NvMaps.Add(Context.Process, Map);
Map.Id = NvMapsById.Add(Context.Process, Map);
Context.Memory.WriteInt32(Position + 4, Map.Handle);
Context.Ns.Log.PrintInfo(LogClass.ServiceNv, $"NvMap {Map.Id} created with size {Size:x8}!");
return 0;
}
private long NvMapIocFromId(ServiceCtx Context)
{
long Position = Context.Request.GetSendBuffPtr();
int Id = Context.Memory.ReadInt32(Position);
NvMap Map = NvMapsById.GetData<NvMap>(Context.Process, Id);
if (Map == null)
{
Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Invalid NvMap Id {Id}!");
return -1; //TODO: Corrent error code.
}
Context.Memory.WriteInt32(Position + 4, Map.Handle);
return 0;
}
private long NvMapIocAlloc(ServiceCtx Context)
{
long Position = Context.Request.GetSendBuffPtr();
MemReader Reader = new MemReader(Context.Memory, Position);
int Handle = Reader.ReadInt32();
int HeapMask = Reader.ReadInt32();
int Flags = Reader.ReadInt32();
int Align = Reader.ReadInt32();
byte Kind = (byte)Reader.ReadInt64();
long Addr = Reader.ReadInt64();
NvMap Map = NvMaps.GetData<NvMap>(Context.Process, Handle);
if (Map == null)
{
Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Invalid NvMap Handle {Handle}!");
return -1; //TODO: Corrent error code.
}
Map.CpuAddress = Addr;
Map.Align = Align;
Map.Kind = Kind;
return 0;
}
private long NvMapIocFree(ServiceCtx Context)
{
long Position = Context.Request.GetSendBuffPtr();
MemReader Reader = new MemReader(Context.Memory, Position);
MemWriter Writer = new MemWriter(Context.Memory, Position + 8);
int Handle = Reader.ReadInt32();
int Padding = Reader.ReadInt32();
NvMap Map = NvMaps.GetData<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;
}
private long NvMapIocParam(ServiceCtx Context)
{
long Position = Context.Request.GetSendBuffPtr();
MemReader Reader = new MemReader(Context.Memory, Position);
int Handle = Reader.ReadInt32();
int Param = Reader.ReadInt32();
NvMap Map = NvMaps.GetData<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;
}
private long NvMapIocGetId(ServiceCtx Context)
{
long Position = Context.Request.GetSendBuffPtr();
int Handle = Context.Memory.ReadInt32(Position + 4);
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.Memory.WriteInt32(Position, Map.Id);
return 0;
NvMapIoctl.UnloadProcess(Process);
}
public void Dispose()

View file

@ -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);
}
}
}

View file

@ -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;
}
}

View file

@ -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 _);
}
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -0,0 +1,7 @@
namespace Ryujinx.Core.OsHle.Services.Nv.NvGpuAS
{
struct NvGpuASUnmapBuffer
{
public long Offset;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}
}

View file

@ -0,0 +1,10 @@
namespace Ryujinx.Core.OsHle.Services.Nv
{
static class NvHelper
{
public static void Crash()
{
}
}
}

View file

@ -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;
}
}
}

View file

@ -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;
}
}

View file

@ -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 _);
}
}
}

View file

@ -0,0 +1,8 @@
namespace Ryujinx.Core.OsHle.Services.Nv.NvHostCtrl
{
struct NvHostCtrlSyncptRead
{
public int Id;
public int Value;
}
}

View file

@ -0,0 +1,9 @@
namespace Ryujinx.Core.OsHle.Services.Nv.NvHostCtrl
{
struct NvHostCtrlSyncptWait
{
public int Id;
public int Thresh;
public int Timeout;
}
}

View file

@ -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;
}
}

View file

@ -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];
}
}
}

View file

@ -0,0 +1,10 @@
namespace Ryujinx.Core.OsHle.Services.Nv.NvHostCtrl
{
class NvHostEvent
{
public int Id;
public int Thresh;
public NvHostEventState State;
}
}

View file

@ -0,0 +1,10 @@
namespace Ryujinx.Core.OsHle.Services.Nv.NvHostCtrl
{
enum NvHostEventState
{
Registered = 0,
Waiting = 1,
Busy = 2,
Free = 5
}
}

View file

@ -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;
}
}
}
}

View file

@ -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;
}
}
}

View file

@ -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;
}
}

View file

@ -0,0 +1,8 @@
namespace Ryujinx.Core.OsHle.Services.Nv.NvMap
{
struct NvMapCreate
{
public int Size;
public int Handle;
}
}

View file

@ -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;
}
}

View file

@ -0,0 +1,8 @@
namespace Ryujinx.Core.OsHle.Services.Nv.NvMap
{
struct NvMapFromId
{
public int Id;
public int Handle;
}
}

View file

@ -0,0 +1,8 @@
namespace Ryujinx.Core.OsHle.Services.Nv.NvMap
{
struct NvMapGetId
{
public int Id;
public int Handle;
}
}

View file

@ -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);
}
}
}

View file

@ -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
}
}

View file

@ -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 _);
}
}
}

View file

@ -0,0 +1,9 @@
namespace Ryujinx.Core.OsHle.Services.Nv.NvMap
{
struct NvMapParam
{
public int Handle;
public int Param;
public int Result;
}
}

View file

@ -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];
}
}
}

View file

@ -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;
}
}

View file

@ -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;

View file

@ -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);
}
}
}

View file

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

View file

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

View file

@ -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();