statusfailed
statusfailed

Reputation: 900

What is the best way to clear a `VkImage` to a single color?

I'm learning vulkan, and as a (very) simple project I want to simply clear a single swapchain image to a single color (red). My code works, but I get two validation errors. I would like to know:

  1. How can I fix the validation errors in my code
  2. Is there a better way to simply clear swapchain images

Regarding (2): I specifically don't want to use a graphics pipeline: in the future I would like to use a compute shader to draw directly to the screen.

My current approach

My project uses vk-bootstrap to set up, and then I try to render a single frame as follows:

  1. Acquire an image from the swapchain
  2. Record a command buffer with the following commands:
    • vkCmdPipelineBarrier
    • vkCmdClearColorImage
    • vkEndCommandBuffer
  3. Submit the command buffer to the graphics queue
  4. present the previously acquired swapchain image using vkQueuePresentKHR

The relevant code can be found below, but it seems that the validation errors arise from the calls to vkCmdClearColorImage and vkQueuePresentKHR.

Error messages

The first validation error is from the vkCmdClearColorImage call, and seems to be triggered by my choice of layout:

VkImageLayout layout = VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR;
// (... snip ...)
vkCmdClearColorImage(commandBuffer, swapChainImages[nextImageIndex], layout, &color, 1, &imageSubresourceRange);

The error message says:

[ERROR: Validation]
Validation Error: [ VUID-vkCmdClearColorImage-imageLayout-00005 ] Object 0: handle = 0xf56c9b0000000004, type = VK_OBJECT_TYPE_IMAGE; | MessageID = 0x9740ed23 | vkCmdClearColorImage(): Layout for cleared image is VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR but can only be TRANSFER_DST_OPTIMAL or GENERAL. The Vulkan spec states: imageLayout must be VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL or VK_IMAGE_LAYOUT_GENERAL (https://www.khronos.org/registry/vulkan/specs/1.2/html/vkspec.html#VUID-vkCmdClearColorImage-imageLayout-00005)

I'm confused by this message, because although the link in this error message indeed says that

imageLayout must be VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL or VK_IMAGE_LAYOUT_GENERAL

I also found this page in the spec that says

imageLayout specifies the current layout of the image subresource ranges to be cleared, and must be VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR, VK_IMAGE_LAYOUT_GENERAL or VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL

The second error is triggered by the call to vkQueuePresentKHR and is especially weird...

[ERROR: Validation]
Validation Error: [ VUID-VkPresentInfoKHR-pImageIndices-01296 ] Object 0: handle = 0x55a3c6b9e408, type = VK_OBJECT_TYPE_QUEUE; | MessageID = 0xc7aabc16 | vkQueuePresentKHR(): pSwapchains[0] images passed to present must be in layout VK_IMAGE_LAYOUT_PRESENT_SRC_KHR or VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR but is in VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR. The Vulkan spec states: Each element of pImageIndices must be the index of a presentable image acquired from the swapchain specified by the corresponding element of the pSwapchains array, and the presented image subresource must be in the VK_IMAGE_LAYOUT_PRESENT_SRC_KHR layout at the time the operation is executed on a VkDevice (https://github.com/KhronosGroup/Vulkan-Docs/search?q=)VUID-VkPresentInfoKHR-pImageIndices-01296)

... because the message seems to contradict itself (linebreaks added):

images passed to present must be in layout VK_IMAGE_LAYOUT_PRESENT_SRC_KHR
or VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR but is
in VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR

Code for step (2)

  VkCommandBufferBeginInfo beginInfo{};
  beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
  beginInfo.flags = 0;
  beginInfo.pInheritanceInfo = nullptr;
  if(vkBeginCommandBuffer(commandBuffer, &beginInfo) != VK_SUCCESS) {
    throw std::runtime_error("failed vkBeginCommandBuffer");
  }

  VkImageMemoryBarrier barrier{};
  barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
  barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
  barrier.newLayout = VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR;
  barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
  barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
  barrier.image = swapChainImages[nextImageIndex];
  barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
  barrier.subresourceRange.baseMipLevel = 0;
  barrier.subresourceRange.levelCount = 1;
  barrier.subresourceRange.baseArrayLayer = 0;
  barrier.subresourceRange.layerCount = 1;

  vkCmdPipelineBarrier(
      commandBuffer,
      VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
      VK_PIPELINE_STAGE_TRANSFER_BIT,
      0,
      0, nullptr,
      0, nullptr,
      1, &barrier);


  VkImageLayout layout = VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR;
  VkClearColorValue color = { .float32 = {1.0, 0.0, 0.0} };
  VkImageSubresourceRange imageSubresourceRange { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
  vkCmdClearColorImage(commandBuffer, swapChainImages[nextImageIndex], layout, &color, 1, &imageSubresourceRange);

  vkEndCommandBuffer(commandBuffer);

The Question (again)

So to repeat: I would like to know:

  1. How can I fix the two validation errors in my code?
  2. Is there a better way to simply clear swapchain images?

Software Versions

The vulkaninfo command reports Vulkan Instance Version 1.2.194

Upvotes: 4

Views: 2025

Answers (1)

statusfailed
statusfailed

Reputation: 900

I found a solution which works but seems kinda gross. Basically I modify step (2) to the following:

  1. Record a command buffer with the following commands:
    • vkCmdPipelineBarrier
    • vkCmdClearColorImage
    • vkCmdPipelineBarrier
    • vkEndCommandBuffer

Essentially the logical flow of this pipeline is:

  1. Using a barrier, convert image from VK_IMAGE_LAYOUT_UNDEFINED to VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL
  2. Clear image using vkCmdClearColorImage
  3. Using a barrier, convert the image to VK_IMAGE_LAYOUT_PRESENT_SRC_KHR so it can be presented

This works because:

  • vkCmdClearColorImage requires the image to have layout VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, but
  • vkQueuePresent requires the image to have layout VK_IMAGE_LAYOUT_PRESENT_SRC_KHR

Since this seems a little hacky / gross, I will leave this question open for a while to see if anyone has a better solution. For completeness, here is the new code for step (2)

Modified code for step (2)

  VkCommandBufferBeginInfo beginInfo{};
  beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
  beginInfo.flags = 0;
  beginInfo.pInheritanceInfo = nullptr;
  if(vkBeginCommandBuffer(commandBuffer, &beginInfo) != VK_SUCCESS) {
    throw std::runtime_error("failed vkBeginCommandBuffer");
  }

  //VkImageLayout clearLayout = VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR;
  VkImageLayout clearLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;

  VkImageMemoryBarrier barrier{};
  barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
  barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
  barrier.newLayout = clearLayout;
  barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
  barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
  barrier.image = swapChainImages[nextImageIndex];
  barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
  barrier.subresourceRange.baseMipLevel = 0;
  barrier.subresourceRange.levelCount = 1;
  barrier.subresourceRange.baseArrayLayer = 0;
  barrier.subresourceRange.layerCount = 1;

  vkCmdPipelineBarrier(
      commandBuffer,
      VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
      VK_PIPELINE_STAGE_TRANSFER_BIT,
      0,
      0, nullptr,
      0, nullptr,
      1, &barrier);


  VkImageLayout layout = clearLayout;
  VkClearColorValue color = { .float32 = {1.0, 0.0, 0.0} };
  VkImageSubresourceRange imageSubresourceRange { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
  vkCmdClearColorImage(commandBuffer, swapChainImages[nextImageIndex], layout, &color, 1, &imageSubresourceRange);

  // Add another barrier and put the image in VK_IMAGE_LAYOUT_PRESENT_SRC_KHR
  VkImageLayout finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
  VkImageMemoryBarrier finalBarrier{};
  finalBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
  finalBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
  finalBarrier.newLayout = finalLayout;
  finalBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
  finalBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
  finalBarrier.image = swapChainImages[nextImageIndex];
  finalBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
  finalBarrier.subresourceRange.baseMipLevel = 0;
  finalBarrier.subresourceRange.levelCount = 1;
  finalBarrier.subresourceRange.baseArrayLayer = 0;
  finalBarrier.subresourceRange.layerCount = 1;

  vkCmdPipelineBarrier(
      commandBuffer,
      VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
      VK_PIPELINE_STAGE_TRANSFER_BIT,
      0,
      0, nullptr,
      0, nullptr,
      1, &finalBarrier);

  vkEndCommandBuffer(commandBuffer);

Upvotes: 2

Related Questions