Fix some Vulkan validation errors (mostly related to barriers) (#5603)

* Replace image barriers inside render pass with more generic memory barrier

* Remove forceStorage since it was creating images with storage bit for formats that are not StorageImage compatible

* Add missing flags on subpass dependency

* Don't call vkCmdSetScissor with a scissor count of 0

* One semaphore per swapchain image

* Remove compute stage from read to write barriers

* Try to improve Pipeline.Barrier nonsense

* Set PipelineStateFlags based on supported stages
This commit is contained in:
gdkchan 2023-09-14 14:58:11 -03:00 committed by GitHub
parent a745913329
commit 7ccff037e8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 145 additions and 54 deletions

View file

@ -257,14 +257,22 @@ namespace Ryujinx.Graphics.Vulkan
if (realIndex != -1) if (realIndex != -1)
{ {
_colors[realIndex].Storage?.InsertReadToWriteBarrier(cbs, AccessFlags.ColorAttachmentWriteBit, PipelineStageFlags.ColorAttachmentOutputBit); _colors[realIndex].Storage?.InsertReadToWriteBarrier(
cbs,
AccessFlags.ColorAttachmentWriteBit,
PipelineStageFlags.ColorAttachmentOutputBit,
insideRenderPass: true);
} }
} }
} }
public void InsertClearBarrierDS(CommandBufferScoped cbs) public void InsertClearBarrierDS(CommandBufferScoped cbs)
{ {
_depthStencil?.Storage?.InsertReadToWriteBarrier(cbs, AccessFlags.DepthStencilAttachmentWriteBit, PipelineStageFlags.LateFragmentTestsBit); _depthStencil?.Storage?.InsertReadToWriteBarrier(
cbs,
AccessFlags.DepthStencilAttachmentWriteBit,
PipelineStageFlags.LateFragmentTestsBit,
insideRenderPass: true);
} }
} }
} }

View file

@ -41,6 +41,7 @@ namespace Ryujinx.Graphics.Vulkan
public readonly bool SupportsPreciseOcclusionQueries; public readonly bool SupportsPreciseOcclusionQueries;
public readonly bool SupportsPipelineStatisticsQuery; public readonly bool SupportsPipelineStatisticsQuery;
public readonly bool SupportsGeometryShader; public readonly bool SupportsGeometryShader;
public readonly bool SupportsTessellationShader;
public readonly bool SupportsViewportArray2; public readonly bool SupportsViewportArray2;
public readonly bool SupportsHostImportedMemory; public readonly bool SupportsHostImportedMemory;
public readonly bool SupportsDepthClipControl; public readonly bool SupportsDepthClipControl;
@ -77,6 +78,7 @@ namespace Ryujinx.Graphics.Vulkan
bool supportsPreciseOcclusionQueries, bool supportsPreciseOcclusionQueries,
bool supportsPipelineStatisticsQuery, bool supportsPipelineStatisticsQuery,
bool supportsGeometryShader, bool supportsGeometryShader,
bool supportsTessellationShader,
bool supportsViewportArray2, bool supportsViewportArray2,
bool supportsHostImportedMemory, bool supportsHostImportedMemory,
bool supportsDepthClipControl, bool supportsDepthClipControl,
@ -112,6 +114,7 @@ namespace Ryujinx.Graphics.Vulkan
SupportsPreciseOcclusionQueries = supportsPreciseOcclusionQueries; SupportsPreciseOcclusionQueries = supportsPreciseOcclusionQueries;
SupportsPipelineStatisticsQuery = supportsPipelineStatisticsQuery; SupportsPipelineStatisticsQuery = supportsPipelineStatisticsQuery;
SupportsGeometryShader = supportsGeometryShader; SupportsGeometryShader = supportsGeometryShader;
SupportsTessellationShader = supportsTessellationShader;
SupportsViewportArray2 = supportsViewportArray2; SupportsViewportArray2 = supportsViewportArray2;
SupportsHostImportedMemory = supportsHostImportedMemory; SupportsHostImportedMemory = supportsHostImportedMemory;
SupportsDepthClipControl = supportsDepthClipControl; SupportsDepthClipControl = supportsDepthClipControl;

View file

@ -149,10 +149,22 @@ namespace Ryujinx.Graphics.Vulkan
DstAccessMask = 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( Gd.Api.CmdPipelineBarrier(
CommandBuffer, CommandBuffer,
PipelineStageFlags.FragmentShaderBit, pipelineStageFlags,
PipelineStageFlags.FragmentShaderBit, pipelineStageFlags,
0, 0,
1, 1,
memoryBarrier, memoryBarrier,

View file

@ -9,8 +9,12 @@ namespace Ryujinx.Graphics.Vulkan
{ {
static class PipelineConverter static class PipelineConverter
{ {
private const AccessFlags SubpassSrcAccessMask = AccessFlags.MemoryReadBit | AccessFlags.MemoryWriteBit | AccessFlags.ColorAttachmentWriteBit; private const AccessFlags SubpassAccessMask =
private const AccessFlags SubpassDstAccessMask = AccessFlags.MemoryReadBit | AccessFlags.MemoryWriteBit | AccessFlags.ShaderReadBit; 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)
{ {
@ -132,8 +136,8 @@ namespace Ryujinx.Graphics.Vulkan
0, 0,
PipelineStageFlags.AllGraphicsBit, PipelineStageFlags.AllGraphicsBit,
PipelineStageFlags.AllGraphicsBit, PipelineStageFlags.AllGraphicsBit,
SubpassSrcAccessMask, SubpassAccessMask,
SubpassDstAccessMask, SubpassAccessMask,
0); 0);
} }
@ -146,8 +150,8 @@ namespace Ryujinx.Graphics.Vulkan
0, 0,
PipelineStageFlags.AllGraphicsBit, PipelineStageFlags.AllGraphicsBit,
PipelineStageFlags.AllGraphicsBit, PipelineStageFlags.AllGraphicsBit,
SubpassSrcAccessMask, SubpassAccessMask,
SubpassDstAccessMask, SubpassAccessMask,
0); 0);
} }

View file

@ -146,7 +146,10 @@ namespace Ryujinx.Graphics.Vulkan
private void RecordScissor(Vk api, CommandBuffer commandBuffer) private void RecordScissor(Vk api, CommandBuffer commandBuffer)
{ {
api.CmdSetScissor(commandBuffer, 0, (uint)ScissorsCount, _scissors.AsSpan()); if (ScissorsCount != 0)
{
api.CmdSetScissor(commandBuffer, 0, (uint)ScissorsCount, _scissors.AsSpan());
}
} }
private readonly void RecordStencilMasks(Vk api, CommandBuffer commandBuffer) private readonly void RecordStencilMasks(Vk api, CommandBuffer commandBuffer)

View file

@ -78,7 +78,7 @@ namespace Ryujinx.Graphics.Vulkan
var sampleCountFlags = ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)info.Samples); var sampleCountFlags = ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)info.Samples);
var usage = GetImageUsage(info.Format, info.Target, gd.Capabilities.SupportsShaderStorageImageMultisample, forceStorage: true); var usage = GetImageUsage(info.Format, info.Target, gd.Capabilities.SupportsShaderStorageImageMultisample);
var flags = ImageCreateFlags.CreateMutableFormatBit; var flags = ImageCreateFlags.CreateMutableFormatBit;
@ -291,7 +291,7 @@ namespace Ryujinx.Graphics.Vulkan
} }
} }
public static ImageUsageFlags GetImageUsage(Format format, Target target, bool supportsMsStorage, bool forceStorage = false) public static ImageUsageFlags GetImageUsage(Format format, Target target, bool supportsMsStorage)
{ {
var usage = DefaultUsageFlags; var usage = DefaultUsageFlags;
@ -304,7 +304,7 @@ namespace Ryujinx.Graphics.Vulkan
usage |= ImageUsageFlags.ColorAttachmentBit; usage |= ImageUsageFlags.ColorAttachmentBit;
} }
if (((forceStorage && !format.IsDepthOrStencil()) || format.IsImageCompatible()) && (supportsMsStorage || !target.IsMultisample())) if (format.IsImageCompatible() && (supportsMsStorage || !target.IsMultisample()))
{ {
usage |= ImageUsageFlags.StorageBit; usage |= ImageUsageFlags.StorageBit;
} }
@ -440,25 +440,27 @@ namespace Ryujinx.Graphics.Vulkan
_lastModificationStage = stage; _lastModificationStage = stage;
} }
public void InsertReadToWriteBarrier(CommandBufferScoped cbs, AccessFlags dstAccessFlags, PipelineStageFlags dstStageFlags) public void InsertReadToWriteBarrier(CommandBufferScoped cbs, AccessFlags dstAccessFlags, PipelineStageFlags dstStageFlags, bool insideRenderPass)
{ {
if (_lastReadAccess != AccessFlags.None) var lastReadStage = _lastReadStage;
{
ImageAspectFlags aspectFlags = Info.Format.ConvertAspectFlags();
TextureView.InsertImageBarrier( if (insideRenderPass)
{
// We can't have barrier from compute inside a render pass,
// as it is invalid to specify compute in the subpass dependency stage mask.
lastReadStage &= ~PipelineStageFlags.ComputeShaderBit;
}
if (lastReadStage != PipelineStageFlags.None)
{
TextureView.InsertMemoryBarrier(
_gd.Api, _gd.Api,
cbs.CommandBuffer, cbs.CommandBuffer,
_imageAuto.Get(cbs).Value,
_lastReadAccess, _lastReadAccess,
dstAccessFlags, dstAccessFlags,
_lastReadStage, lastReadStage,
dstStageFlags, dstStageFlags);
aspectFlags,
0,
0,
_info.GetLayers(),
_info.Levels);
_lastReadAccess = AccessFlags.None; _lastReadAccess = AccessFlags.None;
_lastReadStage = PipelineStageFlags.None; _lastReadStage = PipelineStageFlags.None;
@ -472,21 +474,13 @@ namespace Ryujinx.Graphics.Vulkan
if (_lastModificationAccess != AccessFlags.None) if (_lastModificationAccess != AccessFlags.None)
{ {
ImageAspectFlags aspectFlags = Info.Format.ConvertAspectFlags(); TextureView.InsertMemoryBarrier(
TextureView.InsertImageBarrier(
_gd.Api, _gd.Api,
cbs.CommandBuffer, cbs.CommandBuffer,
_imageAuto.Get(cbs).Value,
_lastModificationAccess, _lastModificationAccess,
dstAccessFlags, dstAccessFlags,
_lastModificationStage, _lastModificationStage,
dstStageFlags, dstStageFlags);
aspectFlags,
0,
0,
_info.GetLayers(),
_info.Levels);
_lastModificationAccess = AccessFlags.None; _lastModificationAccess = AccessFlags.None;
} }

View file

@ -435,6 +435,34 @@ namespace Ryujinx.Graphics.Vulkan
ImageAspectFlags.ColorBit); ImageAspectFlags.ColorBit);
} }
public static unsafe void InsertMemoryBarrier(
Vk api,
CommandBuffer commandBuffer,
AccessFlags srcAccessMask,
AccessFlags dstAccessMask,
PipelineStageFlags srcStageMask,
PipelineStageFlags dstStageMask)
{
MemoryBarrier memoryBarrier = new()
{
SType = StructureType.MemoryBarrier,
SrcAccessMask = srcAccessMask,
DstAccessMask = dstAccessMask,
};
api.CmdPipelineBarrier(
commandBuffer,
srcStageMask,
dstStageMask,
DependencyFlags.None,
1,
memoryBarrier,
0,
null,
0,
null);
}
public static unsafe void InsertImageBarrier( public static unsafe void InsertImageBarrier(
Vk api, Vk api,
CommandBuffer commandBuffer, CommandBuffer commandBuffer,

View file

@ -327,6 +327,7 @@ namespace Ryujinx.Graphics.Vulkan
features2.Features.OcclusionQueryPrecise, features2.Features.OcclusionQueryPrecise,
_physicalDevice.PhysicalDeviceFeatures.PipelineStatisticsQuery, _physicalDevice.PhysicalDeviceFeatures.PipelineStatisticsQuery,
_physicalDevice.PhysicalDeviceFeatures.GeometryShader, _physicalDevice.PhysicalDeviceFeatures.GeometryShader,
_physicalDevice.PhysicalDeviceFeatures.TessellationShader,
_physicalDevice.IsDeviceExtensionPresent("VK_NV_viewport_array2"), _physicalDevice.IsDeviceExtensionPresent("VK_NV_viewport_array2"),
_physicalDevice.IsDeviceExtensionPresent(ExtExternalMemoryHost.ExtensionName), _physicalDevice.IsDeviceExtensionPresent(ExtExternalMemoryHost.ExtensionName),
supportsDepthClipControl && featuresDepthClipControl.DepthClipControl, supportsDepthClipControl && featuresDepthClipControl.DepthClipControl,

View file

@ -22,8 +22,10 @@ namespace Ryujinx.Graphics.Vulkan
private Image[] _swapchainImages; private Image[] _swapchainImages;
private Auto<DisposableImageView>[] _swapchainImageViews; private Auto<DisposableImageView>[] _swapchainImageViews;
private Semaphore _imageAvailableSemaphore; private Semaphore[] _imageAvailableSemaphores;
private Semaphore _renderFinishedSemaphore; private Semaphore[] _renderFinishedSemaphores;
private int _frameIndex;
private int _width; private int _width;
private int _height; private int _height;
@ -48,14 +50,6 @@ namespace Ryujinx.Graphics.Vulkan
_surface = surface; _surface = surface;
CreateSwapchain(); CreateSwapchain();
var semaphoreCreateInfo = new SemaphoreCreateInfo
{
SType = StructureType.SemaphoreCreateInfo,
};
gd.Api.CreateSemaphore(device, semaphoreCreateInfo, null, out _imageAvailableSemaphore).ThrowOnError();
gd.Api.CreateSemaphore(device, semaphoreCreateInfo, null, out _renderFinishedSemaphore).ThrowOnError();
} }
private void RecreateSwapchain() private void RecreateSwapchain()
@ -69,7 +63,22 @@ namespace Ryujinx.Graphics.Vulkan
} }
// Destroy old Swapchain. // Destroy old Swapchain.
_gd.Api.DeviceWaitIdle(_device); _gd.Api.DeviceWaitIdle(_device);
unsafe
{
for (int i = 0; i < _imageAvailableSemaphores.Length; i++)
{
_gd.Api.DestroySemaphore(_device, _imageAvailableSemaphores[i], null);
}
for (int i = 0; i < _renderFinishedSemaphores.Length; i++)
{
_gd.Api.DestroySemaphore(_device, _renderFinishedSemaphores[i], null);
}
}
_gd.SwapchainApi.DestroySwapchain(_device, oldSwapchain, Span<AllocationCallbacks>.Empty); _gd.SwapchainApi.DestroySwapchain(_device, oldSwapchain, Span<AllocationCallbacks>.Empty);
CreateSwapchain(); CreateSwapchain();
@ -151,6 +160,25 @@ namespace Ryujinx.Graphics.Vulkan
{ {
_swapchainImageViews[i] = CreateSwapchainImageView(_swapchainImages[i], surfaceFormat.Format); _swapchainImageViews[i] = CreateSwapchainImageView(_swapchainImages[i], surfaceFormat.Format);
} }
var semaphoreCreateInfo = new SemaphoreCreateInfo
{
SType = StructureType.SemaphoreCreateInfo,
};
_imageAvailableSemaphores = new Semaphore[imageCount];
for (int i = 0; i < _imageAvailableSemaphores.Length; i++)
{
_gd.Api.CreateSemaphore(_device, semaphoreCreateInfo, null, out _imageAvailableSemaphores[i]).ThrowOnError();
}
_renderFinishedSemaphores = new Semaphore[imageCount];
for (int i = 0; i < _renderFinishedSemaphores.Length; i++)
{
_gd.Api.CreateSemaphore(_device, semaphoreCreateInfo, null, out _renderFinishedSemaphores[i]).ThrowOnError();
}
} }
private unsafe Auto<DisposableImageView> CreateSwapchainImageView(Image swapchainImage, VkFormat format) private unsafe Auto<DisposableImageView> CreateSwapchainImageView(Image swapchainImage, VkFormat format)
@ -185,6 +213,7 @@ namespace Ryujinx.Graphics.Vulkan
{ {
return new SurfaceFormatKHR(VkFormat.B8G8R8A8Unorm, ColorSpaceKHR.PaceSrgbNonlinearKhr); return new SurfaceFormatKHR(VkFormat.B8G8R8A8Unorm, ColorSpaceKHR.PaceSrgbNonlinearKhr);
} }
var formatToReturn = availableFormats[0]; var formatToReturn = availableFormats[0];
if (colorSpacePassthroughEnabled) if (colorSpacePassthroughEnabled)
{ {
@ -212,6 +241,7 @@ namespace Ryujinx.Graphics.Vulkan
} }
} }
} }
return formatToReturn; return formatToReturn;
} }
@ -265,6 +295,7 @@ namespace Ryujinx.Graphics.Vulkan
_gd.PipelineInternal.AutoFlush.Present(); _gd.PipelineInternal.AutoFlush.Present();
uint nextImage = 0; uint nextImage = 0;
int semaphoreIndex = _frameIndex++ % _imageAvailableSemaphores.Length;
while (true) while (true)
{ {
@ -272,7 +303,7 @@ namespace Ryujinx.Graphics.Vulkan
_device, _device,
_swapchain, _swapchain,
ulong.MaxValue, ulong.MaxValue,
_imageAvailableSemaphore, _imageAvailableSemaphores[semaphoreIndex],
new Fence(), new Fence(),
ref nextImage); ref nextImage);
@ -411,12 +442,12 @@ namespace Ryujinx.Graphics.Vulkan
_gd.CommandBufferPool.Return( _gd.CommandBufferPool.Return(
cbs, cbs,
stackalloc[] { _imageAvailableSemaphore }, stackalloc[] { _imageAvailableSemaphores[semaphoreIndex] },
stackalloc[] { PipelineStageFlags.ColorAttachmentOutputBit }, stackalloc[] { PipelineStageFlags.ColorAttachmentOutputBit },
stackalloc[] { _renderFinishedSemaphore }); stackalloc[] { _renderFinishedSemaphores[semaphoreIndex] });
// TODO: Present queue. // TODO: Present queue.
var semaphore = _renderFinishedSemaphore; var semaphore = _renderFinishedSemaphores[semaphoreIndex];
var swapchain = _swapchain; var swapchain = _swapchain;
Result result; Result result;
@ -593,14 +624,21 @@ namespace Ryujinx.Graphics.Vulkan
{ {
unsafe unsafe
{ {
_gd.Api.DestroySemaphore(_device, _renderFinishedSemaphore, null);
_gd.Api.DestroySemaphore(_device, _imageAvailableSemaphore, null);
for (int i = 0; i < _swapchainImageViews.Length; i++) for (int i = 0; i < _swapchainImageViews.Length; i++)
{ {
_swapchainImageViews[i].Dispose(); _swapchainImageViews[i].Dispose();
} }
for (int i = 0; i < _imageAvailableSemaphores.Length; i++)
{
_gd.Api.DestroySemaphore(_device, _imageAvailableSemaphores[i], null);
}
for (int i = 0; i < _renderFinishedSemaphores.Length; i++)
{
_gd.Api.DestroySemaphore(_device, _renderFinishedSemaphores[i], null);
}
_gd.SwapchainApi.DestroySwapchain(_device, _swapchain, null); _gd.SwapchainApi.DestroySwapchain(_device, _swapchain, null);
} }