diff --git a/Ryujinx.Core/OsHle/Handles/KProcessScheduler.cs b/Ryujinx.Core/OsHle/Handles/KProcessScheduler.cs index c601d5221..1c35b23c5 100644 --- a/Ryujinx.Core/OsHle/Handles/KProcessScheduler.cs +++ b/Ryujinx.Core/OsHle/Handles/KProcessScheduler.cs @@ -14,23 +14,23 @@ namespace Ryujinx.Core.OsHle.Handles { public KThread Thread { get; private set; } - public ManualResetEvent SyncWaitEvent { get; private set; } - public AutoResetEvent SchedWaitEvent { get; private set; } + public bool IsActive { get; set; } - public bool Active { get; set; } - - public int SyncTimeout { get; set; } + public AutoResetEvent WaitSync { get; private set; } + public ManualResetEvent WaitActivity { get; private set; } + public AutoResetEvent WaitSched { get; private set; } public SchedulerThread(KThread Thread) { this.Thread = Thread; - SyncWaitEvent = new ManualResetEvent(true); - SchedWaitEvent = new AutoResetEvent(false); + IsActive = true; - Active = true; + WaitSync = new AutoResetEvent(false); - SyncTimeout = 0; + WaitActivity = new ManualResetEvent(true); + + WaitSched = new AutoResetEvent(false); } public void Dispose() @@ -42,8 +42,11 @@ namespace Ryujinx.Core.OsHle.Handles { if (Disposing) { - SyncWaitEvent.Dispose(); - SchedWaitEvent.Dispose(); + WaitSync.Dispose(); + + WaitActivity.Dispose(); + + WaitSched.Dispose(); } } } @@ -206,25 +209,46 @@ namespace Ryujinx.Core.OsHle.Handles throw new InvalidOperationException(); } - SchedThread.Active = Active; + SchedThread.IsActive = Active; - UpdateSyncWaitEvent(SchedThread); - - WaitIfNeeded(SchedThread); + if (Active) + { + SchedThread.WaitActivity.Set(); + } + else + { + SchedThread.WaitActivity.Reset(); + } } - public bool EnterWait(KThread Thread, int Timeout = -1) + public void EnterWait(KThread Thread) { if (!AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread)) { throw new InvalidOperationException(); } - SchedThread.SyncTimeout = Timeout; + Suspend(Thread.ProcessorId); - UpdateSyncWaitEvent(SchedThread); + SchedThread.WaitSync.WaitOne(); - return WaitIfNeeded(SchedThread); + TryResumingExecution(SchedThread); + } + + public bool EnterWait(KThread Thread, int Timeout) + { + if (!AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread)) + { + throw new InvalidOperationException(); + } + + Suspend(Thread.ProcessorId); + + bool Result = SchedThread.WaitSync.WaitOne(Timeout); + + TryResumingExecution(SchedThread); + + return Result; } public void WakeUp(KThread Thread) @@ -234,39 +258,7 @@ namespace Ryujinx.Core.OsHle.Handles throw new InvalidOperationException(); } - SchedThread.SyncTimeout = 0; - - UpdateSyncWaitEvent(SchedThread); - - WaitIfNeeded(SchedThread); - } - - private void UpdateSyncWaitEvent(SchedulerThread SchedThread) - { - if (SchedThread.Active && SchedThread.SyncTimeout == 0) - { - SchedThread.SyncWaitEvent.Set(); - } - else - { - SchedThread.SyncWaitEvent.Reset(); - } - } - - private bool WaitIfNeeded(SchedulerThread SchedThread) - { - KThread Thread = SchedThread.Thread; - - if (!IsActive(SchedThread) && Thread.Thread.IsCurrentThread()) - { - Suspend(Thread.ProcessorId); - - return Resume(Thread); - } - else - { - return false; - } + SchedThread.WaitSync.Set(); } public void Suspend(int ProcessorId) @@ -292,53 +284,52 @@ namespace Ryujinx.Core.OsHle.Handles { PrintDbgThreadInfo(Thread, "yielded execution."); - lock (SchedLock) + if (IsActive(Thread)) { - SchedulerThread SchedThread = WaitingToRun[Thread.ProcessorId].Pop(Thread.ActualPriority); - - if (IsActive(Thread) && SchedThread == null) + lock (SchedLock) { - PrintDbgThreadInfo(Thread, "resumed because theres nothing better to run."); + SchedulerThread SchedThread = WaitingToRun[Thread.ProcessorId].Pop(Thread.ActualPriority); - return; - } + if (SchedThread == null) + { + PrintDbgThreadInfo(Thread, "resumed because theres nothing better to run."); - if (SchedThread != null) - { - RunThread(SchedThread); + return; + } + + if (SchedThread != null) + { + RunThread(SchedThread); + } } } + else + { + //Just stop running the thread if it's not active, + //and run whatever is waiting to run with the higuest priority. + Suspend(Thread.ProcessorId); + } Resume(Thread); } - public bool Resume(KThread Thread) + public void Resume(KThread Thread) { if (!AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread)) { throw new InvalidOperationException(); } - return TryResumingExecution(SchedThread); + TryResumingExecution(SchedThread); } - private bool TryResumingExecution(SchedulerThread SchedThread) + private void TryResumingExecution(SchedulerThread SchedThread) { KThread Thread = SchedThread.Thread; - if (!SchedThread.Active || SchedThread.SyncTimeout != 0) - { - PrintDbgThreadInfo(Thread, "entering inactive wait state..."); - } + PrintDbgThreadInfo(Thread, "trying to resume..."); - bool Result = false; - - if (SchedThread.SyncTimeout != 0) - { - Result = SchedThread.SyncWaitEvent.WaitOne(SchedThread.SyncTimeout); - - SchedThread.SyncTimeout = 0; - } + SchedThread.WaitActivity.WaitOne(); lock (SchedLock) { @@ -346,7 +337,7 @@ namespace Ryujinx.Core.OsHle.Handles { PrintDbgThreadInfo(Thread, "resuming execution..."); - return Result; + return; } WaitingToRun[Thread.ProcessorId].Push(SchedThread); @@ -354,18 +345,16 @@ namespace Ryujinx.Core.OsHle.Handles PrintDbgThreadInfo(Thread, "entering wait state..."); } - SchedThread.SchedWaitEvent.WaitOne(); + SchedThread.WaitSched.WaitOne(); PrintDbgThreadInfo(Thread, "resuming execution..."); - - return Result; } private void RunThread(SchedulerThread SchedThread) { if (!SchedThread.Thread.Thread.Execute()) { - SchedThread.SchedWaitEvent.Set(); + SchedThread.WaitSched.Set(); } else { @@ -380,21 +369,16 @@ namespace Ryujinx.Core.OsHle.Handles throw new InvalidOperationException(); } - return IsActive(SchedThread); - } - - private bool IsActive(SchedulerThread SchedThread) - { - return SchedThread.Active && SchedThread.SyncTimeout == 0; + return SchedThread.IsActive; } private void PrintDbgThreadInfo(KThread Thread, string Message) { Log.PrintDebug(LogClass.KernelScheduler, "(" + - "ThreadId: " + Thread.ThreadId + ", " + - "ProcessorId: " + Thread.ProcessorId + ", " + - "ActualPriority: " + Thread.ActualPriority + ", " + - "WantedPriority: " + Thread.WantedPriority + ") " + Message); + "ThreadId = " + Thread.ThreadId + ", " + + "ProcessorId = " + Thread.ProcessorId + ", " + + "ActualPriority = " + Thread.ActualPriority + ", " + + "WantedPriority = " + Thread.WantedPriority + ") " + Message); } public void Dispose() diff --git a/Ryujinx.Core/OsHle/Ipc/IpcMessage.cs b/Ryujinx.Core/OsHle/Ipc/IpcMessage.cs index 4c4fa56ed..d81f44bd9 100644 --- a/Ryujinx.Core/OsHle/Ipc/IpcMessage.cs +++ b/Ryujinx.Core/OsHle/Ipc/IpcMessage.cs @@ -198,5 +198,35 @@ namespace Ryujinx.Core.OsHle.Ipc return -1; } + + public long GetBufferType0x21Position() + { + if (PtrBuff.Count > 0 && PtrBuff[0].Position != 0) + { + return PtrBuff[0].Position; + } + + if (SendBuff.Count > 0) + { + return SendBuff[0].Position; + } + + return 0; + } + + public long GetBufferType0x22Position() + { + if (RecvListBuff.Count > 0 && RecvListBuff[0].Position != 0) + { + return RecvListBuff[0].Position; + } + + if (ReceiveBuff.Count > 0) + { + return ReceiveBuff[0].Position; + } + + return 0; + } } } diff --git a/Ryujinx.Core/OsHle/Kernel/SvcHandler.cs b/Ryujinx.Core/OsHle/Kernel/SvcHandler.cs index da7dce894..e855b77df 100644 --- a/Ryujinx.Core/OsHle/Kernel/SvcHandler.cs +++ b/Ryujinx.Core/OsHle/Kernel/SvcHandler.cs @@ -86,6 +86,8 @@ namespace Ryujinx.Core.OsHle.Kernel if (SvcFuncs.TryGetValue(e.Id, out SvcFunc Func)) { + Ns.Log.PrintDebug(LogClass.KernelSvc, $"{Func.Method.Name} called."); + Func(ThreadState); Ns.Log.PrintDebug(LogClass.KernelSvc, $"{Func.Method.Name} ended."); diff --git a/Ryujinx.Core/OsHle/Kernel/SvcThreadSync.cs b/Ryujinx.Core/OsHle/Kernel/SvcThreadSync.cs index d5593e022..ec109b2d5 100644 --- a/Ryujinx.Core/OsHle/Kernel/SvcThreadSync.cs +++ b/Ryujinx.Core/OsHle/Kernel/SvcThreadSync.cs @@ -18,6 +18,11 @@ namespace Ryujinx.Core.OsHle.Kernel long MutexAddress = (long)ThreadState.X1; int WaitThreadHandle = (int)ThreadState.X2; + Ns.Log.PrintDebug(LogClass.KernelSvc, + "OwnerThreadHandle = " + OwnerThreadHandle.ToString("x8") + ", " + + "MutexAddress = " + MutexAddress .ToString("x16") + ", " + + "WaitThreadHandle = " + WaitThreadHandle .ToString("x8")); + if (IsPointingInsideKernel(MutexAddress)) { Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!"); @@ -38,6 +43,8 @@ namespace Ryujinx.Core.OsHle.Kernel KThread OwnerThread = Process.HandleTable.GetData(OwnerThreadHandle); + Ns.Log.PrintDebug(LogClass.KernelSvc, "lock tid: " + OwnerThread.ThreadId.ToString()); + if (OwnerThread == null) { Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid owner thread handle 0x{OwnerThreadHandle:x8}!"); @@ -69,6 +76,8 @@ namespace Ryujinx.Core.OsHle.Kernel { long MutexAddress = (long)ThreadState.X0; + Ns.Log.PrintDebug(LogClass.KernelSvc, "MutexAddress = " + MutexAddress.ToString("x16")); + if (IsPointingInsideKernel(MutexAddress)) { Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!"); @@ -102,6 +111,12 @@ namespace Ryujinx.Core.OsHle.Kernel int ThreadHandle = (int)ThreadState.X2; ulong Timeout = ThreadState.X3; + Ns.Log.PrintDebug(LogClass.KernelSvc, + "OwnerThreadHandle = " + MutexAddress .ToString("x16") + ", " + + "MutexAddress = " + CondVarAddress.ToString("x16") + ", " + + "WaitThreadHandle = " + ThreadHandle .ToString("x8") + ", " + + "Timeout = " + Timeout .ToString("x16")); + if (IsPointingInsideKernel(MutexAddress)) { Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!"); @@ -166,6 +181,8 @@ namespace Ryujinx.Core.OsHle.Kernel { int MutexValue = Process.Memory.ReadInt32(MutexAddress); + Ns.Log.PrintDebug(LogClass.KernelSvc, "MutexValue = " + MutexValue.ToString("x8")); + if (MutexValue != (OwnerThreadHandle | MutexHasListenersMask)) { return; @@ -176,7 +193,7 @@ namespace Ryujinx.Core.OsHle.Kernel InsertWaitingMutexThread(OwnerThreadHandle, WaitThread); - Process.Scheduler.EnterWait(WaitThread); + Process.Scheduler.EnterWait(CurrThread); } private bool MutexUnlock(KThread CurrThread, long MutexAddress) @@ -199,8 +216,12 @@ namespace Ryujinx.Core.OsHle.Kernel OwnerThread = OwnerThread.NextMutexThread; } + UpdateMutexOwner(CurrThread, OwnerThread, MutexAddress); + CurrThread.NextMutexThread = null; + CurrThread.UpdatePriority(); + if (OwnerThread != null) { int HasListeners = OwnerThread.NextMutexThread != null ? MutexHasListenersMask : 0; @@ -284,7 +305,9 @@ namespace Ryujinx.Core.OsHle.Kernel } else { - return Process.Scheduler.EnterWait(WaitThread); + Process.Scheduler.EnterWait(WaitThread); + + return true; } } @@ -314,8 +337,6 @@ namespace Ryujinx.Core.OsHle.Kernel int MutexValue = Process.Memory.ReadInt32(CurrThread.MutexAddress); - MutexValue &= ~MutexHasListenersMask; - if (MutexValue == 0) { //Give the lock to this thread. @@ -325,15 +346,17 @@ namespace Ryujinx.Core.OsHle.Kernel CurrThread.MutexAddress = 0; CurrThread.CondVarAddress = 0; - CurrThread.MutexOwner = null; + CurrThread.MutexOwner?.UpdatePriority(); - CurrThread.UpdatePriority(); + CurrThread.MutexOwner = null; Process.Scheduler.WakeUp(CurrThread); } else { //Wait until the lock is released. + MutexValue &= ~MutexHasListenersMask; + InsertWaitingMutexThread(MutexValue, CurrThread); MutexValue |= MutexHasListenersMask; @@ -399,6 +422,23 @@ namespace Ryujinx.Core.OsHle.Kernel OwnerThread.UpdatePriority(); } + private void UpdateMutexOwner(KThread CurrThread, KThread NewOwner, long MutexAddress) + { + //Go through all threads waiting for the mutex, + //and update the MutexOwner field to point to the new owner. + CurrThread = CurrThread.NextMutexThread; + + while (CurrThread != null) + { + if (CurrThread.MutexAddress == MutexAddress) + { + CurrThread.MutexOwner = NewOwner; + } + + CurrThread = CurrThread.NextMutexThread; + } + } + private void AcquireMutexValue(long MutexAddress) { while (!Process.Memory.AcquireAddress(MutexAddress)) diff --git a/Ryujinx.Core/OsHle/Services/Nv/INvDrvServices.cs b/Ryujinx.Core/OsHle/Services/Nv/INvDrvServices.cs index 10d638944..f41a98d0a 100644 --- a/Ryujinx.Core/OsHle/Services/Nv/INvDrvServices.cs +++ b/Ryujinx.Core/OsHle/Services/Nv/INvDrvServices.cs @@ -44,6 +44,7 @@ namespace Ryujinx.Core.OsHle.Services.Nv { { ("/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 }, @@ -201,6 +202,19 @@ namespace Ryujinx.Core.OsHle.Services.Nv 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(); @@ -319,6 +333,19 @@ namespace Ryujinx.Core.OsHle.Services.Nv int Padding = Reader.ReadInt32(); int Offset = Reader.ReadInt32(); int Pages = Reader.ReadInt32(); + + NvMap Map = NvMaps.GetData(Context.Process, Handle); + + if (Map == null) + { + Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"invalid NvMap Handle {Handle}!"); + + return -1; //TODO: Corrent error code. + } + + Context.Ns.Gpu.MapMemory(Map.CpuAddress, + (long)(uint)Offset << 16, + (long)(uint)Pages << 16); } //TODO diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvChNvMap.cs b/Ryujinx.Core/OsHle/Services/Nv/NvChNvMap.cs new file mode 100644 index 000000000..9ea3ae6e5 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvChNvMap.cs @@ -0,0 +1,31 @@ +using System.Collections.Concurrent; + +namespace Ryujinx.Core.OsHle.Services.Nv +{ + class NvChNvMap + { + private static ConcurrentDictionary NvMaps; + + public void Create(ServiceCtx Context) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + int Size = Context.Memory.ReadInt32(InputPosition); + + int Handle = AddNvMap(Context, new NvMap(Size)); + + Context.Memory.WriteInt32(OutputPosition, Handle); + } + + private int AddNvMap(ServiceCtx Context, NvMap Map) + { + return NvMaps[Context.Process].Add(Map); + } + + public NvMap GetNvMap(ServiceCtx Context, int Handle) + { + return NvMaps[Context.Process].GetData(Handle); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvMap.cs b/Ryujinx.Core/OsHle/Services/Nv/NvMap.cs index f3dd1f471..570cef682 100644 --- a/Ryujinx.Core/OsHle/Services/Nv/NvMap.cs +++ b/Ryujinx.Core/OsHle/Services/Nv/NvMap.cs @@ -9,5 +9,12 @@ namespace Ryujinx.Core.OsHle.Services.Nv public int Kind; public long CpuAddress; public long GpuAddress; + + public NvMap() { } + + public NvMap(int Size) + { + this.Size = Size; + } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/IGalRenderer.cs b/Ryujinx.Graphics/Gal/IGalRenderer.cs index af88412a2..e4aef2060 100644 --- a/Ryujinx.Graphics/Gal/IGalRenderer.cs +++ b/Ryujinx.Graphics/Gal/IGalRenderer.cs @@ -44,6 +44,8 @@ namespace Ryujinx.Graphics.Gal void SetViewport(int X, int Y, int Width, int Height); + void GetFrameBufferData(long Tag, Action Callback); + //Rasterizer void ClearBuffers(int RtIndex, GalClearBufferFlags Flags); @@ -51,7 +53,7 @@ namespace Ryujinx.Graphics.Gal void SetIndexArray(byte[] Buffer, GalIndexFormat Format); - void DrawArrays(int VbIndex, GalPrimitiveType PrimType); + void DrawArrays(int VbIndex, int First, int PrimCount, GalPrimitiveType PrimType); void DrawElements(int VbIndex, int First, GalPrimitiveType PrimType); diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs index 05a7288a8..8f265f544 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs @@ -270,6 +270,31 @@ namespace Ryujinx.Graphics.Gal.OpenGL } } + public void GetBufferData(long Tag, Action Callback) + { + if (Fbs.TryGetValue(Tag, out FrameBuffer Fb)) + { + GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, Fb.Handle); + + byte[] Data = new byte[Fb.Width * Fb.Height * 4]; + + (PixelFormat Format, PixelType Type) = OGLEnumConverter.GetTextureFormat(GalTextureFormat.A8B8G8R8); + + GL.ReadPixels( + 0, + 0, + Fb.Width, + Fb.Height, + Format, + Type, + Data); + + Callback(Data); + + GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, CurrFbHandle); + } + } + private void SetViewport(Rect Viewport) { GL.Viewport( diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs index 9e0d45233..b1504563f 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs @@ -48,8 +48,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL { public int VaoHandle; public int VboHandle; - - public int PrimCount; } private struct IbInfo @@ -102,8 +100,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL { EnsureVbInitialized(VbIndex); - VertexBuffers[VbIndex].PrimCount = Buffer.Length / Stride; - VbInfo Vb = VertexBuffers[VbIndex]; IntPtr Length = new IntPtr(Buffer.Length); @@ -171,29 +167,24 @@ namespace Ryujinx.Graphics.Gal.OpenGL GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0); } - public void DrawArrays(int VbIndex, GalPrimitiveType PrimType) + public void DrawArrays(int VbIndex, int First, int PrimCount, GalPrimitiveType PrimType) { - VbInfo Vb = VertexBuffers[VbIndex]; - - if (Vb.PrimCount == 0) + if (PrimCount == 0) { return; } + VbInfo Vb = VertexBuffers[VbIndex]; + GL.BindVertexArray(Vb.VaoHandle); - GL.DrawArrays(OGLEnumConverter.GetPrimitiveType(PrimType), 0, Vb.PrimCount); + GL.DrawArrays(OGLEnumConverter.GetPrimitiveType(PrimType), First, PrimCount); } public void DrawElements(int VbIndex, int First, GalPrimitiveType PrimType) { VbInfo Vb = VertexBuffers[VbIndex]; - if (Vb.PrimCount == 0) - { - return; - } - PrimitiveType Mode = OGLEnumConverter.GetPrimitiveType(PrimType); GL.BindVertexArray(Vb.VaoHandle); diff --git a/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs b/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs index cf2da91c5..f94105737 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs @@ -146,6 +146,11 @@ namespace Ryujinx.Graphics.Gal.OpenGL ActionsQueue.Enqueue(() => FrameBuffer.SetViewport(X, Y, Width, Height)); } + public void GetFrameBufferData(long Tag, Action Callback) + { + ActionsQueue.Enqueue(() => FrameBuffer.GetBufferData(Tag, Callback)); + } + public void ClearBuffers(int RtIndex, GalClearBufferFlags Flags) { ActionsQueue.Enqueue(() => Rasterizer.ClearBuffers(RtIndex, Flags)); @@ -173,14 +178,14 @@ namespace Ryujinx.Graphics.Gal.OpenGL ActionsQueue.Enqueue(() => Rasterizer.SetIndexArray(Buffer, Format)); } - public void DrawArrays(int VbIndex, GalPrimitiveType PrimType) + public void DrawArrays(int VbIndex, int First, int PrimCount, GalPrimitiveType PrimType) { if ((uint)VbIndex > 31) { throw new ArgumentOutOfRangeException(nameof(VbIndex)); } - ActionsQueue.Enqueue(() => Rasterizer.DrawArrays(VbIndex, PrimType)); + ActionsQueue.Enqueue(() => Rasterizer.DrawArrays(VbIndex, First, PrimCount, PrimType)); } public void DrawElements(int VbIndex, int First, GalPrimitiveType PrimType) diff --git a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs index d7173bcd6..c22f59264 100644 --- a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs +++ b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs @@ -31,51 +31,53 @@ namespace Ryujinx.Graphics.Gal.Shader { InstsExpr = new Dictionary() { - { ShaderIrInst.And, GetAndExpr }, - { ShaderIrInst.Asr, GetAsrExpr }, - { ShaderIrInst.Band, GetBandExpr }, - { ShaderIrInst.Bnot, GetBnotExpr }, - { ShaderIrInst.Ceil, GetCeilExpr }, - { ShaderIrInst.Ceq, GetCeqExpr }, - { ShaderIrInst.Cge, GetCgeExpr }, - { ShaderIrInst.Cgt, GetCgtExpr }, - { ShaderIrInst.Clamp, GetClampExpr }, - { ShaderIrInst.Cle, GetCleExpr }, - { ShaderIrInst.Clt, GetCltExpr }, - { ShaderIrInst.Cne, GetCneExpr }, - { ShaderIrInst.Exit, GetExitExpr }, - { ShaderIrInst.Fabs, GetFabsExpr }, - { ShaderIrInst.Fadd, GetFaddExpr }, - { ShaderIrInst.Fceq, GetCeqExpr }, - { ShaderIrInst.Fcge, GetCgeExpr }, - { ShaderIrInst.Fcgt, GetCgtExpr }, - { ShaderIrInst.Fcle, GetCleExpr }, - { ShaderIrInst.Fclt, GetCltExpr }, - { ShaderIrInst.Fcne, GetCneExpr }, - { ShaderIrInst.Fcos, GetFcosExpr }, - { ShaderIrInst.Fex2, GetFex2Expr }, - { ShaderIrInst.Ffma, GetFfmaExpr }, - { ShaderIrInst.Flg2, GetFlg2Expr }, - { ShaderIrInst.Floor, GetFloorExpr }, - { ShaderIrInst.Fmul, GetFmulExpr }, - { ShaderIrInst.Fneg, GetFnegExpr }, - { ShaderIrInst.Frcp, GetFrcpExpr }, - { ShaderIrInst.Frsq, GetFrsqExpr }, - { ShaderIrInst.Fsin, GetFsinExpr }, - { ShaderIrInst.Ftos, GetFtosExpr }, - { ShaderIrInst.Ftou, GetFtouExpr }, - { ShaderIrInst.Ipa, GetIpaExpr }, - { ShaderIrInst.Kil, GetKilExpr }, - { ShaderIrInst.Lsr, GetLsrExpr }, - { ShaderIrInst.Not, GetNotExpr }, - { ShaderIrInst.Or, GetOrExpr }, - { ShaderIrInst.Stof, GetStofExpr }, - { ShaderIrInst.Texq, GetTexqExpr }, - { ShaderIrInst.Texs, GetTexsExpr }, - { ShaderIrInst.Trunc, GetTruncExpr }, - { ShaderIrInst.Txlf, GetTxlfExpr }, - { ShaderIrInst.Utof, GetUtofExpr }, - { ShaderIrInst.Xor, GetXorExpr } + { ShaderIrInst.And, GetAndExpr }, + { ShaderIrInst.Asr, GetAsrExpr }, + { ShaderIrInst.Band, GetBandExpr }, + { ShaderIrInst.Bnot, GetBnotExpr }, + { ShaderIrInst.Ceil, GetCeilExpr }, + { ShaderIrInst.Ceq, GetCeqExpr }, + { ShaderIrInst.Cge, GetCgeExpr }, + { ShaderIrInst.Cgt, GetCgtExpr }, + { ShaderIrInst.Clamps, GetClampsExpr }, + { ShaderIrInst.Clampu, GetClampuExpr }, + { ShaderIrInst.Cle, GetCleExpr }, + { ShaderIrInst.Clt, GetCltExpr }, + { ShaderIrInst.Cne, GetCneExpr }, + { ShaderIrInst.Exit, GetExitExpr }, + { ShaderIrInst.Fabs, GetFabsExpr }, + { ShaderIrInst.Fadd, GetFaddExpr }, + { ShaderIrInst.Fceq, GetCeqExpr }, + { ShaderIrInst.Fcge, GetCgeExpr }, + { ShaderIrInst.Fcgt, GetCgtExpr }, + { ShaderIrInst.Fclamp, GetFclampExpr }, + { ShaderIrInst.Fcle, GetCleExpr }, + { ShaderIrInst.Fclt, GetCltExpr }, + { ShaderIrInst.Fcne, GetCneExpr }, + { ShaderIrInst.Fcos, GetFcosExpr }, + { ShaderIrInst.Fex2, GetFex2Expr }, + { ShaderIrInst.Ffma, GetFfmaExpr }, + { ShaderIrInst.Flg2, GetFlg2Expr }, + { ShaderIrInst.Floor, GetFloorExpr }, + { ShaderIrInst.Fmul, GetFmulExpr }, + { ShaderIrInst.Fneg, GetFnegExpr }, + { ShaderIrInst.Frcp, GetFrcpExpr }, + { ShaderIrInst.Frsq, GetFrsqExpr }, + { ShaderIrInst.Fsin, GetFsinExpr }, + { ShaderIrInst.Ftos, GetFtosExpr }, + { ShaderIrInst.Ftou, GetFtouExpr }, + { ShaderIrInst.Ipa, GetIpaExpr }, + { ShaderIrInst.Kil, GetKilExpr }, + { ShaderIrInst.Lsr, GetLsrExpr }, + { ShaderIrInst.Not, GetNotExpr }, + { ShaderIrInst.Or, GetOrExpr }, + { ShaderIrInst.Stof, GetStofExpr }, + { ShaderIrInst.Texq, GetTexqExpr }, + { ShaderIrInst.Texs, GetTexsExpr }, + { ShaderIrInst.Trunc, GetTruncExpr }, + { ShaderIrInst.Txlf, GetTxlfExpr }, + { ShaderIrInst.Utof, GetUtofExpr }, + { ShaderIrInst.Xor, GetXorExpr } }; } @@ -478,7 +480,19 @@ namespace Ryujinx.Graphics.Gal.Shader private string GetCeilExpr(ShaderIrOp Op) => GetUnaryCall(Op, "ceil"); - private string GetClampExpr(ShaderIrOp Op) => GetTernaryCall(Op, "clamp"); + private string GetClampsExpr(ShaderIrOp Op) + { + return "clamp(" + GetOperExpr(Op, Op.OperandA) + ", " + + GetOperExpr(Op, Op.OperandB) + ", " + + GetOperExpr(Op, Op.OperandC) + ")"; + } + + private string GetClampuExpr(ShaderIrOp Op) + { + return "int(clamp(uint(" + GetOperExpr(Op, Op.OperandA) + "), " + + "uint(" + GetOperExpr(Op, Op.OperandB) + "), " + + "uint(" + GetOperExpr(Op, Op.OperandC) + ")))"; + } private string GetCltExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "<"); private string GetCeqExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "=="); @@ -499,6 +513,8 @@ namespace Ryujinx.Graphics.Gal.Shader private string GetFfmaExpr(ShaderIrOp Op) => GetTernaryExpr(Op, "*", "+"); + private string GetFclampExpr(ShaderIrOp Op) => GetTernaryCall(Op, "clamp"); + private string GetFlg2Expr(ShaderIrOp Op) => GetUnaryCall(Op, "log2"); private string GetFloorExpr(ShaderIrOp Op) => GetUnaryCall(Op, "floor"); diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs index 830aeb8cb..42609bcef 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs @@ -66,6 +66,21 @@ namespace Ryujinx.Graphics.Gal.Shader EmitAluBinaryF(Block, OpCode, ShaderOper.RR, ShaderIrInst.Fmul); } + public static void Fset_C(ShaderIrBlock Block, long OpCode) + { + EmitFset(Block, OpCode, ShaderOper.CR); + } + + public static void Fset_I(ShaderIrBlock Block, long OpCode) + { + EmitFset(Block, OpCode, ShaderOper.Immf); + } + + public static void Fset_R(ShaderIrBlock Block, long OpCode) + { + EmitFset(Block, OpCode, ShaderOper.RR); + } + public static void Fsetp_C(ShaderIrBlock Block, long OpCode) { EmitFsetp(Block, OpCode, ShaderOper.CR); @@ -279,6 +294,78 @@ namespace Ryujinx.Graphics.Gal.Shader Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode)); } + private static void EmitFset(ShaderIrBlock Block, long OpCode, ShaderOper Oper) + { + EmitSet(Block, OpCode, true, Oper); + } + + private static void EmitIset(ShaderIrBlock Block, long OpCode, ShaderOper Oper) + { + EmitSet(Block, OpCode, false, Oper); + } + + private static void EmitSet(ShaderIrBlock Block, long OpCode, bool IsFloat, ShaderOper Oper) + { + bool Na = ((OpCode >> 43) & 1) != 0; + bool Ab = ((OpCode >> 44) & 1) != 0; + bool Nb = ((OpCode >> 53) & 1) != 0; + bool Aa = ((OpCode >> 54) & 1) != 0; + + ShaderIrNode OperA = GetOperGpr8(OpCode), OperB; + + switch (Oper) + { + case ShaderOper.CR: OperB = GetOperCbuf34 (OpCode); break; + case ShaderOper.Imm: OperB = GetOperImm19_20 (OpCode); break; + case ShaderOper.Immf: OperB = GetOperImmf19_20(OpCode); break; + case ShaderOper.RR: OperB = GetOperGpr20 (OpCode); break; + + default: throw new ArgumentException(nameof(Oper)); + } + + ShaderIrInst CmpInst; + + if (IsFloat) + { + OperA = GetAluAbsNeg(OperA, Aa, Na); + OperB = GetAluAbsNeg(OperB, Ab, Nb); + + CmpInst = GetCmpF(OpCode); + } + else + { + CmpInst = GetCmp(OpCode); + } + + ShaderIrOp Op = new ShaderIrOp(CmpInst, OperA, OperB); + + ShaderIrInst LopInst = GetBLop(OpCode); + + ShaderIrOperPred PNode = GetOperPred39(OpCode); + + ShaderIrOperImmf Imm0 = new ShaderIrOperImmf(0); + ShaderIrOperImmf Imm1 = new ShaderIrOperImmf(1); + + ShaderIrNode Asg0 = new ShaderIrAsg(GetOperGpr0(OpCode), Imm0); + ShaderIrNode Asg1 = new ShaderIrAsg(GetOperGpr0(OpCode), Imm1); + + if (LopInst != ShaderIrInst.Band || !PNode.IsConst) + { + ShaderIrOp Op2 = new ShaderIrOp(LopInst, Op, PNode); + + Asg0 = new ShaderIrCond(Op2, Asg0, Not: true); + Asg1 = new ShaderIrCond(Op2, Asg1, Not: false); + } + else + { + Asg0 = new ShaderIrCond(Op, Asg0, Not: true); + Asg1 = new ShaderIrCond(Op, Asg1, Not: false); + } + + Block.AddNode(GetPredNode(Asg0, OpCode)); + Block.AddNode(GetPredNode(Asg1, OpCode)); + } + private static void EmitFsetp(ShaderIrBlock Block, long OpCode, ShaderOper Oper) { EmitSetp(Block, OpCode, true, Oper); diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMove.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMove.cs index 6d30cfed8..b9cf02f1f 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMove.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMove.cs @@ -70,6 +70,21 @@ namespace Ryujinx.Graphics.Gal.Shader EmitI2f(Block, OpCode, ShaderOper.RR); } + public static void I2i_C(ShaderIrBlock Block, long OpCode) + { + EmitI2i(Block, OpCode, ShaderOper.CR); + } + + public static void I2i_I(ShaderIrBlock Block, long OpCode) + { + EmitI2i(Block, OpCode, ShaderOper.Imm); + } + + public static void I2i_R(ShaderIrBlock Block, long OpCode) + { + EmitI2i(Block, OpCode, ShaderOper.RR); + } + public static void Mov_C(ShaderIrBlock Block, long OpCode) { ShaderIrOperCbuf Cbuf = GetOperCbuf34(OpCode); @@ -183,7 +198,7 @@ namespace Ryujinx.Graphics.Gal.Shader ShaderIrOperImmf IMin = new ShaderIrOperImmf(CMin); ShaderIrOperImmf IMax = new ShaderIrOperImmf(CMax); - OperA = new ShaderIrOp(ShaderIrInst.Clamp, OperA, IMin, IMax); + OperA = new ShaderIrOp(ShaderIrInst.Fclamp, OperA, IMin, IMax); } ShaderIrInst Inst = Signed @@ -252,6 +267,81 @@ namespace Ryujinx.Graphics.Gal.Shader Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode)); } + private static void EmitI2i(ShaderIrBlock Block, long OpCode, ShaderOper Oper) + { + IntType Type = GetIntType(OpCode); + + if (Type == IntType.U64 || + Type == IntType.S64) + { + //TODO: 64-bits support. + //Note: GLSL doesn't support 64-bits integers. + throw new NotImplementedException(); + } + + int Sel = (int)(OpCode >> 41) & 3; + + bool NegA = ((OpCode >> 45) & 1) != 0; + bool AbsA = ((OpCode >> 49) & 1) != 0; + bool SatA = ((OpCode >> 50) & 1) != 0; + + ShaderIrNode OperA; + + switch (Oper) + { + case ShaderOper.CR: OperA = GetOperCbuf34 (OpCode); break; + case ShaderOper.Immf: OperA = GetOperImmf19_20(OpCode); break; + case ShaderOper.RR: OperA = GetOperGpr20 (OpCode); break; + + default: throw new ArgumentException(nameof(Oper)); + } + + OperA = GetAluAbsNeg(OperA, AbsA, NegA); + + bool Signed = Type >= IntType.S8; + + int Shift = Sel * 8; + + int Size = 8 << ((int)Type & 3); + + if (Shift != 0) + { + OperA = new ShaderIrOp(ShaderIrInst.Asr, OperA, new ShaderIrOperImm(Shift)); + } + + if (Size < 32) + { + uint Mask = uint.MaxValue >> (32 - Size); + + if (SatA) + { + uint CMin = 0; + uint CMax = Mask; + + if (Signed) + { + uint HalfMask = Mask >> 1; + + CMin -= HalfMask + 1; + CMax = HalfMask; + } + + ShaderIrOperImm IMin = new ShaderIrOperImm((int)CMin); + ShaderIrOperImm IMax = new ShaderIrOperImm((int)CMax); + + OperA = new ShaderIrOp(Signed + ? ShaderIrInst.Clamps + : ShaderIrInst.Clampu, OperA, IMin, IMax); + } + else + { + OperA = new ShaderIrOp(ShaderIrInst.And, OperA, new ShaderIrOperImm((int)Mask)); + } + } + + Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), OperA), OpCode)); + } + private static IntType GetIntType(long OpCode) { bool Signed = ((OpCode >> 13) & 1) != 0; diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs index 1b72f6476..ce2b98fe9 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs @@ -13,7 +13,7 @@ namespace Ryujinx.Graphics.Gal.Shader F_Start, Ceil, - Clamp, + Fabs, Fadd, Fceq, @@ -22,6 +22,7 @@ namespace Ryujinx.Graphics.Gal.Shader Fcgeu, Fcgt, Fcgtu, + Fclamp, Fcle, Fcleu, Fclt, @@ -53,6 +54,8 @@ namespace Ryujinx.Graphics.Gal.Shader Ceq, Cge, Cgt, + Clamps, + Clampu, Cle, Clt, Cne, diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs b/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs index 762544cb9..65e249286 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs @@ -6,11 +6,24 @@ namespace Ryujinx.Graphics.Gal.Shader { private const int EncodingBits = 14; - private static ShaderDecodeFunc[] OpCodes; + private class ShaderDecodeEntry + { + public ShaderDecodeFunc Func; + + public int XBits; + + public ShaderDecodeEntry(ShaderDecodeFunc Func, int XBits) + { + this.Func = Func; + this.XBits = XBits; + } + } + + private static ShaderDecodeEntry[] OpCodes; static ShaderOpCodeTable() { - OpCodes = new ShaderDecodeFunc[1 << EncodingBits]; + OpCodes = new ShaderDecodeEntry[1 << EncodingBits]; #region Instructions Set("111000110000xx", ShaderDecode.Exit); @@ -31,12 +44,18 @@ namespace Ryujinx.Graphics.Gal.Shader Set("0100110001101x", ShaderDecode.Fmul_C); Set("0011100x01101x", ShaderDecode.Fmul_I); Set("0101110001101x", ShaderDecode.Fmul_R); + Set("0100100xxxxxxx", ShaderDecode.Fset_C); + Set("0011000xxxxxxx", ShaderDecode.Fset_I); + Set("01011000xxxxxx", ShaderDecode.Fset_R); Set("010010111011xx", ShaderDecode.Fsetp_C); Set("0011011x1011xx", ShaderDecode.Fsetp_I); Set("010110111011xx", ShaderDecode.Fsetp_R); Set("0100110010111x", ShaderDecode.I2f_C); Set("0011100x10111x", ShaderDecode.I2f_I); Set("0101110010111x", ShaderDecode.I2f_R); + Set("0100110011100x", ShaderDecode.I2i_C); + Set("0011100x11100x", ShaderDecode.I2i_I); + Set("0101110011100x", ShaderDecode.I2i_R); Set("11100000xxxxxx", ShaderDecode.Ipa); Set("010010110110xx", ShaderDecode.Isetp_C); Set("0011011x0110xx", ShaderDecode.Isetp_I); @@ -91,6 +110,8 @@ namespace Ryujinx.Graphics.Gal.Shader XMask = ~XMask; + ShaderDecodeEntry Entry = new ShaderDecodeEntry(Func, XBits); + for (int Index = 0; Index < (1 << XBits); Index++) { Value &= XMask; @@ -100,13 +121,16 @@ namespace Ryujinx.Graphics.Gal.Shader Value |= ((Index >> X) & 1) << XPos[X]; } - OpCodes[Value] = Func; + if (OpCodes[Value] == null || OpCodes[Value].XBits > XBits) + { + OpCodes[Value] = Entry; + } } } public static ShaderDecodeFunc GetDecoder(long OpCode) { - return OpCodes[(ulong)OpCode >> (64 - EncodingBits)]; + return OpCodes[(ulong)OpCode >> (64 - EncodingBits)]?.Func; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gpu/NsGpu.cs b/Ryujinx.Graphics/Gpu/NsGpu.cs index 9a2e90128..e10889821 100644 --- a/Ryujinx.Graphics/Gpu/NsGpu.cs +++ b/Ryujinx.Graphics/Gpu/NsGpu.cs @@ -7,10 +7,11 @@ namespace Ryujinx.Graphics.Gpu { public IGalRenderer Renderer { get; private set; } - internal NsGpuMemoryMgr MemoryMgr { get; private set; } + public NsGpuMemoryMgr MemoryMgr { get; private set; } public NvGpuFifo Fifo { get; private set; } + public NvGpuEngine2d Engine2d { get; private set; } public NvGpuEngine3d Engine3d { get; private set; } private Thread FifoProcessing; @@ -25,6 +26,7 @@ namespace Ryujinx.Graphics.Gpu Fifo = new NvGpuFifo(this); + Engine2d = new NvGpuEngine2d(this); Engine3d = new NvGpuEngine3d(this); KeepRunning = true; diff --git a/Ryujinx.Graphics/Gpu/NsGpuMemoryMgr.cs b/Ryujinx.Graphics/Gpu/NsGpuMemoryMgr.cs index 7f50640d5..eff517831 100644 --- a/Ryujinx.Graphics/Gpu/NsGpuMemoryMgr.cs +++ b/Ryujinx.Graphics/Gpu/NsGpuMemoryMgr.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Graphics.Gpu { - class NsGpuMemoryMgr + public class NsGpuMemoryMgr { private const long AddrSize = 1L << 40; @@ -50,12 +50,20 @@ namespace Ryujinx.Graphics.Gpu 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) diff --git a/Ryujinx.Graphics/Gpu/NvGpuEngine2d.cs b/Ryujinx.Graphics/Gpu/NvGpuEngine2d.cs new file mode 100644 index 000000000..c2bee167d --- /dev/null +++ b/Ryujinx.Graphics/Gpu/NvGpuEngine2d.cs @@ -0,0 +1,158 @@ +using ChocolArm64.Memory; +using Ryujinx.Graphics.Gal; +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Gpu +{ + public class NvGpuEngine2d : INvGpuEngine + { + private enum CopyOperation + { + SrcCopyAnd, + RopAnd, + Blend, + SrcCopy, + Rop, + SrcCopyPremult, + BlendPremult + } + + public int[] Registers { get; private set; } + + private NsGpu Gpu; + + private Dictionary Methods; + + public NvGpuEngine2d(NsGpu Gpu) + { + this.Gpu = Gpu; + + Registers = new int[0xe00]; + + Methods = new Dictionary(); + + void AddMethod(int Meth, int Count, int Stride, NvGpuMethod Method) + { + while (Count-- > 0) + { + Methods.Add(Meth, Method); + + Meth += Stride; + } + } + + AddMethod(0xb5, 1, 1, TextureCopy); + } + + public void CallMethod(AMemory Memory, NsGpuPBEntry PBEntry) + { + if (Methods.TryGetValue(PBEntry.Method, out NvGpuMethod Method)) + { + Method(Memory, PBEntry); + } + else + { + WriteRegister(PBEntry); + } + } + + private void TextureCopy(AMemory Memory, NsGpuPBEntry PBEntry) + { + CopyOperation Operation = (CopyOperation)ReadRegister(NvGpuEngine2dReg.CopyOperation); + + bool SrcLinear = ReadRegister(NvGpuEngine2dReg.SrcLinear) != 0; + int SrcWidth = ReadRegister(NvGpuEngine2dReg.SrcWidth); + int SrcHeight = ReadRegister(NvGpuEngine2dReg.SrcHeight); + + bool DstLinear = ReadRegister(NvGpuEngine2dReg.DstLinear) != 0; + int DstWidth = ReadRegister(NvGpuEngine2dReg.DstWidth); + int DstHeight = ReadRegister(NvGpuEngine2dReg.DstHeight); + int DstPitch = ReadRegister(NvGpuEngine2dReg.DstPitch); + int DstBlkDim = ReadRegister(NvGpuEngine2dReg.DstBlockDimensions); + + TextureSwizzle DstSwizzle = DstLinear + ? TextureSwizzle.Pitch + : TextureSwizzle.BlockLinear; + + int DstBlockHeight = 1 << ((DstBlkDim >> 4) & 0xf); + + long Tag = MakeInt64From2xInt32(NvGpuEngine2dReg.SrcAddress); + + TryGetCpuAddr(NvGpuEngine2dReg.SrcAddress, out long SrcAddress); + TryGetCpuAddr(NvGpuEngine2dReg.DstAddress, out long DstAddress); + + bool IsFbTexture = Gpu.Engine3d.IsFrameBufferPosition(Tag); + + if (IsFbTexture && DstLinear) + { + DstSwizzle = TextureSwizzle.BlockLinear; + } + + Texture DstTexture = new Texture( + DstAddress, + DstWidth, + DstHeight, + DstBlockHeight, + DstBlockHeight, + DstSwizzle, + GalTextureFormat.A8B8G8R8); + + if (IsFbTexture) + { + Gpu.Renderer.GetFrameBufferData(Tag, (byte[] Buffer) => + { + CopyTexture(Memory, DstTexture, Buffer); + }); + } + else + { + long Size = SrcWidth * SrcHeight * 4; + + byte[] Buffer = AMemoryHelper.ReadBytes(Memory, SrcAddress, Size); + + CopyTexture(Memory, DstTexture, Buffer); + } + } + + private void CopyTexture(AMemory Memory, Texture Texture, byte[] Buffer) + { + TextureWriter.Write(Memory, Texture, Buffer); + } + + private bool TryGetCpuAddr(NvGpuEngine2dReg Reg, out long Position) + { + Position = MakeInt64From2xInt32(Reg); + + Position = Gpu.GetCpuAddr(Position); + + return Position != -1; + } + + private long MakeInt64From2xInt32(NvGpuEngine2dReg Reg) + { + return + (long)Registers[(int)Reg + 0] << 32 | + (uint)Registers[(int)Reg + 1]; + } + + private void WriteRegister(NsGpuPBEntry PBEntry) + { + int ArgsCount = PBEntry.Arguments.Count; + + if (ArgsCount > 0) + { + Registers[PBEntry.Method] = PBEntry.Arguments[ArgsCount - 1]; + } + } + + private int ReadRegister(NvGpuEngine2dReg Reg) + { + return Registers[(int)Reg]; + } + + private void WriteRegister(NvGpuEngine2dReg Reg, int Value) + { + Registers[(int)Reg] = Value; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gpu/NvGpuEngine2dReg.cs b/Ryujinx.Graphics/Gpu/NvGpuEngine2dReg.cs new file mode 100644 index 000000000..903baca87 --- /dev/null +++ b/Ryujinx.Graphics/Gpu/NvGpuEngine2dReg.cs @@ -0,0 +1,25 @@ +namespace Ryujinx.Graphics.Gpu +{ + enum NvGpuEngine2dReg + { + DstFormat = 0x80, + DstLinear = 0x81, + DstBlockDimensions = 0x82, + DstDepth = 0x83, + DstLayer = 0x84, + DstPitch = 0x85, + DstWidth = 0x86, + DstHeight = 0x87, + DstAddress = 0x88, + SrcFormat = 0x8c, + SrcLinear = 0x8d, + SrcBlockDimensions = 0x8e, + SrcDepth = 0x8f, + SrcLayer = 0x90, + SrcPitch = 0x91, + SrcWidth = 0x92, + SrcHeight = 0x93, + SrcAddress = 0x94, + CopyOperation = 0xab + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs b/Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs index 88ad76334..bf04db367 100644 --- a/Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs +++ b/Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs @@ -360,6 +360,9 @@ namespace Ryujinx.Graphics.Gpu for (int Index = 0; Index < 32; Index++) { + int VertexFirst = ReadRegister(NvGpuEngine3dReg.VertexArrayFirst); + int VertexCount = ReadRegister(NvGpuEngine3dReg.VertexArrayCount); + int Control = ReadRegister(NvGpuEngine3dReg.VertexArrayNControl + Index * 4); bool Enable = (Control & 0x1000) != 0; @@ -394,7 +397,7 @@ namespace Ryujinx.Graphics.Gpu } else { - Gpu.Renderer.DrawArrays(Index, PrimType); + Gpu.Renderer.DrawArrays(Index, VertexFirst, VertexCount, PrimType); } } } diff --git a/Ryujinx.Graphics/Gpu/NvGpuEngine3dReg.cs b/Ryujinx.Graphics/Gpu/NvGpuEngine3dReg.cs index 605ca9dab..0d995619d 100644 --- a/Ryujinx.Graphics/Gpu/NvGpuEngine3dReg.cs +++ b/Ryujinx.Graphics/Gpu/NvGpuEngine3dReg.cs @@ -12,6 +12,8 @@ namespace Ryujinx.Graphics.Gpu ViewportTranslateX = 0x283, ViewportTranslateY = 0x284, ViewportTranslateZ = 0x285, + VertexArrayFirst = 0x35d, + VertexArrayCount = 0x35e, VertexAttribNFormat = 0x458, IBlendEnable = 0x4b9, BlendSeparateAlpha = 0x4cf, diff --git a/Ryujinx.Graphics/Gpu/NvGpuFifo.cs b/Ryujinx.Graphics/Gpu/NvGpuFifo.cs index df765895c..68c2902a5 100644 --- a/Ryujinx.Graphics/Gpu/NvGpuFifo.cs +++ b/Ryujinx.Graphics/Gpu/NvGpuFifo.cs @@ -139,11 +139,17 @@ namespace Ryujinx.Graphics.Gpu { switch (SubChannels[PBEntry.SubChannel]) { + case NvGpuEngine._2d: Call2dMethod(Memory, PBEntry); break; case NvGpuEngine._3d: Call3dMethod(Memory, PBEntry); break; } } } + private void Call2dMethod(AMemory Memory, NsGpuPBEntry PBEntry) + { + Gpu.Engine2d.CallMethod(Memory, PBEntry); + } + private void Call3dMethod(AMemory Memory, NsGpuPBEntry PBEntry) { if (PBEntry.Method < 0xe00) diff --git a/Ryujinx.Graphics/Gpu/TextureHelper.cs b/Ryujinx.Graphics/Gpu/TextureHelper.cs new file mode 100644 index 000000000..d3c2ac14b --- /dev/null +++ b/Ryujinx.Graphics/Gpu/TextureHelper.cs @@ -0,0 +1,23 @@ +using System; + +namespace Ryujinx.Graphics.Gpu +{ + static class TextureHelper + { + public static ISwizzle GetSwizzle(Texture Texture, int Width, int Bpp) + { + switch (Texture.Swizzle) + { + case TextureSwizzle.Pitch: + case TextureSwizzle.PitchColorKey: + return new LinearSwizzle(Texture.Pitch, Bpp); + + case TextureSwizzle.BlockLinear: + case TextureSwizzle.BlockLinearColorKey: + return new BlockLinearSwizzle(Width, Bpp, Texture.BlockHeight); + } + + throw new NotImplementedException(Texture.Swizzle.ToString()); + } + } +} diff --git a/Ryujinx.Graphics/Gpu/TextureReader.cs b/Ryujinx.Graphics/Gpu/TextureReader.cs index 4c3b4fb17..17fd95c5c 100644 --- a/Ryujinx.Graphics/Gpu/TextureReader.cs +++ b/Ryujinx.Graphics/Gpu/TextureReader.cs @@ -30,7 +30,7 @@ namespace Ryujinx.Graphics.Gpu byte[] Output = new byte[Width * Height * 2]; - ISwizzle Swizzle = GetSwizzle(Texture, Width, 2); + ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, Width, 2); fixed (byte* BuffPtr = Output) { @@ -59,7 +59,7 @@ namespace Ryujinx.Graphics.Gpu byte[] Output = new byte[Width * Height * 4]; - ISwizzle Swizzle = GetSwizzle(Texture, Width, 4); + ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, Width, 4); fixed (byte* BuffPtr = Output) { @@ -88,7 +88,7 @@ namespace Ryujinx.Graphics.Gpu byte[] Output = new byte[Width * Height * 8]; - ISwizzle Swizzle = GetSwizzle(Texture, Width, 8); + ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, Width, 8); fixed (byte* BuffPtr = Output) { @@ -117,7 +117,7 @@ namespace Ryujinx.Graphics.Gpu byte[] Output = new byte[Width * Height * 16]; - ISwizzle Swizzle = GetSwizzle(Texture, Width, 16); + ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, Width, 16); fixed (byte* BuffPtr = Output) { @@ -140,21 +140,5 @@ namespace Ryujinx.Graphics.Gpu return Output; } - - private static ISwizzle GetSwizzle(Texture Texture, int Width, int Bpp) - { - switch (Texture.Swizzle) - { - case TextureSwizzle.Pitch: - case TextureSwizzle.PitchColorKey: - return new LinearSwizzle(Texture.Pitch, Bpp); - - case TextureSwizzle.BlockLinear: - case TextureSwizzle.BlockLinearColorKey: - return new BlockLinearSwizzle(Width, Bpp, Texture.BlockHeight); - } - - throw new NotImplementedException(Texture.Swizzle.ToString()); - } } } diff --git a/Ryujinx.Graphics/Gpu/TextureWriter.cs b/Ryujinx.Graphics/Gpu/TextureWriter.cs new file mode 100644 index 000000000..2f25de73f --- /dev/null +++ b/Ryujinx.Graphics/Gpu/TextureWriter.cs @@ -0,0 +1,45 @@ +using ChocolArm64.Memory; +using Ryujinx.Graphics.Gal; +using System; + +namespace Ryujinx.Graphics.Gpu +{ + public static class TextureWriter + { + public static void Write(AMemory Memory, Texture Texture, byte[] Data) + { + switch (Texture.Format) + { + case GalTextureFormat.A8B8G8R8: Write4Bpp(Memory, Texture, Data); break; + + default: + throw new NotImplementedException(Texture.Format.ToString()); + } + } + + private unsafe static void Write4Bpp(AMemory Memory, Texture Texture, byte[] Data) + { + int Width = Texture.Width; + int Height = Texture.Height; + + ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, Width, 4); + + fixed (byte* BuffPtr = Data) + { + long InOffs = 0; + + for (int Y = 0; Y < Height; Y++) + for (int X = 0; X < Width; X++) + { + long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y); + + int Pixel = *(int*)(BuffPtr + InOffs); + + Memory.WriteInt32Unchecked(Texture.Position + Offset, Pixel); + + InOffs += 4; + } + } + } + } +} diff --git a/Ryujinx/Config.cs b/Ryujinx/Config.cs index d5364edcb..86b74c966 100644 --- a/Ryujinx/Config.cs +++ b/Ryujinx/Config.cs @@ -22,14 +22,11 @@ namespace Ryujinx AOptimizations.DisableMemoryChecks = !Convert.ToBoolean(Parser.Value("Enable_Memory_Checks")); - Console.WriteLine(Parser.Value("Logging_Enable_Warn")); - - bool LoggingEnableDebug = Convert.ToBoolean(Parser.Value("Logging_Enable_Debug")); - bool LoggingEnableStub = Convert.ToBoolean(Parser.Value("Logging_Enable_Stub")); - bool LoggingEnableInfo = Convert.ToBoolean(Parser.Value("Logging_Enable_Info")); - bool LoggingEnableTrace = Convert.ToBoolean(Parser.Value("Logging_Enable_Trace")); - bool LoggingEnableWarn = Convert.ToBoolean(Parser.Value("Logging_Enable_Warn")); - bool LoggingEnableError = Convert.ToBoolean(Parser.Value("Logging_Enable_Error")); + Log.SetEnable(LogLevel.Debug, Convert.ToBoolean(Parser.Value("Logging_Enable_Debug"))); + Log.SetEnable(LogLevel.Stub, Convert.ToBoolean(Parser.Value("Logging_Enable_Stub"))); + Log.SetEnable(LogLevel.Info, Convert.ToBoolean(Parser.Value("Logging_Enable_Info"))); + Log.SetEnable(LogLevel.Warning, Convert.ToBoolean(Parser.Value("Logging_Enable_Warn"))); + Log.SetEnable(LogLevel.Error, Convert.ToBoolean(Parser.Value("Logging_Enable_Error"))); string[] FilteredLogClasses = Parser.Value("Logging_Filtered_Classes").Split(',', StringSplitOptions.RemoveEmptyEntries);