aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics.GAL/Multithreading/Resources
diff options
context:
space:
mode:
Diffstat (limited to 'Ryujinx.Graphics.GAL/Multithreading/Resources')
-rw-r--r--Ryujinx.Graphics.GAL/Multithreading/Resources/ProgramQueue.cs107
-rw-r--r--Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/BinaryProgramRequest.cs21
-rw-r--r--Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/IProgramRequest.cs8
-rw-r--r--Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/SourceProgramRequest.cs32
-rw-r--r--Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedCounterEvent.cs80
-rw-r--r--Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedProgram.cs48
-rw-r--r--Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedSampler.cs22
-rw-r--r--Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedShader.cs38
-rw-r--r--Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTexture.cs116
9 files changed, 472 insertions, 0 deletions
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();
+ }
+ }
+}