aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorriperiperi <rhy3756547@hotmail.com>2024-07-18 00:21:32 +0100
committerGitHub <noreply@github.com>2024-07-17 20:21:32 -0300
commit1a919e99b29fff4e2158e622cb3dfbee21293b6d (patch)
tree00f1f3263411b96301be80b26a3b10967374328e
parentf77bebac80bd2fcbee72b00845e56faf3de3bad6 (diff)
Vulkan: Defer guest barriers, and improve image barrier timings (#7012)1.1.1352
* More guarantees for buffer correct placement, defer guest requested buffers * Split RP on indirect barrier rn * Better handling for feedback loops. * Qualcomm barriers suck too * Fix condition * Remove unused field * Allow render pass barriers on turnip for now
-rw-r--r--src/Ryujinx.Graphics.GAL/ResourceLayout.cs4
-rw-r--r--src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs24
-rw-r--r--src/Ryujinx.Graphics.Vulkan/BarrierBatch.cs260
-rw-r--r--src/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs4
-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.cs17
-rw-r--r--src/Ryujinx.Graphics.Vulkan/HelperShader.cs10
-rw-r--r--src/Ryujinx.Graphics.Vulkan/PipelineBase.cs101
-rw-r--r--src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs33
-rw-r--r--src/Ryujinx.Graphics.Vulkan/PipelineFull.cs2
-rw-r--r--src/Ryujinx.Graphics.Vulkan/RenderPassHolder.cs40
-rw-r--r--src/Ryujinx.Graphics.Vulkan/ResourceLayoutBuilder.cs4
-rw-r--r--src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs71
-rw-r--r--src/Ryujinx.Graphics.Vulkan/TextureCopy.cs2
-rw-r--r--src/Ryujinx.Graphics.Vulkan/TextureStorage.cs19
-rw-r--r--src/Ryujinx.Graphics.Vulkan/TextureView.cs4
-rw-r--r--src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs5
18 files changed, 452 insertions, 156 deletions
diff --git a/src/Ryujinx.Graphics.GAL/ResourceLayout.cs b/src/Ryujinx.Graphics.GAL/ResourceLayout.cs
index 998c046f..b7464ee1 100644
--- a/src/Ryujinx.Graphics.GAL/ResourceLayout.cs
+++ b/src/Ryujinx.Graphics.GAL/ResourceLayout.cs
@@ -74,13 +74,15 @@ namespace Ryujinx.Graphics.GAL
public int ArrayLength { get; }
public ResourceType Type { get; }
public ResourceStages Stages { get; }
+ public bool Write { get; }
- public ResourceUsage(int binding, int arrayLength, ResourceType type, ResourceStages stages)
+ public ResourceUsage(int binding, int arrayLength, ResourceType type, ResourceStages stages, bool write)
{
Binding = binding;
ArrayLength = arrayLength;
Type = type;
Stages = stages;
+ Write = write;
}
public override int GetHashCode()
diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs
index 42b2cbb5..49823562 100644
--- a/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs
+++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs
@@ -78,9 +78,9 @@ namespace Ryujinx.Graphics.Gpu.Shader
ResourceStages stages = vertexAsCompute ? ResourceStages.Compute | ResourceStages.Vertex : VtgStages;
PopulateDescriptorAndUsages(stages, ResourceType.UniformBuffer, uniformSetIndex, 1, rrc.ReservedConstantBuffers - 1);
- PopulateDescriptorAndUsages(stages, ResourceType.StorageBuffer, storageSetIndex, 0, rrc.ReservedStorageBuffers);
+ PopulateDescriptorAndUsages(stages, ResourceType.StorageBuffer, storageSetIndex, 0, rrc.ReservedStorageBuffers, true);
PopulateDescriptorAndUsages(stages, ResourceType.BufferTexture, textureSetIndex, 0, rrc.ReservedTextures);
- PopulateDescriptorAndUsages(stages, ResourceType.BufferImage, imageSetIndex, 0, rrc.ReservedImages);
+ PopulateDescriptorAndUsages(stages, ResourceType.BufferImage, imageSetIndex, 0, rrc.ReservedImages, true);
}
/// <summary>
@@ -91,10 +91,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <param name="setIndex">Resource set index where the resources are used</param>
/// <param name="start">First binding number</param>
/// <param name="count">Amount of bindings</param>
- private void PopulateDescriptorAndUsages(ResourceStages stages, ResourceType type, int setIndex, int start, int count)
+ /// <param name="write">True if the binding is written from the shader, false otherwise</param>
+ private void PopulateDescriptorAndUsages(ResourceStages stages, ResourceType type, int setIndex, int start, int count, bool write = false)
{
AddDescriptor(stages, type, setIndex, start, count);
- AddUsage(stages, type, setIndex, start, count);
+ AddUsage(stages, type, setIndex, start, count, write);
}
/// <summary>
@@ -216,11 +217,12 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <param name="setIndex">Descriptor set number where the resource will be bound</param>
/// <param name="binding">Binding number where the resource will be bound</param>
/// <param name="count">Number of resources bound at the binding location</param>
- private void AddUsage(ResourceStages stages, ResourceType type, int setIndex, int binding, int count)
+ /// <param name="write">True if the binding is written from the shader, false otherwise</param>
+ private void AddUsage(ResourceStages stages, ResourceType type, int setIndex, int binding, int count, bool write = false)
{
for (int index = 0; index < count; index++)
{
- _resourceUsages[setIndex].Add(new ResourceUsage(binding + index, 1, type, stages));
+ _resourceUsages[setIndex].Add(new ResourceUsage(binding + index, 1, type, stages, write));
}
}
@@ -238,7 +240,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
buffer.Binding,
1,
isStorage ? ResourceType.StorageBuffer : ResourceType.UniformBuffer,
- stages));
+ stages,
+ buffer.Flags.HasFlag(BufferUsageFlags.Write)));
}
}
@@ -254,7 +257,12 @@ namespace Ryujinx.Graphics.Gpu.Shader
{
ResourceType type = GetTextureResourceType(texture, isImage);
- GetUsages(texture.Set).Add(new ResourceUsage(texture.Binding, texture.ArrayLength, type, stages));
+ GetUsages(texture.Set).Add(new ResourceUsage(
+ texture.Binding,
+ texture.ArrayLength,
+ type,
+ stages,
+ texture.Flags.HasFlag(TextureUsageFlags.ImageStore)));
}
}
diff --git a/src/Ryujinx.Graphics.Vulkan/BarrierBatch.cs b/src/Ryujinx.Graphics.Vulkan/BarrierBatch.cs
index 24642af2..a6a006bb 100644
--- a/src/Ryujinx.Graphics.Vulkan/BarrierBatch.cs
+++ b/src/Ryujinx.Graphics.Vulkan/BarrierBatch.cs
@@ -1,6 +1,7 @@
using Silk.NET.Vulkan;
using System;
using System.Collections.Generic;
+using System.Runtime.CompilerServices;
namespace Ryujinx.Graphics.Vulkan
{
@@ -8,22 +9,64 @@ namespace Ryujinx.Graphics.Vulkan
{
private const int MaxBarriersPerCall = 16;
+ private const AccessFlags BaseAccess = AccessFlags.ShaderReadBit | AccessFlags.ShaderWriteBit;
+ private const AccessFlags BufferAccess = AccessFlags.IndexReadBit | AccessFlags.VertexAttributeReadBit | AccessFlags.UniformReadBit;
+ private const AccessFlags CommandBufferAccess = AccessFlags.IndirectCommandReadBit;
+
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 readonly List<BarrierWithStageFlags<MemoryBarrier, int>> _memoryBarriers = new();
+ private readonly List<BarrierWithStageFlags<BufferMemoryBarrier, int>> _bufferBarriers = new();
+ private readonly List<BarrierWithStageFlags<ImageMemoryBarrier, TextureStorage>> _imageBarriers = new();
private int _queuedBarrierCount;
+ private enum IncoherentBarrierType
+ {
+ None,
+ Texture,
+ All,
+ CommandBuffer
+ }
+
+ private PipelineStageFlags _incoherentBufferWriteStages;
+ private PipelineStageFlags _incoherentTextureWriteStages;
+ private PipelineStageFlags _extraStages;
+ private IncoherentBarrierType _queuedIncoherentBarrier;
+
public BarrierBatch(VulkanRenderer gd)
{
_gd = gd;
}
+ public static (AccessFlags Access, PipelineStageFlags Stages) GetSubpassAccessSuperset(VulkanRenderer gd)
+ {
+ AccessFlags access = BufferAccess;
+ PipelineStageFlags stages = PipelineStageFlags.AllGraphicsBit;
+
+ if (gd.TransformFeedbackApi != null)
+ {
+ access |= AccessFlags.TransformFeedbackWriteBitExt;
+ stages |= PipelineStageFlags.TransformFeedbackBitExt;
+ }
+
+ if (!gd.IsTBDR)
+ {
+ // Desktop GPUs can transform image barriers into memory barriers.
+
+ access |= AccessFlags.DepthStencilAttachmentWriteBit | AccessFlags.ColorAttachmentWriteBit;
+ access |= AccessFlags.DepthStencilAttachmentReadBit | AccessFlags.ColorAttachmentReadBit;
+
+ stages |= PipelineStageFlags.EarlyFragmentTestsBit | PipelineStageFlags.LateFragmentTestsBit;
+ stages |= PipelineStageFlags.ColorAttachmentOutputBit;
+ }
+
+ return (access, stages);
+ }
+
private readonly record struct StageFlags : IEquatable<StageFlags>
{
public readonly PipelineStageFlags Source;
@@ -36,47 +79,130 @@ namespace Ryujinx.Graphics.Vulkan
}
}
- private readonly struct BarrierWithStageFlags<T> where T : unmanaged
+ private readonly struct BarrierWithStageFlags<T, T2> where T : unmanaged
{
public readonly StageFlags Flags;
public readonly T Barrier;
+ public readonly T2 Resource;
public BarrierWithStageFlags(StageFlags flags, T barrier)
{
Flags = flags;
Barrier = barrier;
+ Resource = default;
}
- public BarrierWithStageFlags(PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags, T barrier)
+ public BarrierWithStageFlags(PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags, T barrier, T2 resource)
{
Flags = new StageFlags(srcStageFlags, dstStageFlags);
Barrier = barrier;
+ Resource = resource;
}
}
- private void QueueBarrier<T>(List<BarrierWithStageFlags<T>> list, T barrier, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags) where T : unmanaged
+ private void QueueBarrier<T, T2>(List<BarrierWithStageFlags<T, T2>> list, T barrier, T2 resource, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags) where T : unmanaged
{
- list.Add(new BarrierWithStageFlags<T>(srcStageFlags, dstStageFlags, barrier));
+ list.Add(new BarrierWithStageFlags<T, T2>(srcStageFlags, dstStageFlags, barrier, resource));
_queuedBarrierCount++;
}
public void QueueBarrier(MemoryBarrier barrier, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags)
{
- QueueBarrier(_memoryBarriers, barrier, srcStageFlags, dstStageFlags);
+ QueueBarrier(_memoryBarriers, barrier, default, srcStageFlags, dstStageFlags);
}
public void QueueBarrier(BufferMemoryBarrier barrier, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags)
{
- QueueBarrier(_bufferBarriers, barrier, srcStageFlags, dstStageFlags);
+ QueueBarrier(_bufferBarriers, barrier, default, srcStageFlags, dstStageFlags);
}
- public void QueueBarrier(ImageMemoryBarrier barrier, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags)
+ public void QueueBarrier(ImageMemoryBarrier barrier, TextureStorage resource, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags)
{
- QueueBarrier(_imageBarriers, barrier, srcStageFlags, dstStageFlags);
+ QueueBarrier(_imageBarriers, barrier, resource, srcStageFlags, dstStageFlags);
}
- public unsafe void Flush(CommandBuffer cb, bool insideRenderPass, Action endRenderPass)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public unsafe void FlushMemoryBarrier(ShaderCollection program, bool inRenderPass)
{
+ if (_queuedIncoherentBarrier > IncoherentBarrierType.None)
+ {
+ // We should emit a memory barrier if there's a write access in the program (current program, or program since last barrier)
+ bool hasTextureWrite = _incoherentTextureWriteStages != PipelineStageFlags.None;
+ bool hasBufferWrite = _incoherentBufferWriteStages != PipelineStageFlags.None;
+ bool hasBufferBarrier = _queuedIncoherentBarrier > IncoherentBarrierType.Texture;
+
+ if (hasTextureWrite || (hasBufferBarrier && hasBufferWrite))
+ {
+ AccessFlags access = BaseAccess;
+
+ PipelineStageFlags stages = inRenderPass ? PipelineStageFlags.AllGraphicsBit : PipelineStageFlags.AllCommandsBit;
+
+ if (hasBufferBarrier && hasBufferWrite)
+ {
+ access |= BufferAccess;
+
+ if (_gd.TransformFeedbackApi != null)
+ {
+ access |= AccessFlags.TransformFeedbackWriteBitExt;
+ stages |= PipelineStageFlags.TransformFeedbackBitExt;
+ }
+ }
+
+ if (_queuedIncoherentBarrier == IncoherentBarrierType.CommandBuffer)
+ {
+ access |= CommandBufferAccess;
+ stages |= PipelineStageFlags.DrawIndirectBit;
+ }
+
+ MemoryBarrier barrier = new MemoryBarrier()
+ {
+ SType = StructureType.MemoryBarrier,
+ SrcAccessMask = access,
+ DstAccessMask = access
+ };
+
+ QueueBarrier(barrier, stages, stages);
+
+ _incoherentTextureWriteStages = program?.IncoherentTextureWriteStages ?? PipelineStageFlags.None;
+
+ if (_queuedIncoherentBarrier > IncoherentBarrierType.Texture)
+ {
+ if (program != null)
+ {
+ _incoherentBufferWriteStages = program.IncoherentBufferWriteStages | _extraStages;
+ }
+ else
+ {
+ _incoherentBufferWriteStages = PipelineStageFlags.None;
+ }
+ }
+
+ _queuedIncoherentBarrier = IncoherentBarrierType.None;
+ }
+ }
+ }
+
+ public unsafe void Flush(CommandBufferScoped cbs, bool inRenderPass, RenderPassHolder rpHolder, Action endRenderPass)
+ {
+ Flush(cbs, null, inRenderPass, rpHolder, endRenderPass);
+ }
+
+ public unsafe void Flush(CommandBufferScoped cbs, ShaderCollection program, bool inRenderPass, RenderPassHolder rpHolder, Action endRenderPass)
+ {
+ if (program != null)
+ {
+ _incoherentBufferWriteStages |= program.IncoherentBufferWriteStages | _extraStages;
+ _incoherentTextureWriteStages |= program.IncoherentTextureWriteStages;
+ }
+
+ FlushMemoryBarrier(program, inRenderPass);
+
+ if (!inRenderPass && rpHolder != null)
+ {
+ // Render pass is about to begin. Queue any fences that normally interrupt the pass.
+ rpHolder.InsertForcedFences(cbs);
+ }
+
while (_queuedBarrierCount > 0)
{
int memoryCount = 0;
@@ -86,20 +212,20 @@ namespace Ryujinx.Graphics.Vulkan
bool hasBarrier = false;
StageFlags flags = default;
- static void AddBarriers<T>(
+ static void AddBarriers<T, T2>(
Span<T> target,
ref int queuedBarrierCount,
ref bool hasBarrier,
ref StageFlags flags,
ref int count,
- List<BarrierWithStageFlags<T>> list) where T : unmanaged
+ List<BarrierWithStageFlags<T, T2>> list) where T : unmanaged
{
int firstMatch = -1;
int end = list.Count;
for (int i = 0; i < list.Count; i++)
{
- BarrierWithStageFlags<T> barrier = list[i];
+ BarrierWithStageFlags<T, T2> barrier = list[i];
if (!hasBarrier)
{
@@ -162,21 +288,60 @@ namespace Ryujinx.Graphics.Vulkan
}
}
- if (insideRenderPass)
+ if (inRenderPass && _imageBarriers.Count > 0)
{
// 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.
+ // Generally, we want to avoid this from happening in the future, so flag the texture to immediately
+ // emit a barrier whenever the current render pass is bound again.
+
+ bool anyIsNonAttachment = false;
+
+ foreach (BarrierWithStageFlags<ImageMemoryBarrier, TextureStorage> barrier in _imageBarriers)
+ {
+ // If the binding is an attachment, don't add it as a forced fence.
+ bool isAttachment = rpHolder.ContainsAttachment(barrier.Resource);
+
+ if (!isAttachment)
+ {
+ rpHolder.AddForcedFence(barrier.Resource, barrier.Flags.Dest);
+ anyIsNonAttachment = true;
+ }
+ }
+
+ if (_gd.IsTBDR)
+ {
+ if (!_gd.IsMoltenVk)
+ {
+ if (!anyIsNonAttachment)
+ {
+ // This case is a feedback loop. To prevent this from causing an absolute performance disaster,
+ // remove the barriers entirely.
+ // If this is not here, there will be a lot of single draw render passes.
+ // TODO: explicit handling for feedback loops, likely outside this class.
- if (!_gd.IsMoltenVk)
+ _queuedBarrierCount -= _imageBarriers.Count;
+ _imageBarriers.Clear();
+ }
+ else
+ {
+ // TBDR GPUs are sensitive to barriers, so we need to end the pass to ensure the data is available.
+ // Metal already has hazard tracking so MVK doesn't need this.
+ endRenderPass();
+ inRenderPass = false;
+ }
+ }
+ }
+ else
{
+ // Generic pipeline memory barriers will work for desktop GPUs.
+ // They do require a few more access flags on the subpass dependency, though.
foreach (var barrier in _imageBarriers)
{
- _memoryBarriers.Add(new BarrierWithStageFlags<MemoryBarrier>(
+ _memoryBarriers.Add(new BarrierWithStageFlags<MemoryBarrier, int>(
barrier.Flags,
new MemoryBarrier()
{
@@ -190,6 +355,22 @@ namespace Ryujinx.Graphics.Vulkan
}
}
+ if (inRenderPass && _memoryBarriers.Count > 0)
+ {
+ PipelineStageFlags allFlags = PipelineStageFlags.None;
+
+ foreach (var barrier in _memoryBarriers)
+ {
+ allFlags |= barrier.Flags.Dest;
+ }
+
+ if (allFlags.HasFlag(PipelineStageFlags.DrawIndirectBit) || !_gd.SupportsRenderPassBarrier(allFlags))
+ {
+ endRenderPass();
+ inRenderPass = false;
+ }
+ }
+
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);
@@ -198,14 +379,14 @@ namespace Ryujinx.Graphics.Vulkan
{
PipelineStageFlags srcStageFlags = flags.Source;
- if (insideRenderPass)
+ if (inRenderPass)
{
// Inside a render pass, barrier stages can only be from rasterization.
srcStageFlags &= ~PipelineStageFlags.ComputeShaderBit;
}
_gd.Api.CmdPipelineBarrier(
- cb,
+ cbs.CommandBuffer,
srcStageFlags,
flags.Dest,
0,
@@ -219,6 +400,41 @@ namespace Ryujinx.Graphics.Vulkan
}
}
+ private void QueueIncoherentBarrier(IncoherentBarrierType type)
+ {
+ if (type > _queuedIncoherentBarrier)
+ {
+ _queuedIncoherentBarrier = type;
+ }
+ }
+
+ public void QueueTextureBarrier()
+ {
+ QueueIncoherentBarrier(IncoherentBarrierType.Texture);
+ }
+
+ public void QueueMemoryBarrier()
+ {
+ QueueIncoherentBarrier(IncoherentBarrierType.All);
+ }
+
+ public void QueueCommandBufferBarrier()
+ {
+ QueueIncoherentBarrier(IncoherentBarrierType.CommandBuffer);
+ }
+
+ public void EnableTfbBarriers(bool enable)
+ {
+ if (enable)
+ {
+ _extraStages |= PipelineStageFlags.TransformFeedbackBitExt;
+ }
+ else
+ {
+ _extraStages &= ~PipelineStageFlags.TransformFeedbackBitExt;
+ }
+ }
+
public void Dispose()
{
_memoryBarrierBatch.Dispose();
diff --git a/src/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs b/src/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs
index 5a5ddf8c..c4501ca1 100644
--- a/src/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs
+++ b/src/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs
@@ -59,14 +59,14 @@ namespace Ryujinx.Graphics.Vulkan.Effects
var scalingResourceLayout = new ResourceLayoutBuilder()
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
- .Add(ResourceStages.Compute, ResourceType.Image, 0).Build();
+ .Add(ResourceStages.Compute, ResourceType.Image, 0, true).Build();
var sharpeningResourceLayout = new ResourceLayoutBuilder()
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 3)
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 4)
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
- .Add(ResourceStages.Compute, ResourceType.Image, 0).Build();
+ .Add(ResourceStages.Compute, ResourceType.Image, 0, true).Build();
_sampler = _renderer.CreateSampler(SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));
diff --git a/src/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs b/src/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs
index c1293333..70b3b32a 100644
--- a/src/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs
+++ b/src/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs
@@ -42,7 +42,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
var resourceLayout = new ResourceLayoutBuilder()
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
- .Add(ResourceStages.Compute, ResourceType.Image, 0).Build();
+ .Add(ResourceStages.Compute, ResourceType.Image, 0, true).Build();
_samplerLinear = _renderer.CreateSampler(SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));
diff --git a/src/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs b/src/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs
index 08e07f25..6d80f4a4 100644
--- a/src/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs
+++ b/src/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs
@@ -81,20 +81,20 @@ namespace Ryujinx.Graphics.Vulkan.Effects
var edgeResourceLayout = new ResourceLayoutBuilder()
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
- .Add(ResourceStages.Compute, ResourceType.Image, 0).Build();
+ .Add(ResourceStages.Compute, ResourceType.Image, 0, true).Build();
var blendResourceLayout = new ResourceLayoutBuilder()
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 3)
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 4)
- .Add(ResourceStages.Compute, ResourceType.Image, 0).Build();
+ .Add(ResourceStages.Compute, ResourceType.Image, 0, true).Build();
var neighbourResourceLayout = new ResourceLayoutBuilder()
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 3)
- .Add(ResourceStages.Compute, ResourceType.Image, 0).Build();
+ .Add(ResourceStages.Compute, ResourceType.Image, 0, true).Build();
_samplerLinear = _renderer.CreateSampler(SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));
diff --git a/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs b/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs
index ea0fd42e..5c5a8f3a 100644
--- a/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs
+++ b/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs
@@ -286,10 +286,23 @@ namespace Ryujinx.Graphics.Vulkan
_depthStencil?.Storage?.QueueLoadOpBarrier(cbs, true);
- gd.Barriers.Flush(cbs.CommandBuffer, false, null);
+ gd.Barriers.Flush(cbs, false, null, null);
}
- public (Auto<DisposableRenderPass> renderPass, Auto<DisposableFramebuffer> framebuffer) GetPassAndFramebuffer(
+ public void AddStoreOpUsage()
+ {
+ if (_colors != null)
+ {
+ foreach (var color in _colors)
+ {
+ color.Storage?.AddStoreOpUsage(false);
+ }
+ }
+
+ _depthStencil?.Storage?.AddStoreOpUsage(true);
+ }
+
+ public (RenderPassHolder rpHolder, Auto<DisposableFramebuffer> framebuffer) GetPassAndFramebuffer(
VulkanRenderer gd,
Device device,
CommandBufferScoped cbs)
diff --git a/src/Ryujinx.Graphics.Vulkan/HelperShader.cs b/src/Ryujinx.Graphics.Vulkan/HelperShader.cs
index 3efb1119..73aa95c7 100644
--- a/src/Ryujinx.Graphics.Vulkan/HelperShader.cs
+++ b/src/Ryujinx.Graphics.Vulkan/HelperShader.cs
@@ -115,7 +115,7 @@ namespace Ryujinx.Graphics.Vulkan
var strideChangeResourceLayout = new ResourceLayoutBuilder()
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1)
- .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2).Build();
+ .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2, true).Build();
_programStrideChange = gd.CreateProgramWithMinimalLayout(new[]
{
@@ -125,7 +125,7 @@ namespace Ryujinx.Graphics.Vulkan
var colorCopyResourceLayout = new ResourceLayoutBuilder()
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 0)
- .Add(ResourceStages.Compute, ResourceType.Image, 0).Build();
+ .Add(ResourceStages.Compute, ResourceType.Image, 0, true).Build();
_programColorCopyShortening = gd.CreateProgramWithMinimalLayout(new[]
{
@@ -155,7 +155,7 @@ namespace Ryujinx.Graphics.Vulkan
var convertD32S8ToD24S8ResourceLayout = new ResourceLayoutBuilder()
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1)
- .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2).Build();
+ .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2, true).Build();
_programConvertD32S8ToD24S8 = gd.CreateProgramWithMinimalLayout(new[]
{
@@ -165,7 +165,7 @@ namespace Ryujinx.Graphics.Vulkan
var convertIndexBufferResourceLayout = new ResourceLayoutBuilder()
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1)
- .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2).Build();
+ .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2, true).Build();
_programConvertIndexBuffer = gd.CreateProgramWithMinimalLayout(new[]
{
@@ -175,7 +175,7 @@ namespace Ryujinx.Graphics.Vulkan
var convertIndirectDataResourceLayout = new ResourceLayoutBuilder()
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1)
- .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2)
+ .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2, true)
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 3).Build();
_programConvertIndirectData = gd.CreateProgramWithMinimalLayout(new[]
diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs
index 2b2caeae..bda6167d 100644
--- a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs
+++ b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs
@@ -55,6 +55,7 @@ namespace Ryujinx.Graphics.Vulkan
protected FramebufferParams FramebufferParams;
private Auto<DisposableFramebuffer> _framebuffer;
+ private RenderPassHolder _rpHolder;
private Auto<DisposableRenderPass> _renderPass;
private RenderPassHolder _nullRenderPass;
private int _writtenAttachmentCount;
@@ -85,8 +86,6 @@ namespace Ryujinx.Graphics.Vulkan
private bool _tfActive;
private readonly PipelineColorBlendAttachmentState[] _storedBlend;
-
- private ulong _drawCountSinceBarrier;
public ulong DrawCount { get; private set; }
public bool RenderPassActive { get; private set; }
@@ -135,48 +134,7 @@ namespace Ryujinx.Graphics.Vulkan
public unsafe void Barrier()
{
- if (_drawCountSinceBarrier != DrawCount)
- {
- _drawCountSinceBarrier = DrawCount;
-
- // Barriers are not supported inside a render pass on Apple GPUs.
- // As a workaround, end the render pass.
- if (Gd.Vendor == Vendor.Apple)
- {
- EndRenderPass();
- }
- }
-
- MemoryBarrier memoryBarrier = new()
- {
- SType = StructureType.MemoryBarrier,
- SrcAccessMask = AccessFlags.MemoryReadBit | AccessFlags.MemoryWriteBit,
- DstAccessMask = AccessFlags.MemoryReadBit | AccessFlags.MemoryWriteBit,
- };
-
- PipelineStageFlags pipelineStageFlags = PipelineStageFlags.VertexShaderBit | PipelineStageFlags.FragmentShaderBit;
-
- if (Gd.Capabilities.SupportsGeometryShader)
- {
- pipelineStageFlags |= PipelineStageFlags.GeometryShaderBit;
- }
-
- if (Gd.Capabilities.SupportsTessellationShader)
- {
- pipelineStageFlags |= PipelineStageFlags.TessellationControlShaderBit | PipelineStageFlags.TessellationEvaluationShaderBit;
- }
-
- Gd.Api.CmdPipelineBarrier(
- CommandBuffer,
- pipelineStageFlags,
- pipelineStageFlags,
- 0,
- 1,
- memoryBarrier,
- 0,
- null,
- 0,
- null);
+ Gd.Barriers.QueueMemoryBarrier();
}
public void ComputeBarrier()
@@ -203,6 +161,7 @@ namespace Ryujinx.Graphics.Vulkan
public void BeginTransformFeedback(PrimitiveTopology topology)
{
+ Gd.Barriers.EnableTfbBarriers(true);
_tfEnabled = true;
}
@@ -249,7 +208,7 @@ namespace Ryujinx.Graphics.Vulkan
CreateRenderPass();
}
- Gd.Barriers.Flush(Cbs.CommandBuffer, RenderPassActive, EndRenderPassDelegate);
+ Gd.Barriers.Flush(Cbs, RenderPassActive, _rpHolder, EndRenderPassDelegate);
BeginRenderPass();
@@ -287,7 +246,7 @@ namespace Ryujinx.Graphics.Vulkan
CreateRenderPass();
}
- Gd.Barriers.Flush(Cbs.CommandBuffer, RenderPassActive, EndRenderPassDelegate);
+ Gd.Barriers.Flush(Cbs, RenderPassActive, _rpHolder, EndRenderPassDelegate);
BeginRenderPass();
@@ -299,24 +258,7 @@ namespace Ryujinx.Graphics.Vulkan
public unsafe void CommandBufferBarrier()
{
- MemoryBarrier memoryBarrier = new()
- {
- SType = StructureType.MemoryBarrier,
- SrcAccessMask = BufferHolder.DefaultAccessFlags,
- DstAccessMask = AccessFlags.IndirectCommandReadBit,
- };
-
- Gd.Api.CmdPipelineBarrier(
- CommandBuffer,
- PipelineStageFlags.AllCommandsBit,
- PipelineStageFlags.DrawIndirectBit,
- 0,
- 1,
- memoryBarrier,
- 0,
- null,
- 0,
- null);
+ Gd.Barriers.QueueCommandBufferBarrier();
}
public void CopyBuffer(BufferHandle source, BufferHandle destination, int srcOffset, int dstOffset, int size)
@@ -722,6 +664,7 @@ namespace Ryujinx.Graphics.Vulkan
public void EndTransformFeedback()
{
+ Gd.Barriers.EnableTfbBarriers(false);
PauseTransformFeedbackInternal();
_tfEnabled = false;
}
@@ -1408,24 +1351,7 @@ namespace Ryujinx.Graphics.Vulkan
public unsafe void TextureBarrier()
{
- MemoryBarrier memoryBarrier = new()
- {
- SType = StructureType.MemoryBarrier,
- SrcAccessMask = AccessFlags.MemoryReadBit | AccessFlags.MemoryWriteBit,
- DstAccessMask = AccessFlags.MemoryReadBit | AccessFlags.MemoryWriteBit,
- };
-
- Gd.Api.CmdPipelineBarrier(
- CommandBuffer,
- PipelineStageFlags.FragmentShaderBit,
- PipelineStageFlags.FragmentShaderBit,
- 0,
- 1,
- memoryBarrier,
- 0,
- null,
- 0,
- null);
+ Gd.Barriers.QueueTextureBarrier();
}
public void TextureBarrierTiled()
@@ -1532,12 +1458,15 @@ namespace Ryujinx.Graphics.Vulkan
// Use the null framebuffer.
_nullRenderPass ??= new RenderPassHolder(Gd, Device, new RenderPassCacheKey(), FramebufferParams);
+ _rpHolder = _nullRenderPass;
_renderPass = _nullRenderPass.GetRenderPass();
_framebuffer = _nullRenderPass.GetFramebuffer(Gd, Cbs, FramebufferParams);
}
else
{
- (_renderPass, _framebuffer) = FramebufferParams.GetPassAndFramebuffer(Gd, Device, Cbs);
+ (_rpHolder, _framebuffer) = FramebufferParams.GetPassAndFramebuffer(Gd, Device, Cbs);
+
+ _renderPass = _rpHolder.GetRenderPass();
}
}
@@ -1564,7 +1493,7 @@ namespace Ryujinx.Graphics.Vulkan
}
}
- Gd.Barriers.Flush(Cbs.CommandBuffer, RenderPassActive, EndRenderPassDelegate);
+ Gd.Barriers.Flush(Cbs, _program, RenderPassActive, _rpHolder, EndRenderPassDelegate);
_descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, PipelineBindPoint.Compute);
}
@@ -1629,7 +1558,7 @@ namespace Ryujinx.Graphics.Vulkan
}
}
- Gd.Barriers.Flush(Cbs.CommandBuffer, RenderPassActive, EndRenderPassDelegate);
+ Gd.Barriers.Flush(Cbs, _program, RenderPassActive, _rpHolder, EndRenderPassDelegate);
_descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, PipelineBindPoint.Graphics);
@@ -1708,6 +1637,8 @@ namespace Ryujinx.Graphics.Vulkan
{
if (RenderPassActive)
{
+ FramebufferParams.AddStoreOpUsage();
+
PauseTransformFeedbackInternal();
Gd.Api.CmdEndRenderPass(CommandBuffer);
SignalRenderPassEnd();
diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs b/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs
index 7d124c83..89ce10b0 100644
--- a/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs
+++ b/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs
@@ -9,13 +9,6 @@ namespace Ryujinx.Graphics.Vulkan
{
static class PipelineConverter
{
- private const AccessFlags SubpassAccessMask =
- AccessFlags.MemoryReadBit |
- AccessFlags.MemoryWriteBit |
- AccessFlags.ShaderReadBit |
- AccessFlags.ColorAttachmentWriteBit |
- AccessFlags.DepthStencilAttachmentWriteBit;
-
public static unsafe DisposableRenderPass ToRenderPass(this ProgramPipelineState state, VulkanRenderer gd, Device device)
{
const int MaxAttachments = Constants.MaxRenderTargets + 1;
@@ -108,7 +101,7 @@ namespace Ryujinx.Graphics.Vulkan
}
}
- var subpassDependency = CreateSubpassDependency();
+ var subpassDependency = CreateSubpassDependency(gd);
fixed (AttachmentDescription* pAttachmentDescs = attachmentDescs)
{
@@ -129,29 +122,33 @@ namespace Ryujinx.Graphics.Vulkan
}
}
- public static SubpassDependency CreateSubpassDependency()
+ public static SubpassDependency CreateSubpassDependency(VulkanRenderer gd)
{
+ var (access, stages) = BarrierBatch.GetSubpassAccessSuperset(gd);
+
return new SubpassDependency(
0,
0,
- PipelineStageFlags.AllGraphicsBit,
- PipelineStageFlags.AllGraphicsBit,
- SubpassAccessMask,
- SubpassAccessMask,
+ stages,
+ stages,
+ access,
+ access,
0);
}
- public unsafe static SubpassDependency2 CreateSubpassDependency2()
+ public unsafe static SubpassDependency2 CreateSubpassDependency2(VulkanRenderer gd)
{
+ var (access, stages) = BarrierBatch.GetSubpassAccessSuperset(gd);
+
return new SubpassDependency2(
StructureType.SubpassDependency2,
null,
0,
0,
- PipelineStageFlags.AllGraphicsBit,
- PipelineStageFlags.AllGraphicsBit,
- SubpassAccessMask,
- SubpassAccessMask,
+ stages,
+ stages,
+ access,
+ access,
0);
}
diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineFull.cs b/src/Ryujinx.Graphics.Vulkan/PipelineFull.cs
index 5808406d..cf65eefb 100644
--- a/src/Ryujinx.Graphics.Vulkan/PipelineFull.cs
+++ b/src/Ryujinx.Graphics.Vulkan/PipelineFull.cs
@@ -257,7 +257,7 @@ namespace Ryujinx.Graphics.Vulkan
PreloadCbs = null;
}
- Gd.Barriers.Flush(Cbs.CommandBuffer, false, null);
+ Gd.Barriers.Flush(Cbs, false, null, null);
CommandBuffer = (Cbs = Gd.CommandBufferPool.ReturnAndRent(Cbs)).CommandBuffer;
Gd.RegisterFlush();
diff --git a/src/Ryujinx.Graphics.Vulkan/RenderPassHolder.cs b/src/Ryujinx.Graphics.Vulkan/RenderPassHolder.cs
index 9edea578..b2dd0dd8 100644
--- a/src/Ryujinx.Graphics.Vulkan/RenderPassHolder.cs
+++ b/src/Ryujinx.Graphics.Vulkan/RenderPassHolder.cs
@@ -1,5 +1,7 @@
using Silk.NET.Vulkan;
using System;
+using System.Collections.Generic;
+using System.Linq;
namespace Ryujinx.Graphics.Vulkan
{
@@ -29,10 +31,13 @@ namespace Ryujinx.Graphics.Vulkan
}
}
+ private readonly record struct ForcedFence(TextureStorage Texture, PipelineStageFlags StageFlags);
+
private readonly TextureView[] _textures;
private readonly Auto<DisposableRenderPass> _renderPass;
private readonly HashTableSlim<FramebufferCacheKey, Auto<DisposableFramebuffer>> _framebuffers;
private readonly RenderPassCacheKey _key;
+ private readonly List<ForcedFence> _forcedFences;
public unsafe RenderPassHolder(VulkanRenderer gd, Device device, RenderPassCacheKey key, FramebufferParams fb)
{
@@ -105,7 +110,7 @@ namespace Ryujinx.Graphics.Vulkan
}
}
- var subpassDependency = PipelineConverter.CreateSubpassDependency();
+ var subpassDependency = PipelineConverter.CreateSubpassDependency(gd);
fixed (AttachmentDescription* pAttachmentDescs = attachmentDescs)
{
@@ -138,6 +143,8 @@ namespace Ryujinx.Graphics.Vulkan
_textures = textures;
_key = key;
+
+ _forcedFences = new List<ForcedFence>();
}
public Auto<DisposableFramebuffer> GetFramebuffer(VulkanRenderer gd, CommandBufferScoped cbs, FramebufferParams fb)
@@ -159,6 +166,37 @@ namespace Ryujinx.Graphics.Vulkan
return _renderPass;
}
+ public void AddForcedFence(TextureStorage storage, PipelineStageFlags stageFlags)
+ {
+ if (!_forcedFences.Any(fence => fence.Texture == storage))
+ {
+ _forcedFences.Add(new ForcedFence(storage, stageFlags));
+ }
+ }
+
+ public void InsertForcedFences(CommandBufferScoped cbs)
+ {
+ if (_forcedFences.Count > 0)
+ {
+ _forcedFences.RemoveAll((entry) =>
+ {
+ if (entry.Texture.Disposed)
+ {
+ return true;
+ }
+
+ entry.Texture.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, entry.StageFlags);
+
+ return false;
+ });
+ }
+ }
+
+ public bool ContainsAttachment(TextureStorage storage)
+ {
+ return _textures.Any(view => view.Storage == storage);
+ }
+
public void Dispose()
{
// Dispose all framebuffers.
diff --git a/src/Ryujinx.Graphics.Vulkan/ResourceLayoutBuilder.cs b/src/Ryujinx.Graphics.Vulkan/ResourceLayoutBuilder.cs
index 76a5ef4f..730a0a2f 100644
--- a/src/Ryujinx.Graphics.Vulkan/ResourceLayoutBuilder.cs
+++ b/src/Ryujinx.Graphics.Vulkan/ResourceLayoutBuilder.cs
@@ -23,7 +23,7 @@ namespace Ryujinx.Graphics.Vulkan
}
}
- public ResourceLayoutBuilder Add(ResourceStages stages, ResourceType type, int binding)
+ public ResourceLayoutBuilder Add(ResourceStages stages, ResourceType type, int binding, bool write = false)
{
int setIndex = type switch
{
@@ -35,7 +35,7 @@ namespace Ryujinx.Graphics.Vulkan
};
_resourceDescriptors[setIndex].Add(new ResourceDescriptor(binding, 1, type, stages));
- _resourceUsages[setIndex].Add(new ResourceUsage(binding, 1, type, stages));
+ _resourceUsages[setIndex].Add(new ResourceUsage(binding, 1, type, stages, write));
return this;
}
diff --git a/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs b/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs
index b1547b79..c9aab401 100644
--- a/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs
+++ b/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs
@@ -27,6 +27,9 @@ namespace Ryujinx.Graphics.Vulkan
public uint Stages { get; }
+ public PipelineStageFlags IncoherentBufferWriteStages { get; }
+ public PipelineStageFlags IncoherentTextureWriteStages { get; }
+
public ResourceBindingSegment[][] ClearSegments { get; }
public ResourceBindingSegment[][] BindingSegments { get; }
public DescriptorSetTemplate[] Templates { get; }
@@ -131,6 +134,7 @@ namespace Ryujinx.Graphics.Vulkan
ClearSegments = BuildClearSegments(sets);
BindingSegments = BuildBindingSegments(resourceLayout.SetUsages, out bool usesBufferTextures);
Templates = BuildTemplates(usePushDescriptors);
+ (IncoherentBufferWriteStages, IncoherentTextureWriteStages) = BuildIncoherentStages(resourceLayout.SetUsages);
// Updating buffer texture bindings using template updates crashes the Adreno driver on Windows.
UpdateTexturesWithoutTemplate = gd.IsQualcommProprietary && usesBufferTextures;
@@ -377,6 +381,73 @@ namespace Ryujinx.Graphics.Vulkan
return templates;
}
+ private PipelineStageFlags GetPipelineStages(ResourceStages stages)
+ {
+ PipelineStageFlags result = 0;
+
+ if ((stages & ResourceStages.Compute) != 0)
+ {
+ result |= PipelineStageFlags.ComputeShaderBit;
+ }
+
+ if ((stages & ResourceStages.Vertex) != 0)
+ {
+ result |= PipelineStageFlags.VertexShaderBit;
+ }
+
+ if ((stages & ResourceStages.Fragment) != 0)
+ {
+ result |= PipelineStageFlags.FragmentShaderBit;
+ }
+
+ if ((stages & ResourceStages.Geometry) != 0)
+ {
+ result |= PipelineStageFlags.GeometryShaderBit;
+ }
+
+ if ((stages & ResourceStages.TessellationControl) != 0)
+ {
+ result |= PipelineStageFlags.TessellationControlShaderBit;
+ }
+
+ if ((stages & ResourceStages.TessellationEvaluation) != 0)
+ {
+ result |= PipelineStageFlags.TessellationEvaluationShaderBit;
+ }
+
+ return result;
+ }
+
+ private (PipelineStageFlags Buffer, PipelineStageFlags Texture) BuildIncoherentStages(ReadOnlyCollection<ResourceUsageCollection> setUsages)
+ {
+ PipelineStageFlags buffer = PipelineStageFlags.None;
+ PipelineStageFlags texture = PipelineStageFlags.None;
+
+ foreach (var set in setUsages)
+ {
+ foreach (var range in set.Usages)
+ {
+ if (range.Write)
+ {
+ PipelineStageFlags stages = GetPipelineStages(range.Stages);
+
+ switch (range.Type)
+ {
+ case ResourceType.Image:
+ texture |= stages;
+ break;
+ case ResourceType.StorageBuffer:
+ case ResourceType.BufferImage:
+ buffer |= stages;
+ break;
+ }
+ }
+ }
+ }
+
+ return (buffer, texture);
+ }
+
private async Task BackgroundCompilation()
{
await Task.WhenAll(_shaders.Select(shader => shader.CompileTask));
diff --git a/src/Ryujinx.Graphics.Vulkan/TextureCopy.cs b/src/Ryujinx.Graphics.Vulkan/TextureCopy.cs
index 7c06a5df..fdc0a248 100644
--- a/src/Ryujinx.Graphics.Vulkan/TextureCopy.cs
+++ b/src/Ryujinx.Graphics.Vulkan/TextureCopy.cs
@@ -407,7 +407,7 @@ namespace Ryujinx.Graphics.Vulkan
ImageLayout.General,
ImageLayout.General);
- var subpassDependency = PipelineConverter.CreateSubpassDependency2();
+ var subpassDependency = PipelineConverter.CreateSubpassDependency2(gd);
fixed (AttachmentDescription2* pAttachmentDescs = attachmentDescs)
{
diff --git a/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs b/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs
index 1aaf2fbb..f36db68d 100644
--- a/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs
+++ b/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs
@@ -38,6 +38,8 @@ namespace Ryujinx.Graphics.Vulkan
public TextureCreateInfo Info => _info;
+ public bool Disposed { get; private set; }
+
private readonly Image _image;
private readonly Auto<DisposableImage> _imageAuto;
private readonly Auto<MemoryAllocation> _allocationAuto;
@@ -433,6 +435,17 @@ namespace Ryujinx.Graphics.Vulkan
return FormatCapabilities.IsD24S8(Info.Format) && VkFormat == VkFormat.D32SfloatS8Uint;
}
+ public void AddStoreOpUsage(bool depthStencil)
+ {
+ _lastModificationStage = depthStencil ?
+ PipelineStageFlags.LateFragmentTestsBit :
+ PipelineStageFlags.ColorAttachmentOutputBit;
+
+ _lastModificationAccess = depthStencil ?
+ AccessFlags.DepthStencilAttachmentWriteBit :
+ AccessFlags.ColorAttachmentWriteBit;
+ }
+
public void QueueLoadOpBarrier(CommandBufferScoped cbs, bool depthStencil)
{
PipelineStageFlags srcStageFlags = _lastReadStage | _lastModificationStage;
@@ -458,7 +471,7 @@ namespace Ryujinx.Graphics.Vulkan
_info.GetLayers(),
_info.Levels);
- _gd.Barriers.QueueBarrier(barrier, srcStageFlags, dstStageFlags);
+ _gd.Barriers.QueueBarrier(barrier, this, srcStageFlags, dstStageFlags);
_lastReadStage = PipelineStageFlags.None;
_lastReadAccess = AccessFlags.None;
@@ -491,7 +504,7 @@ namespace Ryujinx.Graphics.Vulkan
_info.GetLayers(),
_info.Levels);
- _gd.Barriers.QueueBarrier(barrier, _lastModificationStage, dstStageFlags);
+ _gd.Barriers.QueueBarrier(barrier, this, _lastModificationStage, dstStageFlags);
_lastModificationAccess = AccessFlags.None;
}
@@ -514,6 +527,8 @@ namespace Ryujinx.Graphics.Vulkan
public void Dispose()
{
+ Disposed = true;
+
if (_aliasedStorages != null)
{
foreach (var storage in _aliasedStorages.Values)
diff --git a/src/Ryujinx.Graphics.Vulkan/TextureView.cs b/src/Ryujinx.Graphics.Vulkan/TextureView.cs
index 52066802..eb612da7 100644
--- a/src/Ryujinx.Graphics.Vulkan/TextureView.cs
+++ b/src/Ryujinx.Graphics.Vulkan/TextureView.cs
@@ -993,7 +993,7 @@ namespace Ryujinx.Graphics.Vulkan
throw new NotImplementedException();
}
- public (Auto<DisposableRenderPass> renderPass, Auto<DisposableFramebuffer> framebuffer) GetPassAndFramebuffer(
+ public (RenderPassHolder rpHolder, Auto<DisposableFramebuffer> framebuffer) GetPassAndFramebuffer(
VulkanRenderer gd,
Device device,
CommandBufferScoped cbs,
@@ -1006,7 +1006,7 @@ namespace Ryujinx.Graphics.Vulkan
rpHolder = new RenderPassHolder(gd, device, key, fb);
}
- return (rpHolder.GetRenderPass(), rpHolder.GetFramebuffer(gd, cbs, fb));
+ return (rpHolder, rpHolder.GetFramebuffer(gd, cbs, fb));
}
public void AddRenderPass(RenderPassCacheKey key, RenderPassHolder renderPass)
diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
index e46eac95..c9ce678b 100644
--- a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
+++ b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
@@ -939,6 +939,11 @@ namespace Ryujinx.Graphics.Vulkan
ScreenCaptured?.Invoke(this, bitmap);
}
+ public bool SupportsRenderPassBarrier(PipelineStageFlags flags)
+ {
+ return !(IsMoltenVk || IsQualcommProprietary);
+ }
+
public unsafe void Dispose()
{
if (!_initialized)