aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs
diff options
context:
space:
mode:
authorTSR Berry <20988865+TSRBerry@users.noreply.github.com>2023-04-08 01:22:00 +0200
committerMary <thog@protonmail.com>2023-04-27 23:51:14 +0200
commitcee712105850ac3385cd0091a923438167433f9f (patch)
tree4a5274b21d8b7f938c0d0ce18736d3f2993b11b1 /Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs
parentcd124bda587ef09668a971fa1cac1c3f0cfc9f21 (diff)
Move solution and projects to src
Diffstat (limited to 'Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs')
-rw-r--r--Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs488
1 files changed, 0 insertions, 488 deletions
diff --git a/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs b/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs
deleted file mode 100644
index 2148f43f..00000000
--- a/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs
+++ /dev/null
@@ -1,488 +0,0 @@
-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 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 Thread _backendThread;
- private bool _disposed;
- private bool _running;
-
- private AutoResetEvent _frameComplete = new AutoResetEvent(true);
-
- private ManualResetEventSlim _galWorkAvailable;
- private CircularSpanPool _spanPool;
-
- private ManualResetEventSlim _invokeRun;
- private AutoResetEvent _interruptRun;
-
- 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;
-
- private Action _interruptAction;
-
- 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 += (sender, info) => ScreenCaptured?.Invoke(this, info);
- renderer.SetInterruptAction(Interrupt);
-
- Pipeline = new ThreadedPipeline(this, renderer.Pipeline);
- Window = new ThreadedWindow(this, renderer);
- Buffers = new BufferMap();
- Sync = new SyncMap();
- Programs = new ProgramQueue(renderer);
-
- _galWorkAvailable = new ManualResetEventSlim(false);
- _invokeRun = new ManualResetEventSlim();
- _interruptRun = new AutoResetEvent(false);
- _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;
-
- _backendThread = Thread.CurrentThread;
-
- _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();
-
- if (Volatile.Read(ref _interruptAction) != null)
- {
- _interruptAction();
- _interruptRun.Set();
-
- Interlocked.Exchange(ref _interruptAction, null);
- }
-
- // 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 (Volatile.Read(ref _commandCount) > 0 && Volatile.Read(ref _interruptAction) == null)
- {
- 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 == (Volatile.Read(ref _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 BufferHandle CreateBuffer(int size, BufferHandle storageHint)
- {
- BufferHandle handle = Buffers.CreateBufferHandle();
- New<CreateBufferCommand>().Set(handle, size, storageHint);
- QueueCommand();
-
- return handle;
- }
-
- public IProgram CreateProgram(ShaderSource[] shaders, ShaderInfo info)
- {
- var program = new ThreadedProgram(this);
-
- SourceProgramRequest request = new SourceProgramRequest(program, shaders, info);
-
- 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, bool strict)
- {
- Sync.CreateSyncHandle(id);
- New<CreateSyncCommand>().Set(id, strict);
- 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 PinnedSpan<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;
- }
- 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;
- }
-
- public ulong GetCurrentSync()
- {
- return _baseRenderer.GetCurrentSync();
- }
-
- public HardwareInfo GetHardwareInfo()
- {
- return _baseRenderer.GetHardwareInfo();
- }
-
- /// <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, bool hasFragmentShader, ShaderInfo info)
- {
- var program = new ThreadedProgram(this);
-
- BinaryProgramRequest request = new BinaryProgramRequest(program, programBinary, hasFragmentShader, info);
- 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);
- }
-
- private void Interrupt(Action action)
- {
- // Interrupt the backend thread from any external thread and invoke the given action.
-
- if (Thread.CurrentThread == _backendThread)
- {
- // If this is called from the backend thread, the action can run immediately.
- action();
- }
- else
- {
- while (Interlocked.CompareExchange(ref _interruptAction, action, null) != null) { }
-
- _galWorkAvailable.Set();
-
- _interruptRun.WaitOne();
- }
- }
-
- public void SetInterruptAction(Action<Action> interruptAction)
- {
- // Threaded renderer ignores given interrupt action, as it provides its own to the child renderer.
- }
-
- public void Dispose()
- {
- // Dispose must happen from the render thread, after all commands have completed.
-
- // Stop the GPU thread.
- _disposed = true;
-
- if (_gpuThread != null && _gpuThread.IsAlive)
- {
- _gpuThread.Join();
- }
-
- // Dispose the renderer.
- _baseRenderer.Dispose();
-
- // Dispose events.
- _frameComplete.Dispose();
- _galWorkAvailable.Dispose();
- _invokeRun.Dispose();
- _interruptRun.Dispose();
-
- Sync.Dispose();
- }
- }
-} \ No newline at end of file