diff options
Diffstat (limited to 'Ryujinx.Graphics.GAL/Multithreading')
105 files changed, 3894 insertions, 0 deletions
diff --git a/Ryujinx.Graphics.GAL/Multithreading/BufferMap.cs b/Ryujinx.Graphics.GAL/Multithreading/BufferMap.cs new file mode 100644 index 00000000..fcf09f9f --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/BufferMap.cs @@ -0,0 +1,170 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Threading; + +namespace Ryujinx.Graphics.GAL.Multithreading +{ + /// <summary> + /// Buffer handles given to the client are not the same as those provided by the backend, + /// as their handle is created at a later point on the queue. + /// The handle returned is a unique identifier that will map to the real buffer when it is available. + /// Note that any uses within the queue should be safe, but outside you must use MapBufferBlocking. + /// </summary> + class BufferMap + { + private ulong _bufferHandle = 0; + + private Dictionary<BufferHandle, BufferHandle> _bufferMap = new Dictionary<BufferHandle, BufferHandle>(); + private HashSet<BufferHandle> _inFlight = new HashSet<BufferHandle>(); + private AutoResetEvent _inFlightChanged = new AutoResetEvent(false); + + internal BufferHandle CreateBufferHandle() + { + ulong handle64 = Interlocked.Increment(ref _bufferHandle); + + BufferHandle threadedHandle = Unsafe.As<ulong, BufferHandle>(ref handle64); + + lock (_inFlight) + { + _inFlight.Add(threadedHandle); + } + + return threadedHandle; + } + + internal void AssignBuffer(BufferHandle threadedHandle, BufferHandle realHandle) + { + lock (_bufferMap) + { + _bufferMap[threadedHandle] = realHandle; + } + + lock (_inFlight) + { + _inFlight.Remove(threadedHandle); + } + + _inFlightChanged.Set(); + } + + internal void UnassignBuffer(BufferHandle threadedHandle) + { + lock (_bufferMap) + { + _bufferMap.Remove(threadedHandle); + } + } + + internal BufferHandle MapBuffer(BufferHandle handle) + { + // Maps a threaded buffer to a backend one. + // Threaded buffers are returned on creation as the buffer + // isn't actually created until the queue runs the command. + + BufferHandle result; + + lock (_bufferMap) + { + if (!_bufferMap.TryGetValue(handle, out result)) + { + result = BufferHandle.Null; + } + + return result; + } + } + + internal BufferHandle MapBufferBlocking(BufferHandle handle) + { + // Blocks until the handle is available. + + BufferHandle result; + + lock (_bufferMap) + { + if (_bufferMap.TryGetValue(handle, out result)) + { + return result; + } + } + + bool signal = false; + + while (true) + { + lock (_inFlight) + { + if (!_inFlight.Contains(handle)) + { + break; + } + } + + _inFlightChanged.WaitOne(); + signal = true; + } + + if (signal) + { + // Signal other threads which might still be waiting. + _inFlightChanged.Set(); + } + + return MapBuffer(handle); + } + + internal BufferRange MapBufferRange(BufferRange range) + { + return new BufferRange(MapBuffer(range.Handle), range.Offset, range.Size); + } + + internal Span<BufferRange> MapBufferRanges(Span<BufferRange> ranges) + { + // Rewrite the buffer ranges to point to the mapped handles. + + lock (_bufferMap) + { + for (int i = 0; i < ranges.Length; i++) + { + ref BufferRange range = ref ranges[i]; + BufferHandle result; + + if (!_bufferMap.TryGetValue(range.Handle, out result)) + { + result = BufferHandle.Null; + } + + range = new BufferRange(result, range.Offset, range.Size); + } + } + + return ranges; + } + + internal Span<VertexBufferDescriptor> MapBufferRanges(Span<VertexBufferDescriptor> ranges) + { + // Rewrite the buffer ranges to point to the mapped handles. + + lock (_bufferMap) + { + for (int i = 0; i < ranges.Length; i++) + { + BufferRange range = ranges[i].Buffer; + BufferHandle result; + + if (!_bufferMap.TryGetValue(range.Handle, out result)) + { + result = BufferHandle.Null; + } + + range = new BufferRange(result, range.Offset, range.Size); + + ranges[i] = new VertexBufferDescriptor(range, ranges[i].Stride, ranges[i].Divisor); + } + } + + return ranges; + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/CommandHelper.cs b/Ryujinx.Graphics.GAL/Multithreading/CommandHelper.cs new file mode 100644 index 00000000..82a75ea7 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/CommandHelper.cs @@ -0,0 +1,234 @@ +using Ryujinx.Graphics.GAL.Multithreading.Commands; +using Ryujinx.Graphics.GAL.Multithreading.Commands.Buffer; +using Ryujinx.Graphics.GAL.Multithreading.Commands.CounterEvent; +using Ryujinx.Graphics.GAL.Multithreading.Commands.Program; +using Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer; +using Ryujinx.Graphics.GAL.Multithreading.Commands.Sampler; +using Ryujinx.Graphics.GAL.Multithreading.Commands.Shader; +using Ryujinx.Graphics.GAL.Multithreading.Commands.Texture; +using Ryujinx.Graphics.GAL.Multithreading.Commands.Window; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Ryujinx.Graphics.GAL.Multithreading +{ + static class CommandHelper + { + private delegate void CommandDelegate(Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer); + + private static int _totalCommands = (int)Enum.GetValues<CommandType>().Max() + 1; + private static CommandDelegate[] _lookup = new CommandDelegate[_totalCommands]; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ref T GetCommand<T>(Span<byte> memory) + { + return ref Unsafe.As<byte, T>(ref MemoryMarshal.GetReference(memory)); + } + + public static int GetMaxCommandSize() + { + Assembly assembly = typeof(CommandHelper).Assembly; + + IEnumerable<Type> commands = assembly.GetTypes().Where(type => typeof(IGALCommand).IsAssignableFrom(type) && type.IsValueType); + + int maxSize = commands.Max(command => + { + MethodInfo method = typeof(Unsafe).GetMethod(nameof(Unsafe.SizeOf)); + MethodInfo generic = method.MakeGenericMethod(command); + int size = (int)generic.Invoke(null, null); + + return size; + }); + + InitLookup(); + + return maxSize + 1; // 1 byte reserved for command size. + } + + private static void InitLookup() + { + _lookup[(int)CommandType.Action] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + ActionCommand.Run(ref GetCommand<ActionCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.CompileShader] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + CompileShaderCommand.Run(ref GetCommand<CompileShaderCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.CreateBuffer] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + CreateBufferCommand.Run(ref GetCommand<CreateBufferCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.CreateProgram] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + CreateProgramCommand.Run(ref GetCommand<CreateProgramCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.CreateSampler] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + CreateSamplerCommand.Run(ref GetCommand<CreateSamplerCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.CreateSync] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + CreateSyncCommand.Run(ref GetCommand<CreateSyncCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.CreateTexture] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + CreateTextureCommand.Run(ref GetCommand<CreateTextureCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.GetCapabilities] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + GetCapabilitiesCommand.Run(ref GetCommand<GetCapabilitiesCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.PreFrame] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + PreFrameCommand.Run(ref GetCommand<PreFrameCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.ReportCounter] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + ReportCounterCommand.Run(ref GetCommand<ReportCounterCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.ResetCounter] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + ResetCounterCommand.Run(ref GetCommand<ResetCounterCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.UpdateCounters] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + UpdateCountersCommand.Run(ref GetCommand<UpdateCountersCommand>(memory), threaded, renderer); + + _lookup[(int)CommandType.BufferDispose] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + BufferDisposeCommand.Run(ref GetCommand<BufferDisposeCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.BufferGetData] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + BufferGetDataCommand.Run(ref GetCommand<BufferGetDataCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.BufferSetData] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + BufferSetDataCommand.Run(ref GetCommand<BufferSetDataCommand>(memory), threaded, renderer); + + _lookup[(int)CommandType.CounterEventDispose] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + CounterEventDisposeCommand.Run(ref GetCommand<CounterEventDisposeCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.CounterEventFlush] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + CounterEventFlushCommand.Run(ref GetCommand<CounterEventFlushCommand>(memory), threaded, renderer); + + _lookup[(int)CommandType.ProgramDispose] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + ProgramDisposeCommand.Run(ref GetCommand<ProgramDisposeCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.ProgramGetBinary] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + ProgramGetBinaryCommand.Run(ref GetCommand<ProgramGetBinaryCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.ProgramCheckLink] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + ProgramCheckLinkCommand.Run(ref GetCommand<ProgramCheckLinkCommand>(memory), threaded, renderer); + + _lookup[(int)CommandType.SamplerDispose] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + SamplerDisposeCommand.Run(ref GetCommand<SamplerDisposeCommand>(memory), threaded, renderer); + + _lookup[(int)CommandType.ShaderDispose] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + ShaderDisposeCommand.Run(ref GetCommand<ShaderDisposeCommand>(memory), threaded, renderer); + + _lookup[(int)CommandType.TextureCopyTo] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + TextureCopyToCommand.Run(ref GetCommand<TextureCopyToCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.TextureCopyToScaled] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + TextureCopyToScaledCommand.Run(ref GetCommand<TextureCopyToScaledCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.TextureCopyToSlice] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + TextureCopyToSliceCommand.Run(ref GetCommand<TextureCopyToSliceCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.TextureCreateView] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + TextureCreateViewCommand.Run(ref GetCommand<TextureCreateViewCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.TextureGetData] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + TextureGetDataCommand.Run(ref GetCommand<TextureGetDataCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.TextureRelease] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + TextureReleaseCommand.Run(ref GetCommand<TextureReleaseCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.TextureSetData] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + TextureSetDataCommand.Run(ref GetCommand<TextureSetDataCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.TextureSetDataSlice] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + TextureSetDataSliceCommand.Run(ref GetCommand<TextureSetDataSliceCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.TextureSetStorage] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + TextureSetStorageCommand.Run(ref GetCommand<TextureSetStorageCommand>(memory), threaded, renderer); + + _lookup[(int)CommandType.WindowPresent] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + WindowPresentCommand.Run(ref GetCommand<WindowPresentCommand>(memory), threaded, renderer); + + _lookup[(int)CommandType.Barrier] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + BarrierCommand.Run(ref GetCommand<BarrierCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.BeginTransformFeedback] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + BeginTransformFeedbackCommand.Run(ref GetCommand<BeginTransformFeedbackCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.ClearBuffer] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + ClearBufferCommand.Run(ref GetCommand<ClearBufferCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.ClearRenderTargetColor] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + ClearRenderTargetColorCommand.Run(ref GetCommand<ClearRenderTargetColorCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.ClearRenderTargetDepthStencil] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + ClearRenderTargetDepthStencilCommand.Run(ref GetCommand<ClearRenderTargetDepthStencilCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.CommandBufferBarrier] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + CommandBufferBarrierCommand.Run(ref GetCommand<CommandBufferBarrierCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.CopyBuffer] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + CopyBufferCommand.Run(ref GetCommand<CopyBufferCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.DispatchCompute] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + DispatchComputeCommand.Run(ref GetCommand<DispatchComputeCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.Draw] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + DrawCommand.Run(ref GetCommand<DrawCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.DrawIndexed] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + DrawIndexedCommand.Run(ref GetCommand<DrawIndexedCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.EndHostConditionalRendering] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + EndHostConditionalRenderingCommand.Run(renderer); + _lookup[(int)CommandType.EndTransformFeedback] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + EndTransformFeedbackCommand.Run(ref GetCommand<EndTransformFeedbackCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.MultiDrawIndirectCount] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + MultiDrawIndirectCountCommand.Run(ref GetCommand<MultiDrawIndirectCountCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.MultiDrawIndexedIndirectCount] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + MultiDrawIndexedIndirectCountCommand.Run(ref GetCommand<MultiDrawIndexedIndirectCountCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.SetAlphaTest] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + SetAlphaTestCommand.Run(ref GetCommand<SetAlphaTestCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.SetBlendState] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + SetBlendStateCommand.Run(ref GetCommand<SetBlendStateCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.SetDepthBias] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + SetDepthBiasCommand.Run(ref GetCommand<SetDepthBiasCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.SetDepthClamp] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + SetDepthClampCommand.Run(ref GetCommand<SetDepthClampCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.SetDepthMode] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + SetDepthModeCommand.Run(ref GetCommand<SetDepthModeCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.SetDepthTest] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + SetDepthTestCommand.Run(ref GetCommand<SetDepthTestCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.SetFaceCulling] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + SetFaceCullingCommand.Run(ref GetCommand<SetFaceCullingCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.SetFrontFace] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + SetFrontFaceCommand.Run(ref GetCommand<SetFrontFaceCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.SetStorageBuffers] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + SetStorageBuffersCommand.Run(ref GetCommand<SetStorageBuffersCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.SetTransformFeedbackBuffers] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + SetTransformFeedbackBuffersCommand.Run(ref GetCommand<SetTransformFeedbackBuffersCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.SetUniformBuffers] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + SetUniformBuffersCommand.Run(ref GetCommand<SetUniformBuffersCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.SetImage] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + SetImageCommand.Run(ref GetCommand<SetImageCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.SetIndexBuffer] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + SetIndexBufferCommand.Run(ref GetCommand<SetIndexBufferCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.SetLineParameters] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + SetLineParametersCommand.Run(ref GetCommand<SetLineParametersCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.SetLogicOpState] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + SetLogicOpStateCommand.Run(ref GetCommand<SetLogicOpStateCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.SetPointParameters] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + SetPointParametersCommand.Run(ref GetCommand<SetPointParametersCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.SetPrimitiveRestart] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + SetPrimitiveRestartCommand.Run(ref GetCommand<SetPrimitiveRestartCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.SetPrimitiveTopology] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + SetPrimitiveTopologyCommand.Run(ref GetCommand<SetPrimitiveTopologyCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.SetProgram] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + SetProgramCommand.Run(ref GetCommand<SetProgramCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.SetRasterizerDiscard] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + SetRasterizerDiscardCommand.Run(ref GetCommand<SetRasterizerDiscardCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.SetRenderTargetColorMasks] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + SetRenderTargetColorMasksCommand.Run(ref GetCommand<SetRenderTargetColorMasksCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.SetRenderTargetScale] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + SetRenderTargetScaleCommand.Run(ref GetCommand<SetRenderTargetScaleCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.SetRenderTargets] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + SetRenderTargetsCommand.Run(ref GetCommand<SetRenderTargetsCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.SetSampler] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + SetSamplerCommand.Run(ref GetCommand<SetSamplerCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.SetScissor] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + SetScissorCommand.Run(ref GetCommand<SetScissorCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.SetStencilTest] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + SetStencilTestCommand.Run(ref GetCommand<SetStencilTestCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.SetTexture] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + SetTextureCommand.Run(ref GetCommand<SetTextureCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.SetUserClipDistance] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + SetUserClipDistanceCommand.Run(ref GetCommand<SetUserClipDistanceCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.SetVertexAttribs] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + SetVertexAttribsCommand.Run(ref GetCommand<SetVertexAttribsCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.SetVertexBuffers] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + SetVertexBuffersCommand.Run(ref GetCommand<SetVertexBuffersCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.SetViewports] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + SetViewportsCommand.Run(ref GetCommand<SetViewportsCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.TextureBarrier] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + TextureBarrierCommand.Run(ref GetCommand<TextureBarrierCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.TextureBarrierTiled] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + TextureBarrierTiledCommand.Run(ref GetCommand<TextureBarrierTiledCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.TryHostConditionalRendering] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + TryHostConditionalRenderingCommand.Run(ref GetCommand<TryHostConditionalRenderingCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.TryHostConditionalRenderingFlush] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + TryHostConditionalRenderingFlushCommand.Run(ref GetCommand<TryHostConditionalRenderingFlushCommand>(memory), threaded, renderer); + _lookup[(int)CommandType.UpdateRenderScale] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => + UpdateRenderScaleCommand.Run(ref GetCommand<UpdateRenderScaleCommand>(memory), threaded, renderer); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void RunCommand(Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) + { + _lookup[memory[memory.Length - 1]](memory, threaded, renderer); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/CommandType.cs b/Ryujinx.Graphics.GAL/Multithreading/CommandType.cs new file mode 100644 index 00000000..0761a7f0 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/CommandType.cs @@ -0,0 +1,97 @@ +namespace Ryujinx.Graphics.GAL.Multithreading +{ + enum CommandType : byte + { + Action, + CompileShader, + CreateBuffer, + CreateProgram, + CreateSampler, + CreateSync, + CreateTexture, + GetCapabilities, + Unused, + PreFrame, + ReportCounter, + ResetCounter, + UpdateCounters, + + BufferDispose, + BufferGetData, + BufferSetData, + + CounterEventDispose, + CounterEventFlush, + + ProgramDispose, + ProgramGetBinary, + ProgramCheckLink, + + SamplerDispose, + + ShaderDispose, + + TextureCopyTo, + TextureCopyToScaled, + TextureCopyToSlice, + TextureCreateView, + TextureGetData, + TextureRelease, + TextureSetData, + TextureSetDataSlice, + TextureSetStorage, + + WindowPresent, + + Barrier, + BeginTransformFeedback, + ClearBuffer, + ClearRenderTargetColor, + ClearRenderTargetDepthStencil, + CommandBufferBarrier, + CopyBuffer, + DispatchCompute, + Draw, + DrawIndexed, + EndHostConditionalRendering, + EndTransformFeedback, + MultiDrawIndirectCount, + MultiDrawIndexedIndirectCount, + SetAlphaTest, + SetBlendState, + SetDepthBias, + SetDepthClamp, + SetDepthMode, + SetDepthTest, + SetFaceCulling, + SetFrontFace, + SetStorageBuffers, + SetTransformFeedbackBuffers, + SetUniformBuffers, + SetImage, + SetIndexBuffer, + SetLineParameters, + SetLogicOpState, + SetPointParameters, + SetPrimitiveRestart, + SetPrimitiveTopology, + SetProgram, + SetRasterizerDiscard, + SetRenderTargetColorMasks, + SetRenderTargetScale, + SetRenderTargets, + SetSampler, + SetScissor, + SetStencilTest, + SetTexture, + SetUserClipDistance, + SetVertexAttribs, + SetVertexBuffers, + SetViewports, + TextureBarrier, + TextureBarrierTiled, + TryHostConditionalRendering, + TryHostConditionalRenderingFlush, + UpdateRenderScale + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/BarrierCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/BarrierCommand.cs new file mode 100644 index 00000000..f187c3c2 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/BarrierCommand.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct BarrierCommand : IGALCommand + { + public CommandType CommandType => CommandType.Barrier; + + public static void Run(ref BarrierCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.Barrier(); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/BeginTransformFeedbackCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/BeginTransformFeedbackCommand.cs new file mode 100644 index 00000000..ea547d8b --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/BeginTransformFeedbackCommand.cs @@ -0,0 +1,18 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct BeginTransformFeedbackCommand : IGALCommand + { + public CommandType CommandType => CommandType.BeginTransformFeedback; + private PrimitiveTopology _topology; + + public void Set(PrimitiveTopology topology) + { + _topology = topology; + } + + public static void Run(ref BeginTransformFeedbackCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.BeginTransformFeedback(command._topology); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Buffer/BufferDisposeCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Buffer/BufferDisposeCommand.cs new file mode 100644 index 00000000..68167be0 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Buffer/BufferDisposeCommand.cs @@ -0,0 +1,19 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Buffer +{ + struct BufferDisposeCommand : IGALCommand + { + public CommandType CommandType => CommandType.BufferDispose; + private BufferHandle _buffer; + + public void Set(BufferHandle buffer) + { + _buffer = buffer; + } + + public static void Run(ref BufferDisposeCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.DeleteBuffer(threaded.Buffers.MapBuffer(command._buffer)); + threaded.Buffers.UnassignBuffer(command._buffer); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Buffer/BufferGetDataCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Buffer/BufferGetDataCommand.cs new file mode 100644 index 00000000..786ed87c --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Buffer/BufferGetDataCommand.cs @@ -0,0 +1,29 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using System; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Buffer +{ + struct BufferGetDataCommand : IGALCommand + { + public CommandType CommandType => CommandType.BufferGetData; + private BufferHandle _buffer; + private int _offset; + private int _size; + private TableRef<ResultBox<PinnedSpan<byte>>> _result; + + public void Set(BufferHandle buffer, int offset, int size, TableRef<ResultBox<PinnedSpan<byte>>> result) + { + _buffer = buffer; + _offset = offset; + _size = size; + _result = result; + } + + public static void Run(ref BufferGetDataCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + ReadOnlySpan<byte> result = renderer.GetBufferData(threaded.Buffers.MapBuffer(command._buffer), command._offset, command._size); + + command._result.Get(threaded).Result = new PinnedSpan<byte>(result); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Buffer/BufferSetDataCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Buffer/BufferSetDataCommand.cs new file mode 100644 index 00000000..6f39898e --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Buffer/BufferSetDataCommand.cs @@ -0,0 +1,27 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using System; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Buffer +{ + struct BufferSetDataCommand : IGALCommand + { + public CommandType CommandType => CommandType.BufferSetData; + private BufferHandle _buffer; + private int _offset; + private SpanRef<byte> _data; + + public void Set(BufferHandle buffer, int offset, SpanRef<byte> data) + { + _buffer = buffer; + _offset = offset; + _data = data; + } + + public static void Run(ref BufferSetDataCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + ReadOnlySpan<byte> data = command._data.Get(threaded); + renderer.SetBufferData(threaded.Buffers.MapBuffer(command._buffer), command._offset, data); + command._data.Dispose(threaded); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearBufferCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearBufferCommand.cs new file mode 100644 index 00000000..2b194b46 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearBufferCommand.cs @@ -0,0 +1,24 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct ClearBufferCommand : IGALCommand + { + public CommandType CommandType => CommandType.ClearBuffer; + private BufferHandle _destination; + private int _offset; + private int _size; + private uint _value; + + public void Set(BufferHandle destination, int offset, int size, uint value) + { + _destination = destination; + _offset = offset; + _size = size; + _value = value; + } + + public static void Run(ref ClearBufferCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.ClearBuffer(threaded.Buffers.MapBuffer(command._destination), command._offset, command._size, command._value); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearRenderTargetColorCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearRenderTargetColorCommand.cs new file mode 100644 index 00000000..57509f1c --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearRenderTargetColorCommand.cs @@ -0,0 +1,22 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct ClearRenderTargetColorCommand : IGALCommand + { + public CommandType CommandType => CommandType.ClearRenderTargetColor; + private int _index; + private uint _componentMask; + private ColorF _color; + + public void Set(int index, uint componentMask, ColorF color) + { + _index = index; + _componentMask = componentMask; + _color = color; + } + + public static void Run(ref ClearRenderTargetColorCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.ClearRenderTargetColor(command._index, command._componentMask, command._color); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearRenderTargetDepthStencilCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearRenderTargetDepthStencilCommand.cs new file mode 100644 index 00000000..3692cd37 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearRenderTargetDepthStencilCommand.cs @@ -0,0 +1,24 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct ClearRenderTargetDepthStencilCommand : IGALCommand + { + public CommandType CommandType => CommandType.ClearRenderTargetDepthStencil; + private float _depthValue; + private bool _depthMask; + private int _stencilValue; + private int _stencilMask; + + public void Set(float depthValue, bool depthMask, int stencilValue, int stencilMask) + { + _depthValue = depthValue; + _depthMask = depthMask; + _stencilValue = stencilValue; + _stencilMask = stencilMask; + } + + public static void Run(ref ClearRenderTargetDepthStencilCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.ClearRenderTargetDepthStencil(command._depthValue, command._depthMask, command._stencilValue, command._stencilMask); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/CommandBufferBarrierCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/CommandBufferBarrierCommand.cs new file mode 100644 index 00000000..8c828648 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/CommandBufferBarrierCommand.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct CommandBufferBarrierCommand : IGALCommand + { + public CommandType CommandType => CommandType.CommandBufferBarrier; + + public static void Run(ref CommandBufferBarrierCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.CommandBufferBarrier(); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/CopyBufferCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/CopyBufferCommand.cs new file mode 100644 index 00000000..e8f80d98 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/CopyBufferCommand.cs @@ -0,0 +1,26 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct CopyBufferCommand : IGALCommand + { + public CommandType CommandType => CommandType.CopyBuffer; + private BufferHandle _source; + private BufferHandle _destination; + private int _srcOffset; + private int _dstOffset; + private int _size; + + public void Set(BufferHandle source, BufferHandle destination, int srcOffset, int dstOffset, int size) + { + _source = source; + _destination = destination; + _srcOffset = srcOffset; + _dstOffset = dstOffset; + _size = size; + } + + public static void Run(ref CopyBufferCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.CopyBuffer(threaded.Buffers.MapBuffer(command._source), threaded.Buffers.MapBuffer(command._destination), command._srcOffset, command._dstOffset, command._size); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/CounterEvent/CounterEventDisposeCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/CounterEvent/CounterEventDisposeCommand.cs new file mode 100644 index 00000000..ae634e6a --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/CounterEvent/CounterEventDisposeCommand.cs @@ -0,0 +1,21 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.CounterEvent +{ + struct CounterEventDisposeCommand : IGALCommand + { + public CommandType CommandType => CommandType.CounterEventDispose; + private TableRef<ThreadedCounterEvent> _event; + + public void Set(TableRef<ThreadedCounterEvent> evt) + { + _event = evt; + } + + public static void Run(ref CounterEventDisposeCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + command._event.Get(threaded).Base.Dispose(); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/CounterEvent/CounterEventFlushCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/CounterEvent/CounterEventFlushCommand.cs new file mode 100644 index 00000000..e4ff4c18 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/CounterEvent/CounterEventFlushCommand.cs @@ -0,0 +1,21 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.CounterEvent +{ + struct CounterEventFlushCommand : IGALCommand + { + public CommandType CommandType => CommandType.CounterEventFlush; + private TableRef<ThreadedCounterEvent> _event; + + public void Set(TableRef<ThreadedCounterEvent> evt) + { + _event = evt; + } + + public static void Run(ref CounterEventFlushCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + command._event.Get(threaded).Base.Flush(); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/DispatchComputeCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/DispatchComputeCommand.cs new file mode 100644 index 00000000..26c88062 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/DispatchComputeCommand.cs @@ -0,0 +1,22 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct DispatchComputeCommand : IGALCommand + { + public CommandType CommandType => CommandType.DispatchCompute; + private int _groupsX; + private int _groupsY; + private int _groupsZ; + + public void Set(int groupsX, int groupsY, int groupsZ) + { + _groupsX = groupsX; + _groupsY = groupsY; + _groupsZ = groupsZ; + } + + public static void Run(ref DispatchComputeCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.DispatchCompute(command._groupsX, command._groupsY, command._groupsZ); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawCommand.cs new file mode 100644 index 00000000..ff27303a --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawCommand.cs @@ -0,0 +1,26 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct DrawIndexedCommand : IGALCommand + { + public CommandType CommandType => CommandType.DrawIndexed; + private int _indexCount; + private int _instanceCount; + private int _firstIndex; + private int _firstVertex; + private int _firstInstance; + + public void Set(int indexCount, int instanceCount, int firstIndex, int firstVertex, int firstInstance) + { + _indexCount = indexCount; + _instanceCount = instanceCount; + _firstIndex = firstIndex; + _firstVertex = firstVertex; + _firstInstance = firstInstance; + } + + public static void Run(ref DrawIndexedCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.DrawIndexed(command._indexCount, command._instanceCount, command._firstIndex, command._firstVertex, command._firstInstance); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedCommand.cs new file mode 100644 index 00000000..fc84819a --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedCommand.cs @@ -0,0 +1,24 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct DrawCommand : IGALCommand + { + public CommandType CommandType => CommandType.Draw; + private int _vertexCount; + private int _instanceCount; + private int _firstVertex; + private int _firstInstance; + + public void Set(int vertexCount, int instanceCount, int firstVertex, int firstInstance) + { + _vertexCount = vertexCount; + _instanceCount = instanceCount; + _firstVertex = firstVertex; + _firstInstance = firstInstance; + } + + public static void Run(ref DrawCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.Draw(command._vertexCount, command._instanceCount, command._firstVertex, command._firstInstance); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/EndHostConditionalRenderingCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/EndHostConditionalRenderingCommand.cs new file mode 100644 index 00000000..e0edd9ab --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/EndHostConditionalRenderingCommand.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct EndHostConditionalRenderingCommand : IGALCommand + { + public CommandType CommandType => CommandType.EndHostConditionalRendering; + + public static void Run(IRenderer renderer) + { + renderer.Pipeline.EndHostConditionalRendering(); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/EndTransformFeedbackCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/EndTransformFeedbackCommand.cs new file mode 100644 index 00000000..561996e3 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/EndTransformFeedbackCommand.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct EndTransformFeedbackCommand : IGALCommand + { + public CommandType CommandType => CommandType.EndTransformFeedback; + + public static void Run(ref EndTransformFeedbackCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.EndTransformFeedback(); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/IGALCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/IGALCommand.cs new file mode 100644 index 00000000..5fb04c80 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/IGALCommand.cs @@ -0,0 +1,7 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + interface IGALCommand + { + CommandType CommandType { get; } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/MultiDrawIndexedIndirectCountCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/MultiDrawIndexedIndirectCountCommand.cs new file mode 100644 index 00000000..6798f8cc --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/MultiDrawIndexedIndirectCountCommand.cs @@ -0,0 +1,29 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct MultiDrawIndexedIndirectCountCommand : IGALCommand + { + public CommandType CommandType => CommandType.MultiDrawIndexedIndirectCount; + private BufferRange _indirectBuffer; + private BufferRange _parameterBuffer; + private int _maxDrawCount; + private int _stride; + + public void Set(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride) + { + _indirectBuffer = indirectBuffer; + _parameterBuffer = parameterBuffer; + _maxDrawCount = maxDrawCount; + _stride = stride; + } + + public static void Run(ref MultiDrawIndexedIndirectCountCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.MultiDrawIndexedIndirectCount( + threaded.Buffers.MapBufferRange(command._indirectBuffer), + threaded.Buffers.MapBufferRange(command._parameterBuffer), + command._maxDrawCount, + command._stride + ); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/MultiDrawIndirectCountCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/MultiDrawIndirectCountCommand.cs new file mode 100644 index 00000000..7a9d07f3 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/MultiDrawIndirectCountCommand.cs @@ -0,0 +1,29 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct MultiDrawIndirectCountCommand : IGALCommand + { + public CommandType CommandType => CommandType.MultiDrawIndirectCount; + private BufferRange _indirectBuffer; + private BufferRange _parameterBuffer; + private int _maxDrawCount; + private int _stride; + + public void Set(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride) + { + _indirectBuffer = indirectBuffer; + _parameterBuffer = parameterBuffer; + _maxDrawCount = maxDrawCount; + _stride = stride; + } + + public static void Run(ref MultiDrawIndirectCountCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.MultiDrawIndirectCount( + threaded.Buffers.MapBufferRange(command._indirectBuffer), + threaded.Buffers.MapBufferRange(command._parameterBuffer), + command._maxDrawCount, + command._stride + ); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Program/ProgramCheckLinkCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Program/ProgramCheckLinkCommand.cs new file mode 100644 index 00000000..7ae887f4 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Program/ProgramCheckLinkCommand.cs @@ -0,0 +1,27 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Program +{ + struct ProgramCheckLinkCommand : IGALCommand + { + public CommandType CommandType => CommandType.ProgramCheckLink; + private TableRef<ThreadedProgram> _program; + private bool _blocking; + private TableRef<ResultBox<ProgramLinkStatus>> _result; + + public void Set(TableRef<ThreadedProgram> program, bool blocking, TableRef<ResultBox<ProgramLinkStatus>> result) + { + _program = program; + _blocking = blocking; + _result = result; + } + + public static void Run(ref ProgramCheckLinkCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + ProgramLinkStatus result = command._program.Get(threaded).Base.CheckProgramLink(command._blocking); + + command._result.Get(threaded).Result = result; + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Program/ProgramDisposeCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Program/ProgramDisposeCommand.cs new file mode 100644 index 00000000..e614c392 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Program/ProgramDisposeCommand.cs @@ -0,0 +1,21 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Program +{ + struct ProgramDisposeCommand : IGALCommand + { + public CommandType CommandType => CommandType.ProgramDispose; + private TableRef<ThreadedProgram> _program; + + public void Set(TableRef<ThreadedProgram> program) + { + _program = program; + } + + public static void Run(ref ProgramDisposeCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + command._program.Get(threaded).Base.Dispose(); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Program/ProgramGetBinaryCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Program/ProgramGetBinaryCommand.cs new file mode 100644 index 00000000..92c0a6d6 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Program/ProgramGetBinaryCommand.cs @@ -0,0 +1,25 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Program +{ + struct ProgramGetBinaryCommand : IGALCommand + { + public CommandType CommandType => CommandType.ProgramGetBinary; + private TableRef<ThreadedProgram> _program; + private TableRef<ResultBox<byte[]>> _result; + + public void Set(TableRef<ThreadedProgram> program, TableRef<ResultBox<byte[]>> result) + { + _program = program; + _result = result; + } + + public static void Run(ref ProgramGetBinaryCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + byte[] result = command._program.Get(threaded).Base.GetBinary(); + + command._result.Get(threaded).Result = result; + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/ActionCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/ActionCommand.cs new file mode 100644 index 00000000..07e55c96 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/ActionCommand.cs @@ -0,0 +1,21 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using System; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer +{ + struct ActionCommand : IGALCommand + { + public CommandType CommandType => CommandType.Action; + private TableRef<Action> _action; + + public void Set(TableRef<Action> action) + { + _action = action; + } + + public static void Run(ref ActionCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + command._action.Get(threaded)(); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CompileShaderCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CompileShaderCommand.cs new file mode 100644 index 00000000..2bd9725d --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CompileShaderCommand.cs @@ -0,0 +1,22 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer +{ + struct CompileShaderCommand : IGALCommand + { + public CommandType CommandType => CommandType.CompileShader; + private TableRef<ThreadedShader> _shader; + + public void Set(TableRef<ThreadedShader> shader) + { + _shader = shader; + } + + public static void Run(ref CompileShaderCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + ThreadedShader shader = command._shader.Get(threaded); + shader.EnsureCreated(); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateBufferCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateBufferCommand.cs new file mode 100644 index 00000000..4d1cbb28 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateBufferCommand.cs @@ -0,0 +1,23 @@ +using Ryujinx.Graphics.GAL.Multithreading.Resources; +using Ryujinx.Graphics.Shader; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer +{ + struct CreateBufferCommand : IGALCommand + { + public CommandType CommandType => CommandType.CreateBuffer; + private BufferHandle _threadedHandle; + private int _size; + + public void Set(BufferHandle threadedHandle, int size) + { + _threadedHandle = threadedHandle; + _size = size; + } + + public static void Run(ref CreateBufferCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + threaded.Buffers.AssignBuffer(command._threadedHandle, renderer.CreateBuffer(command._size)); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateProgramCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateProgramCommand.cs new file mode 100644 index 00000000..e24505e5 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateProgramCommand.cs @@ -0,0 +1,28 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources.Programs; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer +{ + struct CreateProgramCommand : IGALCommand + { + public CommandType CommandType => CommandType.CreateProgram; + private TableRef<IProgramRequest> _request; + + public void Set(TableRef<IProgramRequest> request) + { + _request = request; + } + + public static void Run(ref CreateProgramCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + IProgramRequest request = command._request.Get(threaded); + + if (request.Threaded.Base == null) + { + request.Threaded.Base = request.Create(renderer); + } + + threaded.Programs.ProcessQueue(); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateSamplerCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateSamplerCommand.cs new file mode 100644 index 00000000..bca98cfb --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateSamplerCommand.cs @@ -0,0 +1,23 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer +{ + struct CreateSamplerCommand : IGALCommand + { + public CommandType CommandType => CommandType.CreateSampler; + private TableRef<ThreadedSampler> _sampler; + private SamplerCreateInfo _info; + + public void Set(TableRef<ThreadedSampler> sampler, SamplerCreateInfo info) + { + _sampler = sampler; + _info = info; + } + + public static void Run(ref CreateSamplerCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + command._sampler.Get(threaded).Base = renderer.CreateSampler(command._info); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateSyncCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateSyncCommand.cs new file mode 100644 index 00000000..2e23760e --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateSyncCommand.cs @@ -0,0 +1,20 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer +{ + struct CreateSyncCommand : IGALCommand + { + public CommandType CommandType => CommandType.CreateSync; + private ulong _id; + + public void Set(ulong id) + { + _id = id; + } + + public static void Run(ref CreateSyncCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.CreateSync(command._id); + + threaded.Sync.AssignSync(command._id); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateTextureCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateTextureCommand.cs new file mode 100644 index 00000000..f9240125 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateTextureCommand.cs @@ -0,0 +1,25 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer +{ + struct CreateTextureCommand : IGALCommand + { + public CommandType CommandType => CommandType.CreateTexture; + private TableRef<ThreadedTexture> _texture; + private TextureCreateInfo _info; + private float _scale; + + public void Set(TableRef<ThreadedTexture> texture, TextureCreateInfo info, float scale) + { + _texture = texture; + _info = info; + _scale = scale; + } + + public static void Run(ref CreateTextureCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + command._texture.Get(threaded).Base = renderer.CreateTexture(command._info, command._scale); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/GetCapabilitiesCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/GetCapabilitiesCommand.cs new file mode 100644 index 00000000..102ed9da --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/GetCapabilitiesCommand.cs @@ -0,0 +1,20 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer +{ + struct GetCapabilitiesCommand : IGALCommand + { + public CommandType CommandType => CommandType.GetCapabilities; + private TableRef<ResultBox<Capabilities>> _result; + + public void Set(TableRef<ResultBox<Capabilities>> result) + { + _result = result; + } + + public static void Run(ref GetCapabilitiesCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + command._result.Get(threaded).Result = renderer.GetCapabilities(); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/PreFrameCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/PreFrameCommand.cs new file mode 100644 index 00000000..67cafd18 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/PreFrameCommand.cs @@ -0,0 +1,14 @@ +using System; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer +{ + struct PreFrameCommand : IGALCommand + { + public CommandType CommandType => CommandType.PreFrame; + + public static void Run(ref PreFrameCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.PreFrame(); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/ReportCounterCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/ReportCounterCommand.cs new file mode 100644 index 00000000..d477f235 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/ReportCounterCommand.cs @@ -0,0 +1,30 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; +using System; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer +{ + struct ReportCounterCommand : IGALCommand + { + public CommandType CommandType => CommandType.ReportCounter; + private TableRef<ThreadedCounterEvent> _event; + private CounterType _type; + private TableRef<EventHandler<ulong>> _resultHandler; + private bool _hostReserved; + + public void Set(TableRef<ThreadedCounterEvent> evt, CounterType type, TableRef<EventHandler<ulong>> resultHandler, bool hostReserved) + { + _event = evt; + _type = type; + _resultHandler = resultHandler; + _hostReserved = hostReserved; + } + + public static void Run(ref ReportCounterCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + ThreadedCounterEvent evt = command._event.Get(threaded); + + evt.Create(renderer, command._type, command._resultHandler.Get(threaded), command._hostReserved); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/ResetCounterCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/ResetCounterCommand.cs new file mode 100644 index 00000000..2835bf31 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/ResetCounterCommand.cs @@ -0,0 +1,18 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer +{ + struct ResetCounterCommand : IGALCommand + { + public CommandType CommandType => CommandType.ResetCounter; + private CounterType _type; + + public void Set(CounterType type) + { + _type = type; + } + + public static void Run(ref ResetCounterCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.ResetCounter(command._type); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/UpdateCountersCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/UpdateCountersCommand.cs new file mode 100644 index 00000000..f28bf080 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/UpdateCountersCommand.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer +{ + struct UpdateCountersCommand : IGALCommand + { + public CommandType CommandType => CommandType.UpdateCounters; + + public static void Run(ref UpdateCountersCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.UpdateCounters(); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Sampler/SamplerDisposeCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Sampler/SamplerDisposeCommand.cs new file mode 100644 index 00000000..8f4dfb7e --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Sampler/SamplerDisposeCommand.cs @@ -0,0 +1,21 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Sampler +{ + struct SamplerDisposeCommand : IGALCommand + { + public CommandType CommandType => CommandType.SamplerDispose; + private TableRef<ThreadedSampler> _sampler; + + public void Set(TableRef<ThreadedSampler> sampler) + { + _sampler = sampler; + } + + public static void Run(ref SamplerDisposeCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + command._sampler.Get(threaded).Base.Dispose(); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetAlphaTestCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetAlphaTestCommand.cs new file mode 100644 index 00000000..89379387 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetAlphaTestCommand.cs @@ -0,0 +1,22 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetAlphaTestCommand : IGALCommand + { + public CommandType CommandType => CommandType.SetAlphaTest; + private bool _enable; + private float _reference; + private CompareOp _op; + + public void Set(bool enable, float reference, CompareOp op) + { + _enable = enable; + _reference = reference; + _op = op; + } + + public static void Run(ref SetAlphaTestCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.SetAlphaTest(command._enable, command._reference, command._op); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetBlendStateCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetBlendStateCommand.cs new file mode 100644 index 00000000..6cc4894e --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetBlendStateCommand.cs @@ -0,0 +1,20 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetBlendStateCommand : IGALCommand + { + public CommandType CommandType => CommandType.SetBlendState; + private int _index; + private BlendDescriptor _blend; + + public void Set(int index, BlendDescriptor blend) + { + _index = index; + _blend = blend; + } + + public static void Run(ref SetBlendStateCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.SetBlendState(command._index, command._blend); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthBiasCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthBiasCommand.cs new file mode 100644 index 00000000..352242a3 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthBiasCommand.cs @@ -0,0 +1,24 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetDepthBiasCommand : IGALCommand + { + public CommandType CommandType => CommandType.SetDepthBias; + private PolygonModeMask _enables; + private float _factor; + private float _units; + private float _clamp; + + public void Set(PolygonModeMask enables, float factor, float units, float clamp) + { + _enables = enables; + _factor = factor; + _units = units; + _clamp = clamp; + } + + public static void Run(ref SetDepthBiasCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.SetDepthBias(command._enables, command._factor, command._units, command._clamp); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthClampCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthClampCommand.cs new file mode 100644 index 00000000..21c8f3e6 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthClampCommand.cs @@ -0,0 +1,18 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetDepthClampCommand : IGALCommand + { + public CommandType CommandType => CommandType.SetDepthClamp; + private bool _clamp; + + public void Set(bool clamp) + { + _clamp = clamp; + } + + public static void Run(ref SetDepthClampCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.SetDepthClamp(command._clamp); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthModeCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthModeCommand.cs new file mode 100644 index 00000000..28c36be8 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthModeCommand.cs @@ -0,0 +1,18 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetDepthModeCommand : IGALCommand + { + public CommandType CommandType => CommandType.SetDepthMode; + private DepthMode _mode; + + public void Set(DepthMode mode) + { + _mode = mode; + } + + public static void Run(ref SetDepthModeCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.SetDepthMode(command._mode); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthTestCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthTestCommand.cs new file mode 100644 index 00000000..585d3e8b --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthTestCommand.cs @@ -0,0 +1,18 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetDepthTestCommand : IGALCommand + { + public CommandType CommandType => CommandType.SetDepthTest; + private DepthTestDescriptor _depthTest; + + public void Set(DepthTestDescriptor depthTest) + { + _depthTest = depthTest; + } + + public static void Run(ref SetDepthTestCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.SetDepthTest(command._depthTest); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetFaceCullingCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetFaceCullingCommand.cs new file mode 100644 index 00000000..2a2b41ca --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetFaceCullingCommand.cs @@ -0,0 +1,20 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetFaceCullingCommand : IGALCommand + { + public CommandType CommandType => CommandType.SetFaceCulling; + private bool _enable; + private Face _face; + + public void Set(bool enable, Face face) + { + _enable = enable; + _face = face; + } + + public static void Run(ref SetFaceCullingCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.SetFaceCulling(command._enable, command._face); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetFrontFaceCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetFrontFaceCommand.cs new file mode 100644 index 00000000..a415237f --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetFrontFaceCommand.cs @@ -0,0 +1,18 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetFrontFaceCommand : IGALCommand + { + public CommandType CommandType => CommandType.SetFrontFace; + private FrontFace _frontFace; + + public void Set(FrontFace frontFace) + { + _frontFace = frontFace; + } + + public static void Run(ref SetFrontFaceCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.SetFrontFace(command._frontFace); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetImageCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetImageCommand.cs new file mode 100644 index 00000000..4223a621 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetImageCommand.cs @@ -0,0 +1,25 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetImageCommand : IGALCommand + { + public CommandType CommandType => CommandType.SetImage; + private int _binding; + private TableRef<ITexture> _texture; + private Format _imageFormat; + + public void Set(int binding, TableRef<ITexture> texture, Format imageFormat) + { + _binding = binding; + _texture = texture; + _imageFormat = imageFormat; + } + + public static void Run(ref SetImageCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.SetImage(command._binding, command._texture.GetAs<ThreadedTexture>(threaded)?.Base, command._imageFormat); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetIndexBufferCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetIndexBufferCommand.cs new file mode 100644 index 00000000..753e21f9 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetIndexBufferCommand.cs @@ -0,0 +1,21 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetIndexBufferCommand : IGALCommand + { + public CommandType CommandType => CommandType.SetIndexBuffer; + private BufferRange _buffer; + private IndexType _type; + + public void Set(BufferRange buffer, IndexType type) + { + _buffer = buffer; + _type = type; + } + + public static void Run(ref SetIndexBufferCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + BufferRange range = threaded.Buffers.MapBufferRange(command._buffer); + renderer.Pipeline.SetIndexBuffer(range, command._type); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetLineParametersCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetLineParametersCommand.cs new file mode 100644 index 00000000..7fd2e5b1 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetLineParametersCommand.cs @@ -0,0 +1,20 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetLineParametersCommand : IGALCommand + { + public CommandType CommandType => CommandType.SetLineParameters; + private float _width; + private bool _smooth; + + public void Set(float width, bool smooth) + { + _width = width; + _smooth = smooth; + } + + public static void Run(ref SetLineParametersCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.SetLineParameters(command._width, command._smooth); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetLogicOpStateCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetLogicOpStateCommand.cs new file mode 100644 index 00000000..253ef138 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetLogicOpStateCommand.cs @@ -0,0 +1,20 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetLogicOpStateCommand : IGALCommand + { + public CommandType CommandType => CommandType.SetLogicOpState; + private bool _enable; + private LogicalOp _op; + + public void Set(bool enable, LogicalOp op) + { + _enable = enable; + _op = op; + } + + public static void Run(ref SetLogicOpStateCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.SetLogicOpState(command._enable, command._op); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPointParametersCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPointParametersCommand.cs new file mode 100644 index 00000000..37833a0e --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPointParametersCommand.cs @@ -0,0 +1,24 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetPointParametersCommand : IGALCommand + { + public CommandType CommandType => CommandType.SetPointParameters; + private float _size; + private bool _isProgramPointSize; + private bool _enablePointSprite; + private Origin _origin; + + public void Set(float size, bool isProgramPointSize, bool enablePointSprite, Origin origin) + { + _size = size; + _isProgramPointSize = isProgramPointSize; + _enablePointSprite = enablePointSprite; + _origin = origin; + } + + public static void Run(ref SetPointParametersCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.SetPointParameters(command._size, command._isProgramPointSize, command._enablePointSprite, command._origin); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPrimitiveRestartCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPrimitiveRestartCommand.cs new file mode 100644 index 00000000..e5f6ecf3 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPrimitiveRestartCommand.cs @@ -0,0 +1,20 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetPrimitiveRestartCommand : IGALCommand + { + public CommandType CommandType => CommandType.SetPrimitiveRestart; + private bool _enable; + private int _index; + + public void Set(bool enable, int index) + { + _enable = enable; + _index = index; + } + + public static void Run(ref SetPrimitiveRestartCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.SetPrimitiveRestart(command._enable, command._index); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPrimitiveTopologyCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPrimitiveTopologyCommand.cs new file mode 100644 index 00000000..0bf29260 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPrimitiveTopologyCommand.cs @@ -0,0 +1,18 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetPrimitiveTopologyCommand : IGALCommand + { + public CommandType CommandType => CommandType.SetPrimitiveTopology; + private PrimitiveTopology _topology; + + public void Set(PrimitiveTopology topology) + { + _topology = topology; + } + + public static void Run(ref SetPrimitiveTopologyCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.SetPrimitiveTopology(command._topology); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetProgramCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetProgramCommand.cs new file mode 100644 index 00000000..c35d9c1f --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetProgramCommand.cs @@ -0,0 +1,25 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetProgramCommand : IGALCommand + { + public CommandType CommandType => CommandType.SetProgram; + private TableRef<IProgram> _program; + + public void Set(TableRef<IProgram> program) + { + _program = program; + } + + public static void Run(ref SetProgramCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + ThreadedProgram program = command._program.GetAs<ThreadedProgram>(threaded); + + threaded.Programs.WaitForProgram(program); + + renderer.Pipeline.SetProgram(program.Base); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRasterizerDiscardCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRasterizerDiscardCommand.cs new file mode 100644 index 00000000..4f92ce99 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRasterizerDiscardCommand.cs @@ -0,0 +1,18 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetRasterizerDiscardCommand : IGALCommand + { + public CommandType CommandType => CommandType.SetRasterizerDiscard; + private bool _discard; + + public void Set(bool discard) + { + _discard = discard; + } + + public static void Run(ref SetRasterizerDiscardCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.SetRasterizerDiscard(command._discard); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetColorMasksCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetColorMasksCommand.cs new file mode 100644 index 00000000..1e75ddb8 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetColorMasksCommand.cs @@ -0,0 +1,23 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using System; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetRenderTargetColorMasksCommand : IGALCommand + { + public CommandType CommandType => CommandType.SetRenderTargetColorMasks; + private SpanRef<uint> _componentMask; + + public void Set(SpanRef<uint> componentMask) + { + _componentMask = componentMask; + } + + public static void Run(ref SetRenderTargetColorMasksCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + ReadOnlySpan<uint> componentMask = command._componentMask.Get(threaded); + renderer.Pipeline.SetRenderTargetColorMasks(componentMask); + command._componentMask.Dispose(threaded); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetScaleCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetScaleCommand.cs new file mode 100644 index 00000000..a97a63db --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetScaleCommand.cs @@ -0,0 +1,18 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetRenderTargetScaleCommand : IGALCommand + { + public CommandType CommandType => CommandType.SetRenderTargetScale; + private float _scale; + + public void Set(float scale) + { + _scale = scale; + } + + public static void Run(ref SetRenderTargetScaleCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.SetRenderTargetScale(command._scale); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetsCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetsCommand.cs new file mode 100644 index 00000000..30f798dd --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetsCommand.cs @@ -0,0 +1,24 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; +using System.Linq; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetRenderTargetsCommand : IGALCommand + { + public CommandType CommandType => CommandType.SetRenderTargets; + private TableRef<ITexture[]> _colors; + private TableRef<ITexture> _depthStencil; + + public void Set(TableRef<ITexture[]> colors, TableRef<ITexture> depthStencil) + { + _colors = colors; + _depthStencil = depthStencil; + } + + public static void Run(ref SetRenderTargetsCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.SetRenderTargets(command._colors.Get(threaded).Select(color => ((ThreadedTexture)color)?.Base).ToArray(), command._depthStencil.GetAs<ThreadedTexture>(threaded)?.Base); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetSamplerCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetSamplerCommand.cs new file mode 100644 index 00000000..f3be24db --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetSamplerCommand.cs @@ -0,0 +1,23 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetSamplerCommand : IGALCommand + { + public CommandType CommandType => CommandType.SetSampler; + private int _index; + private TableRef<ISampler> _sampler; + + public void Set(int index, TableRef<ISampler> sampler) + { + _index = index; + _sampler = sampler; + } + + public static void Run(ref SetSamplerCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.SetSampler(command._index, command._sampler.GetAs<ThreadedSampler>(threaded)?.Base); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetScissorCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetScissorCommand.cs new file mode 100644 index 00000000..6c95d096 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetScissorCommand.cs @@ -0,0 +1,28 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetScissorCommand : IGALCommand + { + public CommandType CommandType => CommandType.SetScissor; + private int _index; + private bool _enable; + private int _x; + private int _y; + private int _width; + private int _height; + + public void Set(int index, bool enable, int x, int y, int width, int height) + { + _index = index; + _enable = enable; + _x = x; + _y = y; + _width = width; + _height = height; + } + + public static void Run(ref SetScissorCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.SetScissor(command._index, command._enable, command._x, command._y, command._width, command._height); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetStencilTestCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetStencilTestCommand.cs new file mode 100644 index 00000000..cc5db4df --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetStencilTestCommand.cs @@ -0,0 +1,18 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetStencilTestCommand : IGALCommand + { + public CommandType CommandType => CommandType.SetStencilTest; + private StencilTestDescriptor _stencilTest; + + public void Set(StencilTestDescriptor stencilTest) + { + _stencilTest = stencilTest; + } + + public static void Run(ref SetStencilTestCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.SetStencilTest(command._stencilTest); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetStorageBuffersCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetStorageBuffersCommand.cs new file mode 100644 index 00000000..c2963373 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetStorageBuffersCommand.cs @@ -0,0 +1,25 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using System; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetStorageBuffersCommand : IGALCommand + { + public CommandType CommandType => CommandType.SetStorageBuffers; + private int _first; + private SpanRef<BufferRange> _buffers; + + public void Set(int first, SpanRef<BufferRange> buffers) + { + _first = first; + _buffers = buffers; + } + + public static void Run(ref SetStorageBuffersCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + Span<BufferRange> buffers = command._buffers.Get(threaded); + renderer.Pipeline.SetStorageBuffers(command._first, threaded.Buffers.MapBufferRanges(buffers)); + command._buffers.Dispose(threaded); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetTextureCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetTextureCommand.cs new file mode 100644 index 00000000..e86f512b --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetTextureCommand.cs @@ -0,0 +1,23 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetTextureCommand : IGALCommand + { + public CommandType CommandType => CommandType.SetTexture; + private int _binding; + private TableRef<ITexture> _texture; + + public void Set(int binding, TableRef<ITexture> texture) + { + _binding = binding; + _texture = texture; + } + + public static void Run(ref SetTextureCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.SetTexture(command._binding, command._texture.GetAs<ThreadedTexture>(threaded)?.Base); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetTransformFeedbackBuffersCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetTransformFeedbackBuffersCommand.cs new file mode 100644 index 00000000..5125447c --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetTransformFeedbackBuffersCommand.cs @@ -0,0 +1,23 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using System; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetTransformFeedbackBuffersCommand : IGALCommand + { + public CommandType CommandType => CommandType.SetTransformFeedbackBuffers; + private SpanRef<BufferRange> _buffers; + + public void Set(SpanRef<BufferRange> buffers) + { + _buffers = buffers; + } + + public static void Run(ref SetTransformFeedbackBuffersCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + Span<BufferRange> buffers = command._buffers.Get(threaded); + renderer.Pipeline.SetTransformFeedbackBuffers(threaded.Buffers.MapBufferRanges(buffers)); + command._buffers.Dispose(threaded); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetUniformBuffersCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetUniformBuffersCommand.cs new file mode 100644 index 00000000..750d8dac --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetUniformBuffersCommand.cs @@ -0,0 +1,25 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using System; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetUniformBuffersCommand : IGALCommand + { + public CommandType CommandType => CommandType.SetUniformBuffers; + private int _first; + private SpanRef<BufferRange> _buffers; + + public void Set(int first, SpanRef<BufferRange> buffers) + { + _first = first; + _buffers = buffers; + } + + public static void Run(ref SetUniformBuffersCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + Span<BufferRange> buffers = command._buffers.Get(threaded); + renderer.Pipeline.SetUniformBuffers(command._first, threaded.Buffers.MapBufferRanges(buffers)); + command._buffers.Dispose(threaded); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetUserClipDistanceCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetUserClipDistanceCommand.cs new file mode 100644 index 00000000..f0f05779 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetUserClipDistanceCommand.cs @@ -0,0 +1,20 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetUserClipDistanceCommand : IGALCommand + { + public CommandType CommandType => CommandType.SetUserClipDistance; + private int _index; + private bool _enableClip; + + public void Set(int index, bool enableClip) + { + _index = index; + _enableClip = enableClip; + } + + public static void Run(ref SetUserClipDistanceCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.SetUserClipDistance(command._index, command._enableClip); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetVertexAttribsCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetVertexAttribsCommand.cs new file mode 100644 index 00000000..cbc313e9 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetVertexAttribsCommand.cs @@ -0,0 +1,23 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using System; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetVertexAttribsCommand : IGALCommand + { + public CommandType CommandType => CommandType.SetVertexAttribs; + private SpanRef<VertexAttribDescriptor> _vertexAttribs; + + public void Set(SpanRef<VertexAttribDescriptor> vertexAttribs) + { + _vertexAttribs = vertexAttribs; + } + + public static void Run(ref SetVertexAttribsCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + ReadOnlySpan<VertexAttribDescriptor> vertexAttribs = command._vertexAttribs.Get(threaded); + renderer.Pipeline.SetVertexAttribs(vertexAttribs); + command._vertexAttribs.Dispose(threaded); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetVertexBuffersCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetVertexBuffersCommand.cs new file mode 100644 index 00000000..bdaba2c9 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetVertexBuffersCommand.cs @@ -0,0 +1,24 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using System; +using System.Buffers; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetVertexBuffersCommand : IGALCommand + { + public CommandType CommandType => CommandType.SetVertexBuffers; + private SpanRef<VertexBufferDescriptor> _vertexBuffers; + + public void Set(SpanRef<VertexBufferDescriptor> vertexBuffers) + { + _vertexBuffers = vertexBuffers; + } + + public static void Run(ref SetVertexBuffersCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + Span<VertexBufferDescriptor> vertexBuffers = command._vertexBuffers.Get(threaded); + renderer.Pipeline.SetVertexBuffers(threaded.Buffers.MapBufferRanges(vertexBuffers)); + command._vertexBuffers.Dispose(threaded); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetViewportsCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetViewportsCommand.cs new file mode 100644 index 00000000..e11b00e8 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetViewportsCommand.cs @@ -0,0 +1,26 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using System; +using System.Buffers; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetViewportsCommand : IGALCommand + { + public CommandType CommandType => CommandType.SetViewports; + private int _first; + private SpanRef<Viewport> _viewports; + + public void Set(int first, SpanRef<Viewport> viewports) + { + _first = first; + _viewports = viewports; + } + + public static void Run(ref SetViewportsCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + ReadOnlySpan<Viewport> viewports = command._viewports.Get(threaded); + renderer.Pipeline.SetViewports(command._first, viewports); + command._viewports.Dispose(threaded); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Shader/ShaderDisposeCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Shader/ShaderDisposeCommand.cs new file mode 100644 index 00000000..ebb2c927 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Shader/ShaderDisposeCommand.cs @@ -0,0 +1,21 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Shader +{ + struct ShaderDisposeCommand : IGALCommand + { + public CommandType CommandType => CommandType.ShaderDispose; + private TableRef<ThreadedShader> _shader; + + public void Set(TableRef<ThreadedShader> shader) + { + _shader = shader; + } + + public static void Run(ref ShaderDisposeCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + command._shader.Get(threaded).Base.Dispose(); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToCommand.cs new file mode 100644 index 00000000..112c1fd1 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToCommand.cs @@ -0,0 +1,28 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture +{ + struct TextureCopyToCommand : IGALCommand + { + public CommandType CommandType => CommandType.TextureCopyTo; + private TableRef<ThreadedTexture> _texture; + private TableRef<ThreadedTexture> _destination; + private int _firstLayer; + private int _firstLevel; + + public void Set(TableRef<ThreadedTexture> texture, TableRef<ThreadedTexture> destination, int firstLayer, int firstLevel) + { + _texture = texture; + _destination = destination; + _firstLayer = firstLayer; + _firstLevel = firstLevel; + } + + public static void Run(ref TextureCopyToCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + ThreadedTexture source = command._texture.Get(threaded); + source.Base.CopyTo(command._destination.Get(threaded).Base, command._firstLayer, command._firstLevel); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToScaledCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToScaledCommand.cs new file mode 100644 index 00000000..11843361 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToScaledCommand.cs @@ -0,0 +1,30 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture +{ + struct TextureCopyToScaledCommand : IGALCommand + { + public CommandType CommandType => CommandType.TextureCopyToScaled; + private TableRef<ThreadedTexture> _texture; + private TableRef<ThreadedTexture> _destination; + private Extents2D _srcRegion; + private Extents2D _dstRegion; + private bool _linearFilter; + + public void Set(TableRef<ThreadedTexture> texture, TableRef<ThreadedTexture> destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter) + { + _texture = texture; + _destination = destination; + _srcRegion = srcRegion; + _dstRegion = dstRegion; + _linearFilter = linearFilter; + } + + public static void Run(ref TextureCopyToScaledCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + ThreadedTexture source = command._texture.Get(threaded); + source.Base.CopyTo(command._destination.Get(threaded).Base, command._srcRegion, command._dstRegion, command._linearFilter); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToSliceCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToSliceCommand.cs new file mode 100644 index 00000000..363edb00 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToSliceCommand.cs @@ -0,0 +1,32 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture +{ + struct TextureCopyToSliceCommand : IGALCommand + { + public CommandType CommandType => CommandType.TextureCopyToSlice; + private TableRef<ThreadedTexture> _texture; + private TableRef<ThreadedTexture> _destination; + private int _srcLayer; + private int _dstLayer; + private int _srcLevel; + private int _dstLevel; + + public void Set(TableRef<ThreadedTexture> texture, TableRef<ThreadedTexture> destination, int srcLayer, int dstLayer, int srcLevel, int dstLevel) + { + _texture = texture; + _destination = destination; + _srcLayer = srcLayer; + _dstLayer = dstLayer; + _srcLevel = srcLevel; + _dstLevel = dstLevel; + } + + public static void Run(ref TextureCopyToSliceCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + ThreadedTexture source = command._texture.Get(threaded); + source.Base.CopyTo(command._destination.Get(threaded).Base, command._srcLayer, command._dstLayer, command._srcLevel, command._dstLevel); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCreateViewCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCreateViewCommand.cs new file mode 100644 index 00000000..7c385407 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCreateViewCommand.cs @@ -0,0 +1,30 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture +{ + struct TextureCreateViewCommand : IGALCommand + { + public CommandType CommandType => CommandType.TextureCreateView; + private TableRef<ThreadedTexture> _texture; + private TableRef<ThreadedTexture> _destination; + private TextureCreateInfo _info; + private int _firstLayer; + private int _firstLevel; + + public void Set(TableRef<ThreadedTexture> texture, TableRef<ThreadedTexture> destination, TextureCreateInfo info, int firstLayer, int firstLevel) + { + _texture = texture; + _destination = destination; + _info = info; + _firstLayer = firstLayer; + _firstLevel = firstLevel; + } + + public static void Run(ref TextureCreateViewCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + ThreadedTexture source = command._texture.Get(threaded); + command._destination.Get(threaded).Base = source.Base.CreateView(command._info, command._firstLayer, command._firstLevel); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureGetDataCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureGetDataCommand.cs new file mode 100644 index 00000000..9e7d0c64 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureGetDataCommand.cs @@ -0,0 +1,26 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; +using System; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture +{ + struct TextureGetDataCommand : IGALCommand + { + public CommandType CommandType => CommandType.TextureGetData; + private TableRef<ThreadedTexture> _texture; + private TableRef<ResultBox<PinnedSpan<byte>>> _result; + + public void Set(TableRef<ThreadedTexture> texture, TableRef<ResultBox<PinnedSpan<byte>>> result) + { + _texture = texture; + _result = result; + } + + public static void Run(ref TextureGetDataCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + ReadOnlySpan<byte> result = command._texture.Get(threaded).Base.GetData(); + + command._result.Get(threaded).Result = new PinnedSpan<byte>(result); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureReleaseCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureReleaseCommand.cs new file mode 100644 index 00000000..591b2214 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureReleaseCommand.cs @@ -0,0 +1,21 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture +{ + struct TextureReleaseCommand : IGALCommand + { + public CommandType CommandType => CommandType.TextureRelease; + private TableRef<ThreadedTexture> _texture; + + public void Set(TableRef<ThreadedTexture> texture) + { + _texture = texture; + } + + public static void Run(ref TextureReleaseCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + command._texture.Get(threaded).Base.Release(); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataCommand.cs new file mode 100644 index 00000000..a8a6d274 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataCommand.cs @@ -0,0 +1,25 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; +using System; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture +{ + struct TextureSetDataCommand : IGALCommand + { + public CommandType CommandType => CommandType.TextureSetData; + private TableRef<ThreadedTexture> _texture; + private TableRef<byte[]> _data; + + public void Set(TableRef<ThreadedTexture> texture, TableRef<byte[]> data) + { + _texture = texture; + _data = data; + } + + public static void Run(ref TextureSetDataCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + ThreadedTexture texture = command._texture.Get(threaded); + texture.Base.SetData(new ReadOnlySpan<byte>(command._data.Get(threaded))); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceCommand.cs new file mode 100644 index 00000000..0179ff11 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceCommand.cs @@ -0,0 +1,29 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; +using System; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture +{ + struct TextureSetDataSliceCommand : IGALCommand + { + public CommandType CommandType => CommandType.TextureSetDataSlice; + private TableRef<ThreadedTexture> _texture; + private TableRef<byte[]> _data; + private int _layer; + private int _level; + + public void Set(TableRef<ThreadedTexture> texture, TableRef<byte[]> data, int layer, int level) + { + _texture = texture; + _data = data; + _layer = layer; + _level = level; + } + + public static void Run(ref TextureSetDataSliceCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + ThreadedTexture texture = command._texture.Get(threaded); + texture.Base.SetData(new ReadOnlySpan<byte>(command._data.Get(threaded)), command._layer, command._level); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetStorageCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetStorageCommand.cs new file mode 100644 index 00000000..f86a9c44 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetStorageCommand.cs @@ -0,0 +1,23 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture +{ + struct TextureSetStorageCommand : IGALCommand + { + public CommandType CommandType => CommandType.TextureSetStorage; + private TableRef<ThreadedTexture> _texture; + private BufferRange _storage; + + public void Set(TableRef<ThreadedTexture> texture, BufferRange storage) + { + _texture = texture; + _storage = storage; + } + + public static void Run(ref TextureSetStorageCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + command._texture.Get(threaded).Base.SetStorage(threaded.Buffers.MapBufferRange(command._storage)); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/TextureBarrierCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/TextureBarrierCommand.cs new file mode 100644 index 00000000..b0b46021 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/TextureBarrierCommand.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct TextureBarrierCommand : IGALCommand + { + public CommandType CommandType => CommandType.TextureBarrier; + + public static void Run(ref TextureBarrierCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.TextureBarrier(); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/TextureBarrierTiledCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/TextureBarrierTiledCommand.cs new file mode 100644 index 00000000..f92abe5b --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/TextureBarrierTiledCommand.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct TextureBarrierTiledCommand : IGALCommand + { + public CommandType CommandType => CommandType.TextureBarrierTiled; + + public static void Run(ref TextureBarrierTiledCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.TextureBarrierTiled(); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/TryHostConditionalRenderingCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/TryHostConditionalRenderingCommand.cs new file mode 100644 index 00000000..65e1748d --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/TryHostConditionalRenderingCommand.cs @@ -0,0 +1,25 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct TryHostConditionalRenderingCommand : IGALCommand + { + public CommandType CommandType => CommandType.TryHostConditionalRendering; + private TableRef<ThreadedCounterEvent> _value; + private ulong _compare; + private bool _isEqual; + + public void Set(TableRef<ThreadedCounterEvent> value, ulong compare, bool isEqual) + { + _value = value; + _compare = compare; + _isEqual = isEqual; + } + + public static void Run(ref TryHostConditionalRenderingCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.TryHostConditionalRendering(command._value.Get(threaded)?.Base, command._compare, command._isEqual); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/TryHostConditionalRenderingFlushCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/TryHostConditionalRenderingFlushCommand.cs new file mode 100644 index 00000000..29eb8dd4 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/TryHostConditionalRenderingFlushCommand.cs @@ -0,0 +1,25 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct TryHostConditionalRenderingFlushCommand : IGALCommand + { + public CommandType CommandType => CommandType.TryHostConditionalRenderingFlush; + private TableRef<ThreadedCounterEvent> _value; + private TableRef<ThreadedCounterEvent> _compare; + private bool _isEqual; + + public void Set(TableRef<ThreadedCounterEvent> value, TableRef<ThreadedCounterEvent> compare, bool isEqual) + { + _value = value; + _compare = compare; + _isEqual = isEqual; + } + + public static void Run(ref TryHostConditionalRenderingFlushCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.TryHostConditionalRendering(command._value.Get(threaded)?.Base, command._compare.Get(threaded)?.Base, command._isEqual); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/UpdateRenderScaleCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/UpdateRenderScaleCommand.cs new file mode 100644 index 00000000..fafb52a8 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/UpdateRenderScaleCommand.cs @@ -0,0 +1,28 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.Shader; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct UpdateRenderScaleCommand : IGALCommand + { + public CommandType CommandType => CommandType.UpdateRenderScale; + private ShaderStage _stage; + private SpanRef<float> _scales; + private int _textureCount; + private int _imageCount; + + public void Set(ShaderStage stage, SpanRef<float> scales, int textureCount, int imageCount) + { + _stage = stage; + _scales = scales; + _textureCount = textureCount; + _imageCount = imageCount; + } + + public static void Run(ref UpdateRenderScaleCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.UpdateRenderScale(command._stage, command._scales.Get(threaded), command._textureCount, command._imageCount); + command._scales.Dispose(threaded); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Window/WindowPresentCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Window/WindowPresentCommand.cs new file mode 100644 index 00000000..c4f3b553 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Window/WindowPresentCommand.cs @@ -0,0 +1,27 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; +using System; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Window +{ + struct WindowPresentCommand : IGALCommand + { + public CommandType CommandType => CommandType.WindowPresent; + private TableRef<ThreadedTexture> _texture; + private ImageCrop _crop; + private TableRef<Action> _swapBuffersCallback; + + public void Set(TableRef<ThreadedTexture> texture, ImageCrop crop, TableRef<Action> swapBuffersCallback) + { + _texture = texture; + _crop = crop; + _swapBuffersCallback = swapBuffersCallback; + } + + public static void Run(ref WindowPresentCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + threaded.SignalFrame(); + renderer.Window.Present(command._texture.Get(threaded)?.Base, command._crop, command._swapBuffersCallback.Get(threaded)); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Model/CircularSpanPool.cs b/Ryujinx.Graphics.GAL/Multithreading/Model/CircularSpanPool.cs new file mode 100644 index 00000000..4ea1a2c7 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Model/CircularSpanPool.cs @@ -0,0 +1,89 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading; + +namespace Ryujinx.Graphics.GAL.Multithreading.Model +{ + /// <summary> + /// A memory pool for passing through Span<T> resources with one producer and consumer. + /// Data is copied on creation to part of the pool, then that region is reserved until it is disposed by the consumer. + /// Similar to the command queue, this pool assumes that data is created and disposed in the same order. + /// </summary> + class CircularSpanPool + { + private ThreadedRenderer _renderer; + private byte[] _pool; + private int _size; + + private int _producerPtr; + private int _producerSkipPosition = -1; + private int _consumerPtr; + + public CircularSpanPool(ThreadedRenderer renderer, int size) + { + _renderer = renderer; + _size = size; + _pool = new byte[size]; + } + + public SpanRef<T> Insert<T>(ReadOnlySpan<T> data) where T : unmanaged + { + int size = data.Length * Unsafe.SizeOf<T>(); + + // Wrapping aware circular queue. + // If there's no space at the end of the pool for this span, we can't fragment it. + // So just loop back around to the start. Remember the last skipped position. + + bool wraparound = _producerPtr + size >= _size; + int index = wraparound ? 0 : _producerPtr; + + // _consumerPtr is from another thread, and we're taking it without a lock, so treat this as a snapshot in the past. + // We know that it will always be before or equal to the producer pointer, and it cannot pass it. + // This is enough to reason about if there is space in the queue for the data, even if we're checking against an outdated value. + + int consumer = _consumerPtr; + bool beforeConsumer = _producerPtr < consumer; + + if (size > _size - 1 || (wraparound && beforeConsumer) || ((index < consumer || wraparound) && index + size >= consumer)) + { + // Just get an array in the following situations: + // - The data is too large to fit in the pool. + // - A wraparound would happen but the consumer would be covered by it. + // - The producer would catch up to the consumer as a result. + + return new SpanRef<T>(_renderer, data.ToArray()); + } + + data.CopyTo(MemoryMarshal.Cast<byte, T>(new Span<byte>(_pool).Slice(index, size))); + + if (wraparound) + { + _producerSkipPosition = _producerPtr; + } + + _producerPtr = index + size; + + return new SpanRef<T>(data.Length); + } + + public Span<T> Get<T>(int length) where T : unmanaged + { + int size = length * Unsafe.SizeOf<T>(); + + if (_consumerPtr == Interlocked.CompareExchange(ref _producerSkipPosition, -1, _consumerPtr)) + { + _consumerPtr = 0; + } + + return MemoryMarshal.Cast<byte, T>(new Span<byte>(_pool).Slice(_consumerPtr, size)); + } + + public void Dispose<T>(int length) where T : unmanaged + { + int size = length * Unsafe.SizeOf<T>(); + + _consumerPtr = _consumerPtr + size; + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Model/PinnedSpan.cs b/Ryujinx.Graphics.GAL/Multithreading/Model/PinnedSpan.cs new file mode 100644 index 00000000..16e148c2 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Model/PinnedSpan.cs @@ -0,0 +1,23 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Ryujinx.Graphics.GAL.Multithreading.Model +{ + unsafe struct PinnedSpan<T> where T : unmanaged + { + private void* _ptr; + private int _size; + + public PinnedSpan(ReadOnlySpan<T> span) + { + _ptr = Unsafe.AsPointer(ref MemoryMarshal.GetReference(span)); + _size = span.Length; + } + + public ReadOnlySpan<T> Get() + { + return new ReadOnlySpan<T>(_ptr, _size * Unsafe.SizeOf<T>()); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Model/ResultBox.cs b/Ryujinx.Graphics.GAL/Multithreading/Model/ResultBox.cs new file mode 100644 index 00000000..7a0be785 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Model/ResultBox.cs @@ -0,0 +1,7 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Model +{ + public class ResultBox<T> + { + public T Result; + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Model/SpanRef.cs b/Ryujinx.Graphics.GAL/Multithreading/Model/SpanRef.cs new file mode 100644 index 00000000..7dbebc76 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Model/SpanRef.cs @@ -0,0 +1,39 @@ +using System; + +namespace Ryujinx.Graphics.GAL.Multithreading.Model +{ + struct SpanRef<T> where T : unmanaged + { + private int _packedLengthId; + + public SpanRef(ThreadedRenderer renderer, T[] data) + { + _packedLengthId = -(renderer.AddTableRef(data) + 1); + } + + public SpanRef(int length) + { + _packedLengthId = length; + } + + public Span<T> Get(ThreadedRenderer renderer) + { + if (_packedLengthId >= 0) + { + return renderer.SpanPool.Get<T>(_packedLengthId); + } + else + { + return new Span<T>((T[])renderer.RemoveTableRef(-(_packedLengthId + 1))); + } + } + + public void Dispose(ThreadedRenderer renderer) + { + if (_packedLengthId > 0) + { + renderer.SpanPool.Dispose<T>(_packedLengthId); + } + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Model/TableRef.cs b/Ryujinx.Graphics.GAL/Multithreading/Model/TableRef.cs new file mode 100644 index 00000000..166aa71a --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Model/TableRef.cs @@ -0,0 +1,22 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Model +{ + struct TableRef<T> + { + private int _index; + + public TableRef(ThreadedRenderer renderer, T reference) + { + _index = renderer.AddTableRef(reference); + } + + public T Get(ThreadedRenderer renderer) + { + return (T)renderer.RemoveTableRef(_index); + } + + public T2 GetAs<T2>(ThreadedRenderer renderer) where T2 : T + { + return (T2)renderer.RemoveTableRef(_index); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Resources/ProgramQueue.cs b/Ryujinx.Graphics.GAL/Multithreading/Resources/ProgramQueue.cs new file mode 100644 index 00000000..3f982d31 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Resources/ProgramQueue.cs @@ -0,0 +1,107 @@ +using Ryujinx.Graphics.GAL.Multithreading.Resources.Programs; +using System; +using System.Collections.Generic; +using System.Threading; + +namespace Ryujinx.Graphics.GAL.Multithreading.Resources +{ + /// <summary> + /// A structure handling multithreaded compilation for programs. + /// </summary> + class ProgramQueue + { + private const int MaxConcurrentCompilations = 8; + + private IRenderer _renderer; + + private Queue<IProgramRequest> _toCompile; + private List<ThreadedProgram> _inProgress; + + public ProgramQueue(IRenderer renderer) + { + _renderer = renderer; + + _toCompile = new Queue<IProgramRequest>(); + _inProgress = new List<ThreadedProgram>(); + } + + public void Add(IProgramRequest request) + { + lock (_toCompile) + { + _toCompile.Enqueue(request); + } + } + + public void ProcessQueue() + { + for (int i = 0; i < _inProgress.Count; i++) + { + ThreadedProgram program = _inProgress[i]; + + ProgramLinkStatus status = program.Base.CheckProgramLink(false); + + if (status != ProgramLinkStatus.Incomplete) + { + program.Compiled = true; + _inProgress.RemoveAt(i--); + } + } + + int freeSpace = MaxConcurrentCompilations - _inProgress.Count; + + for (int i = 0; i < freeSpace; i++) + { + // Begin compilation of some programs in the compile queue. + IProgramRequest program; + + lock (_toCompile) + { + if (!_toCompile.TryDequeue(out program)) + { + break; + } + } + + if (program.Threaded.Base != null) + { + ProgramLinkStatus status = program.Threaded.Base.CheckProgramLink(false); + + if (status != ProgramLinkStatus.Incomplete) + { + // This program is already compiled. Keep going through the queue. + program.Threaded.Compiled = true; + i--; + continue; + } + } + else + { + program.Threaded.Base = program.Create(_renderer); + } + + _inProgress.Add(program.Threaded); + } + } + + /// <summary> + /// Process the queue until the given program has finished compiling. + /// This will begin compilation of other programs on the queue as well. + /// </summary> + /// <param name="program">The program to wait for</param> + public void WaitForProgram(ThreadedProgram program) + { + Span<SpinWait> spinWait = stackalloc SpinWait[1]; + + while (!program.Compiled) + { + ProcessQueue(); + + if (!program.Compiled) + { + spinWait[0].SpinOnce(-1); + } + } + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/BinaryProgramRequest.cs b/Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/BinaryProgramRequest.cs new file mode 100644 index 00000000..96bfedf8 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/BinaryProgramRequest.cs @@ -0,0 +1,21 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Resources.Programs +{ + class BinaryProgramRequest : IProgramRequest + { + public ThreadedProgram Threaded { get; set; } + + private byte[] _data; + + public BinaryProgramRequest(ThreadedProgram program, byte[] data) + { + Threaded = program; + + _data = data; + } + + public IProgram Create(IRenderer renderer) + { + return renderer.LoadProgramBinary(_data); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/IProgramRequest.cs b/Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/IProgramRequest.cs new file mode 100644 index 00000000..cdbfe03c --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/IProgramRequest.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Resources.Programs +{ + interface IProgramRequest + { + ThreadedProgram Threaded { get; set; } + IProgram Create(IRenderer renderer); + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/SourceProgramRequest.cs b/Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/SourceProgramRequest.cs new file mode 100644 index 00000000..d40ce6a4 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/SourceProgramRequest.cs @@ -0,0 +1,32 @@ +using System.Linq; + +namespace Ryujinx.Graphics.GAL.Multithreading.Resources.Programs +{ + class SourceProgramRequest : IProgramRequest + { + public ThreadedProgram Threaded { get; set; } + + private IShader[] _shaders; + private TransformFeedbackDescriptor[] _transformFeedbackDescriptors; + + public SourceProgramRequest(ThreadedProgram program, IShader[] shaders, TransformFeedbackDescriptor[] transformFeedbackDescriptors) + { + Threaded = program; + + _shaders = shaders; + _transformFeedbackDescriptors = transformFeedbackDescriptors; + } + + public IProgram Create(IRenderer renderer) + { + IShader[] shaders = _shaders.Select(shader => + { + var threaded = (ThreadedShader)shader; + threaded?.EnsureCreated(); + return threaded?.Base; + }).ToArray(); + + return renderer.CreateProgram(shaders, _transformFeedbackDescriptors); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedCounterEvent.cs b/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedCounterEvent.cs new file mode 100644 index 00000000..4b7471d6 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedCounterEvent.cs @@ -0,0 +1,80 @@ +using Ryujinx.Graphics.GAL.Multithreading.Commands.CounterEvent; +using Ryujinx.Graphics.GAL.Multithreading.Model; +using System.Threading; + +namespace Ryujinx.Graphics.GAL.Multithreading.Resources +{ + class ThreadedCounterEvent : ICounterEvent + { + private ThreadedRenderer _renderer; + public ICounterEvent Base; + + public bool Invalid { get; set; } + + public CounterType Type { get; } + public bool ClearCounter { get; } + + private bool _reserved; + private int _createLock; + + public ThreadedCounterEvent(ThreadedRenderer renderer, CounterType type, bool clearCounter) + { + _renderer = renderer; + Type = type; + ClearCounter = clearCounter; + } + + private TableRef<T> Ref<T>(T reference) + { + return new TableRef<T>(_renderer, reference); + } + + public void Dispose() + { + _renderer.New<CounterEventDisposeCommand>().Set(Ref(this)); + _renderer.QueueCommand(); + } + + public void Flush() + { + ThreadedHelpers.SpinUntilNonNull(ref Base); + + Base.Flush(); + } + + public bool ReserveForHostAccess() + { + if (Base != null) + { + return Base.ReserveForHostAccess(); + } + else + { + bool result = true; + + // A very light lock, as this case is uncommon. + ThreadedHelpers.SpinUntilExchange(ref _createLock, 1, 0); + + if (Base != null) + { + result = Base.ReserveForHostAccess(); + } + else + { + _reserved = true; + } + + Volatile.Write(ref _createLock, 0); + + return result; + } + } + + public void Create(IRenderer renderer, CounterType type, System.EventHandler<ulong> eventHandler, bool hostReserved) + { + ThreadedHelpers.SpinUntilExchange(ref _createLock, 1, 0); + Base = renderer.ReportCounter(type, eventHandler, hostReserved || _reserved); + Volatile.Write(ref _createLock, 0); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedProgram.cs b/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedProgram.cs new file mode 100644 index 00000000..068d058e --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedProgram.cs @@ -0,0 +1,48 @@ +using Ryujinx.Graphics.GAL.Multithreading.Commands.Program; +using Ryujinx.Graphics.GAL.Multithreading.Model; + +namespace Ryujinx.Graphics.GAL.Multithreading.Resources +{ + class ThreadedProgram : IProgram + { + private ThreadedRenderer _renderer; + + public IProgram Base; + + internal bool Compiled; + + public ThreadedProgram(ThreadedRenderer renderer) + { + _renderer = renderer; + } + + private TableRef<T> Ref<T>(T reference) + { + return new TableRef<T>(_renderer, reference); + } + + public void Dispose() + { + _renderer.New<ProgramDisposeCommand>().Set(Ref(this)); + _renderer.QueueCommand(); + } + + public byte[] GetBinary() + { + ResultBox<byte[]> box = new ResultBox<byte[]>(); + _renderer.New<ProgramGetBinaryCommand>().Set(Ref(this), Ref(box)); + _renderer.InvokeCommand(); + + return box.Result; + } + + public ProgramLinkStatus CheckProgramLink(bool blocking) + { + ResultBox<ProgramLinkStatus> box = new ResultBox<ProgramLinkStatus>(); + _renderer.New<ProgramCheckLinkCommand>().Set(Ref(this), blocking, Ref(box)); + _renderer.InvokeCommand(); + + return box.Result; + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedSampler.cs b/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedSampler.cs new file mode 100644 index 00000000..d8de9a70 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedSampler.cs @@ -0,0 +1,22 @@ +using Ryujinx.Graphics.GAL.Multithreading.Commands.Sampler; +using Ryujinx.Graphics.GAL.Multithreading.Model; + +namespace Ryujinx.Graphics.GAL.Multithreading.Resources +{ + class ThreadedSampler : ISampler + { + private ThreadedRenderer _renderer; + public ISampler Base; + + public ThreadedSampler(ThreadedRenderer renderer) + { + _renderer = renderer; + } + + public void Dispose() + { + _renderer.New<SamplerDisposeCommand>().Set(new TableRef<ThreadedSampler>(_renderer, this)); + _renderer.QueueCommand(); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedShader.cs b/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedShader.cs new file mode 100644 index 00000000..dcbecf38 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedShader.cs @@ -0,0 +1,38 @@ +using Ryujinx.Graphics.GAL.Multithreading.Commands.Shader; +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.Shader; + +namespace Ryujinx.Graphics.GAL.Multithreading.Resources +{ + class ThreadedShader : IShader + { + private ThreadedRenderer _renderer; + private ShaderStage _stage; + private string _code; + + public IShader Base; + + public ThreadedShader(ThreadedRenderer renderer, ShaderStage stage, string code) + { + _renderer = renderer; + + _stage = stage; + _code = code; + } + + internal void EnsureCreated() + { + if (_code != null && Base == null) + { + Base = _renderer.BaseRenderer.CompileShader(_stage, _code); + _code = null; + } + } + + public void Dispose() + { + _renderer.New<ShaderDisposeCommand>().Set(new TableRef<ThreadedShader>(_renderer, this)); + _renderer.QueueCommand(); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTexture.cs b/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTexture.cs new file mode 100644 index 00000000..634d32fa --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTexture.cs @@ -0,0 +1,116 @@ +using Ryujinx.Graphics.GAL.Multithreading.Commands.Texture; +using Ryujinx.Graphics.GAL.Multithreading.Model; +using System; + +namespace Ryujinx.Graphics.GAL.Multithreading.Resources +{ + /// <summary> + /// Threaded representation of a texture. + /// </summary> + class ThreadedTexture : ITexture + { + private ThreadedRenderer _renderer; + private TextureCreateInfo _info; + public ITexture Base; + + public int Width => _info.Width; + + public int Height => _info.Height; + + public float ScaleFactor { get; } + + public ThreadedTexture(ThreadedRenderer renderer, TextureCreateInfo info, float scale) + { + _renderer = renderer; + _info = info; + ScaleFactor = scale; + } + + private TableRef<T> Ref<T>(T reference) + { + return new TableRef<T>(_renderer, reference); + } + + public void CopyTo(ITexture destination, int firstLayer, int firstLevel) + { + _renderer.New<TextureCopyToCommand>().Set(Ref(this), Ref((ThreadedTexture)destination), firstLayer, firstLevel); + _renderer.QueueCommand(); + } + + public void CopyTo(ITexture destination, int srcLayer, int dstLayer, int srcLevel, int dstLevel) + { + _renderer.New<TextureCopyToSliceCommand>().Set(Ref(this), Ref((ThreadedTexture)destination), srcLayer, dstLayer, srcLevel, dstLevel); + _renderer.QueueCommand(); + } + + public void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter) + { + ThreadedTexture dest = (ThreadedTexture)destination; + + if (_renderer.IsGpuThread()) + { + _renderer.New<TextureCopyToScaledCommand>().Set(Ref(this), Ref(dest), srcRegion, dstRegion, linearFilter); + _renderer.QueueCommand(); + } + else + { + // Scaled copy can happen on another thread for a res scale flush. + ThreadedHelpers.SpinUntilNonNull(ref Base); + ThreadedHelpers.SpinUntilNonNull(ref dest.Base); + + Base.CopyTo(dest.Base, srcRegion, dstRegion, linearFilter); + } + } + + public ITexture CreateView(TextureCreateInfo info, int firstLayer, int firstLevel) + { + ThreadedTexture newTex = new ThreadedTexture(_renderer, info, ScaleFactor); + _renderer.New<TextureCreateViewCommand>().Set(Ref(this), Ref(newTex), info, firstLayer, firstLevel); + _renderer.QueueCommand(); + + return newTex; + } + + public ReadOnlySpan<byte> GetData() + { + if (_renderer.IsGpuThread()) + { + ResultBox<PinnedSpan<byte>> box = new ResultBox<PinnedSpan<byte>>(); + _renderer.New<TextureGetDataCommand>().Set(Ref(this), Ref(box)); + _renderer.InvokeCommand(); + + return box.Result.Get(); + } + else + { + ThreadedHelpers.SpinUntilNonNull(ref Base); + + return Base.GetData(); + } + } + + public void SetData(ReadOnlySpan<byte> data) + { + _renderer.New<TextureSetDataCommand>().Set(Ref(this), Ref(data.ToArray())); + _renderer.QueueCommand(); + } + + public void SetData(ReadOnlySpan<byte> data, int layer, int level) + { + _renderer.New<TextureSetDataSliceCommand>().Set(Ref(this), Ref(data.ToArray()), layer, level); + _renderer.QueueCommand(); + } + + public void SetStorage(BufferRange buffer) + { + _renderer.New<TextureSetStorageCommand>().Set(Ref(this), buffer); + _renderer.QueueCommand(); + } + + public void Release() + { + _renderer.New<TextureReleaseCommand>().Set(Ref(this)); + _renderer.QueueCommand(); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/SyncMap.cs b/Ryujinx.Graphics.GAL/Multithreading/SyncMap.cs new file mode 100644 index 00000000..ae09e852 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/SyncMap.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using System.Threading; + +namespace Ryujinx.Graphics.GAL.Multithreading +{ + class SyncMap : IDisposable + { + private HashSet<ulong> _inFlight = new HashSet<ulong>(); + private AutoResetEvent _inFlightChanged = new AutoResetEvent(false); + + internal void CreateSyncHandle(ulong id) + { + lock (_inFlight) + { + _inFlight.Add(id); + } + } + + internal void AssignSync(ulong id) + { + lock (_inFlight) + { + _inFlight.Remove(id); + } + + _inFlightChanged.Set(); + } + + internal void WaitSyncAvailability(ulong id) + { + // Blocks until the handle is available. + + bool signal = false; + + while (true) + { + lock (_inFlight) + { + if (!_inFlight.Contains(id)) + { + break; + } + } + + _inFlightChanged.WaitOne(); + signal = true; + } + + if (signal) + { + // Signal other threads which might still be waiting. + _inFlightChanged.Set(); + } + } + + public void Dispose() + { + _inFlightChanged.Dispose(); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/ThreadedHelpers.cs b/Ryujinx.Graphics.GAL/Multithreading/ThreadedHelpers.cs new file mode 100644 index 00000000..7ddb19e6 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/ThreadedHelpers.cs @@ -0,0 +1,28 @@ +using System; +using System.Threading; + +namespace Ryujinx.Graphics.GAL.Multithreading +{ + static class ThreadedHelpers + { + public static void SpinUntilNonNull<T>(ref T obj) where T : class + { + Span<SpinWait> spinWait = stackalloc SpinWait[1]; + + while (obj == null) + { + spinWait[0].SpinOnce(-1); + } + } + + public static void SpinUntilExchange(ref int target, int value, int comparand) + { + Span<SpinWait> spinWait = stackalloc SpinWait[1]; + + while (Interlocked.CompareExchange(ref target, value, comparand) != comparand) + { + spinWait[0].SpinOnce(-1); + } + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs b/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs new file mode 100644 index 00000000..0f523481 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs @@ -0,0 +1,344 @@ +using Ryujinx.Graphics.GAL.Multithreading.Commands; +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; +using Ryujinx.Graphics.Shader; +using System; +using System.Linq; + +namespace Ryujinx.Graphics.GAL.Multithreading +{ + public class ThreadedPipeline : IPipeline + { + private ThreadedRenderer _renderer; + private IPipeline _impl; + + public ThreadedPipeline(ThreadedRenderer renderer, IPipeline impl) + { + _renderer = renderer; + _impl = impl; + } + + private TableRef<T> Ref<T>(T reference) + { + return new TableRef<T>(_renderer, reference); + } + + public void Barrier() + { + _renderer.New<BarrierCommand>(); + _renderer.QueueCommand(); + } + + public void BeginTransformFeedback(PrimitiveTopology topology) + { + _renderer.New<BeginTransformFeedbackCommand>().Set(topology); + _renderer.QueueCommand(); + } + + public void ClearBuffer(BufferHandle destination, int offset, int size, uint value) + { + _renderer.New<ClearBufferCommand>().Set(destination, offset, size, value); + _renderer.QueueCommand(); + } + + public void ClearRenderTargetColor(int index, uint componentMask, ColorF color) + { + _renderer.New<ClearRenderTargetColorCommand>().Set(index, componentMask, color); + _renderer.QueueCommand(); + } + + public void ClearRenderTargetDepthStencil(float depthValue, bool depthMask, int stencilValue, int stencilMask) + { + _renderer.New<ClearRenderTargetDepthStencilCommand>().Set(depthValue, depthMask, stencilValue, stencilMask); + _renderer.QueueCommand(); + } + + public void CommandBufferBarrier() + { + _renderer.New<CommandBufferBarrierCommand>(); + _renderer.QueueCommand(); + } + + public void CopyBuffer(BufferHandle source, BufferHandle destination, int srcOffset, int dstOffset, int size) + { + _renderer.New<CopyBufferCommand>().Set(source, destination, srcOffset, dstOffset, size); + _renderer.QueueCommand(); + } + + public void DispatchCompute(int groupsX, int groupsY, int groupsZ) + { + _renderer.New<DispatchComputeCommand>().Set(groupsX, groupsY, groupsZ); + _renderer.QueueCommand(); + } + + public void Draw(int vertexCount, int instanceCount, int firstVertex, int firstInstance) + { + _renderer.New<DrawCommand>().Set(vertexCount, instanceCount, firstVertex, firstInstance); + _renderer.QueueCommand(); + } + + public void DrawIndexed(int indexCount, int instanceCount, int firstIndex, int firstVertex, int firstInstance) + { + _renderer.New<DrawIndexedCommand>().Set(indexCount, instanceCount, firstIndex, firstVertex, firstInstance); + _renderer.QueueCommand(); + } + + public void EndHostConditionalRendering() + { + _renderer.New<EndHostConditionalRenderingCommand>(); + _renderer.QueueCommand(); + } + + public void EndTransformFeedback() + { + _renderer.New<EndTransformFeedbackCommand>(); + _renderer.QueueCommand(); + } + + public void MultiDrawIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride) + { + _renderer.New<MultiDrawIndirectCountCommand>().Set(indirectBuffer, parameterBuffer, maxDrawCount, stride); + _renderer.QueueCommand(); + } + + public void MultiDrawIndexedIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride) + { + _renderer.New<MultiDrawIndexedIndirectCountCommand>().Set(indirectBuffer, parameterBuffer, maxDrawCount, stride); + _renderer.QueueCommand(); + } + + public void SetAlphaTest(bool enable, float reference, CompareOp op) + { + _renderer.New<SetAlphaTestCommand>().Set(enable, reference, op); + _renderer.QueueCommand(); + } + + public void SetBlendState(int index, BlendDescriptor blend) + { + _renderer.New<SetBlendStateCommand>().Set(index, blend); + _renderer.QueueCommand(); + } + + public void SetDepthBias(PolygonModeMask enables, float factor, float units, float clamp) + { + _renderer.New<SetDepthBiasCommand>().Set(enables, factor, units, clamp); + _renderer.QueueCommand(); + } + + public void SetDepthClamp(bool clamp) + { + _renderer.New<SetDepthClampCommand>().Set(clamp); + _renderer.QueueCommand(); + } + + public void SetDepthMode(DepthMode mode) + { + _renderer.New<SetDepthModeCommand>().Set(mode); + _renderer.QueueCommand(); + } + + public void SetDepthTest(DepthTestDescriptor depthTest) + { + _renderer.New<SetDepthTestCommand>().Set(depthTest); + _renderer.QueueCommand(); + } + + public void SetFaceCulling(bool enable, Face face) + { + _renderer.New<SetFaceCullingCommand>().Set(enable, face); + _renderer.QueueCommand(); + } + + public void SetFrontFace(FrontFace frontFace) + { + _renderer.New<SetFrontFaceCommand>().Set(frontFace); + _renderer.QueueCommand(); + } + + public void SetImage(int binding, ITexture texture, Format imageFormat) + { + _renderer.New<SetImageCommand>().Set(binding, Ref(texture), imageFormat); + _renderer.QueueCommand(); + } + + public void SetIndexBuffer(BufferRange buffer, IndexType type) + { + _renderer.New<SetIndexBufferCommand>().Set(buffer, type); + _renderer.QueueCommand(); + } + + public void SetLineParameters(float width, bool smooth) + { + _renderer.New<SetLineParametersCommand>().Set(width, smooth); + _renderer.QueueCommand(); + } + + public void SetLogicOpState(bool enable, LogicalOp op) + { + _renderer.New<SetLogicOpStateCommand>().Set(enable, op); + _renderer.QueueCommand(); + } + + public void SetPointParameters(float size, bool isProgramPointSize, bool enablePointSprite, Origin origin) + { + _renderer.New<SetPointParametersCommand>().Set(size, isProgramPointSize, enablePointSprite, origin); + _renderer.QueueCommand(); + } + + public void SetPrimitiveRestart(bool enable, int index) + { + _renderer.New<SetPrimitiveRestartCommand>().Set(enable, index); + _renderer.QueueCommand(); + } + + public void SetPrimitiveTopology(PrimitiveTopology topology) + { + _renderer.New<SetPrimitiveTopologyCommand>().Set(topology); + _renderer.QueueCommand(); + } + + public void SetProgram(IProgram program) + { + _renderer.New<SetProgramCommand>().Set(Ref(program)); + _renderer.QueueCommand(); + } + + public void SetRasterizerDiscard(bool discard) + { + _renderer.New<SetRasterizerDiscardCommand>().Set(discard); + _renderer.QueueCommand(); + } + + public void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMask) + { + _renderer.New<SetRenderTargetColorMasksCommand>().Set(_renderer.CopySpan(componentMask)); + _renderer.QueueCommand(); + } + + public void SetRenderTargets(ITexture[] colors, ITexture depthStencil) + { + _renderer.New<SetRenderTargetsCommand>().Set(Ref(colors.ToArray()), Ref(depthStencil)); + _renderer.QueueCommand(); + } + + public void SetRenderTargetScale(float scale) + { + _renderer.New<SetRenderTargetScaleCommand>().Set(scale); + _renderer.QueueCommand(); + } + + public void SetSampler(int binding, ISampler sampler) + { + _renderer.New<SetSamplerCommand>().Set(binding, Ref(sampler)); + _renderer.QueueCommand(); + } + + public void SetScissor(int index, bool enable, int x, int y, int width, int height) + { + _renderer.New<SetScissorCommand>().Set(index, enable, x, y, width, height); + _renderer.QueueCommand(); + } + + public void SetStencilTest(StencilTestDescriptor stencilTest) + { + _renderer.New<SetStencilTestCommand>().Set(stencilTest); + _renderer.QueueCommand(); + } + + public void SetStorageBuffers(int first, ReadOnlySpan<BufferRange> buffers) + { + _renderer.New<SetStorageBuffersCommand>().Set(first, _renderer.CopySpan(buffers)); + _renderer.QueueCommand(); + } + + public void SetTexture(int binding, ITexture texture) + { + _renderer.New<SetTextureCommand>().Set(binding, Ref(texture)); + _renderer.QueueCommand(); + } + + public void SetTransformFeedbackBuffers(ReadOnlySpan<BufferRange> buffers) + { + _renderer.New<SetTransformFeedbackBuffersCommand>().Set(_renderer.CopySpan(buffers)); + _renderer.QueueCommand(); + } + + public void SetUniformBuffers(int first, ReadOnlySpan<BufferRange> buffers) + { + _renderer.New<SetUniformBuffersCommand>().Set(first, _renderer.CopySpan(buffers)); + _renderer.QueueCommand(); + } + + public void SetUserClipDistance(int index, bool enableClip) + { + _renderer.New<SetUserClipDistanceCommand>().Set(index, enableClip); + _renderer.QueueCommand(); + } + + public void SetVertexAttribs(ReadOnlySpan<VertexAttribDescriptor> vertexAttribs) + { + _renderer.New<SetVertexAttribsCommand>().Set(_renderer.CopySpan(vertexAttribs)); + _renderer.QueueCommand(); + } + + public void SetVertexBuffers(ReadOnlySpan<VertexBufferDescriptor> vertexBuffers) + { + _renderer.New<SetVertexBuffersCommand>().Set(_renderer.CopySpan(vertexBuffers)); + _renderer.QueueCommand(); + } + + public void SetViewports(int first, ReadOnlySpan<Viewport> viewports) + { + _renderer.New<SetViewportsCommand>().Set(first, _renderer.CopySpan(viewports)); + _renderer.QueueCommand(); + } + + public void TextureBarrier() + { + _renderer.New<TextureBarrierCommand>(); + _renderer.QueueCommand(); + } + + public void TextureBarrierTiled() + { + _renderer.New<TextureBarrierTiledCommand>(); + _renderer.QueueCommand(); + } + + public bool TryHostConditionalRendering(ICounterEvent value, ulong compare, bool isEqual) + { + var evt = value as ThreadedCounterEvent; + if (evt != null) + { + if (compare == 0 && evt.Type == CounterType.SamplesPassed && evt.ClearCounter) + { + if (!evt.ReserveForHostAccess()) + { + return false; + } + + _renderer.New<TryHostConditionalRenderingCommand>().Set(Ref(evt), compare, isEqual); + _renderer.QueueCommand(); + return true; + } + } + + _renderer.New<TryHostConditionalRenderingFlushCommand>().Set(Ref(evt), Ref<ThreadedCounterEvent>(null), isEqual); + _renderer.QueueCommand(); + return false; + } + + public bool TryHostConditionalRendering(ICounterEvent value, ICounterEvent compare, bool isEqual) + { + _renderer.New<TryHostConditionalRenderingFlushCommand>().Set(Ref(value as ThreadedCounterEvent), Ref(compare as ThreadedCounterEvent), isEqual); + _renderer.QueueCommand(); + return false; + } + + public void UpdateRenderScale(ShaderStage stage, ReadOnlySpan<float> scales, int textureCount, int imageCount) + { + _renderer.New<UpdateRenderScaleCommand>().Set(stage, _renderer.CopySpan(scales.Slice(0, textureCount + imageCount)), textureCount, imageCount); + _renderer.QueueCommand(); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs b/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs new file mode 100644 index 00000000..52197688 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs @@ -0,0 +1,441 @@ +using Ryujinx.Common; +using Ryujinx.Common.Configuration; +using Ryujinx.Graphics.GAL.Multithreading.Commands; +using Ryujinx.Graphics.GAL.Multithreading.Commands.Buffer; +using Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer; +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; +using Ryujinx.Graphics.GAL.Multithreading.Resources.Programs; +using Ryujinx.Graphics.Shader; +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading; + +namespace Ryujinx.Graphics.GAL.Multithreading +{ + /// <summary> + /// The ThreadedRenderer is a layer that can be put in front of any Renderer backend to make + /// its processing happen on a separate thread, rather than intertwined with the GPU emulation. + /// A new thread is created to handle the GPU command processing, separate from the renderer thread. + /// Calls to the renderer, pipeline and resources are queued to happen on the renderer thread. + /// </summary> + public class ThreadedRenderer : IRenderer + { + private const int SpanPoolBytes = 4 * 1024 * 1024; + private const int MaxRefsPerCommand = 2; + private const int QueueCount = 10000; + + private int _elementSize; + private IRenderer _baseRenderer; + private Thread _gpuThread; + private bool _disposed; + private bool _running; + + private AutoResetEvent _frameComplete = new AutoResetEvent(true); + + private ManualResetEventSlim _galWorkAvailable; + private CircularSpanPool _spanPool; + + private ManualResetEventSlim _invokeRun; + + private bool _lastSampleCounterClear = true; + + private byte[] _commandQueue; + private object[] _refQueue; + + private int _consumerPtr; + private int _commandCount; + + private int _producerPtr; + private int _lastProducedPtr; + private int _invokePtr; + + private int _refProducerPtr; + private int _refConsumerPtr; + + public event EventHandler<ScreenCaptureImageInfo> ScreenCaptured; + + internal BufferMap Buffers { get; } + internal SyncMap Sync { get; } + internal CircularSpanPool SpanPool { get; } + internal ProgramQueue Programs { get; } + + public IPipeline Pipeline { get; } + public IWindow Window { get; } + + public IRenderer BaseRenderer => _baseRenderer; + + public bool PreferThreading => _baseRenderer.PreferThreading; + + public ThreadedRenderer(IRenderer renderer) + { + _baseRenderer = renderer; + + renderer.ScreenCaptured += (object sender, ScreenCaptureImageInfo info) => ScreenCaptured?.Invoke(this, info); + + Pipeline = new ThreadedPipeline(this, renderer.Pipeline); + Window = new ThreadedWindow(this, renderer.Window); + Buffers = new BufferMap(); + Sync = new SyncMap(); + Programs = new ProgramQueue(renderer); + + _galWorkAvailable = new ManualResetEventSlim(false); + _invokeRun = new ManualResetEventSlim(); + _spanPool = new CircularSpanPool(this, SpanPoolBytes); + SpanPool = _spanPool; + + _elementSize = BitUtils.AlignUp(CommandHelper.GetMaxCommandSize(), 4); + + _commandQueue = new byte[_elementSize * QueueCount]; + _refQueue = new object[MaxRefsPerCommand * QueueCount]; + } + + public void RunLoop(Action gpuLoop) + { + _running = true; + + _gpuThread = new Thread(() => { + gpuLoop(); + _running = false; + _galWorkAvailable.Set(); + }); + + _gpuThread.Name = "GPU.MainThread"; + _gpuThread.Start(); + + RenderLoop(); + } + + public void RenderLoop() + { + // Power through the render queue until the Gpu thread work is done. + + while (_running && !_disposed) + { + _galWorkAvailable.Wait(); + _galWorkAvailable.Reset(); + + // The other thread can only increase the command count. + // We can assume that if it is above 0, it will stay there or get higher. + + while (_commandCount > 0) + { + int commandPtr = _consumerPtr; + + Span<byte> command = new Span<byte>(_commandQueue, commandPtr * _elementSize, _elementSize); + + // Run the command. + + CommandHelper.RunCommand(command, this, _baseRenderer); + + if (Interlocked.CompareExchange(ref _invokePtr, -1, commandPtr) == commandPtr) + { + _invokeRun.Set(); + } + + _consumerPtr = (_consumerPtr + 1) % QueueCount; + + Interlocked.Decrement(ref _commandCount); + } + } + } + + internal SpanRef<T> CopySpan<T>(ReadOnlySpan<T> data) where T : unmanaged + { + return _spanPool.Insert(data); + } + + private TableRef<T> Ref<T>(T reference) + { + return new TableRef<T>(this, reference); + } + + internal ref T New<T>() where T : struct + { + while (_producerPtr == (_consumerPtr + QueueCount - 1) % QueueCount) + { + // If incrementing the producer pointer would overflow, we need to wait. + // _consumerPtr can only move forward, so there's no race to worry about here. + + Thread.Sleep(1); + } + + int taken = _producerPtr; + _lastProducedPtr = taken; + + _producerPtr = (_producerPtr + 1) % QueueCount; + + Span<byte> memory = new Span<byte>(_commandQueue, taken * _elementSize, _elementSize); + ref T result = ref Unsafe.As<byte, T>(ref MemoryMarshal.GetReference(memory)); + + memory[memory.Length - 1] = (byte)((IGALCommand)result).CommandType; + + return ref result; + } + + internal int AddTableRef(object obj) + { + // The reference table is sized so that it will never overflow, so long as the references are taken after the command is allocated. + + int index = _refProducerPtr; + + _refQueue[index] = obj; + + _refProducerPtr = (_refProducerPtr + 1) % _refQueue.Length; + + return index; + } + + internal object RemoveTableRef(int index) + { + Debug.Assert(index == _refConsumerPtr); + + object result = _refQueue[_refConsumerPtr]; + _refQueue[_refConsumerPtr] = null; + + _refConsumerPtr = (_refConsumerPtr + 1) % _refQueue.Length; + + return result; + } + + internal void QueueCommand() + { + int result = Interlocked.Increment(ref _commandCount); + + if (result == 1) + { + _galWorkAvailable.Set(); + } + } + + internal void InvokeCommand() + { + _invokeRun.Reset(); + _invokePtr = _lastProducedPtr; + + QueueCommand(); + + // Wait for the command to complete. + _invokeRun.Wait(); + } + + internal void WaitForFrame() + { + _frameComplete.WaitOne(); + } + + internal void SignalFrame() + { + _frameComplete.Set(); + } + + internal bool IsGpuThread() + { + return Thread.CurrentThread == _gpuThread; + } + + public void BackgroundContextAction(Action action, bool alwaysBackground = false) + { + if (IsGpuThread() && !alwaysBackground) + { + // The action must be performed on the render thread. + New<ActionCommand>().Set(Ref(action)); + InvokeCommand(); + } + else + { + _baseRenderer.BackgroundContextAction(action, true); + } + } + + public IShader CompileShader(ShaderStage stage, string code) + { + var shader = new ThreadedShader(this, stage, code); + New<CompileShaderCommand>().Set(Ref(shader)); + QueueCommand(); + + return shader; + } + + public BufferHandle CreateBuffer(int size) + { + BufferHandle handle = Buffers.CreateBufferHandle(); + New<CreateBufferCommand>().Set(handle, size); + QueueCommand(); + + return handle; + } + + public IProgram CreateProgram(IShader[] shaders, TransformFeedbackDescriptor[] transformFeedbackDescriptors) + { + var program = new ThreadedProgram(this); + SourceProgramRequest request = new SourceProgramRequest(program, shaders, transformFeedbackDescriptors); + Programs.Add(request); + + New<CreateProgramCommand>().Set(Ref((IProgramRequest)request)); + QueueCommand(); + + return program; + } + + public ISampler CreateSampler(SamplerCreateInfo info) + { + var sampler = new ThreadedSampler(this); + New<CreateSamplerCommand>().Set(Ref(sampler), info); + QueueCommand(); + + return sampler; + } + + public void CreateSync(ulong id) + { + Sync.CreateSyncHandle(id); + New<CreateSyncCommand>().Set(id); + QueueCommand(); + } + + public ITexture CreateTexture(TextureCreateInfo info, float scale) + { + if (IsGpuThread()) + { + var texture = new ThreadedTexture(this, info, scale); + New<CreateTextureCommand>().Set(Ref(texture), info, scale); + QueueCommand(); + + return texture; + } + else + { + var texture = new ThreadedTexture(this, info, scale); + texture.Base = _baseRenderer.CreateTexture(info, scale); + + return texture; + } + } + + public void DeleteBuffer(BufferHandle buffer) + { + New<BufferDisposeCommand>().Set(buffer); + QueueCommand(); + } + + public ReadOnlySpan<byte> GetBufferData(BufferHandle buffer, int offset, int size) + { + if (IsGpuThread()) + { + ResultBox<PinnedSpan<byte>> box = new ResultBox<PinnedSpan<byte>>(); + New<BufferGetDataCommand>().Set(buffer, offset, size, Ref(box)); + InvokeCommand(); + + return box.Result.Get(); + } + else + { + return _baseRenderer.GetBufferData(Buffers.MapBufferBlocking(buffer), offset, size); + } + } + + public Capabilities GetCapabilities() + { + ResultBox<Capabilities> box = new ResultBox<Capabilities>(); + New<GetCapabilitiesCommand>().Set(Ref(box)); + InvokeCommand(); + + return box.Result; + } + + /// <summary> + /// Initialize the base renderer. Must be called on the render thread. + /// </summary> + /// <param name="logLevel">Log level to use</param> + public void Initialize(GraphicsDebugLevel logLevel) + { + _baseRenderer.Initialize(logLevel); + } + + public IProgram LoadProgramBinary(byte[] programBinary) + { + var program = new ThreadedProgram(this); + + BinaryProgramRequest request = new BinaryProgramRequest(program, programBinary); + Programs.Add(request); + + New<CreateProgramCommand>().Set(Ref((IProgramRequest)request)); + QueueCommand(); + + return program; + } + + public void PreFrame() + { + New<PreFrameCommand>(); + QueueCommand(); + } + + public ICounterEvent ReportCounter(CounterType type, EventHandler<ulong> resultHandler, bool hostReserved) + { + ThreadedCounterEvent evt = new ThreadedCounterEvent(this, type, _lastSampleCounterClear); + New<ReportCounterCommand>().Set(Ref(evt), type, Ref(resultHandler), hostReserved); + QueueCommand(); + + if (type == CounterType.SamplesPassed) + { + _lastSampleCounterClear = false; + } + + return evt; + } + + public void ResetCounter(CounterType type) + { + New<ResetCounterCommand>().Set(type); + QueueCommand(); + _lastSampleCounterClear = true; + } + + public void Screenshot() + { + _baseRenderer.Screenshot(); + } + + public void SetBufferData(BufferHandle buffer, int offset, ReadOnlySpan<byte> data) + { + New<BufferSetDataCommand>().Set(buffer, offset, CopySpan(data)); + QueueCommand(); + } + + public void UpdateCounters() + { + New<UpdateCountersCommand>(); + QueueCommand(); + } + + public void WaitSync(ulong id) + { + Sync.WaitSyncAvailability(id); + + _baseRenderer.WaitSync(id); + } + + public void Dispose() + { + // Dispose must happen from the render thread, after all commands have completed. + + // Stop the GPU thread. + _disposed = true; + _gpuThread.Join(); + + // Dispose the renderer. + _baseRenderer.Dispose(); + + // Dispose events. + _frameComplete.Dispose(); + _galWorkAvailable.Dispose(); + _invokeRun.Dispose(); + + Sync.Dispose(); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/ThreadedWindow.cs b/Ryujinx.Graphics.GAL/Multithreading/ThreadedWindow.cs new file mode 100644 index 00000000..4db3cb19 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/ThreadedWindow.cs @@ -0,0 +1,34 @@ +using Ryujinx.Graphics.GAL.Multithreading.Commands.Window; +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; +using System; + +namespace Ryujinx.Graphics.GAL.Multithreading +{ + public class ThreadedWindow : IWindow + { + private ThreadedRenderer _renderer; + private IWindow _impl; + + public ThreadedWindow(ThreadedRenderer renderer, IWindow window) + { + _renderer = renderer; + _impl = window; + } + + public void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback) + { + // If there's already a frame in the pipeline, wait for it to be presented first. + // This is a multithread rate limit - we can't be more than one frame behind the command queue. + + _renderer.WaitForFrame(); + _renderer.New<WindowPresentCommand>().Set(new TableRef<ThreadedTexture>(_renderer, texture as ThreadedTexture), crop, new TableRef<Action>(_renderer, swapBuffersCallback)); + _renderer.QueueCommand(); + } + + public void SetSize(int width, int height) + { + _impl.SetSize(width, height); + } + } +} |
