From 1f6d425933ff3abe41d67b47c46b7e5875402a9d Mon Sep 17 00:00:00 2001 From: neither-nor Date: Tue, 28 Sep 2021 15:29:01 +0800 Subject: [PATCH] Fix texturesparseresidency memory and image layout bugs --- .../texturesparseresidency.cpp | 55 ++++++++++++++----- .../texturesparseresidency.h | 8 ++- 2 files changed, 45 insertions(+), 18 deletions(-) diff --git a/examples/texturesparseresidency/texturesparseresidency.cpp b/examples/texturesparseresidency/texturesparseresidency.cpp index 7b82a870..246c88f2 100644 --- a/examples/texturesparseresidency/texturesparseresidency.cpp +++ b/examples/texturesparseresidency/texturesparseresidency.cpp @@ -29,11 +29,11 @@ bool VirtualTexturePage::resident() } // Allocate Vulkan memory for the virtual page -void VirtualTexturePage::allocate(VkDevice device, uint32_t memoryTypeIndex) +bool VirtualTexturePage::allocate(VkDevice device, uint32_t memoryTypeIndex) { if (imageMemoryBind.memory != VK_NULL_HANDLE) { - return; + return false; }; imageMemoryBind = {}; @@ -52,16 +52,20 @@ void VirtualTexturePage::allocate(VkDevice device, uint32_t memoryTypeIndex) imageMemoryBind.subresource = subResource; imageMemoryBind.extent = extent; imageMemoryBind.offset = offset; + return true; } // Release Vulkan memory allocated for this page -void VirtualTexturePage::release(VkDevice device) +bool VirtualTexturePage::release(VkDevice device) { + del= false; if (imageMemoryBind.memory != VK_NULL_HANDLE) { vkFreeMemory(device, imageMemoryBind.memory, nullptr); imageMemoryBind.memory = VK_NULL_HANDLE; + return true; } + return false; } /* @@ -81,19 +85,24 @@ VirtualTexturePage* VirtualTexture::addPage(VkOffset3D offset, VkExtent3D extent newPage.imageMemoryBind = {}; newPage.imageMemoryBind.offset = offset; newPage.imageMemoryBind.extent = extent; + newPage.del = false; pages.push_back(newPage); return &pages.back(); } // Call before sparse binding to update memory bind list etc. -void VirtualTexture::updateSparseBindInfo() +void VirtualTexture::updateSparseBindInfo(std::vector &bindingChangedPages, bool del) { // Update list of memory-backed sparse image memory binds //sparseImageMemoryBinds.resize(pages.size()); sparseImageMemoryBinds.clear(); - for (auto page : pages) + for (auto page : bindingChangedPages) { sparseImageMemoryBinds.push_back(page.imageMemoryBind); + if (del) + { + sparseImageMemoryBinds[sparseImageMemoryBinds.size() - 1].memory = VK_NULL_HANDLE; + } } // Update sparse bind info bindSparseInfo = vks::initializers::bindSparseInfo(); @@ -186,6 +195,7 @@ 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); @@ -235,7 +245,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 @@ -420,7 +430,7 @@ void VulkanExample::prepareSparseTexture(uint32_t width, uint32_t height, uint32 VK_CHECK_RESULT(vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &bindSparseSemaphore)); // Prepare bind sparse info for reuse in queue submission - texture.updateSparseBindInfo(); + texture.updateSparseBindInfo(texture.pages); // Bind to queue // todo: in draw? @@ -732,7 +742,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; @@ -740,7 +750,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(); @@ -754,16 +764,20 @@ void VulkanExample::fillRandomPages() std::uniform_real_distribution rndDist(0.0f, 1.0f); std::vector updatedPages; + std::vector bindingChangedPages; for (auto& page : texture.pages) { if (rndDist(rndEngine) < 0.5f) { continue; } - page.allocate(device, texture.memoryTypeIndex); + if (page.allocate(device, texture.memoryTypeIndex)) + { + bindingChangedPages.push_back(page); + } updatedPages.push_back(page); } // Update sparse queue binding - texture.updateSparseBindInfo(); + texture.updateSparseBindInfo(bindingChangedPages); VkFenceCreateInfo fenceInfo = vks::initializers::fenceCreateInfo(VK_FLAGS_NONE); VkFence fence; VK_CHECK_RESULT(vkCreateFence(device, &fenceInfo, nullptr, &fence)); @@ -849,7 +863,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; @@ -857,7 +871,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(); @@ -872,21 +886,32 @@ void VulkanExample::flushRandomPages() std::uniform_real_distribution rndDist(0.0f, 1.0f); std::vector updatedPages; + std::vector bindingChangedPages; for (auto& page : texture.pages) { if (rndDist(rndEngine) < 0.5f) { continue; } - page.release(device); + if (page.imageMemoryBind.memory != VK_NULL_HANDLE){ + page.del = true; + bindingChangedPages.push_back(page); + } } // Update sparse queue binding - texture.updateSparseBindInfo(); + texture.updateSparseBindInfo(bindingChangedPages, true); VkFenceCreateInfo fenceInfo = vks::initializers::fenceCreateInfo(VK_FLAGS_NONE); VkFence fence; VK_CHECK_RESULT(vkCreateFence(device, &fenceInfo, nullptr, &fence)); vkQueueBindSparse(queue, 1, &texture.bindSparseInfo, fence); vkWaitForFences(device, 1, &fence, VK_TRUE, UINT64_MAX); + for (auto& page : texture.pages) + { + if (page.del) + { + page.release(device); + } + } } void VulkanExample::OnUpdateUIOverlay(vks::UIOverlay* overlay) diff --git a/examples/texturesparseresidency/texturesparseresidency.h b/examples/texturesparseresidency/texturesparseresidency.h index bcc19581..3b9101b3 100644 --- a/examples/texturesparseresidency/texturesparseresidency.h +++ b/examples/texturesparseresidency/texturesparseresidency.h @@ -26,11 +26,12 @@ struct VirtualTexturePage uint32_t mipLevel; // Mip level that this page belongs to uint32_t layer; // Array layer that this page belongs to uint32_t index; + bool del; VirtualTexturePage(); bool resident(); - void allocate(VkDevice device, uint32_t memoryTypeIndex); - void release(VkDevice device); + bool allocate(VkDevice device, uint32_t memoryTypeIndex); + bool release(VkDevice device); }; // Virtual texture object containing all pages @@ -55,7 +56,7 @@ struct VirtualTexture } mipTailInfo; VirtualTexturePage *addPage(VkOffset3D offset, VkExtent3D extent, const VkDeviceSize size, const uint32_t mipLevel, uint32_t layer); - void updateSparseBindInfo(); + void updateSparseBindInfo(std::vector &bindingChangedPages, bool del = false); // @todo: replace with dtor? void destroy(); }; @@ -73,6 +74,7 @@ public: uint32_t width, height; uint32_t mipLevels; uint32_t layerCount; + VkImageSubresourceRange subRange; } texture; vkglTF::Model plane;