From 9a9349f0f4e3c6e80bacc782a8d4a4766f89ea74 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Fri, 10 Jun 2022 23:44:49 -0300 Subject: [PATCH 1/5] Fix instanced indexed inline draw index count (#3389) --- .../Engine/Threed/DrawManager.cs | 30 +++++++++---------- .../Engine/Threed/IbStreamer.cs | 5 ++++ 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs index 7e35fd2de..ab3713141 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs @@ -135,14 +135,16 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed { _instancedDrawPending = true; + int ibCount = _drawState.IbStreamer.InlineIndexCount; + _instancedIndexed = _drawState.DrawIndexed; - _instancedIndexedInline = _drawState.IbStreamer.HasInlineIndexData; + _instancedIndexedInline = ibCount != 0; _instancedFirstIndex = firstIndex; _instancedFirstVertex = (int)_state.State.FirstVertex; _instancedFirstInstance = (int)_state.State.FirstInstance; - _instancedIndexCount = indexCount; + _instancedIndexCount = ibCount != 0 ? ibCount : indexCount; var drawState = _state.State.VertexBufferDrawState; @@ -453,22 +455,18 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed { _instancedDrawPending = false; - if (_instancedIndexedInline) - { - int inlineIndexCount = _drawState.IbStreamer.GetAndResetInlineIndexCount(); - BufferRange br = new BufferRange(_drawState.IbStreamer.GetInlineIndexBuffer(), 0, inlineIndexCount * 4); + bool indexedInline = _instancedIndexedInline; - _channel.BufferManager.SetIndexBuffer(br, IndexType.UInt); - - _context.Renderer.Pipeline.DrawIndexed( - inlineIndexCount, - _instanceIndex + 1, - _instancedFirstIndex, - _instancedFirstVertex, - _instancedFirstInstance); - } - else if (_instancedIndexed) + if (_instancedIndexed || indexedInline) { + if (indexedInline) + { + int inlineIndexCount = _drawState.IbStreamer.GetAndResetInlineIndexCount(); + BufferRange br = new BufferRange(_drawState.IbStreamer.GetInlineIndexBuffer(), 0, inlineIndexCount * 4); + + _channel.BufferManager.SetIndexBuffer(br, IndexType.UInt); + } + _context.Renderer.Pipeline.DrawIndexed( _instancedIndexCount, _instanceIndex + 1, diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/IbStreamer.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/IbStreamer.cs index 96b2ed9c7..4862bca18 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Threed/IbStreamer.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/IbStreamer.cs @@ -20,6 +20,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// public bool HasInlineIndexData => _inlineIndexCount != 0; + /// + /// Total numbers of indices that have been pushed. + /// + public int InlineIndexCount => _inlineIndexCount; + /// /// Gets the handle for the host buffer currently holding the inline index buffer data. /// From 830cbf91bb8766e6347291bdff765a8a4e8c95b8 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sat, 11 Jun 2022 14:31:17 -0300 Subject: [PATCH 2/5] Ignore ClipControl on draw texture fallback (#3388) --- Ryujinx.Graphics.OpenGL/Pipeline.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Ryujinx.Graphics.OpenGL/Pipeline.cs b/Ryujinx.Graphics.OpenGL/Pipeline.cs index ced41d48f..637e4606e 100644 --- a/Ryujinx.Graphics.OpenGL/Pipeline.cs +++ b/Ryujinx.Graphics.OpenGL/Pipeline.cs @@ -597,6 +597,8 @@ namespace Ryujinx.Graphics.OpenGL GL.EndTransformFeedback(); } + GL.ClipControl(ClipOrigin.UpperLeft, ClipDepthMode.NegativeOneToOne); + _drawTexture.Draw( view, samp, @@ -627,6 +629,8 @@ namespace Ryujinx.Graphics.OpenGL { GL.BeginTransformFeedback(_tfTopology); } + + RestoreClipControl(); } } } From 70895bdb04c51e92b4d84946c66c122f9d54a73f Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sat, 11 Jun 2022 14:58:30 -0300 Subject: [PATCH 3/5] Allow concurrent BSD EventFd read/write (#3385) --- Ryujinx.HLE/HOS/Horizon.cs | 2 +- Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs | 5 ++- .../HOS/Kernel/SupervisorCall/Syscall.cs | 15 +++++++- Ryujinx.HLE/HOS/Services/ServerBase.cs | 38 +++++++++++++++++-- .../Sockets/Bsd/Impl/EventFileDescriptor.cs | 9 ++--- 5 files changed, 55 insertions(+), 14 deletions(-) diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs index b93ebc032..2e64542e5 100644 --- a/Ryujinx.HLE/HOS/Horizon.cs +++ b/Ryujinx.HLE/HOS/Horizon.cs @@ -309,7 +309,7 @@ namespace Ryujinx.HLE.HOS // only then doing connections to SM is safe. SmServer.InitDone.WaitOne(); - BsdServer = new ServerBase(KernelContext, "BsdServer"); + BsdServer = new ServerBase(KernelContext, "BsdServer", null, 2); AudRenServer = new ServerBase(KernelContext, "AudioRendererServer"); AudOutServer = new ServerBase(KernelContext, "AudioOutServer"); FsServer = new ServerBase(KernelContext, "FsServer"); diff --git a/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs b/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs index 0caeacade..d01c3e3b2 100644 --- a/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs +++ b/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs @@ -735,11 +735,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Process ulong argsPtr, ulong stackTop, int priority, - int cpuCore) + int cpuCore, + ThreadStart customThreadStart = null) { lock (_processLock) { - return thread.Initialize(entrypoint, argsPtr, stackTop, priority, cpuCore, this, ThreadType.User, null); + return thread.Initialize(entrypoint, argsPtr, stackTop, priority, cpuCore, this, ThreadType.User, customThreadStart); } } diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs index 179c7d4b2..189e4a3ea 100644 --- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs +++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs @@ -2350,6 +2350,18 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall [PointerSized] ulong stackTop, int priority, int cpuCore) + { + return CreateThread(out handle, entrypoint, argsPtr, stackTop, priority, cpuCore, null); + } + + public KernelResult CreateThread( + out int handle, + ulong entrypoint, + ulong argsPtr, + ulong stackTop, + int priority, + int cpuCore, + ThreadStart customThreadStart) { handle = 0; @@ -2386,7 +2398,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall argsPtr, stackTop, priority, - cpuCore); + cpuCore, + customThreadStart); if (result == KernelResult.Success) { diff --git a/Ryujinx.HLE/HOS/Services/ServerBase.cs b/Ryujinx.HLE/HOS/Services/ServerBase.cs index 907833441..2aead42e3 100644 --- a/Ryujinx.HLE/HOS/Services/ServerBase.cs +++ b/Ryujinx.HLE/HOS/Services/ServerBase.cs @@ -1,3 +1,4 @@ +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel; using Ryujinx.HLE.HOS.Kernel.Common; @@ -38,15 +39,18 @@ namespace Ryujinx.HLE.HOS.Services private readonly Dictionary> _ports = new Dictionary>(); public ManualResetEvent InitDone { get; } - public Func SmObjectFactory { get; } public string Name { get; } + public Func SmObjectFactory { get; } - public ServerBase(KernelContext context, string name, Func smObjectFactory = null) + private int _threadCount; + + public ServerBase(KernelContext context, string name, Func smObjectFactory = null, int threadCount = 1) { InitDone = new ManualResetEvent(false); + _context = context; Name = name; SmObjectFactory = smObjectFactory; - _context = context; + _threadCount = threadCount; const ProcessCreationFlags flags = ProcessCreationFlags.EnableAslr | @@ -56,7 +60,7 @@ namespace Ryujinx.HLE.HOS.Services ProcessCreationInfo creationInfo = new ProcessCreationInfo("Service", 1, 0, 0x8000000, 1, flags, 0, 0); - KernelStatic.StartInitialProcess(context, creationInfo, DefaultCapabilities, 44, ServerLoop); + KernelStatic.StartInitialProcess(context, creationInfo, DefaultCapabilities, 44, Main); } private void AddPort(int serverPortHandle, Func objectFactory) @@ -80,6 +84,32 @@ namespace Ryujinx.HLE.HOS.Services _sessions.Add(serverSessionHandle, obj); } + private void Main() + { + for (int i = 1; i < _threadCount; i++) + { + KernelResult result = _context.Syscall.CreateThread(out int threadHandle, 0UL, 0UL, 0UL, 44, 3, ServerLoop); + + if (result == KernelResult.Success) + { + result = _context.Syscall.StartThread(threadHandle); + + if (result != KernelResult.Success) + { + Logger.Error?.Print(LogClass.Service, $"Failed to start thread on {Name}: {result}"); + } + + _context.Syscall.CloseHandle(threadHandle); + } + else + { + Logger.Error?.Print(LogClass.Service, $"Failed to create thread on {Name}: {result}"); + } + } + + ServerLoop(); + } + private void ServerLoop() { _selfProcess = KernelStatic.GetCurrentProcess(); diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/EventFileDescriptor.cs b/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/EventFileDescriptor.cs index e92b42ef0..ea63e8425 100644 --- a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/EventFileDescriptor.cs +++ b/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/EventFileDescriptor.cs @@ -8,7 +8,6 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd { private ulong _value; private readonly EventFdFlags _flags; - private AutoResetEvent _event; private object _lock = new object(); @@ -21,7 +20,6 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd { _value = value; _flags = flags; - _event = new AutoResetEvent(false); WriteEvent = new ManualResetEvent(true); ReadEvent = new ManualResetEvent(true); @@ -31,7 +29,6 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd public void Dispose() { - _event.Dispose(); WriteEvent.Dispose(); ReadEvent.Dispose(); } @@ -57,7 +54,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd { while (_value == 0) { - _event.WaitOne(); + Monitor.Wait(_lock); } } else @@ -106,7 +103,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd { if (Blocking) { - _event.WaitOne(); + Monitor.Wait(_lock); } else { @@ -119,7 +116,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd writeSize = sizeof(ulong); _value += count; - _event.Set(); + Monitor.Pulse(_lock); WriteEvent.Set(); From b1bd6a50b5341f444ceb31bbb0fb64f685828d75 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sun, 12 Jun 2022 04:29:12 -0300 Subject: [PATCH 4/5] Less invasive fix for EventFd blocking operations (#3394) --- Ryujinx.HLE/HOS/Horizon.cs | 2 +- Ryujinx.HLE/HOS/Services/ServerBase.cs | 26 +------------------ .../Sockets/Bsd/Impl/EventFileDescriptor.cs | 5 ++++ 3 files changed, 7 insertions(+), 26 deletions(-) diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs index 2e64542e5..b93ebc032 100644 --- a/Ryujinx.HLE/HOS/Horizon.cs +++ b/Ryujinx.HLE/HOS/Horizon.cs @@ -309,7 +309,7 @@ namespace Ryujinx.HLE.HOS // only then doing connections to SM is safe. SmServer.InitDone.WaitOne(); - BsdServer = new ServerBase(KernelContext, "BsdServer", null, 2); + BsdServer = new ServerBase(KernelContext, "BsdServer"); AudRenServer = new ServerBase(KernelContext, "AudioRendererServer"); AudOutServer = new ServerBase(KernelContext, "AudioOutServer"); FsServer = new ServerBase(KernelContext, "FsServer"); diff --git a/Ryujinx.HLE/HOS/Services/ServerBase.cs b/Ryujinx.HLE/HOS/Services/ServerBase.cs index 2aead42e3..c13ee1863 100644 --- a/Ryujinx.HLE/HOS/Services/ServerBase.cs +++ b/Ryujinx.HLE/HOS/Services/ServerBase.cs @@ -42,15 +42,12 @@ namespace Ryujinx.HLE.HOS.Services public string Name { get; } public Func SmObjectFactory { get; } - private int _threadCount; - - public ServerBase(KernelContext context, string name, Func smObjectFactory = null, int threadCount = 1) + public ServerBase(KernelContext context, string name, Func smObjectFactory = null) { InitDone = new ManualResetEvent(false); _context = context; Name = name; SmObjectFactory = smObjectFactory; - _threadCount = threadCount; const ProcessCreationFlags flags = ProcessCreationFlags.EnableAslr | @@ -86,27 +83,6 @@ namespace Ryujinx.HLE.HOS.Services private void Main() { - for (int i = 1; i < _threadCount; i++) - { - KernelResult result = _context.Syscall.CreateThread(out int threadHandle, 0UL, 0UL, 0UL, 44, 3, ServerLoop); - - if (result == KernelResult.Success) - { - result = _context.Syscall.StartThread(threadHandle); - - if (result != KernelResult.Success) - { - Logger.Error?.Print(LogClass.Service, $"Failed to start thread on {Name}: {result}"); - } - - _context.Syscall.CloseHandle(threadHandle); - } - else - { - Logger.Error?.Print(LogClass.Service, $"Failed to create thread on {Name}: {result}"); - } - } - ServerLoop(); } diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/EventFileDescriptor.cs b/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/EventFileDescriptor.cs index ea63e8425..239e2434c 100644 --- a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/EventFileDescriptor.cs +++ b/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/EventFileDescriptor.cs @@ -18,6 +18,11 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd public EventFileDescriptor(ulong value, EventFdFlags flags) { + // FIXME: We should support blocking operations. + // Right now they can't be supported because it would cause the + // service to lock up as we only have one thread processing requests. + flags |= EventFdFlags.NonBlocking; + _value = value; _flags = flags; From 851f56b08a0c3b420f91143b6c6c007b429174a8 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Tue, 14 Jun 2022 13:30:39 -0300 Subject: [PATCH 5/5] Support Array/3D depth-stencil render target, and single layer clears (#3400) * Support Array/3D depth-stencil render target, and single layer clears * Alignment --- Ryujinx.Graphics.GAL/IPipeline.cs | 3 +- .../Commands/ClearRenderTargetColorCommand.cs | 6 +- .../ClearRenderTargetDepthStencilCommand.cs | 6 +- .../Multithreading/ThreadedPipeline.cs | 8 +- .../Engine/Threed/DrawManager.cs | 6 +- .../Engine/Threed/StateUpdater.cs | 6 +- .../Engine/Threed/ThreedClass.cs | 5 +- Ryujinx.Graphics.Gpu/Image/TextureCache.cs | 23 +++- Ryujinx.Graphics.OpenGL/Framebuffer.cs | 116 +++++++++++++++--- Ryujinx.Graphics.OpenGL/Pipeline.cs | 12 +- 10 files changed, 157 insertions(+), 34 deletions(-) diff --git a/Ryujinx.Graphics.GAL/IPipeline.cs b/Ryujinx.Graphics.GAL/IPipeline.cs index aec096e72..83afcaa3a 100644 --- a/Ryujinx.Graphics.GAL/IPipeline.cs +++ b/Ryujinx.Graphics.GAL/IPipeline.cs @@ -10,9 +10,10 @@ namespace Ryujinx.Graphics.GAL void ClearBuffer(BufferHandle destination, int offset, int size, uint value); - void ClearRenderTargetColor(int index, uint componentMask, ColorF color); + void ClearRenderTargetColor(int index, int layer, uint componentMask, ColorF color); void ClearRenderTargetDepthStencil( + int layer, float depthValue, bool depthMask, int stencilValue, diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearRenderTargetColorCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearRenderTargetColorCommand.cs index 57509f1c0..cde69e7bb 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearRenderTargetColorCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearRenderTargetColorCommand.cs @@ -4,19 +4,21 @@ { public CommandType CommandType => CommandType.ClearRenderTargetColor; private int _index; + private int _layer; private uint _componentMask; private ColorF _color; - public void Set(int index, uint componentMask, ColorF color) + public void Set(int index, int layer, uint componentMask, ColorF color) { _index = index; + _layer = layer; _componentMask = componentMask; _color = color; } public static void Run(ref ClearRenderTargetColorCommand command, ThreadedRenderer threaded, IRenderer renderer) { - renderer.Pipeline.ClearRenderTargetColor(command._index, command._componentMask, command._color); + renderer.Pipeline.ClearRenderTargetColor(command._index, command._layer, command._componentMask, command._color); } } } diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearRenderTargetDepthStencilCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearRenderTargetDepthStencilCommand.cs index 3692cd37b..c5c76539e 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearRenderTargetDepthStencilCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearRenderTargetDepthStencilCommand.cs @@ -3,13 +3,15 @@ struct ClearRenderTargetDepthStencilCommand : IGALCommand { public CommandType CommandType => CommandType.ClearRenderTargetDepthStencil; + private int _layer; private float _depthValue; private bool _depthMask; private int _stencilValue; private int _stencilMask; - public void Set(float depthValue, bool depthMask, int stencilValue, int stencilMask) + public void Set(int layer, float depthValue, bool depthMask, int stencilValue, int stencilMask) { + _layer = layer; _depthValue = depthValue; _depthMask = depthMask; _stencilValue = stencilValue; @@ -18,7 +20,7 @@ public static void Run(ref ClearRenderTargetDepthStencilCommand command, ThreadedRenderer threaded, IRenderer renderer) { - renderer.Pipeline.ClearRenderTargetDepthStencil(command._depthValue, command._depthMask, command._stencilValue, command._stencilMask); + renderer.Pipeline.ClearRenderTargetDepthStencil(command._layer, command._depthValue, command._depthMask, command._stencilValue, command._stencilMask); } } } diff --git a/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs b/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs index 010ee7e65..2a1f474a9 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs @@ -40,15 +40,15 @@ namespace Ryujinx.Graphics.GAL.Multithreading _renderer.QueueCommand(); } - public void ClearRenderTargetColor(int index, uint componentMask, ColorF color) + public void ClearRenderTargetColor(int index, int layer, uint componentMask, ColorF color) { - _renderer.New().Set(index, componentMask, color); + _renderer.New().Set(index, layer, componentMask, color); _renderer.QueueCommand(); } - public void ClearRenderTargetDepthStencil(float depthValue, bool depthMask, int stencilValue, int stencilMask) + public void ClearRenderTargetDepthStencil(int layer, float depthValue, bool depthMask, int stencilValue, int stencilMask) { - _renderer.New().Set(depthValue, depthMask, stencilValue, stencilMask); + _renderer.New().Set(layer, depthValue, depthMask, stencilValue, stencilMask); _renderer.QueueCommand(); } diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs index ab3713141..f90baf99e 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs @@ -505,8 +505,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed } int index = (argument >> 6) & 0xf; + int layer = (argument >> 10) & 0x3ff; - engine.UpdateRenderTargetState(useControl: false, singleUse: index); + engine.UpdateRenderTargetState(useControl: false, layered: layer != 0, singleUse: index); // If there is a mismatch on the host clip region and the one explicitly defined by the guest // on the screen scissor state, then we need to force only one texture to be bound to avoid @@ -581,7 +582,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed ColorF color = new ColorF(clearColor.Red, clearColor.Green, clearColor.Blue, clearColor.Alpha); - _context.Renderer.Pipeline.ClearRenderTargetColor(index, componentMask, color); + _context.Renderer.Pipeline.ClearRenderTargetColor(index, layer, componentMask, color); } if (clearDepth || clearStencil) @@ -602,6 +603,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed } _context.Renderer.Pipeline.ClearRenderTargetDepthStencil( + layer, depthValue, clearDepth, stencilValue, diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs index d0c3bc5ae..f648479b6 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs @@ -362,8 +362,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// Updates render targets (color and depth-stencil buffers) based on current render target state. /// /// Use draw buffers information from render target control register + /// Indicates if the texture is layered /// If this is not -1, it indicates that only the given indexed target will be used. - public void UpdateRenderTargetState(bool useControl, int singleUse = -1) + public void UpdateRenderTargetState(bool useControl, bool layered = false, int singleUse = -1) { var memoryManager = _channel.MemoryManager; var rtControl = _state.State.RtControl; @@ -399,7 +400,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed Image.Texture color = memoryManager.Physical.TextureCache.FindOrCreateTexture( memoryManager, colorState, - _vtgWritesRtLayer, + _vtgWritesRtLayer || layered, samplesInX, samplesInY, sizeHint); @@ -433,6 +434,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed memoryManager, dsState, dsSize, + _vtgWritesRtLayer || layered, samplesInX, samplesInY, sizeHint); diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs index a2e8c64c1..764ba2394 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs @@ -131,10 +131,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// Updates render targets (color and depth-stencil buffers) based on current render target state. /// /// Use draw buffers information from render target control register + /// Indicates if the texture is layered /// If this is not -1, it indicates that only the given indexed target will be used. - public void UpdateRenderTargetState(bool useControl, int singleUse = -1) + public void UpdateRenderTargetState(bool useControl, bool layered = false, int singleUse = -1) { - _stateUpdater.UpdateRenderTargetState(useControl, singleUse); + _stateUpdater.UpdateRenderTargetState(useControl, layered, singleUse); } /// diff --git a/Ryujinx.Graphics.Gpu/Image/TextureCache.cs b/Ryujinx.Graphics.Gpu/Image/TextureCache.cs index 045410571..ba863a1e1 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureCache.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureCache.cs @@ -349,6 +349,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// GPU memory manager where the texture is mapped /// Depth-stencil buffer texture to find or create /// Size of the depth-stencil texture + /// Indicates if the texture might be accessed with a non-zero layer index /// Number of samples in the X direction, for MSAA /// Number of samples in the Y direction, for MSAA /// A hint indicating the minimum used size for the texture @@ -357,6 +358,7 @@ namespace Ryujinx.Graphics.Gpu.Image MemoryManager memoryManager, RtDepthStencilState dsState, Size3D size, + bool layered, int samplesInX, int samplesInY, Size sizeHint) @@ -364,9 +366,24 @@ namespace Ryujinx.Graphics.Gpu.Image int gobBlocksInY = dsState.MemoryLayout.UnpackGobBlocksInY(); int gobBlocksInZ = dsState.MemoryLayout.UnpackGobBlocksInZ(); - Target target = (samplesInX | samplesInY) != 1 - ? Target.Texture2DMultisample - : Target.Texture2D; + Target target; + + if (dsState.MemoryLayout.UnpackIsTarget3D()) + { + target = Target.Texture3D; + } + else if ((samplesInX | samplesInY) != 1) + { + target = size.Depth > 1 && layered + ? Target.Texture2DMultisampleArray + : Target.Texture2DMultisample; + } + else + { + target = size.Depth > 1 && layered + ? Target.Texture2DArray + : Target.Texture2D; + } FormatInfo formatInfo = dsState.Format.Convert(); diff --git a/Ryujinx.Graphics.OpenGL/Framebuffer.cs b/Ryujinx.Graphics.OpenGL/Framebuffer.cs index da928b4c8..dafa76723 100644 --- a/Ryujinx.Graphics.OpenGL/Framebuffer.cs +++ b/Ryujinx.Graphics.OpenGL/Framebuffer.cs @@ -9,10 +9,13 @@ namespace Ryujinx.Graphics.OpenGL class Framebuffer : IDisposable { public int Handle { get; private set; } + private int _clearFbHandle; + private bool _clearFbInitialized; private FramebufferAttachment _lastDsAttachment; private readonly TextureView[] _colors; + private TextureView _depthStencil; private int _colorsCount; private bool _dualSourceBlend; @@ -20,6 +23,7 @@ namespace Ryujinx.Graphics.OpenGL public Framebuffer() { Handle = GL.GenFramebuffer(); + _clearFbHandle = GL.GenFramebuffer(); _colors = new TextureView[8]; } @@ -55,20 +59,7 @@ namespace Ryujinx.Graphics.OpenGL if (depthStencil != null) { - FramebufferAttachment attachment; - - if (IsPackedDepthStencilFormat(depthStencil.Format)) - { - attachment = FramebufferAttachment.DepthStencilAttachment; - } - else if (IsDepthOnlyFormat(depthStencil.Format)) - { - attachment = FramebufferAttachment.DepthAttachment; - } - else - { - attachment = FramebufferAttachment.StencilAttachment; - } + FramebufferAttachment attachment = GetAttachment(depthStencil.Format); GL.FramebufferTexture( FramebufferTarget.Framebuffer, @@ -82,6 +73,8 @@ namespace Ryujinx.Graphics.OpenGL { _lastDsAttachment = 0; } + + _depthStencil = depthStencil; } public void SetDualSourceBlend(bool enable) @@ -124,6 +117,22 @@ namespace Ryujinx.Graphics.OpenGL GL.DrawBuffers(colorsCount, drawBuffers); } + private static FramebufferAttachment GetAttachment(Format format) + { + if (IsPackedDepthStencilFormat(format)) + { + return FramebufferAttachment.DepthStencilAttachment; + } + else if (IsDepthOnlyFormat(format)) + { + return FramebufferAttachment.DepthAttachment; + } + else + { + return FramebufferAttachment.StencilAttachment; + } + } + private static bool IsPackedDepthStencilFormat(Format format) { return format == Format.D24UnormS8Uint || @@ -136,6 +145,78 @@ namespace Ryujinx.Graphics.OpenGL return format == Format.D16Unorm || format == Format.D32Float; } + public void AttachColorLayerForClear(int index, int layer) + { + TextureView color = _colors[index]; + + if (!IsLayered(color)) + { + return; + } + + BindClearFb(); + GL.FramebufferTextureLayer(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0 + index, color.Handle, 0, layer); + } + + public void DetachColorLayerForClear(int index) + { + TextureView color = _colors[index]; + + if (!IsLayered(color)) + { + return; + } + + GL.FramebufferTexture(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0 + index, 0, 0); + Bind(); + } + + public void AttachDepthStencilLayerForClear(int layer) + { + TextureView depthStencil = _depthStencil; + + if (!IsLayered(depthStencil)) + { + return; + } + + BindClearFb(); + GL.FramebufferTextureLayer(FramebufferTarget.Framebuffer, GetAttachment(depthStencil.Format), depthStencil.Handle, 0, layer); + } + + public void DetachDepthStencilLayerForClear() + { + TextureView depthStencil = _depthStencil; + + if (!IsLayered(depthStencil)) + { + return; + } + + GL.FramebufferTexture(FramebufferTarget.Framebuffer, GetAttachment(depthStencil.Format), 0, 0); + Bind(); + } + + private void BindClearFb() + { + GL.BindFramebuffer(FramebufferTarget.Framebuffer, _clearFbHandle); + + if (!_clearFbInitialized) + { + SetDrawBuffersImpl(Constants.MaxRenderTargets); + _clearFbInitialized = true; + } + } + + private static bool IsLayered(TextureView view) + { + return view != null && + view.Target != Target.Texture1D && + view.Target != Target.Texture2D && + view.Target != Target.Texture2DMultisample && + view.Target != Target.TextureBuffer; + } + public void Dispose() { if (Handle != 0) @@ -144,6 +225,13 @@ namespace Ryujinx.Graphics.OpenGL Handle = 0; } + + if (_clearFbHandle != 0) + { + GL.DeleteFramebuffer(_clearFbHandle); + + _clearFbHandle = 0; + } } } } diff --git a/Ryujinx.Graphics.OpenGL/Pipeline.cs b/Ryujinx.Graphics.OpenGL/Pipeline.cs index 637e4606e..62d4dee90 100644 --- a/Ryujinx.Graphics.OpenGL/Pipeline.cs +++ b/Ryujinx.Graphics.OpenGL/Pipeline.cs @@ -110,7 +110,7 @@ namespace Ryujinx.Graphics.OpenGL Buffer.Clear(destination, offset, size, value); } - public void ClearRenderTargetColor(int index, uint componentMask, ColorF color) + public void ClearRenderTargetColor(int index, int layer, uint componentMask, ColorF color) { GL.ColorMask( index, @@ -119,14 +119,18 @@ namespace Ryujinx.Graphics.OpenGL (componentMask & 4) != 0, (componentMask & 8) != 0); + _framebuffer.AttachColorLayerForClear(index, layer); + float[] colors = new float[] { color.Red, color.Green, color.Blue, color.Alpha }; GL.ClearBuffer(OpenTK.Graphics.OpenGL.ClearBuffer.Color, index, colors); + _framebuffer.DetachColorLayerForClear(index); + RestoreComponentMask(index); } - public void ClearRenderTargetDepthStencil(float depthValue, bool depthMask, int stencilValue, int stencilMask) + public void ClearRenderTargetDepthStencil(int layer, float depthValue, bool depthMask, int stencilValue, int stencilMask) { bool stencilMaskChanged = stencilMask != 0 && @@ -144,6 +148,8 @@ namespace Ryujinx.Graphics.OpenGL GL.DepthMask(depthMask); } + _framebuffer.AttachDepthStencilLayerForClear(layer); + if (depthMask && stencilMask != 0) { GL.ClearBuffer(ClearBufferCombined.DepthStencil, 0, depthValue, stencilValue); @@ -157,6 +163,8 @@ namespace Ryujinx.Graphics.OpenGL GL.ClearBuffer(OpenTK.Graphics.OpenGL.ClearBuffer.Stencil, 0, ref stencilValue); } + _framebuffer.DetachDepthStencilLayerForClear(); + if (stencilMaskChanged) { GL.StencilMaskSeparate(StencilFace.Front, _stencilFrontMask);