Vulkan: Defer guest barriers, and improve image barrier timings (#7012)

* 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
This commit is contained in:
riperiperi 2024-07-18 00:21:32 +01:00 committed by GitHub
parent f77bebac80
commit 1a919e99b2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 452 additions and 156 deletions

View file

@ -74,13 +74,15 @@ namespace Ryujinx.Graphics.GAL
public int ArrayLength { get; } public int ArrayLength { get; }
public ResourceType Type { get; } public ResourceType Type { get; }
public ResourceStages Stages { 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; Binding = binding;
ArrayLength = arrayLength; ArrayLength = arrayLength;
Type = type; Type = type;
Stages = stages; Stages = stages;
Write = write;
} }
public override int GetHashCode() public override int GetHashCode()

View file

@ -78,9 +78,9 @@ namespace Ryujinx.Graphics.Gpu.Shader
ResourceStages stages = vertexAsCompute ? ResourceStages.Compute | ResourceStages.Vertex : VtgStages; ResourceStages stages = vertexAsCompute ? ResourceStages.Compute | ResourceStages.Vertex : VtgStages;
PopulateDescriptorAndUsages(stages, ResourceType.UniformBuffer, uniformSetIndex, 1, rrc.ReservedConstantBuffers - 1); 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.BufferTexture, textureSetIndex, 0, rrc.ReservedTextures);
PopulateDescriptorAndUsages(stages, ResourceType.BufferImage, imageSetIndex, 0, rrc.ReservedImages); PopulateDescriptorAndUsages(stages, ResourceType.BufferImage, imageSetIndex, 0, rrc.ReservedImages, true);
} }
/// <summary> /// <summary>
@ -91,10 +91,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <param name="setIndex">Resource set index where the resources are used</param> /// <param name="setIndex">Resource set index where the resources are used</param>
/// <param name="start">First binding number</param> /// <param name="start">First binding number</param>
/// <param name="count">Amount of bindings</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); AddDescriptor(stages, type, setIndex, start, count);
AddUsage(stages, type, setIndex, start, count); AddUsage(stages, type, setIndex, start, count, write);
} }
/// <summary> /// <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="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="binding">Binding number where the resource will be bound</param>
/// <param name="count">Number of resources bound at the binding location</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++) 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, buffer.Binding,
1, 1,
isStorage ? ResourceType.StorageBuffer : ResourceType.UniformBuffer, 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); 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)));
} }
} }

View file

@ -1,6 +1,7 @@
using Silk.NET.Vulkan; using Silk.NET.Vulkan;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace Ryujinx.Graphics.Vulkan namespace Ryujinx.Graphics.Vulkan
{ {
@ -8,22 +9,64 @@ namespace Ryujinx.Graphics.Vulkan
{ {
private const int MaxBarriersPerCall = 16; 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 VulkanRenderer _gd;
private readonly NativeArray<MemoryBarrier> _memoryBarrierBatch = new(MaxBarriersPerCall); private readonly NativeArray<MemoryBarrier> _memoryBarrierBatch = new(MaxBarriersPerCall);
private readonly NativeArray<BufferMemoryBarrier> _bufferBarrierBatch = new(MaxBarriersPerCall); private readonly NativeArray<BufferMemoryBarrier> _bufferBarrierBatch = new(MaxBarriersPerCall);
private readonly NativeArray<ImageMemoryBarrier> _imageBarrierBatch = new(MaxBarriersPerCall); private readonly NativeArray<ImageMemoryBarrier> _imageBarrierBatch = new(MaxBarriersPerCall);
private readonly List<BarrierWithStageFlags<MemoryBarrier>> _memoryBarriers = new(); private readonly List<BarrierWithStageFlags<MemoryBarrier, int>> _memoryBarriers = new();
private readonly List<BarrierWithStageFlags<BufferMemoryBarrier>> _bufferBarriers = new(); private readonly List<BarrierWithStageFlags<BufferMemoryBarrier, int>> _bufferBarriers = new();
private readonly List<BarrierWithStageFlags<ImageMemoryBarrier>> _imageBarriers = new(); private readonly List<BarrierWithStageFlags<ImageMemoryBarrier, TextureStorage>> _imageBarriers = new();
private int _queuedBarrierCount; 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) public BarrierBatch(VulkanRenderer gd)
{ {
_gd = 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> private readonly record struct StageFlags : IEquatable<StageFlags>
{ {
public readonly PipelineStageFlags Source; 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 StageFlags Flags;
public readonly T Barrier; public readonly T Barrier;
public readonly T2 Resource;
public BarrierWithStageFlags(StageFlags flags, T barrier) public BarrierWithStageFlags(StageFlags flags, T barrier)
{ {
Flags = flags; Flags = flags;
Barrier = barrier; 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); Flags = new StageFlags(srcStageFlags, dstStageFlags);
Barrier = barrier; 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++; _queuedBarrierCount++;
} }
public void QueueBarrier(MemoryBarrier barrier, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags) 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) 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) while (_queuedBarrierCount > 0)
{ {
int memoryCount = 0; int memoryCount = 0;
@ -86,20 +212,20 @@ namespace Ryujinx.Graphics.Vulkan
bool hasBarrier = false; bool hasBarrier = false;
StageFlags flags = default; StageFlags flags = default;
static void AddBarriers<T>( static void AddBarriers<T, T2>(
Span<T> target, Span<T> target,
ref int queuedBarrierCount, ref int queuedBarrierCount,
ref bool hasBarrier, ref bool hasBarrier,
ref StageFlags flags, ref StageFlags flags,
ref int count, ref int count,
List<BarrierWithStageFlags<T>> list) where T : unmanaged List<BarrierWithStageFlags<T, T2>> list) where T : unmanaged
{ {
int firstMatch = -1; int firstMatch = -1;
int end = list.Count; int end = list.Count;
for (int i = 0; i < list.Count; i++) for (int i = 0; i < list.Count; i++)
{ {
BarrierWithStageFlags<T> barrier = list[i]; BarrierWithStageFlags<T, T2> barrier = list[i];
if (!hasBarrier) 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, // 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. // 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 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. // 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, // Generally, we want to avoid this from happening in the future, so flag the texture to immediately
// notify the resource that it should add a barrier as soon as a render pass ends to avoid this in future. // 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 (!_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.
_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) foreach (var barrier in _imageBarriers)
{ {
_memoryBarriers.Add(new BarrierWithStageFlags<MemoryBarrier>( _memoryBarriers.Add(new BarrierWithStageFlags<MemoryBarrier, int>(
barrier.Flags, barrier.Flags,
new MemoryBarrier() 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(_memoryBarrierBatch.AsSpan(), ref _queuedBarrierCount, ref hasBarrier, ref flags, ref memoryCount, _memoryBarriers);
AddBarriers(_bufferBarrierBatch.AsSpan(), ref _queuedBarrierCount, ref hasBarrier, ref flags, ref bufferCount, _bufferBarriers); AddBarriers(_bufferBarrierBatch.AsSpan(), ref _queuedBarrierCount, ref hasBarrier, ref flags, ref bufferCount, _bufferBarriers);
AddBarriers(_imageBarrierBatch.AsSpan(), ref _queuedBarrierCount, ref hasBarrier, ref flags, ref imageCount, _imageBarriers); AddBarriers(_imageBarrierBatch.AsSpan(), ref _queuedBarrierCount, ref hasBarrier, ref flags, ref imageCount, _imageBarriers);
@ -198,14 +379,14 @@ namespace Ryujinx.Graphics.Vulkan
{ {
PipelineStageFlags srcStageFlags = flags.Source; PipelineStageFlags srcStageFlags = flags.Source;
if (insideRenderPass) if (inRenderPass)
{ {
// Inside a render pass, barrier stages can only be from rasterization. // Inside a render pass, barrier stages can only be from rasterization.
srcStageFlags &= ~PipelineStageFlags.ComputeShaderBit; srcStageFlags &= ~PipelineStageFlags.ComputeShaderBit;
} }
_gd.Api.CmdPipelineBarrier( _gd.Api.CmdPipelineBarrier(
cb, cbs.CommandBuffer,
srcStageFlags, srcStageFlags,
flags.Dest, flags.Dest,
0, 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() public void Dispose()
{ {
_memoryBarrierBatch.Dispose(); _memoryBarrierBatch.Dispose();

View file

@ -59,14 +59,14 @@ namespace Ryujinx.Graphics.Vulkan.Effects
var scalingResourceLayout = new ResourceLayoutBuilder() var scalingResourceLayout = new ResourceLayoutBuilder()
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2) .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1) .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() var sharpeningResourceLayout = new ResourceLayoutBuilder()
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2) .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 3) .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 3)
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 4) .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 4)
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1) .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)); _sampler = _renderer.CreateSampler(SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));

View file

@ -42,7 +42,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
var resourceLayout = new ResourceLayoutBuilder() var resourceLayout = new ResourceLayoutBuilder()
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2) .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1) .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)); _samplerLinear = _renderer.CreateSampler(SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));

View file

@ -81,20 +81,20 @@ namespace Ryujinx.Graphics.Vulkan.Effects
var edgeResourceLayout = new ResourceLayoutBuilder() var edgeResourceLayout = new ResourceLayoutBuilder()
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2) .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1) .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() var blendResourceLayout = new ResourceLayoutBuilder()
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2) .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1) .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 3) .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 3)
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 4) .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() var neighbourResourceLayout = new ResourceLayoutBuilder()
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2) .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1) .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 3) .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)); _samplerLinear = _renderer.CreateSampler(SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));

View file

@ -286,10 +286,23 @@ namespace Ryujinx.Graphics.Vulkan
_depthStencil?.Storage?.QueueLoadOpBarrier(cbs, true); _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, VulkanRenderer gd,
Device device, Device device,
CommandBufferScoped cbs) CommandBufferScoped cbs)

View file

@ -115,7 +115,7 @@ namespace Ryujinx.Graphics.Vulkan
var strideChangeResourceLayout = new ResourceLayoutBuilder() var strideChangeResourceLayout = new ResourceLayoutBuilder()
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0) .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1) .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[] _programStrideChange = gd.CreateProgramWithMinimalLayout(new[]
{ {
@ -125,7 +125,7 @@ namespace Ryujinx.Graphics.Vulkan
var colorCopyResourceLayout = new ResourceLayoutBuilder() var colorCopyResourceLayout = new ResourceLayoutBuilder()
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0) .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 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[] _programColorCopyShortening = gd.CreateProgramWithMinimalLayout(new[]
{ {
@ -155,7 +155,7 @@ namespace Ryujinx.Graphics.Vulkan
var convertD32S8ToD24S8ResourceLayout = new ResourceLayoutBuilder() var convertD32S8ToD24S8ResourceLayout = new ResourceLayoutBuilder()
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0) .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1) .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[] _programConvertD32S8ToD24S8 = gd.CreateProgramWithMinimalLayout(new[]
{ {
@ -165,7 +165,7 @@ namespace Ryujinx.Graphics.Vulkan
var convertIndexBufferResourceLayout = new ResourceLayoutBuilder() var convertIndexBufferResourceLayout = new ResourceLayoutBuilder()
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0) .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1) .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[] _programConvertIndexBuffer = gd.CreateProgramWithMinimalLayout(new[]
{ {
@ -175,7 +175,7 @@ namespace Ryujinx.Graphics.Vulkan
var convertIndirectDataResourceLayout = new ResourceLayoutBuilder() var convertIndirectDataResourceLayout = new ResourceLayoutBuilder()
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0) .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1) .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(); .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 3).Build();
_programConvertIndirectData = gd.CreateProgramWithMinimalLayout(new[] _programConvertIndirectData = gd.CreateProgramWithMinimalLayout(new[]

View file

@ -55,6 +55,7 @@ namespace Ryujinx.Graphics.Vulkan
protected FramebufferParams FramebufferParams; protected FramebufferParams FramebufferParams;
private Auto<DisposableFramebuffer> _framebuffer; private Auto<DisposableFramebuffer> _framebuffer;
private RenderPassHolder _rpHolder;
private Auto<DisposableRenderPass> _renderPass; private Auto<DisposableRenderPass> _renderPass;
private RenderPassHolder _nullRenderPass; private RenderPassHolder _nullRenderPass;
private int _writtenAttachmentCount; private int _writtenAttachmentCount;
@ -85,8 +86,6 @@ namespace Ryujinx.Graphics.Vulkan
private bool _tfActive; private bool _tfActive;
private readonly PipelineColorBlendAttachmentState[] _storedBlend; private readonly PipelineColorBlendAttachmentState[] _storedBlend;
private ulong _drawCountSinceBarrier;
public ulong DrawCount { get; private set; } public ulong DrawCount { get; private set; }
public bool RenderPassActive { get; private set; } public bool RenderPassActive { get; private set; }
@ -135,48 +134,7 @@ namespace Ryujinx.Graphics.Vulkan
public unsafe void Barrier() public unsafe void Barrier()
{ {
if (_drawCountSinceBarrier != DrawCount) Gd.Barriers.QueueMemoryBarrier();
{
_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);
} }
public void ComputeBarrier() public void ComputeBarrier()
@ -203,6 +161,7 @@ namespace Ryujinx.Graphics.Vulkan
public void BeginTransformFeedback(PrimitiveTopology topology) public void BeginTransformFeedback(PrimitiveTopology topology)
{ {
Gd.Barriers.EnableTfbBarriers(true);
_tfEnabled = true; _tfEnabled = true;
} }
@ -249,7 +208,7 @@ namespace Ryujinx.Graphics.Vulkan
CreateRenderPass(); CreateRenderPass();
} }
Gd.Barriers.Flush(Cbs.CommandBuffer, RenderPassActive, EndRenderPassDelegate); Gd.Barriers.Flush(Cbs, RenderPassActive, _rpHolder, EndRenderPassDelegate);
BeginRenderPass(); BeginRenderPass();
@ -287,7 +246,7 @@ namespace Ryujinx.Graphics.Vulkan
CreateRenderPass(); CreateRenderPass();
} }
Gd.Barriers.Flush(Cbs.CommandBuffer, RenderPassActive, EndRenderPassDelegate); Gd.Barriers.Flush(Cbs, RenderPassActive, _rpHolder, EndRenderPassDelegate);
BeginRenderPass(); BeginRenderPass();
@ -299,24 +258,7 @@ namespace Ryujinx.Graphics.Vulkan
public unsafe void CommandBufferBarrier() public unsafe void CommandBufferBarrier()
{ {
MemoryBarrier memoryBarrier = new() Gd.Barriers.QueueCommandBufferBarrier();
{
SType = StructureType.MemoryBarrier,
SrcAccessMask = BufferHolder.DefaultAccessFlags,
DstAccessMask = AccessFlags.IndirectCommandReadBit,
};
Gd.Api.CmdPipelineBarrier(
CommandBuffer,
PipelineStageFlags.AllCommandsBit,
PipelineStageFlags.DrawIndirectBit,
0,
1,
memoryBarrier,
0,
null,
0,
null);
} }
public void CopyBuffer(BufferHandle source, BufferHandle destination, int srcOffset, int dstOffset, int size) public void CopyBuffer(BufferHandle source, BufferHandle destination, int srcOffset, int dstOffset, int size)
@ -722,6 +664,7 @@ namespace Ryujinx.Graphics.Vulkan
public void EndTransformFeedback() public void EndTransformFeedback()
{ {
Gd.Barriers.EnableTfbBarriers(false);
PauseTransformFeedbackInternal(); PauseTransformFeedbackInternal();
_tfEnabled = false; _tfEnabled = false;
} }
@ -1408,24 +1351,7 @@ namespace Ryujinx.Graphics.Vulkan
public unsafe void TextureBarrier() public unsafe void TextureBarrier()
{ {
MemoryBarrier memoryBarrier = new() Gd.Barriers.QueueTextureBarrier();
{
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);
} }
public void TextureBarrierTiled() public void TextureBarrierTiled()
@ -1532,12 +1458,15 @@ namespace Ryujinx.Graphics.Vulkan
// Use the null framebuffer. // Use the null framebuffer.
_nullRenderPass ??= new RenderPassHolder(Gd, Device, new RenderPassCacheKey(), FramebufferParams); _nullRenderPass ??= new RenderPassHolder(Gd, Device, new RenderPassCacheKey(), FramebufferParams);
_rpHolder = _nullRenderPass;
_renderPass = _nullRenderPass.GetRenderPass(); _renderPass = _nullRenderPass.GetRenderPass();
_framebuffer = _nullRenderPass.GetFramebuffer(Gd, Cbs, FramebufferParams); _framebuffer = _nullRenderPass.GetFramebuffer(Gd, Cbs, FramebufferParams);
} }
else 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); _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); _descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, PipelineBindPoint.Graphics);
@ -1708,6 +1637,8 @@ namespace Ryujinx.Graphics.Vulkan
{ {
if (RenderPassActive) if (RenderPassActive)
{ {
FramebufferParams.AddStoreOpUsage();
PauseTransformFeedbackInternal(); PauseTransformFeedbackInternal();
Gd.Api.CmdEndRenderPass(CommandBuffer); Gd.Api.CmdEndRenderPass(CommandBuffer);
SignalRenderPassEnd(); SignalRenderPassEnd();

View file

@ -9,13 +9,6 @@ namespace Ryujinx.Graphics.Vulkan
{ {
static class PipelineConverter 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) public static unsafe DisposableRenderPass ToRenderPass(this ProgramPipelineState state, VulkanRenderer gd, Device device)
{ {
const int MaxAttachments = Constants.MaxRenderTargets + 1; 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) 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( return new SubpassDependency(
0, 0,
0, 0,
PipelineStageFlags.AllGraphicsBit, stages,
PipelineStageFlags.AllGraphicsBit, stages,
SubpassAccessMask, access,
SubpassAccessMask, access,
0); 0);
} }
public unsafe static SubpassDependency2 CreateSubpassDependency2() public unsafe static SubpassDependency2 CreateSubpassDependency2(VulkanRenderer gd)
{ {
var (access, stages) = BarrierBatch.GetSubpassAccessSuperset(gd);
return new SubpassDependency2( return new SubpassDependency2(
StructureType.SubpassDependency2, StructureType.SubpassDependency2,
null, null,
0, 0,
0, 0,
PipelineStageFlags.AllGraphicsBit, stages,
PipelineStageFlags.AllGraphicsBit, stages,
SubpassAccessMask, access,
SubpassAccessMask, access,
0); 0);
} }

View file

@ -257,7 +257,7 @@ namespace Ryujinx.Graphics.Vulkan
PreloadCbs = null; PreloadCbs = null;
} }
Gd.Barriers.Flush(Cbs.CommandBuffer, false, null); Gd.Barriers.Flush(Cbs, false, null, null);
CommandBuffer = (Cbs = Gd.CommandBufferPool.ReturnAndRent(Cbs)).CommandBuffer; CommandBuffer = (Cbs = Gd.CommandBufferPool.ReturnAndRent(Cbs)).CommandBuffer;
Gd.RegisterFlush(); Gd.RegisterFlush();

View file

@ -1,5 +1,7 @@
using Silk.NET.Vulkan; using Silk.NET.Vulkan;
using System; using System;
using System.Collections.Generic;
using System.Linq;
namespace Ryujinx.Graphics.Vulkan 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 TextureView[] _textures;
private readonly Auto<DisposableRenderPass> _renderPass; private readonly Auto<DisposableRenderPass> _renderPass;
private readonly HashTableSlim<FramebufferCacheKey, Auto<DisposableFramebuffer>> _framebuffers; private readonly HashTableSlim<FramebufferCacheKey, Auto<DisposableFramebuffer>> _framebuffers;
private readonly RenderPassCacheKey _key; private readonly RenderPassCacheKey _key;
private readonly List<ForcedFence> _forcedFences;
public unsafe RenderPassHolder(VulkanRenderer gd, Device device, RenderPassCacheKey key, FramebufferParams fb) 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) fixed (AttachmentDescription* pAttachmentDescs = attachmentDescs)
{ {
@ -138,6 +143,8 @@ namespace Ryujinx.Graphics.Vulkan
_textures = textures; _textures = textures;
_key = key; _key = key;
_forcedFences = new List<ForcedFence>();
} }
public Auto<DisposableFramebuffer> GetFramebuffer(VulkanRenderer gd, CommandBufferScoped cbs, FramebufferParams fb) public Auto<DisposableFramebuffer> GetFramebuffer(VulkanRenderer gd, CommandBufferScoped cbs, FramebufferParams fb)
@ -159,6 +166,37 @@ namespace Ryujinx.Graphics.Vulkan
return _renderPass; 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() public void Dispose()
{ {
// Dispose all framebuffers. // Dispose all framebuffers.

View file

@ -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 int setIndex = type switch
{ {
@ -35,7 +35,7 @@ namespace Ryujinx.Graphics.Vulkan
}; };
_resourceDescriptors[setIndex].Add(new ResourceDescriptor(binding, 1, type, stages)); _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; return this;
} }

View file

@ -27,6 +27,9 @@ namespace Ryujinx.Graphics.Vulkan
public uint Stages { get; } public uint Stages { get; }
public PipelineStageFlags IncoherentBufferWriteStages { get; }
public PipelineStageFlags IncoherentTextureWriteStages { get; }
public ResourceBindingSegment[][] ClearSegments { get; } public ResourceBindingSegment[][] ClearSegments { get; }
public ResourceBindingSegment[][] BindingSegments { get; } public ResourceBindingSegment[][] BindingSegments { get; }
public DescriptorSetTemplate[] Templates { get; } public DescriptorSetTemplate[] Templates { get; }
@ -131,6 +134,7 @@ namespace Ryujinx.Graphics.Vulkan
ClearSegments = BuildClearSegments(sets); ClearSegments = BuildClearSegments(sets);
BindingSegments = BuildBindingSegments(resourceLayout.SetUsages, out bool usesBufferTextures); BindingSegments = BuildBindingSegments(resourceLayout.SetUsages, out bool usesBufferTextures);
Templates = BuildTemplates(usePushDescriptors); Templates = BuildTemplates(usePushDescriptors);
(IncoherentBufferWriteStages, IncoherentTextureWriteStages) = BuildIncoherentStages(resourceLayout.SetUsages);
// Updating buffer texture bindings using template updates crashes the Adreno driver on Windows. // Updating buffer texture bindings using template updates crashes the Adreno driver on Windows.
UpdateTexturesWithoutTemplate = gd.IsQualcommProprietary && usesBufferTextures; UpdateTexturesWithoutTemplate = gd.IsQualcommProprietary && usesBufferTextures;
@ -377,6 +381,73 @@ namespace Ryujinx.Graphics.Vulkan
return templates; 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() private async Task BackgroundCompilation()
{ {
await Task.WhenAll(_shaders.Select(shader => shader.CompileTask)); await Task.WhenAll(_shaders.Select(shader => shader.CompileTask));

View file

@ -407,7 +407,7 @@ namespace Ryujinx.Graphics.Vulkan
ImageLayout.General, ImageLayout.General,
ImageLayout.General); ImageLayout.General);
var subpassDependency = PipelineConverter.CreateSubpassDependency2(); var subpassDependency = PipelineConverter.CreateSubpassDependency2(gd);
fixed (AttachmentDescription2* pAttachmentDescs = attachmentDescs) fixed (AttachmentDescription2* pAttachmentDescs = attachmentDescs)
{ {

View file

@ -38,6 +38,8 @@ namespace Ryujinx.Graphics.Vulkan
public TextureCreateInfo Info => _info; public TextureCreateInfo Info => _info;
public bool Disposed { get; private set; }
private readonly Image _image; private readonly Image _image;
private readonly Auto<DisposableImage> _imageAuto; private readonly Auto<DisposableImage> _imageAuto;
private readonly Auto<MemoryAllocation> _allocationAuto; private readonly Auto<MemoryAllocation> _allocationAuto;
@ -433,6 +435,17 @@ namespace Ryujinx.Graphics.Vulkan
return FormatCapabilities.IsD24S8(Info.Format) && VkFormat == VkFormat.D32SfloatS8Uint; 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) public void QueueLoadOpBarrier(CommandBufferScoped cbs, bool depthStencil)
{ {
PipelineStageFlags srcStageFlags = _lastReadStage | _lastModificationStage; PipelineStageFlags srcStageFlags = _lastReadStage | _lastModificationStage;
@ -458,7 +471,7 @@ namespace Ryujinx.Graphics.Vulkan
_info.GetLayers(), _info.GetLayers(),
_info.Levels); _info.Levels);
_gd.Barriers.QueueBarrier(barrier, srcStageFlags, dstStageFlags); _gd.Barriers.QueueBarrier(barrier, this, srcStageFlags, dstStageFlags);
_lastReadStage = PipelineStageFlags.None; _lastReadStage = PipelineStageFlags.None;
_lastReadAccess = AccessFlags.None; _lastReadAccess = AccessFlags.None;
@ -491,7 +504,7 @@ namespace Ryujinx.Graphics.Vulkan
_info.GetLayers(), _info.GetLayers(),
_info.Levels); _info.Levels);
_gd.Barriers.QueueBarrier(barrier, _lastModificationStage, dstStageFlags); _gd.Barriers.QueueBarrier(barrier, this, _lastModificationStage, dstStageFlags);
_lastModificationAccess = AccessFlags.None; _lastModificationAccess = AccessFlags.None;
} }
@ -514,6 +527,8 @@ namespace Ryujinx.Graphics.Vulkan
public void Dispose() public void Dispose()
{ {
Disposed = true;
if (_aliasedStorages != null) if (_aliasedStorages != null)
{ {
foreach (var storage in _aliasedStorages.Values) foreach (var storage in _aliasedStorages.Values)

View file

@ -993,7 +993,7 @@ namespace Ryujinx.Graphics.Vulkan
throw new NotImplementedException(); throw new NotImplementedException();
} }
public (Auto<DisposableRenderPass> renderPass, Auto<DisposableFramebuffer> framebuffer) GetPassAndFramebuffer( public (RenderPassHolder rpHolder, Auto<DisposableFramebuffer> framebuffer) GetPassAndFramebuffer(
VulkanRenderer gd, VulkanRenderer gd,
Device device, Device device,
CommandBufferScoped cbs, CommandBufferScoped cbs,
@ -1006,7 +1006,7 @@ namespace Ryujinx.Graphics.Vulkan
rpHolder = new RenderPassHolder(gd, device, key, fb); 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) public void AddRenderPass(RenderPassCacheKey key, RenderPassHolder renderPass)

View file

@ -939,6 +939,11 @@ namespace Ryujinx.Graphics.Vulkan
ScreenCaptured?.Invoke(this, bitmap); ScreenCaptured?.Invoke(this, bitmap);
} }
public bool SupportsRenderPassBarrier(PipelineStageFlags flags)
{
return !(IsMoltenVk || IsQualcommProprietary);
}
public unsafe void Dispose() public unsafe void Dispose()
{ {
if (!_initialized) if (!_initialized)