forked from Mirror/Ryujinx
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:
parent
f77bebac80
commit
1a919e99b2
18 changed files with 452 additions and 156 deletions
|
@ -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()
|
||||||
|
|
|
@ -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)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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));
|
||||||
|
|
||||||
|
|
|
@ -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));
|
||||||
|
|
||||||
|
|
|
@ -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));
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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[]
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Reference in a new issue