|
|
@@ -29,6 +29,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
|
|
private int _elementSize;
|
|
|
private IRenderer _baseRenderer;
|
|
|
private Thread _gpuThread;
|
|
|
+ private Thread _backendThread;
|
|
|
private bool _disposed;
|
|
|
private bool _running;
|
|
|
|
|
|
@@ -38,6 +39,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
|
|
private CircularSpanPool _spanPool;
|
|
|
|
|
|
private ManualResetEventSlim _invokeRun;
|
|
|
+ private AutoResetEvent _interruptRun;
|
|
|
|
|
|
private bool _lastSampleCounterClear = true;
|
|
|
|
|
|
@@ -54,6 +56,8 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
|
|
private int _refProducerPtr;
|
|
|
private int _refConsumerPtr;
|
|
|
|
|
|
+ private Action _interruptAction;
|
|
|
+
|
|
|
public event EventHandler<ScreenCaptureImageInfo> ScreenCaptured;
|
|
|
|
|
|
internal BufferMap Buffers { get; }
|
|
|
@@ -73,6 +77,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
|
|
_baseRenderer = renderer;
|
|
|
|
|
|
renderer.ScreenCaptured += (sender, info) => ScreenCaptured?.Invoke(this, info);
|
|
|
+ renderer.SetInterruptAction(Interrupt);
|
|
|
|
|
|
Pipeline = new ThreadedPipeline(this, renderer.Pipeline);
|
|
|
Window = new ThreadedWindow(this, renderer);
|
|
|
@@ -82,6 +87,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
|
|
|
|
|
_galWorkAvailable = new ManualResetEventSlim(false);
|
|
|
_invokeRun = new ManualResetEventSlim();
|
|
|
+ _interruptRun = new AutoResetEvent(false);
|
|
|
_spanPool = new CircularSpanPool(this, SpanPoolBytes);
|
|
|
SpanPool = _spanPool;
|
|
|
|
|
|
@@ -95,6 +101,8 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
|
|
{
|
|
|
_running = true;
|
|
|
|
|
|
+ _backendThread = Thread.CurrentThread;
|
|
|
+
|
|
|
_gpuThread = new Thread(() => {
|
|
|
gpuLoop();
|
|
|
_running = false;
|
|
|
@@ -116,10 +124,18 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
|
|
_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 (_commandCount > 0)
|
|
|
+ while (_commandCount > 0 && Volatile.Read(ref _interruptAction) == null)
|
|
|
{
|
|
|
int commandPtr = _consumerPtr;
|
|
|
|
|
|
@@ -281,10 +297,10 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
|
|
return sampler;
|
|
|
}
|
|
|
|
|
|
- public void CreateSync(ulong id)
|
|
|
+ public void CreateSync(ulong id, bool strict)
|
|
|
{
|
|
|
Sync.CreateSyncHandle(id);
|
|
|
- New<CreateSyncCommand>().Set(id);
|
|
|
+ New<CreateSyncCommand>().Set(id, strict);
|
|
|
QueueCommand();
|
|
|
}
|
|
|
|
|
|
@@ -421,6 +437,30 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
|
|
_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.
|
|
|
@@ -440,6 +480,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
|
|
_frameComplete.Dispose();
|
|
|
_galWorkAvailable.Dispose();
|
|
|
_invokeRun.Dispose();
|
|
|
+ _interruptRun.Dispose();
|
|
|
|
|
|
Sync.Dispose();
|
|
|
}
|