Vulkan
Vulkan copied to clipboard
Sparse texture example leaves mips > 0 in UNDEFINED layout
The texturesparseresidency example only seems to barrier/transition image layout for mip 0. This produces validation layer errors because the other mips are in UNDEFINED:
ERROR: [1303270965][UNASSIGNED-CoreValidation-DrawState-InvalidImageLayout] : Validation Error: [ UNASSIGNED-CoreValidation-DrawState-InvalidImageLayout ] Object 0: handle = 0x52f21b8, type = VK_OBJECT_TYPE_COMMAND_BUFFER; | MessageID = 0x4dae5635 | Submitted command buffer expects VkImage 0xd20a5a000000003f[] (subresource: aspectMask 0x1 array layer 0, mip level 1) to be in layout VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL--instead, current layout is VK_IMAGE_LAYOUT_UNDEFINED.
And similarly for mips 2-13.
The overload of vks::tools::setImageLayout that is called within the sample only transitions the first mip in the first slice (I assumed a shortcut for the common case of a render target with no mips/slices). I locally switched to store the subresource range and use the other overload, but there may be a better fix you prefer especially if you don't want to transition the whole image every time:
diff --git a/examples/texturesparseresidency/texturesparseresidency.cpp b/examples/texturesparseresidency/texturesparseresidency.cpp
index 1c7a348..a47bfe1 100644
--- a/examples/texturesparseresidency/texturesparseresidency.cpp
+++ b/examples/texturesparseresidency/texturesparseresidency.cpp
@@ -187,6 +187,8 @@ void VulkanExample::prepareSparseTexture(uint32_t width, uint32_t height, uint32
texture.layerCount = layerCount;
texture.format = format;
+ texture.subRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, texture.mipLevels, 0, 1 };
+
// Get device properties for the requested texture format
VkFormatProperties formatProperties;
vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &formatProperties);
@@ -236,7 +238,7 @@ void VulkanExample::prepareSparseTexture(uint32_t width, uint32_t height, uint32
VK_CHECK_RESULT(vkCreateImage(device, &sparseImageCreateInfo, nullptr, &texture.image));
VkCommandBuffer copyCmd = vulkanDevice->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);
- vks::tools::setImageLayout(copyCmd, texture.image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
+ vks::tools::setImageLayout(copyCmd, texture.image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, texture.subRange);
vulkanDevice->flushCommandBuffer(copyCmd, queue);
// Get memory requirements
@@ -733,7 +735,7 @@ void VulkanExample::uploadContent(VirtualTexturePage page, VkImage image)
}
VkCommandBuffer copyCmd = vulkanDevice->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);
- vks::tools::setImageLayout(copyCmd, image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
+ vks::tools::setImageLayout(copyCmd, image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, texture.subRange, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
VkBufferImageCopy region{};
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.imageSubresource.layerCount = 1;
@@ -741,7 +743,7 @@ void VulkanExample::uploadContent(VirtualTexturePage page, VkImage image)
region.imageOffset = page.offset;
region.imageExtent = page.extent;
vkCmdCopyBufferToImage(copyCmd, imageBuffer.buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion);
- vks::tools::setImageLayout(copyCmd, image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
+ vks::tools::setImageLayout(copyCmd, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, texture.subRange, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
vulkanDevice->flushCommandBuffer(copyCmd, queue);
imageBuffer.destroy();
@@ -850,7 +852,7 @@ void VulkanExample::fillMipTail()
}
VkCommandBuffer copyCmd = vulkanDevice->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);
- vks::tools::setImageLayout(copyCmd, texture.image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
+ vks::tools::setImageLayout(copyCmd, texture.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, texture.subRange, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
VkBufferImageCopy region{};
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.imageSubresource.layerCount = 1;
@@ -858,7 +860,7 @@ void VulkanExample::fillMipTail()
region.imageOffset = {};
region.imageExtent = { width, height, depth };
vkCmdCopyBufferToImage(copyCmd, imageBuffer.buffer, texture.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion);
- vks::tools::setImageLayout(copyCmd, texture.image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
+ vks::tools::setImageLayout(copyCmd, texture.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, texture.subRange, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
vulkanDevice->flushCommandBuffer(copyCmd, queue);
imageBuffer.destroy();
diff --git a/examples/texturesparseresidency/texturesparseresidency.h b/examples/texturesparseresidency/texturesparseresidency.h
index bcc1958..05adaa7 100644
--- a/examples/texturesparseresidency/texturesparseresidency.h
+++ b/examples/texturesparseresidency/texturesparseresidency.h
@@ -73,6 +73,7 @@ public:
uint32_t width, height;
uint32_t mipLevels;
uint32_t layerCount;
+ VkImageSubresourceRange subRange;
} texture;
vkglTF::Model plane;
The dreaded sparse residency sample ;) I never got myself to put much work into it.
Thanks for raising this issue and posting your fix. I'll take a look at it and see if this is viable.
Looks like I fixed this at some point using a very similar approach. At least I'm not getting any validation layer errors with the current state of the sample.