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

@ -6,7 +6,7 @@ using System.Runtime.InteropServices;
namespace ChocolArm64.Memory namespace ChocolArm64.Memory
{ {
public unsafe class AMemory : IDisposable public unsafe class AMemory : IAMemory, IDisposable
{ {
private const long ErgMask = (4 << AThreadState.ErgSizeLog2) - 1; private const long ErgMask = (4 << AThreadState.ErgSizeLog2) - 1;

View file

@ -0,0 +1,37 @@
namespace ChocolArm64.Memory
{
public interface IAMemory
{
sbyte ReadSByte(long Position);
short ReadInt16(long Position);
int ReadInt32(long Position);
long ReadInt64(long Position);
byte ReadByte(long Position);
ushort ReadUInt16(long Position);
uint ReadUInt32(long Position);
ulong ReadUInt64(long Position);
void WriteSByte(long Position, sbyte Value);
void WriteInt16(long Position, short Value);
void WriteInt32(long Position, int Value);
void WriteInt64(long Position, long Value);
void WriteByte(long Position, byte Value);
void WriteUInt16(long Position, ushort Value);
void WriteUInt32(long Position, uint Value);
void WriteUInt64(long Position, ulong Value);
}
}

View file

@ -1,4 +1,4 @@
namespace Ryujinx.Graphics.Gpu namespace Ryujinx.Core.Gpu
{ {
class BlockLinearSwizzle : ISwizzle class BlockLinearSwizzle : ISwizzle
{ {

View file

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

View file

@ -1,4 +1,4 @@
namespace Ryujinx.Graphics.Gpu namespace Ryujinx.Core.Gpu
{ {
interface ISwizzle interface ISwizzle
{ {

View file

@ -1,4 +1,4 @@
namespace Ryujinx.Graphics.Gpu namespace Ryujinx.Core.Gpu
{ {
class LinearSwizzle : ISwizzle class LinearSwizzle : ISwizzle
{ {

View file

@ -1,8 +1,7 @@
using ChocolArm64.Memory;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace Ryujinx.Graphics.Gpu namespace Ryujinx.Core.Gpu
{ {
class MacroInterpreter class MacroInterpreter
{ {
@ -69,7 +68,7 @@ namespace Ryujinx.Graphics.Gpu
Gprs = new int[8]; Gprs = new int[8];
} }
public void Execute(AMemory Memory, long Position, int Param) public void Execute(NvGpuVmm Vmm, long Position, int Param)
{ {
Reset(); Reset();
@ -77,13 +76,13 @@ namespace Ryujinx.Graphics.Gpu
Pc = Position; Pc = Position;
FetchOpCode(Memory); FetchOpCode(Vmm);
while (Step(Memory)); while (Step(Vmm));
//Due to the delay slot, we still need to execute //Due to the delay slot, we still need to execute
//one more instruction before we actually exit. //one more instruction before we actually exit.
Step(Memory); Step(Vmm);
} }
private void Reset() private void Reset()
@ -99,11 +98,11 @@ namespace Ryujinx.Graphics.Gpu
Carry = false; Carry = false;
} }
private bool Step(AMemory Memory) private bool Step(NvGpuVmm Vmm)
{ {
long BaseAddr = Pc - 4; long BaseAddr = Pc - 4;
FetchOpCode(Memory); FetchOpCode(Vmm);
if ((OpCode & 7) < 7) if ((OpCode & 7) < 7)
{ {
@ -145,7 +144,7 @@ namespace Ryujinx.Graphics.Gpu
{ {
SetDstGpr(FetchParam()); SetDstGpr(FetchParam());
Send(Memory, Result); Send(Vmm, Result);
break; break;
} }
@ -155,7 +154,7 @@ namespace Ryujinx.Graphics.Gpu
{ {
SetDstGpr(Result); SetDstGpr(Result);
Send(Memory, Result); Send(Vmm, Result);
break; break;
} }
@ -177,7 +176,7 @@ namespace Ryujinx.Graphics.Gpu
SetMethAddr(Result); SetMethAddr(Result);
Send(Memory, FetchParam()); Send(Vmm, FetchParam());
break; break;
} }
@ -189,7 +188,7 @@ namespace Ryujinx.Graphics.Gpu
SetMethAddr(Result); SetMethAddr(Result);
Send(Memory, (Result >> 12) & 0x3f); Send(Vmm, (Result >> 12) & 0x3f);
break; break;
} }
@ -212,7 +211,7 @@ namespace Ryujinx.Graphics.Gpu
if (NoDelays) if (NoDelays)
{ {
FetchOpCode(Memory); FetchOpCode(Vmm);
} }
return true; return true;
@ -224,11 +223,11 @@ namespace Ryujinx.Graphics.Gpu
return !Exit; return !Exit;
} }
private void FetchOpCode(AMemory Memory) private void FetchOpCode(NvGpuVmm Vmm)
{ {
OpCode = PipeOp; OpCode = PipeOp;
PipeOp = Memory.ReadInt32(Pc); PipeOp = Vmm.ReadInt32(Pc);
Pc += 4; Pc += 4;
} }
@ -408,11 +407,11 @@ namespace Ryujinx.Graphics.Gpu
return Engine.Registers[Reg]; return Engine.Registers[Reg];
} }
private void Send(AMemory Memory, int Value) private void Send(NvGpuVmm Vmm, int Value)
{ {
NsGpuPBEntry PBEntry = new NsGpuPBEntry(MethAddr, 0, Value); NvGpuPBEntry PBEntry = new NvGpuPBEntry(MethAddr, 0, Value);
Engine.CallMethod(Memory, PBEntry); Engine.CallMethod(Vmm, PBEntry);
MethAddr += MethIncr; MethAddr += MethIncr;
} }

View file

@ -1,14 +1,12 @@
using Ryujinx.Graphics.Gal; using Ryujinx.Graphics.Gal;
using System.Threading; using System.Threading;
namespace Ryujinx.Graphics.Gpu namespace Ryujinx.Core.Gpu
{ {
public class NsGpu public class NvGpu
{ {
public IGalRenderer Renderer { get; private set; } public IGalRenderer Renderer { get; private set; }
public NsGpuMemoryMgr MemoryMgr { get; private set; }
public NvGpuFifo Fifo { get; private set; } public NvGpuFifo Fifo { get; private set; }
public NvGpuEngine2d Engine2d { get; private set; } public NvGpuEngine2d Engine2d { get; private set; }
@ -18,12 +16,10 @@ namespace Ryujinx.Graphics.Gpu
private bool KeepRunning; private bool KeepRunning;
public NsGpu(IGalRenderer Renderer) public NvGpu(IGalRenderer Renderer)
{ {
this.Renderer = Renderer; this.Renderer = Renderer;
MemoryMgr = new NsGpuMemoryMgr();
Fifo = new NvGpuFifo(this); Fifo = new NvGpuFifo(this);
Engine2d = new NvGpuEngine2d(this); Engine2d = new NvGpuEngine2d(this);
@ -36,31 +32,6 @@ namespace Ryujinx.Graphics.Gpu
FifoProcessing.Start(); FifoProcessing.Start();
} }
public long GetCpuAddr(long Position)
{
return MemoryMgr.GetCpuAddr(Position);
}
public long MapMemory(long CpuAddr, long Size)
{
return MemoryMgr.Map(CpuAddr, Size);
}
public long MapMemory(long CpuAddr, long GpuAddr, long Size)
{
return MemoryMgr.Map(CpuAddr, GpuAddr, Size);
}
public long ReserveMemory(long Size, long Align)
{
return MemoryMgr.Reserve(Size, Align);
}
public long ReserveMemory(long GpuAddr, long Size, long Align)
{
return MemoryMgr.Reserve(GpuAddr, Size, Align);
}
private void ProcessFifo() private void ProcessFifo()
{ {
while (KeepRunning) while (KeepRunning)

View file

@ -1,4 +1,4 @@
namespace Ryujinx.Graphics.Gpu namespace Ryujinx.Core.Gpu
{ {
enum NvGpuEngine enum NvGpuEngine
{ {

View file

@ -1,8 +1,7 @@
using ChocolArm64.Memory;
using Ryujinx.Graphics.Gal; using Ryujinx.Graphics.Gal;
using System.Collections.Generic; using System.Collections.Generic;
namespace Ryujinx.Graphics.Gpu namespace Ryujinx.Core.Gpu
{ {
public class NvGpuEngine2d : INvGpuEngine public class NvGpuEngine2d : INvGpuEngine
{ {
@ -19,11 +18,11 @@ namespace Ryujinx.Graphics.Gpu
public int[] Registers { get; private set; } public int[] Registers { get; private set; }
private NsGpu Gpu; private NvGpu Gpu;
private Dictionary<int, NvGpuMethod> Methods; private Dictionary<int, NvGpuMethod> Methods;
public NvGpuEngine2d(NsGpu Gpu) public NvGpuEngine2d(NvGpu Gpu)
{ {
this.Gpu = Gpu; this.Gpu = Gpu;
@ -44,11 +43,11 @@ namespace Ryujinx.Graphics.Gpu
AddMethod(0xb5, 1, 1, TextureCopy); AddMethod(0xb5, 1, 1, TextureCopy);
} }
public void CallMethod(AMemory Memory, NsGpuPBEntry PBEntry) public void CallMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry)
{ {
if (Methods.TryGetValue(PBEntry.Method, out NvGpuMethod Method)) if (Methods.TryGetValue(PBEntry.Method, out NvGpuMethod Method))
{ {
Method(Memory, PBEntry); Method(Vmm, PBEntry);
} }
else else
{ {
@ -56,7 +55,7 @@ namespace Ryujinx.Graphics.Gpu
} }
} }
private void TextureCopy(AMemory Memory, NsGpuPBEntry PBEntry) private void TextureCopy(NvGpuVmm Vmm, NvGpuPBEntry PBEntry)
{ {
CopyOperation Operation = (CopyOperation)ReadRegister(NvGpuEngine2dReg.CopyOperation); CopyOperation Operation = (CopyOperation)ReadRegister(NvGpuEngine2dReg.CopyOperation);
@ -76,10 +75,10 @@ namespace Ryujinx.Graphics.Gpu
int DstBlockHeight = 1 << ((DstBlkDim >> 4) & 0xf); int DstBlockHeight = 1 << ((DstBlkDim >> 4) & 0xf);
long Tag = MakeInt64From2xInt32(NvGpuEngine2dReg.SrcAddress); long Tag = Vmm.GetPhysicalAddress(MakeInt64From2xInt32(NvGpuEngine2dReg.SrcAddress));
TryGetCpuAddr(NvGpuEngine2dReg.SrcAddress, out long SrcAddress); long SrcAddress = MakeInt64From2xInt32(NvGpuEngine2dReg.SrcAddress);
TryGetCpuAddr(NvGpuEngine2dReg.DstAddress, out long DstAddress); long DstAddress = MakeInt64From2xInt32(NvGpuEngine2dReg.DstAddress);
bool IsFbTexture = Gpu.Engine3d.IsFrameBufferPosition(Tag); bool IsFbTexture = Gpu.Engine3d.IsFrameBufferPosition(Tag);
@ -101,31 +100,22 @@ namespace Ryujinx.Graphics.Gpu
{ {
Gpu.Renderer.GetFrameBufferData(Tag, (byte[] Buffer) => Gpu.Renderer.GetFrameBufferData(Tag, (byte[] Buffer) =>
{ {
CopyTexture(Memory, DstTexture, Buffer); CopyTexture(Vmm, DstTexture, Buffer);
}); });
} }
else else
{ {
long Size = SrcWidth * SrcHeight * 4; long Size = SrcWidth * SrcHeight * 4;
byte[] Buffer = AMemoryHelper.ReadBytes(Memory, SrcAddress, Size); byte[] Buffer = Vmm.ReadBytes(SrcAddress, Size);
CopyTexture(Memory, DstTexture, Buffer); CopyTexture(Vmm, DstTexture, Buffer);
} }
} }
private void CopyTexture(AMemory Memory, Texture Texture, byte[] Buffer) private void CopyTexture(NvGpuVmm Vmm, Texture Texture, byte[] Buffer)
{ {
TextureWriter.Write(Memory, Texture, Buffer); TextureWriter.Write(Vmm, Texture, Buffer);
}
private bool TryGetCpuAddr(NvGpuEngine2dReg Reg, out long Position)
{
Position = MakeInt64From2xInt32(Reg);
Position = Gpu.GetCpuAddr(Position);
return Position != -1;
} }
private long MakeInt64From2xInt32(NvGpuEngine2dReg Reg) private long MakeInt64From2xInt32(NvGpuEngine2dReg Reg)
@ -135,7 +125,7 @@ namespace Ryujinx.Graphics.Gpu
(uint)Registers[(int)Reg + 1]; (uint)Registers[(int)Reg + 1];
} }
private void WriteRegister(NsGpuPBEntry PBEntry) private void WriteRegister(NvGpuPBEntry PBEntry)
{ {
int ArgsCount = PBEntry.Arguments.Count; int ArgsCount = PBEntry.Arguments.Count;

View file

@ -1,4 +1,4 @@
namespace Ryujinx.Graphics.Gpu namespace Ryujinx.Core.Gpu
{ {
enum NvGpuEngine2dReg enum NvGpuEngine2dReg
{ {

View file

@ -1,15 +1,14 @@
using ChocolArm64.Memory;
using Ryujinx.Graphics.Gal; using Ryujinx.Graphics.Gal;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace Ryujinx.Graphics.Gpu namespace Ryujinx.Core.Gpu
{ {
public class NvGpuEngine3d : INvGpuEngine public class NvGpuEngine3d : INvGpuEngine
{ {
public int[] Registers { get; private set; } public int[] Registers { get; private set; }
private NsGpu Gpu; private NvGpu Gpu;
private Dictionary<int, NvGpuMethod> Methods; private Dictionary<int, NvGpuMethod> Methods;
@ -20,11 +19,11 @@ namespace Ryujinx.Graphics.Gpu
public int Size; public int Size;
} }
private ConstBuffer[] ConstBuffers; private ConstBuffer[][] ConstBuffers;
private HashSet<long> FrameBuffers; private HashSet<long> FrameBuffers;
public NvGpuEngine3d(NsGpu Gpu) public NvGpuEngine3d(NvGpu Gpu)
{ {
this.Gpu = Gpu; this.Gpu = Gpu;
@ -46,18 +45,23 @@ namespace Ryujinx.Graphics.Gpu
AddMethod(0x674, 1, 1, ClearBuffers); AddMethod(0x674, 1, 1, ClearBuffers);
AddMethod(0x6c3, 1, 1, QueryControl); AddMethod(0x6c3, 1, 1, QueryControl);
AddMethod(0x8e4, 16, 1, CbData); AddMethod(0x8e4, 16, 1, CbData);
AddMethod(0x904, 1, 1, CbBind); AddMethod(0x904, 5, 8, CbBind);
ConstBuffers = new ConstBuffer[18]; ConstBuffers = new ConstBuffer[6][];
for (int Index = 0; Index < ConstBuffers.Length; Index++)
{
ConstBuffers[Index] = new ConstBuffer[18];
}
FrameBuffers = new HashSet<long>(); FrameBuffers = new HashSet<long>();
} }
public void CallMethod(AMemory Memory, NsGpuPBEntry PBEntry) public void CallMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry)
{ {
if (Methods.TryGetValue(PBEntry.Method, out NvGpuMethod Method)) if (Methods.TryGetValue(PBEntry.Method, out NvGpuMethod Method))
{ {
Method(Memory, PBEntry); Method(Vmm, PBEntry);
} }
else else
{ {
@ -65,22 +69,22 @@ namespace Ryujinx.Graphics.Gpu
} }
} }
private void VertexEndGl(AMemory Memory, NsGpuPBEntry PBEntry) private void VertexEndGl(NvGpuVmm Vmm, NvGpuPBEntry PBEntry)
{ {
SetFrameBuffer(0); SetFrameBuffer(Vmm, 0);
long[] Tags = UploadShaders(Memory); long[] Tags = UploadShaders(Vmm);
Gpu.Renderer.BindProgram(); Gpu.Renderer.BindProgram();
SetAlphaBlending(); SetAlphaBlending();
UploadTextures(Memory, Tags); UploadTextures(Vmm, Tags);
UploadUniforms(Memory); UploadUniforms(Vmm);
UploadVertexArrays(Memory); UploadVertexArrays(Vmm);
} }
private void ClearBuffers(AMemory Memory, NsGpuPBEntry PBEntry) private void ClearBuffers(NvGpuVmm Vmm, NvGpuPBEntry PBEntry)
{ {
int Arg0 = PBEntry.Arguments[0]; int Arg0 = PBEntry.Arguments[0];
@ -90,28 +94,30 @@ namespace Ryujinx.Graphics.Gpu
GalClearBufferFlags Flags = (GalClearBufferFlags)(Arg0 & 0x3f); GalClearBufferFlags Flags = (GalClearBufferFlags)(Arg0 & 0x3f);
SetFrameBuffer(0); SetFrameBuffer(Vmm, 0);
//TODO: Enable this once the frame buffer problems are fixed. //TODO: Enable this once the frame buffer problems are fixed.
//Gpu.Renderer.ClearBuffers(Layer, Flags); //Gpu.Renderer.ClearBuffers(Layer, Flags);
} }
private void SetFrameBuffer(int FbIndex) private void SetFrameBuffer(NvGpuVmm Vmm, int FbIndex)
{ {
long Address = MakeInt64From2xInt32(NvGpuEngine3dReg.FrameBufferNAddress + FbIndex * 0x10); long VA = MakeInt64From2xInt32(NvGpuEngine3dReg.FrameBufferNAddress + FbIndex * 0x10);
FrameBuffers.Add(Address); long PA = Vmm.GetPhysicalAddress(VA);
FrameBuffers.Add(PA);
int Width = ReadRegister(NvGpuEngine3dReg.FrameBufferNWidth + FbIndex * 0x10); int Width = ReadRegister(NvGpuEngine3dReg.FrameBufferNWidth + FbIndex * 0x10);
int Height = ReadRegister(NvGpuEngine3dReg.FrameBufferNHeight + FbIndex * 0x10); int Height = ReadRegister(NvGpuEngine3dReg.FrameBufferNHeight + FbIndex * 0x10);
//Note: Using the Width/Height results seems to give incorrect results. //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. //Maybe the size of all frame buffers is hardcoded to screen size? This seems unlikely.
Gpu.Renderer.CreateFrameBuffer(Address, 1280, 720); Gpu.Renderer.CreateFrameBuffer(PA, 1280, 720);
Gpu.Renderer.BindFrameBuffer(Address); Gpu.Renderer.BindFrameBuffer(PA);
} }
private long[] UploadShaders(AMemory Memory) private long[] UploadShaders(NvGpuVmm Vmm)
{ {
long[] Tags = new long[5]; long[] Tags = new long[5];
@ -132,12 +138,10 @@ namespace Ryujinx.Graphics.Gpu
long Tag = BasePosition + (uint)Offset; long Tag = BasePosition + (uint)Offset;
long Position = Gpu.GetCpuAddr(Tag);
//TODO: Find a better way to calculate the size. //TODO: Find a better way to calculate the size.
int Size = 0x20000; int Size = 0x20000;
byte[] Code = AMemoryHelper.ReadBytes(Memory, Position, (uint)Size); byte[] Code = Vmm.ReadBytes(Tag, Size);
GalShaderType ShaderType = GetTypeFromProgram(Index); GalShaderType ShaderType = GetTypeFromProgram(Index);
@ -211,16 +215,12 @@ namespace Ryujinx.Graphics.Gpu
} }
} }
private void UploadTextures(AMemory Memory, long[] Tags) private void UploadTextures(NvGpuVmm Vmm, long[] Tags)
{ {
long BaseShPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.ShaderAddress); long BaseShPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.ShaderAddress);
int TextureCbIndex = ReadRegister(NvGpuEngine3dReg.TextureCbIndex); int TextureCbIndex = ReadRegister(NvGpuEngine3dReg.TextureCbIndex);
long BasePosition = ConstBuffers[TextureCbIndex].Position;
long Size = (uint)ConstBuffers[TextureCbIndex].Size;
//Note: On the emulator renderer, Texture Unit 0 is //Note: On the emulator renderer, Texture Unit 0 is
//reserved for drawing the frame buffer. //reserved for drawing the frame buffer.
int TexIndex = 1; int TexIndex = 1;
@ -229,9 +229,9 @@ namespace Ryujinx.Graphics.Gpu
{ {
foreach (ShaderDeclInfo DeclInfo in Gpu.Renderer.GetTextureUsage(Tags[Index])) foreach (ShaderDeclInfo DeclInfo in Gpu.Renderer.GetTextureUsage(Tags[Index]))
{ {
long Position = BasePosition + Index * Size; long Position = ConstBuffers[Index][TextureCbIndex].Position;
UploadTexture(Memory, Position, TexIndex, DeclInfo.Index); UploadTexture(Vmm, Position, TexIndex, DeclInfo.Index);
Gpu.Renderer.SetUniform1(DeclInfo.Name, TexIndex); Gpu.Renderer.SetUniform1(DeclInfo.Name, TexIndex);
@ -240,26 +240,28 @@ namespace Ryujinx.Graphics.Gpu
} }
} }
private void UploadTexture(AMemory Memory, long BasePosition, int TexIndex, int HndIndex) private void UploadTexture(NvGpuVmm Vmm, long BasePosition, int TexIndex, int HndIndex)
{ {
long Position = BasePosition + HndIndex * 4; long Position = BasePosition + HndIndex * 4;
int TextureHandle = Memory.ReadInt32(Position); int TextureHandle = Vmm.ReadInt32(Position);
int TicIndex = (TextureHandle >> 0) & 0xfffff; int TicIndex = (TextureHandle >> 0) & 0xfffff;
int TscIndex = (TextureHandle >> 20) & 0xfff; int TscIndex = (TextureHandle >> 20) & 0xfff;
TryGetCpuAddr(NvGpuEngine3dReg.TexHeaderPoolOffset, out long TicPosition); long TicPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.TexHeaderPoolOffset);
TryGetCpuAddr(NvGpuEngine3dReg.TexSamplerPoolOffset, out long TscPosition); long TscPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.TexSamplerPoolOffset);
TicPosition += TicIndex * 0x20; TicPosition += TicIndex * 0x20;
TscPosition += TscIndex * 0x20; TscPosition += TscIndex * 0x20;
GalTextureSampler Sampler = TextureFactory.MakeSampler(Gpu, Memory, TscPosition); GalTextureSampler Sampler = TextureFactory.MakeSampler(Gpu, Vmm, TscPosition);
long TextureAddress = Memory.ReadInt64(TicPosition + 4) & 0xffffffffffff; long TextureAddress = Vmm.ReadInt64(TicPosition + 4) & 0xffffffffffff;
if (FrameBuffers.Contains(TextureAddress)) TextureAddress = Vmm.GetPhysicalAddress(TextureAddress);
if (IsFrameBufferPosition(TextureAddress))
{ {
//This texture is a frame buffer texture, //This texture is a frame buffer texture,
//we shouldn't read anything from memory and bind //we shouldn't read anything from memory and bind
@ -269,14 +271,14 @@ namespace Ryujinx.Graphics.Gpu
} }
else else
{ {
GalTexture Texture = TextureFactory.MakeTexture(Gpu, Memory, TicPosition); GalTexture Texture = TextureFactory.MakeTexture(Gpu, Vmm, TicPosition);
Gpu.Renderer.SetTextureAndSampler(TexIndex, Texture, Sampler); Gpu.Renderer.SetTextureAndSampler(TexIndex, Texture, Sampler);
Gpu.Renderer.BindTexture(TexIndex); Gpu.Renderer.BindTexture(TexIndex);
} }
} }
private void UploadUniforms(AMemory Memory) private void UploadUniforms(NvGpuVmm Vmm)
{ {
long BasePosition = MakeInt64From2xInt32(NvGpuEngine3dReg.ShaderAddress); long BasePosition = MakeInt64From2xInt32(NvGpuEngine3dReg.ShaderAddress);
@ -295,13 +297,11 @@ namespace Ryujinx.Graphics.Gpu
for (int Cbuf = 0; Cbuf < ConstBuffers.Length; Cbuf++) for (int Cbuf = 0; Cbuf < ConstBuffers.Length; Cbuf++)
{ {
ConstBuffer Cb = ConstBuffers[Cbuf]; ConstBuffer Cb = ConstBuffers[Index][Cbuf];
if (Cb.Enabled) if (Cb.Enabled)
{ {
long CbPosition = Cb.Position + Index * Cb.Size; byte[] Data = Vmm.ReadBytes(Cb.Position, (uint)Cb.Size);
byte[] Data = AMemoryHelper.ReadBytes(Memory, CbPosition, (uint)Cb.Size);
Gpu.Renderer.SetConstBuffer(BasePosition + (uint)Offset, Cbuf, Data); Gpu.Renderer.SetConstBuffer(BasePosition + (uint)Offset, Cbuf, Data);
} }
@ -309,7 +309,7 @@ namespace Ryujinx.Graphics.Gpu
} }
} }
private void UploadVertexArrays(AMemory Memory) private void UploadVertexArrays(NvGpuVmm Vmm)
{ {
long IndexPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.IndexArrayAddress); long IndexPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.IndexArrayAddress);
@ -328,11 +328,9 @@ namespace Ryujinx.Graphics.Gpu
if (IndexSize != 0) if (IndexSize != 0)
{ {
IndexPosition = Gpu.GetCpuAddr(IndexPosition);
int BufferSize = IndexCount * IndexSize; int BufferSize = IndexCount * IndexSize;
byte[] Data = AMemoryHelper.ReadBytes(Memory, IndexPosition, BufferSize); byte[] Data = Vmm.ReadBytes(IndexPosition, BufferSize);
Gpu.Renderer.SetIndexArray(Data, IndexFormat); Gpu.Renderer.SetIndexArray(Data, IndexFormat);
} }
@ -382,7 +380,7 @@ namespace Ryujinx.Graphics.Gpu
if (IndexCount != 0) if (IndexCount != 0)
{ {
Size = GetVertexCountFromIndexBuffer( Size = GetVertexCountFromIndexBuffer(
Memory, Vmm,
IndexPosition, IndexPosition,
IndexCount, IndexCount,
IndexSize); IndexSize);
@ -396,9 +394,7 @@ namespace Ryujinx.Graphics.Gpu
//In this case, we need to use the size of the attribute. //In this case, we need to use the size of the attribute.
Size *= Stride; Size *= Stride;
VertexPosition = Gpu.GetCpuAddr(VertexPosition); byte[] Data = Vmm.ReadBytes(VertexPosition, Size);
byte[] Data = AMemoryHelper.ReadBytes(Memory, VertexPosition, Size);
GalVertexAttrib[] AttribArray = Attribs[Index]?.ToArray() ?? new GalVertexAttrib[0]; GalVertexAttrib[] AttribArray = Attribs[Index]?.ToArray() ?? new GalVertexAttrib[0];
@ -420,10 +416,10 @@ namespace Ryujinx.Graphics.Gpu
} }
private int GetVertexCountFromIndexBuffer( private int GetVertexCountFromIndexBuffer(
AMemory Memory, NvGpuVmm Vmm,
long IndexPosition, long IndexPosition,
int IndexCount, int IndexCount,
int IndexSize) int IndexSize)
{ {
int MaxIndex = -1; int MaxIndex = -1;
@ -431,7 +427,7 @@ namespace Ryujinx.Graphics.Gpu
{ {
while (IndexCount -- > 0) while (IndexCount -- > 0)
{ {
ushort Value = Memory.ReadUInt16(IndexPosition); ushort Value = Vmm.ReadUInt16(IndexPosition);
IndexPosition += 2; IndexPosition += 2;
@ -445,7 +441,7 @@ namespace Ryujinx.Graphics.Gpu
{ {
while (IndexCount -- > 0) while (IndexCount -- > 0)
{ {
byte Value = Memory.ReadByte(IndexPosition++); byte Value = Vmm.ReadByte(IndexPosition++);
if (MaxIndex < Value) if (MaxIndex < Value)
{ {
@ -457,7 +453,7 @@ namespace Ryujinx.Graphics.Gpu
{ {
while (IndexCount -- > 0) while (IndexCount -- > 0)
{ {
uint Value = Memory.ReadUInt32(IndexPosition); uint Value = Vmm.ReadUInt32(IndexPosition);
IndexPosition += 2; IndexPosition += 2;
@ -475,75 +471,56 @@ namespace Ryujinx.Graphics.Gpu
return MaxIndex + 1; return MaxIndex + 1;
} }
private void QueryControl(AMemory Memory, NsGpuPBEntry PBEntry) private void QueryControl(NvGpuVmm Vmm, NvGpuPBEntry PBEntry)
{ {
if (TryGetCpuAddr(NvGpuEngine3dReg.QueryAddress, out long Position)) long Position = MakeInt64From2xInt32(NvGpuEngine3dReg.QueryAddress);
int Seq = Registers[(int)NvGpuEngine3dReg.QuerySequence];
int Ctrl = Registers[(int)NvGpuEngine3dReg.QueryControl];
int Mode = Ctrl & 3;
if (Mode == 0)
{ {
int Seq = Registers[(int)NvGpuEngine3dReg.QuerySequence]; //Write mode.
int Ctrl = Registers[(int)NvGpuEngine3dReg.QueryControl]; Vmm.WriteInt32(Position, Seq);
int Mode = Ctrl & 3;
if (Mode == 0)
{
//Write mode.
Memory.WriteInt32(Position, Seq);
}
} }
WriteRegister(PBEntry); WriteRegister(PBEntry);
} }
private void CbData(AMemory Memory, NsGpuPBEntry PBEntry) private void CbData(NvGpuVmm Vmm, NvGpuPBEntry PBEntry)
{ {
if (TryGetCpuAddr(NvGpuEngine3dReg.ConstBufferNAddress, out long Position)) long Position = MakeInt64From2xInt32(NvGpuEngine3dReg.ConstBufferAddress);
int Offset = ReadRegister(NvGpuEngine3dReg.ConstBufferOffset);
foreach (int Arg in PBEntry.Arguments)
{ {
int Offset = ReadRegister(NvGpuEngine3dReg.ConstBufferNOffset); Vmm.WriteInt32(Position + Offset, Arg);
foreach (int Arg in PBEntry.Arguments) Offset += 4;
{
Memory.WriteInt32(Position + Offset, Arg);
Offset += 4;
}
WriteRegister(NvGpuEngine3dReg.ConstBufferNOffset, Offset);
} }
WriteRegister(NvGpuEngine3dReg.ConstBufferOffset, Offset);
} }
private void CbBind(AMemory Memory, NsGpuPBEntry PBEntry) private void CbBind(NvGpuVmm Vmm, NvGpuPBEntry PBEntry)
{ {
int Stage = (PBEntry.Method - 0x904) >> 3;
int Index = PBEntry.Arguments[0]; int Index = PBEntry.Arguments[0];
bool Enabled = (Index & 1) != 0; bool Enabled = (Index & 1) != 0;
Index = (Index >> 4) & 0x1f; Index = (Index >> 4) & 0x1f;
if (TryGetCpuAddr(NvGpuEngine3dReg.ConstBufferNAddress, out long Position)) long Position = MakeInt64From2xInt32(NvGpuEngine3dReg.ConstBufferAddress);
{
ConstBuffers[Index].Position = Position;
ConstBuffers[Index].Enabled = Enabled;
ConstBuffers[Index].Size = ReadRegister(NvGpuEngine3dReg.ConstBufferNSize); ConstBuffers[Stage][Index].Position = Position;
} ConstBuffers[Stage][Index].Enabled = Enabled;
}
private int ReadCb(AMemory Memory, int Cbuf, int Offset) ConstBuffers[Stage][Index].Size = ReadRegister(NvGpuEngine3dReg.ConstBufferSize);
{
long Position = ConstBuffers[Cbuf].Position;
int Value = Memory.ReadInt32(Position + Offset);
return Value;
}
private bool TryGetCpuAddr(NvGpuEngine3dReg Reg, out long Position)
{
Position = MakeInt64From2xInt32(Reg);
Position = Gpu.GetCpuAddr(Position);
return Position != -1;
} }
private long MakeInt64From2xInt32(NvGpuEngine3dReg Reg) private long MakeInt64From2xInt32(NvGpuEngine3dReg Reg)
@ -553,7 +530,7 @@ namespace Ryujinx.Graphics.Gpu
(uint)Registers[(int)Reg + 1]; (uint)Registers[(int)Reg + 1];
} }
private void WriteRegister(NsGpuPBEntry PBEntry) private void WriteRegister(NvGpuPBEntry PBEntry)
{ {
int ArgsCount = PBEntry.Arguments.Count; int ArgsCount = PBEntry.Arguments.Count;

View file

@ -1,4 +1,4 @@
namespace Ryujinx.Graphics.Gpu namespace Ryujinx.Core.Gpu
{ {
enum NvGpuEngine3dReg enum NvGpuEngine3dReg
{ {
@ -53,9 +53,9 @@ namespace Ryujinx.Graphics.Gpu
ShaderNOffset = 0x801, ShaderNOffset = 0x801,
ShaderNMaxGprs = 0x803, ShaderNMaxGprs = 0x803,
ShaderNType = 0x804, ShaderNType = 0x804,
ConstBufferNSize = 0x8e0, ConstBufferSize = 0x8e0,
ConstBufferNAddress = 0x8e1, ConstBufferAddress = 0x8e1,
ConstBufferNOffset = 0x8e3, ConstBufferOffset = 0x8e3,
TextureCbIndex = 0x982 TextureCbIndex = 0x982
} }
} }

View file

@ -1,16 +1,15 @@
using ChocolArm64.Memory;
using System.Collections.Concurrent; using System.Collections.Concurrent;
namespace Ryujinx.Graphics.Gpu namespace Ryujinx.Core.Gpu
{ {
public class NvGpuFifo public class NvGpuFifo
{ {
private const int MacrosCount = 0x80; private const int MacrosCount = 0x80;
private const int MacroIndexMask = MacrosCount - 1; private const int MacroIndexMask = MacrosCount - 1;
private NsGpu Gpu; private NvGpu Gpu;
private ConcurrentQueue<(AMemory, NsGpuPBEntry)> BufferQueue; private ConcurrentQueue<(NvGpuVmm, NvGpuPBEntry)> BufferQueue;
private NvGpuEngine[] SubChannels; private NvGpuEngine[] SubChannels;
@ -32,9 +31,9 @@ namespace Ryujinx.Graphics.Gpu
Interpreter?.Fifo.Enqueue(Param); Interpreter?.Fifo.Enqueue(Param);
} }
public void Execute(AMemory Memory, int Param) public void Execute(NvGpuVmm Vmm, int Param)
{ {
Interpreter?.Execute(Memory, Position, Param); Interpreter?.Execute(Vmm, Position, Param);
} }
} }
@ -43,22 +42,22 @@ namespace Ryujinx.Graphics.Gpu
private CachedMacro[] Macros; private CachedMacro[] Macros;
public NvGpuFifo(NsGpu Gpu) public NvGpuFifo(NvGpu Gpu)
{ {
this.Gpu = Gpu; this.Gpu = Gpu;
BufferQueue = new ConcurrentQueue<(AMemory, NsGpuPBEntry)>(); BufferQueue = new ConcurrentQueue<(NvGpuVmm, NvGpuPBEntry)>();
SubChannels = new NvGpuEngine[8]; SubChannels = new NvGpuEngine[8];
Macros = new CachedMacro[MacrosCount]; Macros = new CachedMacro[MacrosCount];
} }
public void PushBuffer(AMemory Memory, NsGpuPBEntry[] Buffer) public void PushBuffer(NvGpuVmm Vmm, NvGpuPBEntry[] Buffer)
{ {
foreach (NsGpuPBEntry PBEntry in Buffer) foreach (NvGpuPBEntry PBEntry in Buffer)
{ {
BufferQueue.Enqueue((Memory, PBEntry)); BufferQueue.Enqueue((Vmm, PBEntry));
} }
} }
@ -69,9 +68,9 @@ namespace Ryujinx.Graphics.Gpu
public bool Step() public bool Step()
{ {
if (BufferQueue.TryDequeue(out (AMemory Memory, NsGpuPBEntry PBEntry) Tuple)) if (BufferQueue.TryDequeue(out (NvGpuVmm Vmm, NvGpuPBEntry PBEntry) Tuple))
{ {
CallMethod(Tuple.Memory, Tuple.PBEntry); CallMethod(Tuple.Vmm, Tuple.PBEntry);
return true; return true;
} }
@ -79,7 +78,7 @@ namespace Ryujinx.Graphics.Gpu
return false; return false;
} }
private void CallMethod(AMemory Memory, NsGpuPBEntry PBEntry) private void CallMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry)
{ {
if (PBEntry.Method < 0x80) if (PBEntry.Method < 0x80)
{ {
@ -103,11 +102,11 @@ namespace Ryujinx.Graphics.Gpu
case NvGpuFifoMeth.SendMacroCodeData: case NvGpuFifoMeth.SendMacroCodeData:
{ {
long Position = Gpu.GetCpuAddr(CurrMacroPosition); long Position = CurrMacroPosition;
foreach (int Arg in PBEntry.Arguments) foreach (int Arg in PBEntry.Arguments)
{ {
Memory.WriteInt32(Position, Arg); Vmm.WriteInt32(Position, Arg);
CurrMacroPosition += 4; CurrMacroPosition += 4;
@ -127,8 +126,6 @@ namespace Ryujinx.Graphics.Gpu
{ {
long Position = (long)((ulong)PBEntry.Arguments[0] << 2); long Position = (long)((ulong)PBEntry.Arguments[0] << 2);
Position = Gpu.GetCpuAddr(Position);
Macros[CurrMacroBindIndex] = new CachedMacro(this, Gpu.Engine3d, Position); Macros[CurrMacroBindIndex] = new CachedMacro(this, Gpu.Engine3d, Position);
break; break;
@ -139,22 +136,22 @@ namespace Ryujinx.Graphics.Gpu
{ {
switch (SubChannels[PBEntry.SubChannel]) switch (SubChannels[PBEntry.SubChannel])
{ {
case NvGpuEngine._2d: Call2dMethod(Memory, PBEntry); break; case NvGpuEngine._2d: Call2dMethod(Vmm, PBEntry); break;
case NvGpuEngine._3d: Call3dMethod(Memory, PBEntry); break; case NvGpuEngine._3d: Call3dMethod(Vmm, PBEntry); break;
} }
} }
} }
private void Call2dMethod(AMemory Memory, NsGpuPBEntry PBEntry) private void Call2dMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry)
{ {
Gpu.Engine2d.CallMethod(Memory, PBEntry); Gpu.Engine2d.CallMethod(Vmm, PBEntry);
} }
private void Call3dMethod(AMemory Memory, NsGpuPBEntry PBEntry) private void Call3dMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry)
{ {
if (PBEntry.Method < 0xe00) if (PBEntry.Method < 0xe00)
{ {
Gpu.Engine3d.CallMethod(Memory, PBEntry); Gpu.Engine3d.CallMethod(Vmm, PBEntry);
} }
else else
{ {
@ -169,7 +166,7 @@ namespace Ryujinx.Graphics.Gpu
} }
else else
{ {
Macros[MacroIndex].Execute(Memory, PBEntry.Arguments[0]); Macros[MacroIndex].Execute(Vmm, PBEntry.Arguments[0]);
} }
} }
} }

View file

@ -1,4 +1,4 @@
namespace Ryujinx.Graphics.Gpu namespace Ryujinx.Core.Gpu
{ {
enum NvGpuFifoMeth enum NvGpuFifoMeth
{ {

View file

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

View file

@ -1,9 +1,9 @@
using System; using System;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
namespace Ryujinx.Graphics.Gpu namespace Ryujinx.Core.Gpu
{ {
public struct NsGpuPBEntry public struct NvGpuPBEntry
{ {
public int Method { get; private set; } public int Method { get; private set; }
@ -13,7 +13,7 @@ namespace Ryujinx.Graphics.Gpu
public ReadOnlyCollection<int> Arguments => Array.AsReadOnly(m_Arguments); public ReadOnlyCollection<int> Arguments => Array.AsReadOnly(m_Arguments);
public NsGpuPBEntry(int Method, int SubChannel, params int[] Arguments) public NvGpuPBEntry(int Method, int SubChannel, params int[] Arguments)
{ {
this.Method = Method; this.Method = Method;
this.SubChannel = SubChannel; this.SubChannel = SubChannel;

View file

@ -1,7 +1,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
namespace Ryujinx.Graphics.Gpu namespace Ryujinx.Core.Gpu
{ {
public static class NvGpuPushBuffer public static class NvGpuPushBuffer
{ {
@ -13,13 +13,13 @@ namespace Ryujinx.Graphics.Gpu
IncrementOnce = 5 IncrementOnce = 5
} }
public static NsGpuPBEntry[] Decode(byte[] Data) public static NvGpuPBEntry[] Decode(byte[] Data)
{ {
using (MemoryStream MS = new MemoryStream(Data)) using (MemoryStream MS = new MemoryStream(Data))
{ {
BinaryReader Reader = new BinaryReader(MS); BinaryReader Reader = new BinaryReader(MS);
List<NsGpuPBEntry> PushBuffer = new List<NsGpuPBEntry>(); List<NvGpuPBEntry> PushBuffer = new List<NvGpuPBEntry>();
bool CanRead() => MS.Position + 4 <= MS.Length; bool CanRead() => MS.Position + 4 <= MS.Length;
@ -38,7 +38,7 @@ namespace Ryujinx.Graphics.Gpu
{ {
for (int Index = 0; Index < Args && CanRead(); Index++, Meth++) for (int Index = 0; Index < Args && CanRead(); Index++, Meth++)
{ {
PushBuffer.Add(new NsGpuPBEntry(Meth, SubC, Reader.ReadInt32())); PushBuffer.Add(new NvGpuPBEntry(Meth, SubC, Reader.ReadInt32()));
} }
break; break;
@ -58,14 +58,14 @@ namespace Ryujinx.Graphics.Gpu
Arguments[Index] = Reader.ReadInt32(); Arguments[Index] = Reader.ReadInt32();
} }
PushBuffer.Add(new NsGpuPBEntry(Meth, SubC, Arguments)); PushBuffer.Add(new NvGpuPBEntry(Meth, SubC, Arguments));
break; break;
} }
case SubmissionMode.Immediate: case SubmissionMode.Immediate:
{ {
PushBuffer.Add(new NsGpuPBEntry(Meth, SubC, Args)); PushBuffer.Add(new NvGpuPBEntry(Meth, SubC, Args));
break; break;
} }
@ -74,7 +74,7 @@ namespace Ryujinx.Graphics.Gpu
{ {
if (CanRead()) if (CanRead())
{ {
PushBuffer.Add(new NsGpuPBEntry(Meth, SubC, Reader.ReadInt32())); PushBuffer.Add(new NvGpuPBEntry(Meth, SubC, Reader.ReadInt32()));
} }
if (CanRead() && Args > 1) if (CanRead() && Args > 1)
@ -86,7 +86,7 @@ namespace Ryujinx.Graphics.Gpu
Arguments[Index] = Reader.ReadInt32(); Arguments[Index] = Reader.ReadInt32();
} }
PushBuffer.Add(new NsGpuPBEntry(Meth + 1, SubC, Arguments)); PushBuffer.Add(new NvGpuPBEntry(Meth + 1, SubC, Arguments));
} }
break; break;

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

@ -1,6 +1,6 @@
using Ryujinx.Graphics.Gal; using Ryujinx.Graphics.Gal;
namespace Ryujinx.Graphics.Gpu namespace Ryujinx.Core.Gpu
{ {
public struct Texture public struct Texture
{ {

View file

@ -1,14 +1,13 @@
using ChocolArm64.Memory;
using Ryujinx.Graphics.Gal; using Ryujinx.Graphics.Gal;
using System; using System;
namespace Ryujinx.Graphics.Gpu namespace Ryujinx.Core.Gpu
{ {
static class TextureFactory static class TextureFactory
{ {
public static GalTexture MakeTexture(NsGpu Gpu, AMemory Memory, long TicPosition) public static GalTexture MakeTexture(NvGpu Gpu, NvGpuVmm Vmm, long TicPosition)
{ {
int[] Tic = ReadWords(Memory, TicPosition, 8); int[] Tic = ReadWords(Vmm, TicPosition, 8);
GalTextureFormat Format = (GalTextureFormat)(Tic[0] & 0x7f); GalTextureFormat Format = (GalTextureFormat)(Tic[0] & 0x7f);
@ -16,8 +15,6 @@ namespace Ryujinx.Graphics.Gpu
TextureAddress |= (long)((ushort)Tic[2]) << 32; TextureAddress |= (long)((ushort)Tic[2]) << 32;
TextureAddress = Gpu.GetCpuAddr(TextureAddress);
TextureSwizzle Swizzle = (TextureSwizzle)((Tic[2] >> 21) & 7); TextureSwizzle Swizzle = (TextureSwizzle)((Tic[2] >> 21) & 7);
int Pitch = (Tic[3] & 0xffff) << 5; int Pitch = (Tic[3] & 0xffff) << 5;
@ -38,14 +35,14 @@ namespace Ryujinx.Graphics.Gpu
Swizzle, Swizzle,
Format); Format);
byte[] Data = TextureReader.Read(Memory, Texture); byte[] Data = TextureReader.Read(Vmm, Texture);
return new GalTexture(Data, Width, Height, Format); return new GalTexture(Data, Width, Height, Format);
} }
public static GalTextureSampler MakeSampler(NsGpu Gpu, AMemory Memory, long TscPosition) public static GalTextureSampler MakeSampler(NvGpu Gpu, NvGpuVmm Vmm, long TscPosition)
{ {
int[] Tsc = ReadWords(Memory, TscPosition, 8); int[] Tsc = ReadWords(Vmm, TscPosition, 8);
GalTextureWrap AddressU = (GalTextureWrap)((Tsc[0] >> 0) & 7); GalTextureWrap AddressU = (GalTextureWrap)((Tsc[0] >> 0) & 7);
GalTextureWrap AddressV = (GalTextureWrap)((Tsc[0] >> 3) & 7); GalTextureWrap AddressV = (GalTextureWrap)((Tsc[0] >> 3) & 7);
@ -71,13 +68,13 @@ namespace Ryujinx.Graphics.Gpu
BorderColor); BorderColor);
} }
private static int[] ReadWords(AMemory Memory, long Position, int Count) private static int[] ReadWords(NvGpuVmm Vmm, long Position, int Count)
{ {
int[] Words = new int[Count]; int[] Words = new int[Count];
for (int Index = 0; Index < Count; Index++, Position += 4) for (int Index = 0; Index < Count; Index++, Position += 4)
{ {
Words[Index] = Memory.ReadInt32(Position); Words[Index] = Vmm.ReadInt32(Position);
} }
return Words; return Words;

View file

@ -1,6 +1,7 @@
using ChocolArm64.Memory;
using System; using System;
namespace Ryujinx.Graphics.Gpu namespace Ryujinx.Core.Gpu
{ {
static class TextureHelper static class TextureHelper
{ {
@ -19,5 +20,17 @@ namespace Ryujinx.Graphics.Gpu
throw new NotImplementedException(Texture.Swizzle.ToString()); 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

@ -2,11 +2,11 @@ using ChocolArm64.Memory;
using Ryujinx.Graphics.Gal; using Ryujinx.Graphics.Gal;
using System; using System;
namespace Ryujinx.Graphics.Gpu namespace Ryujinx.Core.Gpu
{ {
public static class TextureReader public static class TextureReader
{ {
public static byte[] Read(AMemory Memory, Texture Texture) public static byte[] Read(IAMemory Memory, Texture Texture)
{ {
switch (Texture.Format) switch (Texture.Format)
{ {
@ -23,7 +23,7 @@ namespace Ryujinx.Graphics.Gpu
throw new NotImplementedException(Texture.Format.ToString()); throw new NotImplementedException(Texture.Format.ToString());
} }
private unsafe static byte[] Read2Bpp(AMemory Memory, Texture Texture) private unsafe static byte[] Read2Bpp(IAMemory Memory, Texture Texture)
{ {
int Width = Texture.Width; int Width = Texture.Width;
int Height = Texture.Height; int Height = Texture.Height;
@ -32,6 +32,10 @@ namespace Ryujinx.Graphics.Gpu
ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, Width, 2); ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, Width, 2);
(AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition(
Memory,
Texture.Position);
fixed (byte* BuffPtr = Output) fixed (byte* BuffPtr = Output)
{ {
long OutOffs = 0; long OutOffs = 0;
@ -41,7 +45,7 @@ namespace Ryujinx.Graphics.Gpu
{ {
long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y); long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
short Pixel = Memory.ReadInt16Unchecked(Texture.Position + Offset); short Pixel = CpuMem.ReadInt16Unchecked(Position + Offset);
*(short*)(BuffPtr + OutOffs) = Pixel; *(short*)(BuffPtr + OutOffs) = Pixel;
@ -52,7 +56,7 @@ namespace Ryujinx.Graphics.Gpu
return Output; return Output;
} }
private unsafe static byte[] Read4Bpp(AMemory Memory, Texture Texture) private unsafe static byte[] Read4Bpp(IAMemory Memory, Texture Texture)
{ {
int Width = Texture.Width; int Width = Texture.Width;
int Height = Texture.Height; int Height = Texture.Height;
@ -61,6 +65,10 @@ namespace Ryujinx.Graphics.Gpu
ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, Width, 4); ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, Width, 4);
(AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition(
Memory,
Texture.Position);
fixed (byte* BuffPtr = Output) fixed (byte* BuffPtr = Output)
{ {
long OutOffs = 0; long OutOffs = 0;
@ -70,7 +78,7 @@ namespace Ryujinx.Graphics.Gpu
{ {
long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y); long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
int Pixel = Memory.ReadInt32Unchecked(Texture.Position + Offset); int Pixel = CpuMem.ReadInt32Unchecked(Position + Offset);
*(int*)(BuffPtr + OutOffs) = Pixel; *(int*)(BuffPtr + OutOffs) = Pixel;
@ -81,7 +89,7 @@ namespace Ryujinx.Graphics.Gpu
return Output; return Output;
} }
private unsafe static byte[] Read8Bpt4x4(AMemory Memory, Texture Texture) private unsafe static byte[] Read8Bpt4x4(IAMemory Memory, Texture Texture)
{ {
int Width = (Texture.Width + 3) / 4; int Width = (Texture.Width + 3) / 4;
int Height = (Texture.Height + 3) / 4; int Height = (Texture.Height + 3) / 4;
@ -90,6 +98,10 @@ namespace Ryujinx.Graphics.Gpu
ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, Width, 8); ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, Width, 8);
(AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition(
Memory,
Texture.Position);
fixed (byte* BuffPtr = Output) fixed (byte* BuffPtr = Output)
{ {
long OutOffs = 0; long OutOffs = 0;
@ -99,7 +111,7 @@ namespace Ryujinx.Graphics.Gpu
{ {
long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y); long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
long Tile = Memory.ReadInt64Unchecked(Texture.Position + Offset); long Tile = CpuMem.ReadInt64Unchecked(Position + Offset);
*(long*)(BuffPtr + OutOffs) = Tile; *(long*)(BuffPtr + OutOffs) = Tile;
@ -110,7 +122,7 @@ namespace Ryujinx.Graphics.Gpu
return Output; return Output;
} }
private unsafe static byte[] Read16Bpt4x4(AMemory Memory, Texture Texture) private unsafe static byte[] Read16Bpt4x4(IAMemory Memory, Texture Texture)
{ {
int Width = (Texture.Width + 3) / 4; int Width = (Texture.Width + 3) / 4;
int Height = (Texture.Height + 3) / 4; int Height = (Texture.Height + 3) / 4;
@ -119,6 +131,10 @@ namespace Ryujinx.Graphics.Gpu
ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, Width, 16); ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, Width, 16);
(AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition(
Memory,
Texture.Position);
fixed (byte* BuffPtr = Output) fixed (byte* BuffPtr = Output)
{ {
long OutOffs = 0; long OutOffs = 0;
@ -128,8 +144,8 @@ namespace Ryujinx.Graphics.Gpu
{ {
long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y); long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
long Tile0 = Memory.ReadInt64Unchecked(Texture.Position + Offset + 0); long Tile0 = CpuMem.ReadInt64Unchecked(Position + Offset + 0);
long Tile1 = Memory.ReadInt64Unchecked(Texture.Position + Offset + 8); long Tile1 = CpuMem.ReadInt64Unchecked(Position + Offset + 8);
*(long*)(BuffPtr + OutOffs + 0) = Tile0; *(long*)(BuffPtr + OutOffs + 0) = Tile0;
*(long*)(BuffPtr + OutOffs + 8) = Tile1; *(long*)(BuffPtr + OutOffs + 8) = Tile1;

View file

@ -1,4 +1,4 @@
namespace Ryujinx.Graphics.Gpu namespace Ryujinx.Core.Gpu
{ {
public enum TextureSwizzle public enum TextureSwizzle
{ {

View file

@ -2,28 +2,31 @@ using ChocolArm64.Memory;
using Ryujinx.Graphics.Gal; using Ryujinx.Graphics.Gal;
using System; using System;
namespace Ryujinx.Graphics.Gpu namespace Ryujinx.Core.Gpu
{ {
public static class TextureWriter public static class TextureWriter
{ {
public static void Write(AMemory Memory, Texture Texture, byte[] Data) public static void Write(IAMemory Memory, Texture Texture, byte[] Data)
{ {
switch (Texture.Format) switch (Texture.Format)
{ {
case GalTextureFormat.A8B8G8R8: Write4Bpp(Memory, Texture, Data); break; case GalTextureFormat.A8B8G8R8: Write4Bpp(Memory, Texture, Data); break;
default: default: throw new NotImplementedException(Texture.Format.ToString());
throw new NotImplementedException(Texture.Format.ToString());
} }
} }
private unsafe static void Write4Bpp(AMemory Memory, Texture Texture, byte[] Data) private unsafe static void Write4Bpp(IAMemory Memory, Texture Texture, byte[] Data)
{ {
int Width = Texture.Width; int Width = Texture.Width;
int Height = Texture.Height; int Height = Texture.Height;
ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, Width, 4); ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, Width, 4);
(AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition(
Memory,
Texture.Position);
fixed (byte* BuffPtr = Data) fixed (byte* BuffPtr = Data)
{ {
long InOffs = 0; long InOffs = 0;
@ -35,7 +38,7 @@ namespace Ryujinx.Graphics.Gpu
int Pixel = *(int*)(BuffPtr + InOffs); int Pixel = *(int*)(BuffPtr + InOffs);
Memory.WriteInt32Unchecked(Texture.Position + Offset, Pixel); CpuMem.WriteInt32Unchecked(Position + Offset, Pixel);
InOffs += 4; InOffs += 4;
} }

View file

@ -32,9 +32,9 @@ namespace Ryujinx.Core.Loaders
this.ImageBase = ImageBase; this.ImageBase = ImageBase;
this.ImageEnd = ImageBase; this.ImageEnd = ImageBase;
WriteData(ImageBase + Exe.TextOffset, Exe.Text, MemoryType.CodeStatic, AMemoryPerm.RX); WriteData(ImageBase + Exe.TextOffset, Exe.Text, MemoryType.CodeStatic, AMemoryPerm.RX);
WriteData(ImageBase + Exe.ROOffset, Exe.RO, MemoryType.Normal, AMemoryPerm.Read); WriteData(ImageBase + Exe.ROOffset, Exe.RO, MemoryType.CodeMutable, AMemoryPerm.Read);
WriteData(ImageBase + Exe.DataOffset, Exe.Data, MemoryType.Normal, AMemoryPerm.RW); WriteData(ImageBase + Exe.DataOffset, Exe.Data, MemoryType.CodeMutable, AMemoryPerm.RW);
if (Exe.Mod0Offset == 0) if (Exe.Mod0Offset == 0)
{ {

View file

@ -20,6 +20,8 @@ namespace Ryujinx.Core.OsHle
public SystemStateMgr SystemState { get; private set; } public SystemStateMgr SystemState { get; private set; }
internal MemoryAllocator Allocator { get; private set; }
internal HSharedMem HidSharedMem { get; private set; } internal HSharedMem HidSharedMem { get; private set; }
internal HSharedMem FontSharedMem { get; private set; } internal HSharedMem FontSharedMem { get; private set; }
@ -35,6 +37,8 @@ namespace Ryujinx.Core.OsHle
SystemState = new SystemStateMgr(); SystemState = new SystemStateMgr();
Allocator = new MemoryAllocator();
HidSharedMem = new HSharedMem(); HidSharedMem = new HSharedMem();
FontSharedMem = new HSharedMem(); FontSharedMem = new HSharedMem();

View file

@ -7,7 +7,7 @@ namespace Ryujinx.Core.OsHle.Ipc
{ {
static class IpcHandler static class IpcHandler
{ {
public static void IpcCall( public static long IpcCall(
Switch Ns, Switch Ns,
Process Process, Process Process,
AMemory Memory, AMemory Memory,
@ -94,6 +94,8 @@ namespace Ryujinx.Core.OsHle.Ipc
AMemoryHelper.WriteBytes(Memory, CmdPtr, Response.GetBytes(CmdPtr)); AMemoryHelper.WriteBytes(Memory, CmdPtr, Response.GetBytes(CmdPtr));
} }
return 0;
} }
private static IpcMessage FillResponse(IpcMessage Response, long Result, params int[] Values) private static IpcMessage FillResponse(IpcMessage Response, long Result, params int[] Values)

View file

@ -174,31 +174,6 @@ namespace Ryujinx.Core.OsHle.Ipc
return 0; 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() public long GetBufferType0x21Position()
{ {
if (PtrBuff.Count > 0 && PtrBuff[0].Position != 0) 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 InvalidMemRange = 110;
public const int InvalidHandle = 114; public const int InvalidHandle = 114;
public const int Timeout = 117; public const int Timeout = 117;
public const int Canceled = 118;
public const int CountOutOfRange = 119;
public const int InvalidInfo = 120; public const int InvalidInfo = 120;
} }
} }

View file

@ -5,6 +5,8 @@ using Ryujinx.Core.Logging;
using Ryujinx.Core.OsHle.Handles; using Ryujinx.Core.OsHle.Handles;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Threading;
namespace Ryujinx.Core.OsHle.Kernel namespace Ryujinx.Core.OsHle.Kernel
{ {
@ -18,12 +20,16 @@ namespace Ryujinx.Core.OsHle.Kernel
private Process Process; private Process Process;
private AMemory Memory; private AMemory Memory;
private ConcurrentDictionary<KThread, AutoResetEvent> SyncWaits;
private object CondVarLock; private object CondVarLock;
private HashSet<(HSharedMem, long)> MappedSharedMems; private HashSet<(HSharedMem, long)> MappedSharedMems;
private ulong CurrentHeapSize; private ulong CurrentHeapSize;
private const uint SelfHandle = 0xffff8001;
private static Random Rng; private static Random Rng;
public SvcHandler(Switch Ns, Process Process) public SvcHandler(Switch Ns, Process Process)
@ -51,6 +57,7 @@ namespace Ryujinx.Core.OsHle.Kernel
{ 0x16, SvcCloseHandle }, { 0x16, SvcCloseHandle },
{ 0x17, SvcResetSignal }, { 0x17, SvcResetSignal },
{ 0x18, SvcWaitSynchronization }, { 0x18, SvcWaitSynchronization },
{ 0x19, SvcCancelSynchronization },
{ 0x1a, SvcArbitrateLock }, { 0x1a, SvcArbitrateLock },
{ 0x1b, SvcArbitrateUnlock }, { 0x1b, SvcArbitrateUnlock },
{ 0x1c, SvcWaitProcessWideKeyAtomic }, { 0x1c, SvcWaitProcessWideKeyAtomic },
@ -70,6 +77,8 @@ namespace Ryujinx.Core.OsHle.Kernel
this.Process = Process; this.Process = Process;
this.Memory = Process.Memory; this.Memory = Process.Memory;
SyncWaits = new ConcurrentDictionary<KThread, AutoResetEvent>();
CondVarLock = new object(); CondVarLock = new object();
MappedSharedMems = new HashSet<(HSharedMem, long)>(); 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() public void Dispose()
{ {
Dispose(true); Dispose(true);

View file

@ -40,7 +40,7 @@ namespace Ryujinx.Core.OsHle.Kernel
if (Obj == null) 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); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
@ -88,9 +88,21 @@ namespace Ryujinx.Core.OsHle.Kernel
int HandlesCount = (int)ThreadState.X2; int HandlesCount = (int)ThreadState.X2;
ulong Timeout = ThreadState.X3; 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); KThread CurrThread = Process.GetThread(ThreadState.Tpidr);
WaitHandle[] Handles = new WaitHandle[HandlesCount]; WaitHandle[] Handles = new WaitHandle[HandlesCount + 1];
for (int Index = 0; Index < HandlesCount; Index++) for (int Index = 0; Index < HandlesCount; Index++)
{ {
@ -110,34 +122,73 @@ namespace Ryujinx.Core.OsHle.Kernel
Handles[Index] = SyncObj.WaitEvent; Handles[Index] = SyncObj.WaitEvent;
} }
Process.Scheduler.Suspend(CurrThread.ProcessorId); using (AutoResetEvent WaitEvent = new AutoResetEvent(false))
int HandleIndex;
ulong Result = 0;
if (Timeout != ulong.MaxValue)
{ {
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) if (HandleIndex == WaitHandle.WaitTimeout)
{ {
Result = MakeError(ErrorModule.Kernel, KernelErr.Timeout); 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); if (SyncWaits.TryRemove(Thread, out AutoResetEvent WaitEvent))
ThreadState.X0 = Result;
if (Result == 0)
{ {
ThreadState.X1 = (ulong)HandleIndex; WaitEvent.Set();
} }
ThreadState.X0 = 0;
} }
private void SvcGetSystemTick(AThreadState ThreadState) private void SvcGetSystemTick(AThreadState ThreadState)
@ -190,13 +241,13 @@ namespace Ryujinx.Core.OsHle.Kernel
IpcMessage Cmd = new IpcMessage(CmdData, CmdPtr); 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(); Thread.Yield();
Process.Scheduler.Resume(CurrThread); Process.Scheduler.Resume(CurrThread);
ThreadState.X0 = 0; ThreadState.X0 = (ulong)Result;
} }
else else
{ {

View file

@ -191,6 +191,8 @@ namespace Ryujinx.Core.OsHle.Kernel
InsertWaitingMutexThread(OwnerThreadHandle, WaitThread); InsertWaitingMutexThread(OwnerThreadHandle, WaitThread);
Ns.Log.PrintDebug(LogClass.KernelSvc, "Entering wait state...");
Process.Scheduler.EnterWait(CurrThread); 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) if (Timeout != ulong.MaxValue)
{ {
return Process.Scheduler.EnterWait(WaitThread, NsTimeConverter.GetTimeMs(Timeout)); return Process.Scheduler.EnterWait(WaitThread, NsTimeConverter.GetTimeMs(Timeout));
@ -407,7 +411,7 @@ namespace Ryujinx.Core.OsHle.Kernel
if (CurrThread != WaitThread) if (CurrThread != WaitThread)
{ {
if (WaitThread.NextCondVarThread != null) if (WaitThread.NextMutexThread != null)
{ {
throw new InvalidOperationException(); 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.UnloadProcess(this);
INvDrvServices.NvMaps .DeleteProcess(this);
INvDrvServices.NvMapsById.DeleteProcess(this);
INvDrvServices.NvMapsFb .DeleteProcess(this);
AppletState.Dispose(); AppletState.Dispose();

View file

@ -2,30 +2,36 @@ using ChocolArm64.Memory;
using Ryujinx.Core.Logging; using Ryujinx.Core.Logging;
using Ryujinx.Core.OsHle.Handles; using Ryujinx.Core.OsHle.Handles;
using Ryujinx.Core.OsHle.Ipc; using Ryujinx.Core.OsHle.Ipc;
using Ryujinx.Core.OsHle.Utilities; using Ryujinx.Core.OsHle.Services.Nv.NvGpuAS;
using Ryujinx.Graphics.Gpu; 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;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
namespace Ryujinx.Core.OsHle.Services.Nv namespace Ryujinx.Core.OsHle.Services.Nv
{ {
class INvDrvServices : IpcService, IDisposable class INvDrvServices : IpcService, IDisposable
{ {
private delegate long ServiceProcessIoctl(ServiceCtx Context); private delegate int IoctlProcessor(ServiceCtx Context, int Cmd);
private Dictionary<int, ServiceProcessRequest> m_Commands; private Dictionary<int, ServiceProcessRequest> m_Commands;
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => 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 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; private KEvent Event;
public INvDrvServices() public INvDrvServices()
@ -37,42 +43,7 @@ namespace Ryujinx.Core.OsHle.Services.Nv
{ 2, Close }, { 2, Close },
{ 3, Initialize }, { 3, Initialize },
{ 4, QueryEvent }, { 4, QueryEvent },
{ 8, SetClientPid }, { 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 },
}; };
Event = new KEvent(); Event = new KEvent();
@ -81,10 +52,6 @@ namespace Ryujinx.Core.OsHle.Services.Nv
static INvDrvServices() static INvDrvServices()
{ {
Fds = new GlobalStateTable(); Fds = new GlobalStateTable();
NvMaps = new GlobalStateTable();
NvMapsById = new GlobalStateTable();
NvMapsFb = new GlobalStateTable();
} }
public long Open(ServiceCtx Context) public long Open(ServiceCtx Context)
@ -104,22 +71,25 @@ namespace Ryujinx.Core.OsHle.Services.Nv
public long Ioctl(ServiceCtx Context) public long Ioctl(ServiceCtx Context)
{ {
int Fd = Context.RequestData.ReadInt32(); int Fd = Context.RequestData.ReadInt32();
int Cmd = Context.RequestData.ReadInt32() & 0xffff; int Cmd = Context.RequestData.ReadInt32();
NvFd FdData = Fds.GetData<NvFd>(Context.Process, Fd); NvFd FdData = Fds.GetData<NvFd>(Context.Process, Fd);
long Position = Context.Request.GetSendBuffPtr(); int Result;
Context.ResponseData.Write(0); if (IoctlProcessors.TryGetValue(FdData.Name, out IoctlProcessor Process))
if (IoctlCmds.TryGetValue((FdData.Name, Cmd), out ServiceProcessIoctl ProcReq))
{ {
return ProcReq(Context); Result = Process(Context, Cmd);
} }
else else
{ {
throw new NotImplementedException($"{FdData.Name} {Cmd:x4}"); 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) public long Close(ServiceCtx Context)
@ -138,9 +108,9 @@ namespace Ryujinx.Core.OsHle.Services.Nv
long TransferMemSize = Context.RequestData.ReadInt64(); long TransferMemSize = Context.RequestData.ReadInt64();
int TransferMemHandle = Context.Request.HandleDesc.ToCopy[0]; 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; return 0;
} }
@ -169,659 +139,69 @@ namespace Ryujinx.Core.OsHle.Services.Nv
return 0; return 0;
} }
private long NvGpuAsIoctlBindChannel(ServiceCtx Context) private static int ProcessIoctlNvGpuAS(ServiceCtx Context, int Cmd)
{ {
long Position = Context.Request.GetSendBuffPtr(); return ProcessIoctl(Context, Cmd, NvGpuASIoctl.ProcessIoctl);
int Fd = Context.Memory.ReadInt32(Position);
return 0;
} }
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(); private static int ProcessIoctlNvHostChannel(ServiceCtx Context, int Cmd)
int PageSize = Reader.ReadInt32(); {
int Flags = Reader.ReadInt32(); return ProcessIoctl(Context, Cmd, NvHostChannelIoctl.ProcessIoctl);
int Padding = Reader.ReadInt32(); }
long Align = Reader.ReadInt64();
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); Context.Ns.Log.PrintError(LogClass.ServiceNv, "Input buffer is null!");
}
else return NvResult.InvalidInput;
{
Align = Context.Ns.Gpu.ReserveMemory((long)Pages * PageSize, Align);
} }
Context.Memory.WriteInt64(Position + 0x10, Align); if (CmdOut(Cmd) && Context.Request.GetBufferType0x22Position() == 0)
return 0;
}
private long NvGpuAsIoctlUnmap(ServiceCtx Context)
{
long Position = Context.Request.GetSendBuffPtr();
MemReader Reader = new MemReader(Context.Memory, Position);
long Offset = Reader.ReadInt64();
Context.Ns.Gpu.MemoryMgr.Unmap(Offset, 0x10000);
return 0;
}
private long NvGpuAsIoctlMapBufferEx(ServiceCtx Context)
{
long Position = Context.Request.GetSendBuffPtr();
MemReader Reader = new MemReader(Context.Memory, Position);
int Flags = Reader.ReadInt32();
int Kind = Reader.ReadInt32();
int Handle = Reader.ReadInt32();
int PageSize = Reader.ReadInt32();
long BuffAddr = Reader.ReadInt64();
long MapSize = Reader.ReadInt64();
long Offset = Reader.ReadInt64();
if (Handle == 0)
{ {
//This is used to store offsets for the Framebuffer(s); Context.Ns.Log.PrintError(LogClass.ServiceNv, "Output buffer is null!");
NvMapFb MapFb = (NvMapFb)NvMapsFb.GetData(Context.Process, 0);
MapFb.AddBufferOffset(BuffAddr); return NvResult.InvalidInput;
return 0;
} }
NvMap Map = NvMaps.GetData<NvMap>(Context.Process, Handle); return Processor(Context, Cmd);
if (Map == null)
{
Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"invalid NvMap Handle {Handle}!");
return -1; //TODO: Corrent error code.
}
if ((Flags & 1) != 0)
{
Offset = Context.Ns.Gpu.MapMemory(Map.CpuAddress, Offset, Map.Size);
}
else
{
Offset = Context.Ns.Gpu.MapMemory(Map.CpuAddress, Map.Size);
}
Context.Memory.WriteInt64(Position + 0x20, Offset);
Map.GpuAddress = Offset;
return 0;
} }
private long NvGpuAsIoctlGetVaRegions(ServiceCtx Context) private static bool CmdIn(int Cmd)
{ {
long Position = Context.Request.GetSendBuffPtr(); return ((Cmd >> 30) & 1) != 0;
MemReader Reader = new MemReader(Context.Memory, Position);
MemWriter Writer = new MemWriter(Context.Memory, Position);
long Unused = Reader.ReadInt64();
int BuffSize = Reader.ReadInt32();
int Padding = Reader.ReadInt32();
BuffSize = 0x30;
Writer.WriteInt64(Unused);
Writer.WriteInt32(BuffSize);
Writer.WriteInt32(Padding);
Writer.WriteInt64(0);
Writer.WriteInt32(0);
Writer.WriteInt32(0);
Writer.WriteInt64(0);
Writer.WriteInt64(0);
Writer.WriteInt32(0);
Writer.WriteInt32(0);
Writer.WriteInt64(0);
return 0;
} }
private long NvGpuAsIoctlInitializeEx(ServiceCtx Context) private static bool CmdOut(int Cmd)
{ {
long Position = Context.Request.GetSendBuffPtr(); return ((Cmd >> 31) & 1) != 0;
MemReader Reader = new MemReader(Context.Memory, Position);
int BigPageSize = Reader.ReadInt32();
int AsFd = Reader.ReadInt32();
int Flags = Reader.ReadInt32();
int Reserved = Reader.ReadInt32();
long Unknown10 = Reader.ReadInt64();
long Unknown18 = Reader.ReadInt64();
long Unknown20 = Reader.ReadInt64();
return 0;
} }
private long NvGpuAsIoctlRemap(ServiceCtx Context) 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; NvMapIoctl.UnloadProcess(Process);
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;
} }
public void Dispose() 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 ChocolArm64.Memory;
using Ryujinx.Core.Gpu;
using Ryujinx.Core.Logging; using Ryujinx.Core.Logging;
using Ryujinx.Core.OsHle.Handles; 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.Gal;
using Ryujinx.Graphics.Gpu;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
@ -282,20 +282,12 @@ namespace Ryujinx.Core.OsHle.Services.Android
int FbWidth = 1280; int FbWidth = 1280;
int FbHeight = 720; 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 FbAddr = Map.Address + BufferOffset;
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);
}
BufferQueue[Slot].State = BufferState.Acquired; 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 //TODO: Support double buffering here aswell, it is broken for GPU
//frame buffers because it seems to be completely out of sync. //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 //Frame buffer is rendered to by the GPU, we can just
//bind the frame buffer texture, it's not necessary to read anything. //bind the frame buffer texture, it's not necessary to read anything.
Renderer.SetFrameBuffer(GpuAddr); Renderer.SetFrameBuffer(FbAddr);
} }
else else
{ {
//Frame buffer is not set on the GPU registers, in this case //Frame buffer is not set on the GPU registers, in this case
//assume that the app is manually writing to it. //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); byte[] Data = TextureReader.Read(Context.Memory, Texture);
@ -372,22 +364,6 @@ namespace Ryujinx.Core.OsHle.Services.Android
Context.Ns.Gpu.Renderer.QueueAction(() => ReleaseBuffer(Slot)); 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) private void ReleaseBuffer(int Slot)
{ {
BufferQueue[Slot].State = BufferState.Free; 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.OsHle;
using Ryujinx.Core.Settings; using Ryujinx.Core.Settings;
using Ryujinx.Graphics.Gal; using Ryujinx.Graphics.Gal;
using Ryujinx.Graphics.Gpu; using Ryujinx.Core.Gpu;
using System; using System;
namespace Ryujinx.Core namespace Ryujinx.Core
@ -15,7 +15,7 @@ namespace Ryujinx.Core
public Logger Log { get; private set; } public Logger Log { get; private set; }
internal NsGpu Gpu { get; private set; } internal NvGpu Gpu { get; private set; }
internal VirtualFileSystem VFs { get; private set; } internal VirtualFileSystem VFs { get; private set; }
@ -45,7 +45,7 @@ namespace Ryujinx.Core
Log = new Logger(); Log = new Logger();
Gpu = new NsGpu(Renderer); Gpu = new NvGpu(Renderer);
VFs = new VirtualFileSystem(); VFs = new VirtualFileSystem();

View file

@ -1,11 +0,0 @@
using ChocolArm64.Memory;
namespace Ryujinx.Graphics.Gpu
{
interface INvGpuEngine
{
int[] Registers { get; }
void CallMethod(AMemory Memory, NsGpuPBEntry PBEntry);
}
}

View file

@ -1,212 +0,0 @@
namespace Ryujinx.Graphics.Gpu
{
public class NsGpuMemoryMgr
{
private const long AddrSize = 1L << 40;
private const int PTLvl0Bits = 14;
private const int PTLvl1Bits = 14;
private const int PTPageBits = 12;
private const int PTLvl0Size = 1 << PTLvl0Bits;
private const int PTLvl1Size = 1 << PTLvl1Bits;
private const int PageSize = 1 << PTPageBits;
private const int PTLvl0Mask = PTLvl0Size - 1;
private const int PTLvl1Mask = PTLvl1Size - 1;
private const int PageMask = PageSize - 1;
private const int PTLvl0Bit = PTPageBits + PTLvl1Bits;
private const int PTLvl1Bit = PTPageBits;
private const long PteUnmapped = -1;
private const long PteReserved = -2;
private long[][] PageTable;
public NsGpuMemoryMgr()
{
PageTable = new long[PTLvl0Size][];
}
public long Map(long CpuAddr, long GpuAddr, long Size)
{
CpuAddr &= ~PageMask;
GpuAddr &= ~PageMask;
for (long Offset = 0; Offset < Size; Offset += PageSize)
{
if (GetPTAddr(GpuAddr + Offset) != PteReserved)
{
return Map(CpuAddr, Size);
}
}
for (long Offset = 0; Offset < Size; Offset += PageSize)
{
SetPTAddr(GpuAddr + Offset, CpuAddr + Offset);
}
return GpuAddr;
}
public void Unmap(long Position, long Size)
{
for (long Offset = 0; Offset < Size; Offset += PageSize)
{
SetPTAddr(Position + Offset, PteUnmapped);
}
}
public long Map(long CpuAddr, long Size)
{
CpuAddr &= ~PageMask;
long Position = GetFreePosition(Size);
if (Position != -1)
{
for (long Offset = 0; Offset < Size; Offset += PageSize)
{
SetPTAddr(Position + Offset, CpuAddr + Offset);
}
}
return Position;
}
public long Reserve(long GpuAddr, long Size, long Align)
{
for (long Offset = 0; Offset < Size; Offset += PageSize)
{
if (HasPTAddr(GpuAddr + Offset))
{
return Reserve(Size, Align);
}
}
for (long Offset = 0; Offset < Size; Offset += PageSize)
{
SetPTAddr(GpuAddr + Offset, PteReserved);
}
return GpuAddr;
}
public long Reserve(long Size, long Align)
{
long Position = GetFreePosition(Size, Align);
if (Position != -1)
{
for (long Offset = 0; Offset < Size; Offset += PageSize)
{
SetPTAddr(Position + Offset, PteReserved);
}
}
return Position;
}
private long GetFreePosition(long Size, long Align = 1)
{
long Position = 0;
long FreeSize = 0;
if (Align < 1)
{
Align = 1;
}
Align = (Align + PageMask) & ~PageMask;
while (Position + FreeSize < AddrSize)
{
if (!HasPTAddr(Position + FreeSize))
{
FreeSize += PageSize;
if (FreeSize >= Size)
{
return Position;
}
}
else
{
Position += FreeSize + PageSize;
FreeSize = 0;
long Remainder = Position % Align;
if (Remainder != 0)
{
Position = (Position - Remainder) + Align;
}
}
}
return -1;
}
public long GetCpuAddr(long Position)
{
long BasePos = GetPTAddr(Position);
if (BasePos < 0)
{
return -1;
}
return BasePos + (Position & PageMask);
}
private bool HasPTAddr(long Position)
{
if (Position >> PTLvl0Bits + PTLvl1Bits + PTPageBits != 0)
{
return false;
}
long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask;
long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask;
if (PageTable[L0] == null)
{
return false;
}
return PageTable[L0][L1] != PteUnmapped;
}
private long GetPTAddr(long Position)
{
long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask;
long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask;
if (PageTable[L0] == null)
{
return -1;
}
return PageTable[L0][L1];
}
private void SetPTAddr(long Position, long TgtAddr)
{
long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask;
long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask;
if (PageTable[L0] == null)
{
PageTable[L0] = new long[PTLvl1Size];
for (int Index = 0; Index < PTLvl1Size; Index++)
{
PageTable[L0][Index] = PteUnmapped;
}
}
PageTable[L0][L1] = TgtAddr;
}
}
}

View file

@ -1,6 +0,0 @@
using ChocolArm64.Memory;
namespace Ryujinx.Graphics.Gpu
{
delegate void NvGpuMethod(AMemory Memory, NsGpuPBEntry PBEntry);
}