aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Ryujinx.Graphics.GAL/IPipeline.cs2
-rw-r--r--src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetImageCommand.cs7
-rw-r--r--src/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs4
-rw-r--r--src/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs4
-rw-r--r--src/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs2
-rw-r--r--src/Ryujinx.Graphics.OpenGL/Pipeline.cs2
-rw-r--r--src/Ryujinx.Graphics.Vulkan/BarrierBatch.cs225
-rw-r--r--src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs102
-rw-r--r--src/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs2
-rw-r--r--src/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs2
-rw-r--r--src/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs6
-rw-r--r--src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs48
-rw-r--r--src/Ryujinx.Graphics.Vulkan/HelperShader.cs4
-rw-r--r--src/Ryujinx.Graphics.Vulkan/PipelineBase.cs37
-rw-r--r--src/Ryujinx.Graphics.Vulkan/PipelineFull.cs1
-rw-r--r--src/Ryujinx.Graphics.Vulkan/TextureStorage.cs120
-rw-r--r--src/Ryujinx.Graphics.Vulkan/TextureView.cs33
-rw-r--r--src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs5
18 files changed, 436 insertions, 170 deletions
diff --git a/src/Ryujinx.Graphics.GAL/IPipeline.cs b/src/Ryujinx.Graphics.GAL/IPipeline.cs
index f5978cef..3ba084aa 100644
--- a/src/Ryujinx.Graphics.GAL/IPipeline.cs
+++ b/src/Ryujinx.Graphics.GAL/IPipeline.cs
@@ -58,7 +58,7 @@ namespace Ryujinx.Graphics.GAL
void SetIndexBuffer(BufferRange buffer, IndexType type);
- void SetImage(int binding, ITexture texture, Format imageFormat);
+ void SetImage(ShaderStage stage, int binding, ITexture texture, Format imageFormat);
void SetLineParameters(float width, bool smooth);
diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetImageCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetImageCommand.cs
index b4e966ca..243480a8 100644
--- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetImageCommand.cs
+++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetImageCommand.cs
@@ -1,17 +1,20 @@
using Ryujinx.Graphics.GAL.Multithreading.Model;
using Ryujinx.Graphics.GAL.Multithreading.Resources;
+using Ryujinx.Graphics.Shader;
namespace Ryujinx.Graphics.GAL.Multithreading.Commands
{
struct SetImageCommand : IGALCommand, IGALCommand<SetImageCommand>
{
public readonly CommandType CommandType => CommandType.SetImage;
+ private ShaderStage _stage;
private int _binding;
private TableRef<ITexture> _texture;
private Format _imageFormat;
- public void Set(int binding, TableRef<ITexture> texture, Format imageFormat)
+ public void Set(ShaderStage stage, int binding, TableRef<ITexture> texture, Format imageFormat)
{
+ _stage = stage;
_binding = binding;
_texture = texture;
_imageFormat = imageFormat;
@@ -19,7 +22,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands
public static void Run(ref SetImageCommand command, ThreadedRenderer threaded, IRenderer renderer)
{
- renderer.Pipeline.SetImage(command._binding, command._texture.GetAs<ThreadedTexture>(threaded)?.Base, command._imageFormat);
+ renderer.Pipeline.SetImage(command._stage, command._binding, command._texture.GetAs<ThreadedTexture>(threaded)?.Base, command._imageFormat);
}
}
}
diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs
index f40d9896..ad50bddf 100644
--- a/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs
+++ b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs
@@ -177,9 +177,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading
_renderer.QueueCommand();
}
- public void SetImage(int binding, ITexture texture, Format imageFormat)
+ public void SetImage(ShaderStage stage, int binding, ITexture texture, Format imageFormat)
{
- _renderer.New<SetImageCommand>().Set(binding, Ref(texture), imageFormat);
+ _renderer.New<SetImageCommand>().Set(stage, binding, Ref(texture), imageFormat);
_renderer.QueueCommand();
}
diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs
index 963bd947..ef5d0dea 100644
--- a/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs
+++ b/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs
@@ -634,7 +634,7 @@ namespace Ryujinx.Graphics.Gpu.Image
state.Texture = hostTextureRebind;
state.ImageFormat = format;
- _context.Renderer.Pipeline.SetImage(bindingInfo.Binding, hostTextureRebind, format);
+ _context.Renderer.Pipeline.SetImage(stage, bindingInfo.Binding, hostTextureRebind, format);
}
continue;
@@ -692,7 +692,7 @@ namespace Ryujinx.Graphics.Gpu.Image
state.ImageFormat = format;
- _context.Renderer.Pipeline.SetImage(bindingInfo.Binding, hostTexture, format);
+ _context.Renderer.Pipeline.SetImage(stage, bindingInfo.Binding, hostTexture, format);
}
state.CachedTexture = texture;
diff --git a/src/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs b/src/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs
index c65602b5..1f02b9d7 100644
--- a/src/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs
+++ b/src/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs
@@ -484,7 +484,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
if (binding.IsImage)
{
- _context.Renderer.Pipeline.SetImage(binding.BindingInfo.Binding, binding.Texture, binding.Format);
+ _context.Renderer.Pipeline.SetImage(binding.Stage, binding.BindingInfo.Binding, binding.Texture, binding.Format);
}
else
{
diff --git a/src/Ryujinx.Graphics.OpenGL/Pipeline.cs b/src/Ryujinx.Graphics.OpenGL/Pipeline.cs
index 923c85d7..e863c696 100644
--- a/src/Ryujinx.Graphics.OpenGL/Pipeline.cs
+++ b/src/Ryujinx.Graphics.OpenGL/Pipeline.cs
@@ -935,7 +935,7 @@ namespace Ryujinx.Graphics.OpenGL
SetFrontFace(_frontFace = frontFace.Convert());
}
- public void SetImage(int binding, ITexture texture, Format imageFormat)
+ public void SetImage(ShaderStage stage, int binding, ITexture texture, Format imageFormat)
{
if ((uint)binding < SavedImages)
{
diff --git a/src/Ryujinx.Graphics.Vulkan/BarrierBatch.cs b/src/Ryujinx.Graphics.Vulkan/BarrierBatch.cs
new file mode 100644
index 00000000..3b44c98c
--- /dev/null
+++ b/src/Ryujinx.Graphics.Vulkan/BarrierBatch.cs
@@ -0,0 +1,225 @@
+using Silk.NET.Vulkan;
+using System;
+using System.Collections.Generic;
+
+namespace Ryujinx.Graphics.Vulkan
+{
+ internal class BarrierBatch : IDisposable
+ {
+ private const int MaxBarriersPerCall = 16;
+
+ private readonly VulkanRenderer _gd;
+
+ private readonly NativeArray<MemoryBarrier> _memoryBarrierBatch = new(MaxBarriersPerCall);
+ private readonly NativeArray<BufferMemoryBarrier> _bufferBarrierBatch = new(MaxBarriersPerCall);
+ private readonly NativeArray<ImageMemoryBarrier> _imageBarrierBatch = new(MaxBarriersPerCall);
+
+ private readonly List<BarrierWithStageFlags<MemoryBarrier>> _memoryBarriers = new();
+ private readonly List<BarrierWithStageFlags<BufferMemoryBarrier>> _bufferBarriers = new();
+ private readonly List<BarrierWithStageFlags<ImageMemoryBarrier>> _imageBarriers = new();
+ private int _queuedBarrierCount;
+
+ public BarrierBatch(VulkanRenderer gd)
+ {
+ _gd = gd;
+ }
+
+ private readonly record struct StageFlags : IEquatable<StageFlags>
+ {
+ public readonly PipelineStageFlags Source;
+ public readonly PipelineStageFlags Dest;
+
+ public StageFlags(PipelineStageFlags source, PipelineStageFlags dest)
+ {
+ Source = source;
+ Dest = dest;
+ }
+ }
+
+ private readonly struct BarrierWithStageFlags<T> where T : unmanaged
+ {
+ public readonly StageFlags Flags;
+ public readonly T Barrier;
+
+ public BarrierWithStageFlags(StageFlags flags, T barrier)
+ {
+ Flags = flags;
+ Barrier = barrier;
+ }
+
+ public BarrierWithStageFlags(PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags, T barrier)
+ {
+ Flags = new StageFlags(srcStageFlags, dstStageFlags);
+ Barrier = barrier;
+ }
+ }
+
+ private void QueueBarrier<T>(List<BarrierWithStageFlags<T>> list, T barrier, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags) where T : unmanaged
+ {
+ list.Add(new BarrierWithStageFlags<T>(srcStageFlags, dstStageFlags, barrier));
+ _queuedBarrierCount++;
+ }
+
+ public void QueueBarrier(MemoryBarrier barrier, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags)
+ {
+ QueueBarrier(_memoryBarriers, barrier, srcStageFlags, dstStageFlags);
+ }
+
+ public void QueueBarrier(BufferMemoryBarrier barrier, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags)
+ {
+ QueueBarrier(_bufferBarriers, barrier, srcStageFlags, dstStageFlags);
+ }
+
+ public void QueueBarrier(ImageMemoryBarrier barrier, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags)
+ {
+ QueueBarrier(_imageBarriers, barrier, srcStageFlags, dstStageFlags);
+ }
+
+ public unsafe void Flush(CommandBuffer cb, bool insideRenderPass, Action endRenderPass)
+ {
+ while (_queuedBarrierCount > 0)
+ {
+ int memoryCount = 0;
+ int bufferCount = 0;
+ int imageCount = 0;
+
+ bool hasBarrier = false;
+ StageFlags flags = default;
+
+ static void AddBarriers<T>(
+ Span<T> target,
+ ref int queuedBarrierCount,
+ ref bool hasBarrier,
+ ref StageFlags flags,
+ ref int count,
+ List<BarrierWithStageFlags<T>> list) where T : unmanaged
+ {
+ int firstMatch = -1;
+
+ for (int i = 0; i < list.Count; i++)
+ {
+ BarrierWithStageFlags<T> barrier = list[i];
+
+ if (!hasBarrier)
+ {
+ flags = barrier.Flags;
+ hasBarrier = true;
+
+ target[count++] = barrier.Barrier;
+ queuedBarrierCount--;
+ firstMatch = i;
+
+ if (count >= target.Length)
+ {
+ break;
+ }
+ }
+ else
+ {
+ if (flags.Equals(barrier.Flags))
+ {
+ target[count++] = barrier.Barrier;
+ queuedBarrierCount--;
+
+ if (firstMatch == -1)
+ {
+ firstMatch = i;
+ }
+
+ if (count >= target.Length)
+ {
+ break;
+ }
+ }
+ else
+ {
+ // Delete consumed barriers from the first match to the current non-match.
+ if (firstMatch != -1)
+ {
+ int deleteCount = i - firstMatch;
+ list.RemoveRange(firstMatch, deleteCount);
+ i -= deleteCount;
+
+ firstMatch = -1;
+ }
+ }
+ }
+ }
+
+ if (firstMatch == 0)
+ {
+ list.Clear();
+ }
+ else if (firstMatch != -1)
+ {
+ int deleteCount = list.Count - firstMatch;
+
+ list.RemoveRange(firstMatch, deleteCount);
+ }
+ }
+
+ if (insideRenderPass)
+ {
+ // Image barriers queued in the batch are meant to be globally scoped,
+ // but inside a render pass they're scoped to just the range of the render pass.
+
+ // On MoltenVK, we just break the rules and always use image barrier.
+ // On desktop GPUs, all barriers are globally scoped, so we just replace it with a generic memory barrier.
+ // TODO: On certain GPUs, we need to split render pass so the barrier scope is global. When this is done,
+ // notify the resource that it should add a barrier as soon as a render pass ends to avoid this in future.
+
+ if (!_gd.IsMoltenVk)
+ {
+ foreach (var barrier in _imageBarriers)
+ {
+ _memoryBarriers.Add(new BarrierWithStageFlags<MemoryBarrier>(
+ barrier.Flags,
+ new MemoryBarrier()
+ {
+ SType = StructureType.MemoryBarrier,
+ SrcAccessMask = barrier.Barrier.SrcAccessMask,
+ DstAccessMask = barrier.Barrier.DstAccessMask
+ }));
+ }
+
+ _imageBarriers.Clear();
+ }
+ }
+
+ AddBarriers(_memoryBarrierBatch.AsSpan(), ref _queuedBarrierCount, ref hasBarrier, ref flags, ref memoryCount, _memoryBarriers);
+ AddBarriers(_bufferBarrierBatch.AsSpan(), ref _queuedBarrierCount, ref hasBarrier, ref flags, ref bufferCount, _bufferBarriers);
+ AddBarriers(_imageBarrierBatch.AsSpan(), ref _queuedBarrierCount, ref hasBarrier, ref flags, ref imageCount, _imageBarriers);
+
+ if (hasBarrier)
+ {
+ PipelineStageFlags srcStageFlags = flags.Source;
+
+ if (insideRenderPass)
+ {
+ // Inside a render pass, barrier stages can only be from rasterization.
+ srcStageFlags &= ~PipelineStageFlags.ComputeShaderBit;
+ }
+
+ _gd.Api.CmdPipelineBarrier(
+ cb,
+ srcStageFlags,
+ flags.Dest,
+ 0,
+ (uint)memoryCount,
+ _memoryBarrierBatch.Pointer,
+ (uint)bufferCount,
+ _bufferBarrierBatch.Pointer,
+ (uint)imageCount,
+ _imageBarrierBatch.Pointer);
+ }
+ }
+ }
+
+ public void Dispose()
+ {
+ _memoryBarrierBatch.Dispose();
+ _bufferBarrierBatch.Dispose();
+ _imageBarrierBatch.Dispose();
+ }
+ }
+}
diff --git a/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs b/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs
index d40b201d..946e3bc1 100644
--- a/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs
+++ b/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs
@@ -35,6 +35,36 @@ namespace Ryujinx.Graphics.Vulkan
}
}
+ private record struct TextureRef
+ {
+ public ShaderStage Stage;
+ public TextureStorage Storage;
+ public Auto<DisposableImageView> View;
+ public Auto<DisposableSampler> Sampler;
+
+ public TextureRef(ShaderStage stage, TextureStorage storage, Auto<DisposableImageView> view, Auto<DisposableSampler> sampler)
+ {
+ Stage = stage;
+ Storage = storage;
+ View = view;
+ Sampler = sampler;
+ }
+ }
+
+ private record struct ImageRef
+ {
+ public ShaderStage Stage;
+ public TextureStorage Storage;
+ public Auto<DisposableImageView> View;
+
+ public ImageRef(ShaderStage stage, TextureStorage storage, Auto<DisposableImageView> view)
+ {
+ Stage = stage;
+ Storage = storage;
+ View = view;
+ }
+ }
+
private readonly VulkanRenderer _gd;
private readonly Device _device;
private readonly PipelineBase _pipeline;
@@ -42,9 +72,8 @@ namespace Ryujinx.Graphics.Vulkan
private readonly BufferRef[] _uniformBufferRefs;
private readonly BufferRef[] _storageBufferRefs;
- private readonly Auto<DisposableImageView>[] _textureRefs;
- private readonly Auto<DisposableSampler>[] _samplerRefs;
- private readonly Auto<DisposableImageView>[] _imageRefs;
+ private readonly TextureRef[] _textureRefs;
+ private readonly ImageRef[] _imageRefs;
private readonly TextureBuffer[] _bufferTextureRefs;
private readonly TextureBuffer[] _bufferImageRefs;
private readonly Format[] _bufferImageFormats;
@@ -95,9 +124,8 @@ namespace Ryujinx.Graphics.Vulkan
_uniformBufferRefs = new BufferRef[Constants.MaxUniformBufferBindings];
_storageBufferRefs = new BufferRef[Constants.MaxStorageBufferBindings];
- _textureRefs = new Auto<DisposableImageView>[Constants.MaxTextureBindings * 2];
- _samplerRefs = new Auto<DisposableSampler>[Constants.MaxTextureBindings * 2];
- _imageRefs = new Auto<DisposableImageView>[Constants.MaxImageBindings * 2];
+ _textureRefs = new TextureRef[Constants.MaxTextureBindings * 2];
+ _imageRefs = new ImageRef[Constants.MaxImageBindings * 2];
_bufferTextureRefs = new TextureBuffer[Constants.MaxTextureBindings * 2];
_bufferImageRefs = new TextureBuffer[Constants.MaxImageBindings * 2];
_bufferImageFormats = new Format[Constants.MaxImageBindings * 2];
@@ -229,6 +257,33 @@ namespace Ryujinx.Graphics.Vulkan
});
}
+ public void InsertBindingBarriers(CommandBufferScoped cbs)
+ {
+ foreach (ResourceBindingSegment segment in _program.BindingSegments[PipelineBase.TextureSetIndex])
+ {
+ if (segment.Type == ResourceType.TextureAndSampler)
+ {
+ for (int i = 0; i < segment.Count; i++)
+ {
+ ref var texture = ref _textureRefs[segment.Binding + i];
+ texture.Storage?.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, texture.Stage.ConvertToPipelineStageFlags());
+ }
+ }
+ }
+
+ foreach (ResourceBindingSegment segment in _program.BindingSegments[PipelineBase.ImageSetIndex])
+ {
+ if (segment.Type == ResourceType.Image)
+ {
+ for (int i = 0; i < segment.Count; i++)
+ {
+ ref var image = ref _imageRefs[segment.Binding + i];
+ image.Storage?.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, image.Stage.ConvertToPipelineStageFlags());
+ }
+ }
+ }
+ }
+
public void AdvancePdSequence()
{
if (++_pdSequence == 0)
@@ -258,7 +313,12 @@ namespace Ryujinx.Graphics.Vulkan
_dirty = DirtyFlags.All;
}
- public void SetImage(int binding, ITexture image, Format imageFormat)
+ public void SetImage(
+ CommandBufferScoped cbs,
+ ShaderStage stage,
+ int binding,
+ ITexture image,
+ Format imageFormat)
{
if (image is TextureBuffer imageBuffer)
{
@@ -267,11 +327,13 @@ namespace Ryujinx.Graphics.Vulkan
}
else if (image is TextureView view)
{
- _imageRefs[binding] = view.GetView(imageFormat).GetIdentityImageView();
+ view.Storage.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags());
+
+ _imageRefs[binding] = new(stage, view.Storage, view.GetView(imageFormat).GetIdentityImageView());
}
else
{
- _imageRefs[binding] = null;
+ _imageRefs[binding] = default;
_bufferImageRefs[binding] = null;
_bufferImageFormats[binding] = default;
}
@@ -281,7 +343,7 @@ namespace Ryujinx.Graphics.Vulkan
public void SetImage(int binding, Auto<DisposableImageView> image)
{
- _imageRefs[binding] = image;
+ _imageRefs[binding] = new(ShaderStage.Compute, null, image);
SignalDirty(DirtyFlags.Image);
}
@@ -366,15 +428,13 @@ namespace Ryujinx.Graphics.Vulkan
}
else if (texture is TextureView view)
{
- view.Storage.InsertWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags());
+ view.Storage.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags());
- _textureRefs[binding] = view.GetImageView();
- _samplerRefs[binding] = ((SamplerHolder)sampler)?.GetSampler();
+ _textureRefs[binding] = new(stage, view.Storage, view.GetImageView(), ((SamplerHolder)sampler)?.GetSampler());
}
else
{
- _textureRefs[binding] = null;
- _samplerRefs[binding] = null;
+ _textureRefs[binding] = default;
_bufferTextureRefs[binding] = null;
}
@@ -390,10 +450,9 @@ namespace Ryujinx.Graphics.Vulkan
{
if (texture is TextureView view)
{
- view.Storage.InsertWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags());
+ view.Storage.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags());
- _textureRefs[binding] = view.GetIdentityImageView();
- _samplerRefs[binding] = ((SamplerHolder)sampler)?.GetSampler();
+ _textureRefs[binding] = new(stage, view.Storage, view.GetIdentityImageView(), ((SamplerHolder)sampler)?.GetSampler());
SignalDirty(DirtyFlags.Texture);
}
@@ -608,9 +667,10 @@ namespace Ryujinx.Graphics.Vulkan
for (int i = 0; i < count; i++)
{
ref var texture = ref textures[i];
+ ref var refs = ref _textureRefs[binding + i];
- texture.ImageView = _textureRefs[binding + i]?.Get(cbs).Value ?? default;
- texture.Sampler = _samplerRefs[binding + i]?.Get(cbs).Value ?? default;
+ texture.ImageView = refs.View?.Get(cbs).Value ?? default;
+ texture.Sampler = refs.Sampler?.Get(cbs).Value ?? default;
if (texture.ImageView.Handle == 0)
{
@@ -645,7 +705,7 @@ namespace Ryujinx.Graphics.Vulkan
for (int i = 0; i < count; i++)
{
- images[i].ImageView = _imageRefs[binding + i]?.Get(cbs).Value ?? default;
+ images[i].ImageView = _imageRefs[binding + i].View?.Get(cbs).Value ?? default;
}
tu.Push<DescriptorImageInfo>(images[..count]);
diff --git a/src/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs b/src/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs
index 5c0fc468..5a5ddf8c 100644
--- a/src/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs
+++ b/src/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs
@@ -154,7 +154,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
int dispatchY = (height + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, buffer.Range) });
- _pipeline.SetImage(0, _intermediaryTexture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format));
+ _pipeline.SetImage(ShaderStage.Compute, 0, _intermediaryTexture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format));
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
_pipeline.ComputeBarrier();
diff --git a/src/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs b/src/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs
index a7dd8eee..c1293333 100644
--- a/src/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs
+++ b/src/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs
@@ -75,7 +75,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
var dispatchX = BitUtils.DivRoundUp(view.Width, IPostProcessingEffect.LocalGroupSize);
var dispatchY = BitUtils.DivRoundUp(view.Height, IPostProcessingEffect.LocalGroupSize);
- _pipeline.SetImage(0, _texture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format));
+ _pipeline.SetImage(ShaderStage.Compute, 0, _texture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format));
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
_pipeline.ComputeBarrier();
diff --git a/src/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs b/src/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs
index be392fe0..259be9d6 100644
--- a/src/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs
+++ b/src/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs
@@ -219,7 +219,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
buffer.Holder.SetDataUnchecked(buffer.Offset, resolutionBuffer);
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, buffer.Range) });
- _pipeline.SetImage(0, _edgeOutputTexture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format));
+ _pipeline.SetImage(ShaderStage.Compute, 0, _edgeOutputTexture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format));
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
_pipeline.ComputeBarrier();
@@ -229,7 +229,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, _edgeOutputTexture, _samplerLinear);
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 3, _areaTexture, _samplerLinear);
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 4, _searchTexture, _samplerLinear);
- _pipeline.SetImage(0, _blendOutputTexture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format));
+ _pipeline.SetImage(ShaderStage.Compute, 0, _blendOutputTexture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format));
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
_pipeline.ComputeBarrier();
@@ -238,7 +238,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
_pipeline.Specialize(_specConstants);
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 3, _blendOutputTexture, _samplerLinear);
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, view, _samplerLinear);
- _pipeline.SetImage(0, _outputTexture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format));
+ _pipeline.SetImage(ShaderStage.Compute, 0, _outputTexture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format));
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
_pipeline.ComputeBarrier();
diff --git a/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs b/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs
index af22f265..8079e5ff 100644
--- a/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs
+++ b/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs
@@ -243,41 +243,6 @@ namespace Ryujinx.Graphics.Vulkan
return new Auto<DisposableFramebuffer>(new DisposableFramebuffer(api, _device, framebuffer), null, _attachments);
}
- public void UpdateModifications()
- {
- if (_colors != null)
- {
- for (int index = 0; index < _colors.Length; index++)
- {
- _colors[index].Storage.SetModification(
- AccessFlags.ColorAttachmentWriteBit,
- PipelineStageFlags.ColorAttachmentOutputBit);
- }
- }
-
- _depthStencil?.Storage.SetModification(
- AccessFlags.DepthStencilAttachmentWriteBit,
- PipelineStageFlags.LateFragmentTestsBit);
- }
-
- public void InsertClearBarrier(CommandBufferScoped cbs, int index)
- {
- _colorsCanonical?[index]?.Storage?.InsertReadToWriteBarrier(
- cbs,
- AccessFlags.ColorAttachmentWriteBit,
- PipelineStageFlags.ColorAttachmentOutputBit,
- insideRenderPass: true);
- }
-
- public void InsertClearBarrierDS(CommandBufferScoped cbs)
- {
- _depthStencil?.Storage?.InsertReadToWriteBarrier(
- cbs,
- AccessFlags.DepthStencilAttachmentWriteBit,
- PipelineStageFlags.LateFragmentTestsBit,
- insideRenderPass: true);
- }
-
public TextureView[] GetAttachmentViews()
{
var result = new TextureView[_attachments.Length];
@@ -297,23 +262,20 @@ namespace Ryujinx.Graphics.Vulkan
return new RenderPassCacheKey(_depthStencil, _colorsCanonical);
}
- public void InsertLoadOpBarriers(CommandBufferScoped cbs)
+ public void InsertLoadOpBarriers(VulkanRenderer gd, CommandBufferScoped cbs)
{
if (_colors != null)
{
foreach (var color in _colors)
{
// If Clear or DontCare were used, this would need to be write bit.
- color.Storage?.InsertWriteToReadBarrier(cbs, AccessFlags.ColorAttachmentReadBit, PipelineStageFlags.ColorAttachmentOutputBit);
- color.Storage?.SetModification(AccessFlags.ColorAttachmentWriteBit, PipelineStageFlags.ColorAttachmentOutputBit);
+ color.Storage?.QueueLoadOpBarrier(cbs, false);
}
}
- if (_depthStencil != null)
- {
- _depthStencil.Storage?.InsertWriteToReadBarrier(cbs, AccessFlags.DepthStencilAttachmentReadBit, PipelineStageFlags.EarlyFragmentTestsBit);
- _depthStencil.Storage?.SetModification(AccessFlags.DepthStencilAttachmentWriteBit, PipelineStageFlags.LateFragmentTestsBit);
- }
+ _depthStencil?.Storage?.QueueLoadOpBarrier(cbs, true);
+
+ gd.Barriers.Flush(cbs.CommandBuffer, false, null);
}
public (Auto<DisposableRenderPass> renderPass, Auto<DisposableFramebuffer> framebuffer) GetPassAndFramebuffer(
diff --git a/src/Ryujinx.Graphics.Vulkan/HelperShader.cs b/src/Ryujinx.Graphics.Vulkan/HelperShader.cs
index c0ded5b3..3efb1119 100644
--- a/src/Ryujinx.Graphics.Vulkan/HelperShader.cs
+++ b/src/Ryujinx.Graphics.Vulkan/HelperShader.cs
@@ -1039,7 +1039,7 @@ namespace Ryujinx.Graphics.Vulkan
var dstView = Create2DLayerView(dst, dstLayer + z, dstLevel + l);
_pipeline.SetTextureAndSamplerIdentitySwizzle(ShaderStage.Compute, 0, srcView, null);
- _pipeline.SetImage(0, dstView, dstFormat);
+ _pipeline.SetImage(ShaderStage.Compute, 0, dstView, dstFormat);
int dispatchX = (Math.Min(srcView.Info.Width, dstView.Info.Width) + 31) / 32;
int dispatchY = (Math.Min(srcView.Info.Height, dstView.Info.Height) + 31) / 32;
@@ -1168,7 +1168,7 @@ namespace Ryujinx.Graphics.Vulkan
var dstView = Create2DLayerView(dst, dstLayer + z, 0);
_pipeline.SetTextureAndSamplerIdentitySwizzle(ShaderStage.Compute, 0, srcView, null);
- _pipeline.SetImage(0, dstView, format);
+ _pipeline.SetImage(ShaderStage.Compute, 0, dstView, format);
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs
index 3b3f5925..2bcab514 100644
--- a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs
+++ b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs
@@ -36,6 +36,7 @@ namespace Ryujinx.Graphics.Vulkan
private PipelineState _newState;
private bool _graphicsStateDirty;
private bool _computeStateDirty;
+ private bool _bindingBarriersDirty;
private PrimitiveTopology _topology;
private ulong _currentPipelineHandle;
@@ -248,14 +249,14 @@ namespace Ryujinx.Graphics.Vulkan
CreateRenderPass();
}
+ Gd.Barriers.Flush(Cbs.CommandBuffer, RenderPassActive, EndRenderPassDelegate);
+
BeginRenderPass();
var clearValue = new ClearValue(new ClearColorValue(color.Red, color.Green, color.Blue, color.Alpha));
var attachment = new ClearAttachment(ImageAspectFlags.ColorBit, (uint)index, clearValue);
var clearRect = FramebufferParams.GetClearRect(ClearScissor, layer, layerCount);
- FramebufferParams.InsertClearBarrier(Cbs, index);
-
Gd.Api.CmdClearAttachments(CommandBuffer, 1, &attachment, 1, &clearRect);
}
@@ -286,13 +287,13 @@ namespace Ryujinx.Graphics.Vulkan
CreateRenderPass();
}
+ Gd.Barriers.Flush(Cbs.CommandBuffer, RenderPassActive, EndRenderPassDelegate);
+
BeginRenderPass();
var attachment = new ClearAttachment(flags, 0, clearValue);
var clearRect = FramebufferParams.GetClearRect(ClearScissor, layer, layerCount);
- FramebufferParams.InsertClearBarrierDS(Cbs);
-
Gd.Api.CmdClearAttachments(CommandBuffer, 1, &attachment, 1, &clearRect);
}
@@ -887,9 +888,9 @@ namespace Ryujinx.Graphics.Vulkan
SignalStateChange();
}
- public void SetImage(int binding, ITexture image, Format imageFormat)
+ public void SetImage(ShaderStage stage, int binding, ITexture image, Format imageFormat)
{
- _descriptorSetUpdater.SetImage(binding, image, imageFormat);
+ _descriptorSetUpdater.SetImage(Cbs, stage, binding, image, imageFormat);
}
public void SetImage(int binding, Auto<DisposableImageView> image)
@@ -977,6 +978,7 @@ namespace Ryujinx.Graphics.Vulkan
_program = internalProgram;
_descriptorSetUpdater.SetProgram(Cbs, internalProgram, _currentPipelineHandle != 0);
+ _bindingBarriersDirty = true;
_newState.PipelineLayout = internalProgram.PipelineLayout;
_newState.StagesCount = (uint)stages.Length;
@@ -1066,7 +1068,6 @@ namespace Ryujinx.Graphics.Vulkan
private void SetRenderTargetsInternal(ITexture[] colors, ITexture depthStencil, bool filterWriteMasked)
{
CreateFramebuffer(colors, depthStencil, filterWriteMasked);
- FramebufferParams?.UpdateModifications();
CreateRenderPass();
SignalStateChange();
SignalAttachmentChange();
@@ -1520,8 +1521,18 @@ namespace Ryujinx.Graphics.Vulkan
CreatePipeline(PipelineBindPoint.Compute);
_computeStateDirty = false;
Pbp = PipelineBindPoint.Compute;
+
+ if (_bindingBarriersDirty)
+ {
+ // Stale barriers may have been activated by switching program. Emit any that are relevant.
+ _descriptorSetUpdater.InsertBindingBarriers(Cbs);
+
+ _bindingBarriersDirty = false;
+ }
}
+ Gd.Barriers.Flush(Cbs.CommandBuffer, RenderPassActive, EndRenderPassDelegate);
+
_descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, PipelineBindPoint.Compute);
}
@@ -1575,8 +1586,18 @@ namespace Ryujinx.Graphics.Vulkan
_graphicsStateDirty = false;
Pbp = PipelineBindPoint.Graphics;
+
+ if (_bindingBarriersDirty)
+ {
+ // Stale barriers may have been activated by switching program. Emit any that are relevant.
+ _descriptorSetUpdater.InsertBindingBarriers(Cbs);
+
+ _bindingBarriersDirty = false;
+ }
}
+ Gd.Barriers.Flush(Cbs.CommandBuffer, RenderPassActive, EndRenderPassDelegate);
+
_descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, PipelineBindPoint.Graphics);
return true;
@@ -1630,6 +1651,8 @@ namespace Ryujinx.Graphics.Vulkan
{
if (!RenderPassActive)
{
+ FramebufferParams.InsertLoadOpBarriers(Gd, Cbs);
+
var renderArea = new Rect2D(null, new Extent2D(FramebufferParams.Width, FramebufferParams.Height));
var clearValue = new ClearValue();
diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineFull.cs b/src/Ryujinx.Graphics.Vulkan/PipelineFull.cs
index 6c4419cd..4987548c 100644
--- a/src/Ryujinx.Graphics.Vulkan/PipelineFull.cs
+++ b/src/Ryujinx.Graphics.Vulkan/PipelineFull.cs
@@ -269,6 +269,7 @@ namespace Ryujinx.Graphics.Vulkan
PreloadCbs = null;
}
+ Gd.Barriers.Flush(Cbs.CommandBuffer, false, null);
CommandBuffer = (Cbs = Gd.CommandBufferPool.ReturnAndRent(Cbs)).CommandBuffer;
Gd.RegisterFlush();
diff --git a/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs b/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs
index bba65921..230dbd4e 100644
--- a/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs
+++ b/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs
@@ -433,99 +433,65 @@ namespace Ryujinx.Graphics.Vulkan
return FormatCapabilities.IsD24S8(Info.Format) && VkFormat == VkFormat.D32SfloatS8Uint;
}
- public void SetModification(AccessFlags accessFlags, PipelineStageFlags stage)
+ public void QueueLoadOpBarrier(CommandBufferScoped cbs, bool depthStencil)
{
- _lastModificationAccess = accessFlags;
- _lastModificationStage = stage;
- }
+ PipelineStageFlags srcStageFlags = _lastReadStage | _lastModificationStage;
+ PipelineStageFlags dstStageFlags = depthStencil ?
+ PipelineStageFlags.EarlyFragmentTestsBit | PipelineStageFlags.LateFragmentTestsBit :
+ PipelineStageFlags.ColorAttachmentOutputBit;
- public void InsertReadToWriteBarrier(CommandBufferScoped cbs, AccessFlags dstAccessFlags, PipelineStageFlags dstStageFlags, bool insideRenderPass)
- {
- var lastReadStage = _lastReadStage;
+ AccessFlags srcAccessFlags = _lastModificationAccess | _lastReadAccess;
+ AccessFlags dstAccessFlags = depthStencil ?
+ AccessFlags.DepthStencilAttachmentWriteBit | AccessFlags.DepthStencilAttachmentReadBit :
+ AccessFlags.ColorAttachmentWriteBit | AccessFlags.ColorAttachmentReadBit;
- if (insideRenderPass)
+ if (srcAccessFlags != AccessFlags.None)
{
- // We can't have barrier from compute inside a render pass,
- // as it is invalid to specify compute in the subpass dependency stage mask.
-
- lastReadStage &= ~PipelineStageFlags.ComputeShaderBit;
- }
+ ImageAspectFlags aspectFlags = Info.Format.ConvertAspectFlags();
+ ImageMemoryBarrier barrier = TextureView.GetImageBarrier(
+ _imageAuto.Get(cbs).Value,
+ srcAccessFlags,
+ dstAccessFlags,
+ aspectFlags,
+ 0,
+ 0,
+ _info.GetLayers(),
+ _info.Levels);
- if (lastReadStage != PipelineStageFlags.None)
- {
- // This would result in a validation error, but is
- // required on MoltenVK as the generic barrier results in
- // severe texture flickering in some scenarios.
- if (_gd.IsMoltenVk)
- {
- ImageAspectFlags aspectFlags = Info.Format.ConvertAspectFlags();
- TextureView.InsertImageBarrier(
- _gd.Api,
- cbs.CommandBuffer,
- _imageAuto.Get(cbs).Value,
- _lastReadAccess,
- dstAccessFlags,
- _lastReadStage,
- dstStageFlags,
- aspectFlags,
- 0,
- 0,
- _info.GetLayers(),
- _info.Levels);
- }
- else
- {
- TextureView.InsertMemoryBarrier(
- _gd.Api,
- cbs.CommandBuffer,
- _lastReadAccess,
- dstAccessFlags,
- lastReadStage,
- dstStageFlags);
- }
+ _gd.Barriers.QueueBarrier(barrier, srcStageFlags, dstStageFlags);
- _lastReadAccess = AccessFlags.None;
_lastReadStage = PipelineStageFlags.None;
+ _lastReadAccess = AccessFlags.None;
}
+
+ _lastModificationStage = depthStencil ?
+ PipelineStageFlags.LateFragmentTestsBit :
+ PipelineStageFlags.ColorAttachmentOutputBit;
+
+ _lastModificationAccess = depthStencil ?
+ AccessFlags.DepthStencilAttachmentWriteBit :
+ AccessFlags.ColorAttachmentWriteBit;
}
- public void InsertWriteToReadBarrier(CommandBufferScoped cbs, AccessFlags dstAccessFlags, PipelineStageFlags dstStageFlags)
+ public void QueueWriteToReadBarrier(CommandBufferScoped cbs, AccessFlags dstAccessFlags, PipelineStageFlags dstStageFlags)
{
_lastReadAccess |= dstAccessFlags;
_lastReadStage |= dstStageFlags;
if (_lastModificationAccess != AccessFlags.None)
{
- // This would result in a validation error, but is
- // required on MoltenVK as the generic barrier results in
- // severe texture flickering in some scenarios.
- if (_gd.IsMoltenVk)
- {
- ImageAspectFlags aspectFlags = Info.Format.ConvertAspectFlags();
- TextureView.InsertImageBarrier(
- _gd.Api,
- cbs.CommandBuffer,
- _imageAuto.Get(cbs).Value,
- _lastModificationAccess,
- dstAccessFlags,
- _lastModificationStage,
- dstStageFlags,
- aspectFlags,
- 0,
- 0,
- _info.GetLayers(),
- _info.Levels);
- }
- else
- {
- TextureView.InsertMemoryBarrier(
- _gd.Api,
- cbs.CommandBuffer,
- _lastModificationAccess,
- dstAccessFlags,
- _lastModificationStage,
- dstStageFlags);
- }
+ ImageAspectFlags aspectFlags = Info.Format.ConvertAspectFlags();
+ ImageMemoryBarrier barrier = TextureView.GetImageBarrier(
+ _imageAuto.Get(cbs).Value,
+ _lastModificationAccess,
+ dstAccessFlags,
+ aspectFlags,
+ 0,
+ 0,
+ _info.GetLayers(),
+ _info.Levels);
+
+ _gd.Barriers.QueueBarrier(barrier, _lastModificationStage, dstStageFlags);
_lastModificationAccess = AccessFlags.None;
}
diff --git a/src/Ryujinx.Graphics.Vulkan/TextureView.cs b/src/Ryujinx.Graphics.Vulkan/TextureView.cs
index ef511565..31d13965 100644
--- a/src/Ryujinx.Graphics.Vulkan/TextureView.cs
+++ b/src/Ryujinx.Graphics.Vulkan/TextureView.cs
@@ -497,21 +497,17 @@ namespace Ryujinx.Graphics.Vulkan
null);
}
- public static unsafe void InsertImageBarrier(
- Vk api,
- CommandBuffer commandBuffer,
+ public static ImageMemoryBarrier GetImageBarrier(
Image image,
AccessFlags srcAccessMask,
AccessFlags dstAccessMask,
- PipelineStageFlags srcStageMask,
- PipelineStageFlags dstStageMask,
ImageAspectFlags aspectFlags,
int firstLayer,
int firstLevel,
int layers,
int levels)
{
- ImageMemoryBarrier memoryBarrier = new()
+ return new()
{
SType = StructureType.ImageMemoryBarrier,
SrcAccessMask = srcAccessMask,
@@ -523,6 +519,31 @@ namespace Ryujinx.Graphics.Vulkan
NewLayout = ImageLayout.General,
SubresourceRange = new ImageSubresourceRange(aspectFlags, (uint)firstLevel, (uint)levels, (uint)firstLayer, (uint)layers),
};
+ }
+
+ public static unsafe void InsertImageBarrier(
+ Vk api,
+ CommandBuffer commandBuffer,
+ Image image,
+ AccessFlags srcAccessMask,
+ AccessFlags dstAccessMask,
+ PipelineStageFlags srcStageMask,
+ PipelineStageFlags dstStageMask,
+ ImageAspectFlags aspectFlags,
+ int firstLayer,
+ int firstLevel,
+ int layers,
+ int levels)
+ {
+ ImageMemoryBarrier memoryBarrier = GetImageBarrier(
+ image,
+ srcAccessMask,
+ dstAccessMask,
+ aspectFlags,
+ firstLayer,
+ firstLevel,
+ layers,
+ levels);
api.CmdPipelineBarrier(
commandBuffer,
diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
index 6aa46b79..434545fe 100644
--- a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
+++ b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
@@ -68,6 +68,8 @@ namespace Ryujinx.Graphics.Vulkan
internal HelperShader HelperShader { get; private set; }
internal PipelineFull PipelineInternal => _pipeline;
+ internal BarrierBatch Barriers { get; private set; }
+
public IPipeline Pipeline => _pipeline;
public IWindow Window => _window;
@@ -381,6 +383,8 @@ namespace Ryujinx.Graphics.Vulkan
HelperShader = new HelperShader(this, _device);
+ Barriers = new BarrierBatch(this);
+
_counters = new Counters(this, _device, _pipeline);
}
@@ -914,6 +918,7 @@ namespace Ryujinx.Graphics.Vulkan
BufferManager.Dispose();
DescriptorSetManager.Dispose();
PipelineLayoutCache.Dispose();
+ Barriers.Dispose();
MemoryAllocator.Dispose();