将 `VkImage` 清除为单一颜色的最佳方法是什么?
What is the best way to clear a `VkImage` to a single color?
我正在学习 vulkan,作为一个(非常)简单的项目,我想简单地将单个交换链图像清除为单一颜色(红色)。我的代码有效,但出现两个验证错误。我想知道:
- 如何修复代码中的验证错误
- 有没有更好的方法来简单地清除交换链图像
关于 (2):我特别不想使用图形管道:将来我想使用计算着色器直接绘制到屏幕上。
我目前的做法
我的项目是用vk-bootstrap设置的,然后我尝试渲染单帧如下:
- 从交换链获取图像
- 使用以下命令记录命令缓冲区:
vkCmdPipelineBarrier
vkCmdClearColorImage
vkEndCommandBuffer
- 将命令缓冲区提交到图形队列
- 使用
vkQueuePresentKHR
呈现先前获取的交换链图像
相关代码可以在下面找到,但似乎验证错误来自对vkCmdClearColorImage
和vkQueuePresentKHR
的调用。
错误信息
第一个验证错误来自 vkCmdClearColorImage
调用,似乎是由我选择 layout
:
触发的
VkImageLayout layout = VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR;
// (... snip ...)
vkCmdClearColorImage(commandBuffer, swapChainImages[nextImageIndex], layout, &color, 1, &imageSubresourceRange);
错误消息说:
[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)
我对此消息感到困惑,因为尽管此错误消息中的 link 确实表示
imageLayout must be VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL
or VK_IMAGE_LAYOUT_GENERAL
我还在规范中发现 this page
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
第二个错误是由调用 vkQueuePresentKHR
触发的,特别奇怪...
[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)
...因为消息似乎自相矛盾(添加了换行符):
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
步骤 (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);
问题(再次)
重复一遍:我想知道:
- 如何修复代码中的两个验证错误?
- 是否有更好的方法来简单地清除交换链图像?
软件版本
vulkaninfo
命令报告 Vulkan Instance Version 1.2.194
我找到了一个有效但看起来有点恶心的解决方案。基本上我将步骤 (2) 修改为以下内容:
- 使用以下命令记录一个命令缓冲区:
vkCmdPipelineBarrier
vkCmdClearColorImage
vkCmdPipelineBarrier
vkEndCommandBuffer
这个管道的逻辑流程本质上是:
- 使用屏障,将图像从
VK_IMAGE_LAYOUT_UNDEFINED
转换为 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL
- 使用
vkCmdClearColorImage
清除图像
- 使用屏障,将图像转换为
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR
以便呈现
之所以有效,是因为:
vkCmdClearColorImage
要求图像具有布局 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL
,但是
vkQueuePresent
要求图像有布局VK_IMAGE_LAYOUT_PRESENT_SRC_KHR
因为这看起来有点 hacky/gross,我将暂时搁置这个问题,看看是否有人有更好的解决方案。为了完整起见,这里是步骤 (2)
的新代码
步骤 (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);
我正在学习 vulkan,作为一个(非常)简单的项目,我想简单地将单个交换链图像清除为单一颜色(红色)。我的代码有效,但出现两个验证错误。我想知道:
- 如何修复代码中的验证错误
- 有没有更好的方法来简单地清除交换链图像
关于 (2):我特别不想使用图形管道:将来我想使用计算着色器直接绘制到屏幕上。
我目前的做法
我的项目是用vk-bootstrap设置的,然后我尝试渲染单帧如下:
- 从交换链获取图像
- 使用以下命令记录命令缓冲区:
vkCmdPipelineBarrier
vkCmdClearColorImage
vkEndCommandBuffer
- 将命令缓冲区提交到图形队列
- 使用
vkQueuePresentKHR
呈现先前获取的交换链图像
相关代码可以在下面找到,但似乎验证错误来自对vkCmdClearColorImage
和vkQueuePresentKHR
的调用。
错误信息
第一个验证错误来自 vkCmdClearColorImage
调用,似乎是由我选择 layout
:
VkImageLayout layout = VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR;
// (... snip ...)
vkCmdClearColorImage(commandBuffer, swapChainImages[nextImageIndex], layout, &color, 1, &imageSubresourceRange);
错误消息说:
[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)
我对此消息感到困惑,因为尽管此错误消息中的 link 确实表示
imageLayout must be
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL
orVK_IMAGE_LAYOUT_GENERAL
我还在规范中发现 this page
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
orVK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL
第二个错误是由调用 vkQueuePresentKHR
触发的,特别奇怪...
[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)
...因为消息似乎自相矛盾(添加了换行符):
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
步骤 (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);
问题(再次)
重复一遍:我想知道:
- 如何修复代码中的两个验证错误?
- 是否有更好的方法来简单地清除交换链图像?
软件版本
vulkaninfo
命令报告 Vulkan Instance Version 1.2.194
我找到了一个有效但看起来有点恶心的解决方案。基本上我将步骤 (2) 修改为以下内容:
- 使用以下命令记录一个命令缓冲区:
vkCmdPipelineBarrier
vkCmdClearColorImage
vkCmdPipelineBarrier
vkEndCommandBuffer
这个管道的逻辑流程本质上是:
- 使用屏障,将图像从
VK_IMAGE_LAYOUT_UNDEFINED
转换为VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL
- 使用
vkCmdClearColorImage
清除图像
- 使用屏障,将图像转换为
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR
以便呈现
之所以有效,是因为:
vkCmdClearColorImage
要求图像具有布局VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL
,但是vkQueuePresent
要求图像有布局VK_IMAGE_LAYOUT_PRESENT_SRC_KHR
因为这看起来有点 hacky/gross,我将暂时搁置这个问题,看看是否有人有更好的解决方案。为了完整起见,这里是步骤 (2)
的新代码步骤 (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);