aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.Graphics.Vulkan/BarrierBatch.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Ryujinx.Graphics.Vulkan/BarrierBatch.cs')
-rw-r--r--src/Ryujinx.Graphics.Vulkan/BarrierBatch.cs225
1 files changed, 225 insertions, 0 deletions
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();
+ }
+ }
+}