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:
parent
a745913329
commit
7ccff037e8
9 changed files with 145 additions and 54 deletions
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -145,9 +145,12 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RecordScissor(Vk api, CommandBuffer commandBuffer)
|
private void RecordScissor(Vk api, CommandBuffer commandBuffer)
|
||||||
|
{
|
||||||
|
if (ScissorsCount != 0)
|
||||||
{
|
{
|
||||||
api.CmdSetScissor(commandBuffer, 0, (uint)ScissorsCount, _scissors.AsSpan());
|
api.CmdSetScissor(commandBuffer, 0, (uint)ScissorsCount, _scissors.AsSpan());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private readonly void RecordStencilMasks(Vk api, CommandBuffer commandBuffer)
|
private readonly void RecordStencilMasks(Vk api, CommandBuffer commandBuffer)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Reference in a new issue