diff options
| author | TSR Berry <20988865+TSRBerry@users.noreply.github.com> | 2023-04-08 01:22:00 +0200 |
|---|---|---|
| committer | Mary <thog@protonmail.com> | 2023-04-27 23:51:14 +0200 |
| commit | cee712105850ac3385cd0091a923438167433f9f (patch) | |
| tree | 4a5274b21d8b7f938c0d0ce18736d3f2993b11b1 /src/Ryujinx.Graphics.GAL/Multithreading/Resources | |
| parent | cd124bda587ef09668a971fa1cac1c3f0cfc9f21 (diff) | |
Move solution and projects to src
Diffstat (limited to 'src/Ryujinx.Graphics.GAL/Multithreading/Resources')
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(); + } + } +} |
