aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.Graphics.GAL/Multithreading/Resources
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 /src/Ryujinx.Graphics.GAL/Multithreading/Resources
parentcd124bda587ef09668a971fa1cac1c3f0cfc9f21 (diff)
Move solution and projects to src
Diffstat (limited to 'src/Ryujinx.Graphics.GAL/Multithreading/Resources')
-rw-r--r--src/Ryujinx.Graphics.GAL/Multithreading/Resources/ProgramQueue.cs107
-rw-r--r--src/Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/BinaryProgramRequest.cs25
-rw-r--r--src/Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/IProgramRequest.cs8
-rw-r--r--src/Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/SourceProgramRequest.cs23
-rw-r--r--src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedCounterEvent.cs80
-rw-r--r--src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedProgram.cs48
-rw-r--r--src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedSampler.cs22
-rw-r--r--src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTexture.cs141
8 files changed, 454 insertions, 0 deletions
diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ProgramQueue.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ProgramQueue.cs
new file mode 100644
index 00000000..3f982d31
--- /dev/null
+++ b/src/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/src/Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/BinaryProgramRequest.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/BinaryProgramRequest.cs
new file mode 100644
index 00000000..b4c6853f
--- /dev/null
+++ b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/BinaryProgramRequest.cs
@@ -0,0 +1,25 @@
+namespace Ryujinx.Graphics.GAL.Multithreading.Resources.Programs
+{
+ class BinaryProgramRequest : IProgramRequest
+ {
+ public ThreadedProgram Threaded { get; set; }
+
+ private byte[] _data;
+ private bool _hasFragmentShader;
+ private ShaderInfo _info;
+
+ public BinaryProgramRequest(ThreadedProgram program, byte[] data, bool hasFragmentShader, ShaderInfo info)
+ {
+ Threaded = program;
+
+ _data = data;
+ _hasFragmentShader = hasFragmentShader;
+ _info = info;
+ }
+
+ public IProgram Create(IRenderer renderer)
+ {
+ return renderer.LoadProgramBinary(_data, _hasFragmentShader, _info);
+ }
+ }
+}
diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/IProgramRequest.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/IProgramRequest.cs
new file mode 100644
index 00000000..cdbfe03c
--- /dev/null
+++ b/src/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/src/Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/SourceProgramRequest.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/SourceProgramRequest.cs
new file mode 100644
index 00000000..ff06abb1
--- /dev/null
+++ b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/SourceProgramRequest.cs
@@ -0,0 +1,23 @@
+namespace Ryujinx.Graphics.GAL.Multithreading.Resources.Programs
+{
+ class SourceProgramRequest : IProgramRequest
+ {
+ public ThreadedProgram Threaded { get; set; }
+
+ private ShaderSource[] _shaders;
+ private ShaderInfo _info;
+
+ public SourceProgramRequest(ThreadedProgram program, ShaderSource[] shaders, ShaderInfo info)
+ {
+ Threaded = program;
+
+ _shaders = shaders;
+ _info = info;
+ }
+
+ public IProgram Create(IRenderer renderer)
+ {
+ return renderer.CreateProgram(_shaders, _info);
+ }
+ }
+}
diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedCounterEvent.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedCounterEvent.cs
new file mode 100644
index 00000000..4b7471d6
--- /dev/null
+++ b/src/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/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedProgram.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedProgram.cs
new file mode 100644
index 00000000..068d058e
--- /dev/null
+++ b/src/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/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedSampler.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedSampler.cs
new file mode 100644
index 00000000..d8de9a70
--- /dev/null
+++ b/src/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/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTexture.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTexture.cs
new file mode 100644
index 00000000..ee1cfa29
--- /dev/null
+++ b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTexture.cs
@@ -0,0 +1,141 @@
+using Ryujinx.Common.Memory;
+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 PinnedSpan<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;
+ }
+ else
+ {
+ ThreadedHelpers.SpinUntilNonNull(ref Base);
+
+ return Base.GetData();
+ }
+ }
+
+ public PinnedSpan<byte> GetData(int layer, int level)
+ {
+ if (_renderer.IsGpuThread())
+ {
+ ResultBox<PinnedSpan<byte>> box = new ResultBox<PinnedSpan<byte>>();
+ _renderer.New<TextureGetDataSliceCommand>().Set(Ref(this), Ref(box), layer, level);
+ _renderer.InvokeCommand();
+
+ return box.Result;
+ }
+ else
+ {
+ ThreadedHelpers.SpinUntilNonNull(ref Base);
+
+ return Base.GetData(layer, level);
+ }
+ }
+
+ public void SetData(SpanOrArray<byte> data)
+ {
+ _renderer.New<TextureSetDataCommand>().Set(Ref(this), Ref(data.ToArray()));
+ _renderer.QueueCommand();
+ }
+
+ public void SetData(SpanOrArray<byte> data, int layer, int level)
+ {
+ _renderer.New<TextureSetDataSliceCommand>().Set(Ref(this), Ref(data.ToArray()), layer, level);
+ _renderer.QueueCommand();
+ }
+
+ public void SetData(SpanOrArray<byte> data, int layer, int level, Rectangle<int> region)
+ {
+ _renderer.New<TextureSetDataSliceRegionCommand>().Set(Ref(this), Ref(data.ToArray()), layer, level, region);
+ _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();
+ }
+ }
+}