Skip to content

Commit b3b9a75

Browse files
sasvdwclaude
andcommitted
fix: transition swapchain images only after acquisition
CreateBackBuffers transitioned every swapchain image to Present and submitted that command buffer before the first acquisition. Vulkan permits use of a presentable image only between vkAcquireNextImageKHR and vkQueuePresentKHR, and a layout transition is such a use. Validation reported the violation once per image per swapchain creation. The transition existed to give the tracked layout a valid source. AcquireNextImage now sets that state to Undefined instead, which is correct because the contents of a newly acquired image are undefined. This also removes a vkQueueWaitIdle from every swapchain creation and resize. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent 53f3709 commit b3b9a75

1 file changed

Lines changed: 8 additions & 40 deletions

File tree

sources/engine/Stride.Graphics/Vulkan/SwapChainGraphicsPresenter.Vulkan.cs

Lines changed: 8 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,14 @@ private unsafe void AcquireNextImage()
232232
// Flip render targets
233233
backBuffer.SetNativeHandles(swapchainImages[currentBufferIndex].NativeImage, swapchainImages[currentBufferIndex].NativeColorAttachmentView);
234234

235+
// The contents of a freshly acquired image are undefined, so the first transition of the
236+
// frame starts from Undefined. Seeding the images to Present at creation instead would
237+
// transition them before they are acquired, which the specification forbids.
238+
backBuffer.NativeLayout = VkImageLayout.Undefined;
239+
backBuffer.NativeAccessMask = VkAccessFlags.None;
240+
backBuffer.NativePipelineStageMask = VkPipelineStageFlags.TopOfPipe;
241+
backBuffer.LayoutTracker.Set(uint.MaxValue, BarrierLayout.Undefined);
242+
235243
lock (GraphicsDevice.QueueLock)
236244
{
237245
// Signal vkAcquireNextImageKHR Fence => GraphicsDevice.CommandList (so that next command list will wait for this to complete)
@@ -637,25 +645,6 @@ private unsafe void CreateBackBuffers()
637645
viewType = VkImageViewType.Image2D,
638646
};
639647

640-
// We initialize swapchain images to PresentSource, since we swap them out while in this layout.
641-
backBuffer.NativeAccessMask = VkAccessFlags.MemoryRead;
642-
backBuffer.NativeLayout = VkImageLayout.PresentSrcKHR;
643-
644-
var imageMemoryBarrier = new VkImageMemoryBarrier
645-
{
646-
sType = VkStructureType.ImageMemoryBarrier,
647-
subresourceRange = new VkImageSubresourceRange(VkImageAspectFlags.Color, 0, 1, 0, 1),
648-
oldLayout = VkImageLayout.Undefined,
649-
newLayout = VkImageLayout.PresentSrcKHR,
650-
srcAccessMask = VkAccessFlags.None,
651-
dstAccessMask = VkAccessFlags.MemoryRead
652-
};
653-
654-
var commandBuffer = GraphicsDevice.NativeCopyCommandPools.Value.GetObject(0);
655-
656-
var beginInfo = new VkCommandBufferBeginInfo { sType = VkStructureType.CommandBufferBeginInfo };
657-
GraphicsDevice.NativeDeviceApi.vkBeginCommandBuffer(commandBuffer, &beginInfo);
658-
659648
GraphicsDevice.NativeDeviceApi.vkGetSwapchainImagesKHR(GraphicsDevice.NativeDevice, swapChain, out uint swapchainImageCount);
660649
Span<VkImage> buffers = stackalloc VkImage[(int)swapchainImageCount];
661650
GraphicsDevice.NativeDeviceApi.vkGetSwapchainImagesKHR(GraphicsDevice.NativeDevice, swapChain, buffers);
@@ -666,29 +655,8 @@ private unsafe void CreateBackBuffers()
666655
// Create image views
667656
swapchainImages[index].NativeImage = createInfo.image = buffers[index];
668657
GraphicsDevice.CheckResult(GraphicsDevice.NativeDeviceApi.vkCreateImageView(GraphicsDevice.NativeDevice, &createInfo, null, out swapchainImages[index].NativeColorAttachmentView));
669-
670-
// Transition to default layout
671-
imageMemoryBarrier.image = buffers[index];
672-
GraphicsDevice.NativeDeviceApi.vkCmdPipelineBarrier(commandBuffer, VkPipelineStageFlags.AllCommands, VkPipelineStageFlags.AllCommands, VkDependencyFlags.None, 0, null, 0, null, 1, &imageMemoryBarrier);
673-
}
674-
675-
// Close and submit
676-
GraphicsDevice.CheckResult(GraphicsDevice.NativeDeviceApi.vkEndCommandBuffer(commandBuffer));
677-
678-
lock (GraphicsDevice.QueueLock)
679-
{
680-
var submitInfo = new VkSubmitInfo
681-
{
682-
sType = VkStructureType.SubmitInfo,
683-
commandBufferCount = 1,
684-
pCommandBuffers = &commandBuffer,
685-
};
686-
GraphicsDevice.CheckResult(GraphicsDevice.NativeDeviceApi.vkQueueSubmit(GraphicsDevice.NativeCommandQueue, 1, &submitInfo, VkFence.Null));
687-
GraphicsDevice.CheckResult(GraphicsDevice.NativeDeviceApi.vkQueueWaitIdle(GraphicsDevice.NativeCommandQueue));
688658
}
689659

690-
GraphicsDevice.NativeCopyCommandPools.Value.RecycleObject(0, commandBuffer);
691-
692660
// Create submit semaphores
693661
submitSemaphores = new VkSemaphore[buffers.Length];
694662
var semaphoreCreateInfo = new VkSemaphoreCreateInfo { sType = VkStructureType.SemaphoreCreateInfo };

0 commit comments

Comments
 (0)