Better handling for feedback loops.

This commit is contained in:
riperiperi 2024-07-09 00:14:44 +01:00
parent 658ad40c16
commit 7ab99e8ea1
5 changed files with 58 additions and 5 deletions

View file

@ -298,19 +298,41 @@ namespace Ryujinx.Graphics.Vulkan
// Generally, we want to avoid this from happening in the future, so flag the texture to immediately
// emit a barrier whenever the current render pass is bound again.
bool anyIsNonAttachment = false;
foreach (BarrierWithStageFlags<ImageMemoryBarrier, TextureStorage> barrier in _imageBarriers)
{
rpHolder.AddForcedFence(barrier.Resource, barrier.Flags.Dest);
// 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)
{
// 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;
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

View file

@ -289,6 +289,19 @@ namespace Ryujinx.Graphics.Vulkan
gd.Barriers.Flush(cbs, false, null, null);
}
public void AddStoreOpUsage()
{
if (_colors != null)
{
foreach (var color in _colors)
{
color.Storage?.AddStoreOpUsage(false);
}
}
_depthStencil?.Storage?.AddStoreOpUsage(true);
}
public (RenderPassHolder rpHolder, Auto<DisposableFramebuffer> framebuffer) GetPassAndFramebuffer(
VulkanRenderer gd,
Device device,

View file

@ -1639,6 +1639,8 @@ namespace Ryujinx.Graphics.Vulkan
{
if (RenderPassActive)
{
FramebufferParams.AddStoreOpUsage();
PauseTransformFeedbackInternal();
Gd.Api.CmdEndRenderPass(CommandBuffer);
SignalRenderPassEnd();

View file

@ -192,6 +192,11 @@ namespace Ryujinx.Graphics.Vulkan
}
}
public bool ContainsAttachment(TextureStorage storage)
{
return _textures.Any(view => view.Storage == storage);
}
public void Dispose()
{
// Dispose all framebuffers.

View file

@ -435,6 +435,17 @@ namespace Ryujinx.Graphics.Vulkan
return FormatCapabilities.IsD24S8(Info.Format) && VkFormat == VkFormat.D32SfloatS8Uint;
}
public void AddStoreOpUsage(bool depthStencil)
{
_lastModificationStage = depthStencil ?
PipelineStageFlags.LateFragmentTestsBit :
PipelineStageFlags.ColorAttachmentOutputBit;
_lastModificationAccess = depthStencil ?
AccessFlags.DepthStencilAttachmentWriteBit :
AccessFlags.ColorAttachmentWriteBit;
}
public void QueueLoadOpBarrier(CommandBufferScoped cbs, bool depthStencil)
{
PipelineStageFlags srcStageFlags = _lastReadStage | _lastModificationStage;