From 92cc5315e67046eff700028275bb02c41b2884ec Mon Sep 17 00:00:00 2001 From: Brad Grantham Date: Tue, 19 Nov 2024 10:12:07 -0800 Subject: [PATCH 01/10] Add sparse image and buffer support The current trim handling for buffers and images does not support sparse buffers or sparse images. It only allows one image or buffer binding to a single memory range of a single memory object. As a result, it cannot manage sparse resources, which has led to missing asset issues in trim trace for some titles that utilize sparse resources. This commit introduces support for sparse buffers and sparse images (using opaque memory binding only), effectively resolving the missing asset issue. Originally by Ming Zheng --- .../encode/custom_vulkan_encoder_commands.h | 20 ++ framework/encode/vulkan_capture_manager.h | 151 +++++++++- framework/encode/vulkan_handle_wrappers.h | 28 ++ .../vulkan_state_tracker_initializers.h | 12 + framework/encode/vulkan_state_writer.cpp | 280 +++++++++++++++--- framework/graphics/vulkan_resources_util.cpp | 131 ++++++++ framework/graphics/vulkan_resources_util.h | 73 ++++- 7 files changed, 655 insertions(+), 40 deletions(-) diff --git a/framework/encode/custom_vulkan_encoder_commands.h b/framework/encode/custom_vulkan_encoder_commands.h index 4a75b94303..f7aa9690ab 100644 --- a/framework/encode/custom_vulkan_encoder_commands.h +++ b/framework/encode/custom_vulkan_encoder_commands.h @@ -505,6 +505,26 @@ struct CustomEncoderPostCall } }; +template <> +struct CustomEncoderPostCall +{ + template + static void Dispatch(VulkanCaptureManager* manager, VkResult result, Args... args) + { + manager->PostProcess_vkCreateBuffer(result, args...); + } +}; + +template <> +struct CustomEncoderPostCall +{ + template + static void Dispatch(VulkanCaptureManager* manager, VkResult result, Args... args) + { + manager->PostProcess_vkCreateImage(result, args...); + } +}; + template <> struct CustomEncoderPostCall { diff --git a/framework/encode/vulkan_capture_manager.h b/framework/encode/vulkan_capture_manager.h index 030442a683..b9afa95077 100644 --- a/framework/encode/vulkan_capture_manager.h +++ b/framework/encode/vulkan_capture_manager.h @@ -551,7 +551,7 @@ class VulkanCaptureManager : public ApiCaptureManager } void PostProcess_vkQueueBindSparse( - VkResult result, VkQueue, uint32_t bindInfoCount, const VkBindSparseInfo* pBindInfo, VkFence) + VkResult result, VkQueue queue, uint32_t bindInfoCount, const VkBindSparseInfo* pBindInfo, VkFence) { if (IsCaptureModeTrack() && (result == VK_SUCCESS)) { @@ -563,6 +563,110 @@ class VulkanCaptureManager : public ApiCaptureManager pBindInfo[i].signalSemaphoreCount, pBindInfo[i].pSignalSemaphores); } + + // In default mode, the capture manager uses a shared mutex to capture every API function. As a result, + // multiple threads may access the sparse resource maps concurrently. Therefore, we use a dedicated mutex + // for write access to these maps. + const std::lock_guard lock(sparse_resource_mutex); + for (uint32_t bind_info_index = 0; bind_info_index < bindInfoCount; bind_info_index++) + { + auto& bind_info = pBindInfo[bind_info_index]; + + // TODO: add device group support. In the following handling, we assume that the system only has one + // physical device or that resourceDeviceIndex and memoryDeviceIndex of VkDeviceGroupBindSparseInfo in + // the pnext chain are zero. + + if (bind_info.pBufferBinds != nullptr) + { + // The title binds sparse buffers to memory ranges, so we need to track the buffer binding + // information. The following updates will reflect the latest binding states for all buffers in this + // vkQueueBindSparse command, covering both fully-resident and partially-resident buffers. + for (uint32_t buffer_bind_index = 0; buffer_bind_index < bind_info.bufferBindCount; + buffer_bind_index++) + { + auto& buffer_bind = bind_info.pBufferBinds[buffer_bind_index]; + auto sparse_buffer = buffer_bind.buffer; + auto wrapper = vulkan_wrappers::GetWrapper(sparse_buffer); + + if (wrapper != nullptr) + { + wrapper->sparse_bind_queue = queue; + for (uint32_t bind_memory_range_index = 0; bind_memory_range_index < buffer_bind.bindCount; + bind_memory_range_index++) + { + auto& bind_memory_range = buffer_bind.pBinds[bind_memory_range_index]; + graphics::UpdateSparseMemoryBindMap(wrapper->sparse_memory_bind_map, bind_memory_range); + } + } + } + } + + if (bind_info.pImageOpaqueBinds != nullptr) + { + // The title binds sparse images to opaque memory ranges, so we need to track the image binding + // information. The following handling will update the latest binding states for all images in this + // vkQueueBindSparse command, which utilizes opaque memory binding. There are two cases covered by + // the tracking. In the first case, the sparse image exclusively uses opaque memory binding. For + // this case, the target title treats the binding memory ranges as a linear unified region. This + // should represent a fully-resident binding because this linear region is entirely opaque, meaning + // there is no application-visible mapping between texel locations and memory offsets. In another + // case, the image utilizes subresource sparse memory binding, just binding only its mip tail region + // to an opaque memory range. For this situation, we use the sparse_opaque_memory_bind_map and + // sparse_subresource_memory_bind_map of the image wrapper to track the subresource bindings and + // opaque bindings separately. + for (uint32_t image_opaque_bind_index = 0; image_opaque_bind_index < bind_info.imageOpaqueBindCount; + image_opaque_bind_index++) + { + auto& image_opaque_bind = bind_info.pImageOpaqueBinds[image_opaque_bind_index]; + auto sparse_image = image_opaque_bind.image; + auto wrapper = vulkan_wrappers::GetWrapper(sparse_image); + + if (wrapper != nullptr) + { + wrapper->sparse_bind_queue = queue; + + for (uint32_t bind_memory_range_index = 0; + bind_memory_range_index < image_opaque_bind.bindCount; + bind_memory_range_index++) + { + auto& bind_memory_range = image_opaque_bind.pBinds[bind_memory_range_index]; + graphics::UpdateSparseMemoryBindMap(wrapper->sparse_opaque_memory_bind_map, + bind_memory_range); + } + } + } + } + + if (bind_info.pImageBinds != nullptr) + { + // The title binds subresources of a sparse image to memory ranges, which requires us to keep track + // of the sparse image subresource binding information. It's important to note that while the image + // mainly use subresource sparse memory binding, its mip tail region must be bound to an opaque + // memory range. Therefore, we use the sparse_opaque_memory_bind_map and + // sparse_subresource_memory_bind_map of the image wrapper to separately track both the + // subresource bindings and the opaque bindings. + for (uint32_t image_bind_index = 0; image_bind_index < bind_info.imageBindCount; image_bind_index++) + { + auto& image_bind = bind_info.pImageBinds[image_bind_index]; + auto sparse_image = image_bind.image; + auto wrapper = vulkan_wrappers::GetWrapper(sparse_image); + + if (wrapper != nullptr) + { + wrapper->sparse_bind_queue = queue; + + for (uint32_t bind_memory_range_index = 0; bind_memory_range_index < image_bind.bindCount; + bind_memory_range_index++) + { + auto& bind_memory_range = image_bind.pBinds[bind_memory_range_index]; + // TODO: Implement handling for tracking binding information of sparse image + // subresources. + GFXRECON_LOG_ERROR_ONCE("Binding of sparse image blocks is not supported!"); + } + } + } + } + } } } @@ -842,6 +946,50 @@ class VulkanCaptureManager : public ApiCaptureManager } } + void PostProcess_vkCreateBuffer(VkResult result, + VkDevice device, + const VkBufferCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkBuffer* pBuffer) + { + if (IsCaptureModeTrack() && (result == VK_SUCCESS) && (pCreateInfo != nullptr)) + { + assert(state_tracker_ != nullptr); + + auto buffer_wrapper = vulkan_wrappers::GetWrapper(*pBuffer); + + if (buffer_wrapper->is_sparse_buffer) + { + // We will need to set the bind_device for handling sparse buffers. There will be no subsequent + // vkBindBufferMemory, vkBindBufferMemory2 or vkBindBufferMemory2KHR calls for sparse buffer, so we + // assign bind_device to the device that created the buffer. + buffer_wrapper->bind_device = vulkan_wrappers::GetWrapper(device); + } + } + } + + void PostProcess_vkCreateImage(VkResult result, + VkDevice device, + const VkImageCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkImage* pImage) + { + if (IsCaptureModeTrack() && (result == VK_SUCCESS) && (pCreateInfo != nullptr)) + { + assert(state_tracker_ != nullptr); + + auto image_wrapper = vulkan_wrappers::GetWrapper(*pImage); + + if (image_wrapper->is_sparse_image) + { + // We will need to set the bind_device for handling sparse images. There will be no subsequent + // vkBindImageMemory, vkBindImageMemory2, or vkBindImageMemory2KHR calls for sparse image, so we assign + // bind_device to the device that created the image. + image_wrapper->bind_device = vulkan_wrappers::GetWrapper(device); + } + } + } + void PostProcess_vkCmdBeginRenderPass(VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo* pRenderPassBegin, VkSubpassContents) @@ -1662,6 +1810,7 @@ class VulkanCaptureManager : public ApiCaptureManager std::unique_ptr state_tracker_; HardwareBufferMap hardware_buffers_; std::mutex deferred_operation_mutex; + std::mutex sparse_resource_mutex; }; GFXRECON_END_NAMESPACE(encode) diff --git a/framework/encode/vulkan_handle_wrappers.h b/framework/encode/vulkan_handle_wrappers.h index f50d2f9120..f4e2bbc8c4 100644 --- a/framework/encode/vulkan_handle_wrappers.h +++ b/framework/encode/vulkan_handle_wrappers.h @@ -30,6 +30,7 @@ #include "format/format.h" #include "generated/generated_vulkan_dispatch_table.h" #include "graphics/vulkan_device_util.h" +#include "graphics/vulkan_resources_util.h" #include "util/defines.h" #include "util/memory_output_stream.h" #include "util/page_guard_manager.h" @@ -210,6 +211,19 @@ struct BufferWrapper : public HandleWrapper, AssetWrapperBase VkBufferUsageFlags usage{ 0 }; std::set buffer_views; + + DeviceWrapper* bind_device{ nullptr }; + const void* bind_pnext{ nullptr }; + std::unique_ptr bind_pnext_memory; + + bool is_sparse_buffer{ false }; + std::map sparse_memory_bind_map; + VkQueue sparse_bind_queue; + + format::HandleId bind_memory_id{ format::kNullHandleId }; + VkDeviceSize bind_offset{ 0 }; + uint32_t queue_family_index{ 0 }; + VkDeviceSize created_size{ 0 }; }; struct ImageViewWrapper; @@ -226,6 +240,20 @@ struct ImageWrapper : public HandleWrapper, AssetWrapperBase bool is_swapchain_image{ false }; std::set image_views; + + DeviceWrapper* bind_device{ nullptr }; + const void* bind_pnext{ nullptr }; + std::unique_ptr bind_pnext_memory; + + bool is_sparse_image{ false }; + std::map sparse_opaque_memory_bind_map; + graphics::VulkanSubresourceSparseImageMemoryBindMap sparse_subresource_memory_bind_map; + VkQueue sparse_bind_queue; + + format::HandleId bind_memory_id{ format::kNullHandleId }; + VkDeviceSize bind_offset{ 0 }; + uint32_t queue_family_index{ 0 }; + std::set parent_swapchains; }; struct SamplerWrapper : public HandleWrapper diff --git a/framework/encode/vulkan_state_tracker_initializers.h b/framework/encode/vulkan_state_tracker_initializers.h index 2361053118..6712598ede 100644 --- a/framework/encode/vulkan_state_tracker_initializers.h +++ b/framework/encode/vulkan_state_tracker_initializers.h @@ -608,6 +608,13 @@ inline void InitializeStatecreate_call_id = create_call_id; wrapper->create_parameters = std::move(create_parameters); + wrapper->created_size = create_info->size; + + if ((create_info->flags & VK_BUFFER_CREATE_SPARSE_BINDING_BIT) != 0) + { + wrapper->is_sparse_buffer = true; + } + // TODO: Do we need to track the queue family that the buffer is actually used with? if ((create_info->queueFamilyIndexCount > 0) && (create_info->pQueueFamilyIndices != nullptr)) { @@ -641,6 +648,11 @@ inline void InitializeStatesamples = create_info->samples; wrapper->tiling = create_info->tiling; + if ((create_info->flags & VK_IMAGE_CREATE_SPARSE_BINDING_BIT) != 0) + { + wrapper->is_sparse_image = true; + } + // TODO: Do we need to track the queue family that the image is actually used with? if ((create_info->queueFamilyIndexCount > 0) && (create_info->pQueueFamilyIndices != nullptr)) { diff --git a/framework/encode/vulkan_state_writer.cpp b/framework/encode/vulkan_state_writer.cpp index c75a0c8431..0369b1eb0f 100644 --- a/framework/encode/vulkan_state_writer.cpp +++ b/framework/encode/vulkan_state_writer.cpp @@ -2020,7 +2020,7 @@ void VulkanStateWriter::ProcessBufferMemory(const vulkan_wrappers::DeviceWrapper const uint8_t* bytes = nullptr; std::vector data; - assert((buffer_wrapper != nullptr) && (memory_wrapper != nullptr)); + assert(buffer_wrapper != nullptr); if (snapshot_entry.need_staging_copy) { @@ -2034,6 +2034,7 @@ void VulkanStateWriter::ProcessBufferMemory(const vulkan_wrappers::DeviceWrapper } else { + assert(memory_wrapper != nullptr); assert((memory_wrapper->mapped_data == nullptr) || (memory_wrapper->mapped_offset == 0)); VkResult result = VK_SUCCESS; @@ -2582,22 +2583,21 @@ void VulkanStateWriter::WriteBufferMemoryState(const VulkanStateTable& state_tab state_table.VisitWrappers([&](vulkan_wrappers::BufferWrapper* wrapper) { assert(wrapper != nullptr); - - // Perform memory binding. - const vulkan_wrappers::DeviceMemoryWrapper* memory_wrapper = - state_table.GetDeviceMemoryWrapper(wrapper->bind_memory_id); - - if (memory_wrapper != nullptr) + if (!wrapper->is_sparse_buffer) { - const vulkan_wrappers::DeviceWrapper* device_wrapper = wrapper->bind_device; - assert(device_wrapper != nullptr); + // Perform memory binding for non-sparse buffer. + const vulkan_wrappers::DeviceMemoryWrapper* memory_wrapper = + state_table.GetDeviceMemoryWrapper(wrapper->bind_memory_id); - if (write_memory_state) + if (memory_wrapper != nullptr) { + const vulkan_wrappers::DeviceWrapper* device_wrapper = wrapper->bind_device; + const VulkanDeviceTable* device_table = &device_wrapper->layer_table; + + assert((device_wrapper != nullptr) && (device_table != nullptr)); + // Write memory requirements query before bind command. - VkMemoryRequirements memory_requirements; - const VulkanDeviceTable* device_table = &device_wrapper->layer_table; - assert(device_table != nullptr); + VkMemoryRequirements memory_requirements; device_table->GetBufferMemoryRequirements( device_wrapper->handle, wrapper->handle, &memory_requirements); @@ -2637,17 +2637,127 @@ void VulkanStateWriter::WriteBufferMemoryState(const VulkanStateTable& state_tab WriteFunctionCall(format::ApiCall_vkBindBufferMemory2, ¶meter_stream_); } parameter_stream_.Clear(); + + // Group buffers with memory bindings by device for memory snapshot. + ResourceSnapshotQueueFamilyTable& snapshot_table = (*resources)[device_wrapper]; + ResourceSnapshotInfo& snapshot_entry = snapshot_table[wrapper->queue_family_index]; + + BufferSnapshotInfo snapshot_info; + snapshot_info.buffer_wrapper = wrapper; + snapshot_info.memory_wrapper = memory_wrapper; + snapshot_info.memory_properties = GetMemoryProperties(device_wrapper, memory_wrapper); + snapshot_info.need_staging_copy = !IsBufferReadable(snapshot_info.memory_properties, memory_wrapper); + + if ((*max_resource_size) < wrapper->created_size) + { + (*max_resource_size) = wrapper->created_size; + } + + if (snapshot_info.need_staging_copy && ((*max_staging_copy_size) < wrapper->created_size)) + { + (*max_staging_copy_size) = wrapper->created_size; + } + + snapshot_entry.buffers.emplace_back(snapshot_info); + } + } + else + { + // Perform memory binding for sparse buffer. + const vulkan_wrappers::DeviceWrapper* device_wrapper = wrapper->bind_device; + const VulkanDeviceTable* device_table = &device_wrapper->layer_table; + assert((device_wrapper != nullptr) && (device_table != nullptr)); + + // We do not need to use sparse_resource_mutex for the access to the following sparse resource maps, as the + // writing states operation is included in the trim start handling, which is protected by an exclusive lock. + // No other API capturing handling occurs concurrently. + if (wrapper->sparse_memory_bind_map.size() != 0) + { + std::vector sparse_memory_binds; + VkSparseBufferMemoryBindInfo buffer_memory_bind_info = {}; + + // Write memory requirements query before vkQueueBindSparse command. For sparse buffer, the alignment of + // VkMemoryRequirements is the sparse block size in bytes which represents both the memory alignment + // requirement and the binding granularity (in bytes) for sparse buffer. + VkMemoryRequirements memory_requirements; + + device_table->GetBufferMemoryRequirements( + device_wrapper->handle, wrapper->handle, &memory_requirements); + + encoder_.EncodeHandleIdValue(device_wrapper->handle_id); + encoder_.EncodeHandleIdValue(wrapper->handle_id); + EncodeStructPtr(&encoder_, &memory_requirements); + + WriteFunctionCall(format::ApiCall_vkGetBufferMemoryRequirements, ¶meter_stream_); + parameter_stream_.Clear(); + + const vulkan_wrappers::QueueWrapper* sparse_bind_queue_wrapper = + vulkan_wrappers::GetWrapper(wrapper->sparse_bind_queue); + + if ((wrapper->sparse_bind_queue != VK_NULL_HANDLE) && (sparse_bind_queue_wrapper != nullptr)) + { + for (auto& item : wrapper->sparse_memory_bind_map) + { + sparse_memory_binds.push_back(item.second); + } + + buffer_memory_bind_info.buffer = wrapper->handle; + buffer_memory_bind_info.bindCount = sparse_memory_binds.size(); + buffer_memory_bind_info.pBinds = sparse_memory_binds.data(); + + VkBindSparseInfo bind_sparse_info{}; + bind_sparse_info.sType = VK_STRUCTURE_TYPE_BIND_SPARSE_INFO; + bind_sparse_info.pNext = nullptr; + bind_sparse_info.waitSemaphoreCount = 0; + bind_sparse_info.pWaitSemaphores = nullptr; + bind_sparse_info.bufferBindCount = 1; + bind_sparse_info.pBufferBinds = &buffer_memory_bind_info; + bind_sparse_info.imageOpaqueBindCount = 0; + bind_sparse_info.pImageOpaqueBinds = nullptr; + bind_sparse_info.imageBindCount = 0; + bind_sparse_info.pImageBinds = nullptr; + bind_sparse_info.signalSemaphoreCount = 0; + bind_sparse_info.pSignalSemaphores = nullptr; + + encoder_.EncodeVulkanHandleValue(wrapper->sparse_bind_queue); + encoder_.EncodeUInt32Value(1); + EncodeStructArray(&encoder_, &bind_sparse_info, 1); + encoder_.EncodeVulkanHandleValue(VK_NULL_HANDLE); + encoder_.EncodeEnumValue(VK_SUCCESS); + WriteFunctionCall(format::ApiCall_vkQueueBindSparse, ¶meter_stream_); + + parameter_stream_.Clear(); + + encoder_.EncodeHandleIdValue(device_wrapper->handle_id); + encoder_.EncodeEnumValue(VK_SUCCESS); + + WriteFunctionCall(format::ApiCall_vkDeviceWaitIdle, ¶meter_stream_); + } + else + { + GFXRECON_LOG_WARNING("Unable to generate vkQueueBindSparse for the sparse buffer (id = %d) due to " + "the related sparse bind queue or its wrapper is invalid.", + wrapper->handle_id); + } } + parameter_stream_.Clear(); + // Group buffers with memory bindings by device for memory snapshot. ResourceSnapshotQueueFamilyTable& snapshot_table = (*resources)[device_wrapper]; ResourceSnapshotInfo& snapshot_entry = snapshot_table[wrapper->queue_family_index]; BufferSnapshotInfo snapshot_info; - snapshot_info.buffer_wrapper = wrapper; - snapshot_info.memory_wrapper = memory_wrapper; - snapshot_info.memory_properties = GetMemoryProperties(device_wrapper, memory_wrapper); - snapshot_info.need_staging_copy = !IsBufferReadable(snapshot_info.memory_properties, memory_wrapper); + snapshot_info.buffer_wrapper = wrapper; + snapshot_info.memory_wrapper = nullptr; + + // We enforce the memory properties to be `VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT`, and we set + // `need_staging_copy` to true for sparse buffers. When dumping buffer data, there are two distinct code + // paths: one involves a staging copy, while the other requires mapping host-visible memory. This latter + // method requires the buffer to be bound to a single range of a single memory, which is not applicable for + // sparse buffers. Therefore, we set the two values to use staging copy for dumping sparse buffers. + snapshot_info.memory_properties = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + snapshot_info.need_staging_copy = true; // Staging copy is needed for sparse buffer. if ((*max_resource_size) < wrapper->size) { @@ -2680,7 +2790,8 @@ void VulkanStateWriter::WriteImageMemoryState(const VulkanStateTable& state_tabl state_table.GetDeviceMemoryWrapper(wrapper->bind_memory_id); if ((wrapper->is_swapchain_image && memory_wrapper == nullptr && wrapper->bind_device != nullptr) || - (!wrapper->is_swapchain_image && memory_wrapper != nullptr)) + (!wrapper->is_swapchain_image && memory_wrapper != nullptr) || + (!wrapper->is_swapchain_image && wrapper->is_sparse_image && wrapper->bind_device != nullptr)) { const vulkan_wrappers::DeviceWrapper* device_wrapper = wrapper->bind_device; assert(device_wrapper != nullptr); @@ -2688,10 +2799,11 @@ void VulkanStateWriter::WriteImageMemoryState(const VulkanStateTable& state_tabl // Write memory requirements query before bind command. if (write_memory_state) { - VkMemoryRequirements memory_requirements; const VulkanDeviceTable* device_table = &device_wrapper->layer_table; assert(device_table != nullptr); + VkMemoryRequirements memory_requirements; + device_table->GetImageMemoryRequirements(device_wrapper->handle, wrapper->handle, &memory_requirements); encoder_.EncodeHandleIdValue(device_wrapper->handle_id); @@ -2702,34 +2814,115 @@ void VulkanStateWriter::WriteImageMemoryState(const VulkanStateTable& state_tabl parameter_stream_.Clear(); // Write memory bind command. - if (wrapper->bind_pnext == nullptr) + if (!wrapper->is_sparse_image) { - encoder_.EncodeHandleIdValue(device_wrapper->handle_id); - encoder_.EncodeHandleIdValue(wrapper->handle_id); - encoder_.EncodeHandleIdValue(memory_wrapper->handle_id); - encoder_.EncodeUInt64Value(wrapper->bind_offset); - encoder_.EncodeEnumValue(VK_SUCCESS); + if (wrapper->bind_pnext == nullptr) + { + encoder_.EncodeHandleIdValue(device_wrapper->handle_id); + encoder_.EncodeHandleIdValue(wrapper->handle_id); + encoder_.EncodeHandleIdValue(memory_wrapper->handle_id); + encoder_.EncodeUInt64Value(wrapper->bind_offset); + encoder_.EncodeEnumValue(VK_SUCCESS); - WriteFunctionCall(format::ApiCall_vkBindImageMemory, ¶meter_stream_); + WriteFunctionCall(format::ApiCall_vkBindImageMemory, ¶meter_stream_); + } + else + { + encoder_.EncodeHandleIdValue(device_wrapper->handle_id); + encoder_.EncodeUInt32Value(1); + + VkBindImageMemoryInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO; + info.pNext = wrapper->bind_pnext; + info.image = wrapper->handle; + info.memory = memory_wrapper->handle; + info.memoryOffset = wrapper->bind_offset; + EncodeStructArray(&encoder_, &info, 1); + encoder_.EncodeEnumValue(VK_SUCCESS); + + WriteFunctionCall(format::ApiCall_vkBindImageMemory2, ¶meter_stream_); + } } - else + } + else + { + const vulkan_wrappers::DeviceWrapper* device_wrapper = wrapper->bind_device; + const VulkanDeviceTable* device_table = &device_wrapper->layer_table; + assert((device_wrapper != nullptr) && (device_table != nullptr)); + + const vulkan_wrappers::QueueWrapper* sparse_bind_queue_wrapper = + vulkan_wrappers::GetWrapper(wrapper->sparse_bind_queue); + + GFXRECON_ASSERT((wrapper->sparse_bind_queue != VK_NULL_HANDLE) && + (sparse_bind_queue_wrapper != nullptr)); + + if ((wrapper->sparse_opaque_memory_bind_map.size() != 0) || + (wrapper->sparse_subresource_memory_bind_map.size() != 0)) { - encoder_.EncodeHandleIdValue(device_wrapper->handle_id); + std::vector sparse_memory_binds; + VkSparseImageOpaqueMemoryBindInfo image_opaque_memory_bind_info = {}; + + for (auto& item : wrapper->sparse_opaque_memory_bind_map) + { + sparse_memory_binds.push_back(item.second); + } + + image_opaque_memory_bind_info.image = wrapper->handle; + image_opaque_memory_bind_info.bindCount = sparse_memory_binds.size(); + image_opaque_memory_bind_info.pBinds = + (sparse_memory_binds.size() == 0) ? nullptr : sparse_memory_binds.data(); + + std::vector sparse_image_memory_binds; + VkSparseImageMemoryBindInfo image_memory_bind_info = {}; + + for (auto& subresource_bind_map : wrapper->sparse_subresource_memory_bind_map) + { + auto& offset_3d_to_memory_range_map = subresource_bind_map.second; + + for (auto& item : offset_3d_to_memory_range_map) + { + sparse_image_memory_binds.push_back(item.second); + } + } + + image_memory_bind_info.image = wrapper->handle; + image_memory_bind_info.bindCount = sparse_image_memory_binds.size(); + image_memory_bind_info.pBinds = + (sparse_image_memory_binds.size() == 0) ? nullptr : sparse_image_memory_binds.data(); + + VkBindSparseInfo bind_sparse_info{}; + + bind_sparse_info.sType = VK_STRUCTURE_TYPE_BIND_SPARSE_INFO; + bind_sparse_info.pNext = nullptr; + bind_sparse_info.waitSemaphoreCount = 0; + bind_sparse_info.pWaitSemaphores = nullptr; + bind_sparse_info.bufferBindCount = 0; + bind_sparse_info.pBufferBinds = nullptr; + bind_sparse_info.imageOpaqueBindCount = (image_opaque_memory_bind_info.bindCount == 0) ? 0 : 1; + bind_sparse_info.pImageOpaqueBinds = + (image_opaque_memory_bind_info.bindCount == 0) ? nullptr : &image_opaque_memory_bind_info; + bind_sparse_info.imageBindCount = (image_memory_bind_info.bindCount == 0) ? 0 : 1; + bind_sparse_info.pImageBinds = + (image_memory_bind_info.bindCount == 0) ? nullptr : &image_memory_bind_info; + bind_sparse_info.signalSemaphoreCount = 0; + bind_sparse_info.pSignalSemaphores = nullptr; + + encoder_.EncodeVulkanHandleValue(wrapper->sparse_bind_queue); encoder_.EncodeUInt32Value(1); + EncodeStructArray(&encoder_, &bind_sparse_info, 1); + encoder_.EncodeVulkanHandleValue(VK_NULL_HANDLE); + encoder_.EncodeEnumValue(VK_SUCCESS); + WriteFunctionCall(format::ApiCall_vkQueueBindSparse, ¶meter_stream_); - VkBindImageMemoryInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO; - info.pNext = wrapper->bind_pnext; - info.image = wrapper->handle; - info.memory = memory_wrapper->handle; - info.memoryOffset = wrapper->bind_offset; - EncodeStructArray(&encoder_, &info, 1); + parameter_stream_.Clear(); + + encoder_.EncodeHandleIdValue(device_wrapper->handle_id); encoder_.EncodeEnumValue(VK_SUCCESS); - WriteFunctionCall(format::ApiCall_vkBindImageMemory2, ¶meter_stream_); + WriteFunctionCall(format::ApiCall_vkDeviceWaitIdle, ¶meter_stream_); } - parameter_stream_.Clear(); } + parameter_stream_.Clear(); VkMemoryPropertyFlags memory_properties = 0; if (memory_wrapper != nullptr) @@ -2743,6 +2936,13 @@ void VulkanStateWriter::WriteImageMemoryState(const VulkanStateTable& state_tabl (wrapper->tiling == VK_IMAGE_TILING_LINEAR) && ((memory_properties & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT); + if (wrapper->is_sparse_image) + { + // The process for dumping host-visible image data requires binding the entire image to a single memory + // range. Since this is not applicable to sparse images, we set is_writable to false. + is_writable = false; + } + // If an image is not host writable and has not been transitioned from the undefined or preinitialized // layouts, no data could have been loaded into it and its data will be omitted from the state snapshot. if (is_transitioned || is_writable) @@ -2756,7 +2956,11 @@ void VulkanStateWriter::WriteImageMemoryState(const VulkanStateTable& state_tabl *device_wrapper->physical_device->layer_table_ref, device_wrapper->physical_device->memory_properties); - bool need_staging_copy = !IsImageReadable(memory_properties, memory_wrapper, wrapper); + // Sparse images require staging copy for the following process because dumping image data with mapping + // memory needs binding the entire image to a single memory range. Sparse image opaque binding allows + // binding to multiple memory objects and various memory ranges. + bool need_staging_copy = + !IsImageReadable(memory_properties, memory_wrapper, wrapper) || wrapper->is_sparse_image; std::vector aspects; bool combined_depth_stencil; diff --git a/framework/graphics/vulkan_resources_util.cpp b/framework/graphics/vulkan_resources_util.cpp index dc84917474..69fd1b2225 100644 --- a/framework/graphics/vulkan_resources_util.cpp +++ b/framework/graphics/vulkan_resources_util.cpp @@ -2077,6 +2077,137 @@ VkResult VulkanResourcesUtil::WriteToImageResourceStaging(VkImage return result; } +bool GetIntersectForSparseMemoryBind(uint32_t new_bind_resource_offset, + uint32_t new_bind_resource_size, + uint32_t existing_bind_resource_offset, + uint32_t existing_bind_resource_size, + uint32_t& intersection_resource_offset, + uint32_t& intersection_resource_size, + std::vector& remaining_resource_offsets, + std::vector& remaining_resource_sizes, + bool& new_bind_range_include_existing_bind_tange, + bool& existing_bind_range_include_new_bind_tange) +{ + bool intersection_exist = false; + uint32_t intersection_start = std::max(new_bind_resource_offset, existing_bind_resource_offset); + uint32_t intersection_end = std::min(new_bind_resource_offset + new_bind_resource_size, + existing_bind_resource_offset + existing_bind_resource_size); + + existing_bind_range_include_new_bind_tange = false; + new_bind_range_include_existing_bind_tange = false; + + if (intersection_start < intersection_end) + { + intersection_exist = true; + intersection_resource_offset = intersection_start; + intersection_resource_size = intersection_end - intersection_start; + + if ((intersection_resource_offset == new_bind_resource_offset) && + (intersection_resource_size == new_bind_resource_size)) + { + existing_bind_range_include_new_bind_tange = true; + } + + if ((intersection_resource_offset == existing_bind_resource_offset) && + (intersection_resource_size == existing_bind_resource_size)) + { + new_bind_range_include_existing_bind_tange = true; + } + + if (intersection_resource_offset > existing_bind_resource_offset) + { + remaining_resource_offsets.push_back(existing_bind_resource_offset); + remaining_resource_sizes.push_back(intersection_resource_offset - existing_bind_resource_offset); + } + + if ((intersection_resource_offset + intersection_resource_size) < + (existing_bind_resource_offset + existing_bind_resource_size)) + { + remaining_resource_offsets.push_back(intersection_resource_offset + intersection_resource_size); + remaining_resource_sizes.push_back((existing_bind_resource_offset + existing_bind_resource_size) - + (intersection_resource_offset + intersection_resource_size)); + } + } + + return intersection_exist; +} + +void UpdateSparseMemoryBindMap(std::map& sparse_memory_bind_map, + const VkSparseMemoryBind& new_sparse_memory_bind) +{ + std::vector all_remaining_existing_bind_ranges{}; + std::vector delete_existing_bind_ranges{}; + + VkDeviceSize search_key = new_sparse_memory_bind.resourceOffset + new_sparse_memory_bind.size; + auto iterator = sparse_memory_bind_map.lower_bound(search_key); + bool is_intersected_with_any_existing_bind = false; + + bool ignored = false; + + if ((sparse_memory_bind_map.size() != 0) && (iterator != sparse_memory_bind_map.begin())) + { + for (auto item = sparse_memory_bind_map.begin(); item != iterator; item++) + { + uint32_t intersection_resource_offset, intersection_resource_size; + std::vector remaining_resource_offsets, remaining_resource_sizes; + bool new_bind_range_include_existing_bind_tange, existing_bind_range_include_new_bind_tange; + + bool is_intersected = GetIntersectForSparseMemoryBind(new_sparse_memory_bind.resourceOffset, + new_sparse_memory_bind.size, + item->second.resourceOffset, + item->second.size, + intersection_resource_offset, + intersection_resource_size, + remaining_resource_offsets, + remaining_resource_sizes, + new_bind_range_include_existing_bind_tange, + existing_bind_range_include_new_bind_tange); + + if (is_intersected) + { + is_intersected_with_any_existing_bind = false; + + VkSparseMemoryBind add_sparse_memory_bind = { 0, 0, item->second.memory, 0, item->second.flags }; + GFXRECON_ASSERT(item->second.flags == new_sparse_memory_bind.flags); + + uint32_t index = 0; + for (auto& bind_offset : remaining_resource_offsets) + { + add_sparse_memory_bind.resourceOffset = bind_offset; + add_sparse_memory_bind.size = remaining_resource_sizes[index]; + add_sparse_memory_bind.memoryOffset = + item->second.memoryOffset + bind_offset - item->second.resourceOffset; + all_remaining_existing_bind_ranges.push_back(add_sparse_memory_bind); + + index++; + } + + delete_existing_bind_ranges.push_back(item->second); + } + } + } + + if (is_intersected_with_any_existing_bind) + { + for (auto& delete_item : delete_existing_bind_ranges) + { + sparse_memory_bind_map.erase(delete_item.resourceOffset); + } + + size_t index = 0, remaining_range_base = 0; + + for (auto add_item : all_remaining_existing_bind_ranges) + { + sparse_memory_bind_map[add_item.resourceOffset] = add_item; + } + } + + if (new_sparse_memory_bind.memory != VK_NULL_HANDLE) + { + sparse_memory_bind_map[new_sparse_memory_bind.resourceOffset] = new_sparse_memory_bind; + } +} + bool VulkanResourcesUtil::IsBlitSupported(VkFormat src_format, VkImageTiling src_image_tiling, VkFormat dst_format, diff --git a/framework/graphics/vulkan_resources_util.h b/framework/graphics/vulkan_resources_util.h index 87c154221a..a55f3af59f 100644 --- a/framework/graphics/vulkan_resources_util.h +++ b/framework/graphics/vulkan_resources_util.h @@ -31,6 +31,7 @@ #include "vulkan/vulkan_core.h" #include +#include GFXRECON_BEGIN_NAMESPACE(gfxrecon) GFXRECON_BEGIN_NAMESPACE(graphics) @@ -280,6 +281,76 @@ bool FindMemoryTypeIndex(const VkPhysicalDeviceMemoryProperties& memory_properti uint32_t* found_index, VkMemoryPropertyFlags* found_flags); +struct VkOffset3DComparator +{ + bool operator()(const VkOffset3D& l, const VkOffset3D& r) const + { + bool result = (l.x < r.x); + + if (l.x == r.x) + { + result = (l.y < r.y); + + if (l.y == r.y) + { + result = (l.z < r.z); + } + } + + return result; + } +}; + +typedef std::map + VulkanOffset3DSparseImageMemoryBindMap; + +struct VkImageSubresourceComparator +{ + bool operator()(const VkImageSubresource& l, const VkImageSubresource& r) const + { + bool result = (l.arrayLayer < r.arrayLayer); + + if (l.arrayLayer == r.arrayLayer) + { + result = (l.mipLevel < r.mipLevel); + + if (l.mipLevel == r.mipLevel) + { + result = (l.aspectMask < r.aspectMask); + } + } + + return result; + } +}; + +typedef std:: + map + VulkanSubresourceSparseImageMemoryBindMap; + +// Get the intersection of the new bind range and the existing bind range for sparse buffer or sparse image (opaque +// bind). +// If the intersection range exists, further get the remaining ranges for the existing bind range after removing +// the intersection range. For instance, if the new/existing bind range (offset, size) are (196608, 327680) and (0, +// 655360) respectively, the old range (0, 655360) completely covers the new range (196608, 327680). The intersection +// range is (196608, 327680), and the remaining ranges for the existing bind are (0, 196608) and (524288, 131072). So +// for the return of the function, remaining_resource_offsets will be a std::vector of [0, 524288], and +// remaining_resource_sizes will be [196608, 131072]. +// +bool GetIntersectForSparseMemoryBind(uint32_t new_bind_resource_offset, + uint32_t new_bind_resource_size, + uint32_t existing_bind_resource_offset, + uint32_t existing_bind_resource_size, + uint32_t& intersection_resource_offset, + uint32_t& intersection_resource_size, + std::vector& remaining_resource_offsets, + std::vector& remaining_resource_sizes, + bool& new_bind_range_include_existing_bind_tange, + bool& existing_bind_range_include_new_bind_tange); + +void UpdateSparseMemoryBindMap(std::map& sparse_memory_bind_map, + const VkSparseMemoryBind& new_sparse_memory_bind); + bool GetImageTexelSize(VkFormat format, VkDeviceSize* texel_size, bool* is_texel_block_size, @@ -323,7 +394,7 @@ bool NextRowTexelCoordinates(VkImageType imageType, uint32_t& z, uint32_t& layer); +GFXRECON_END_NAMESPACE(graphics) GFXRECON_END_NAMESPACE(gfxrecon) -GFXRECON_END_NAMESPACE(encode) #endif /* GFXRECON_GRAPHICS_VULKAN_RESOURCES_UTIL_H */ From c0f816f6c888b44a1a1128daeb6cc2339b2e0fc4 Mon Sep 17 00:00:00 2001 From: Nick Driscoll Date: Mon, 27 Jan 2025 13:58:00 -0500 Subject: [PATCH 02/10] format --- framework/encode/vulkan_handle_wrappers.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/encode/vulkan_handle_wrappers.h b/framework/encode/vulkan_handle_wrappers.h index f4e2bbc8c4..567123b8f8 100644 --- a/framework/encode/vulkan_handle_wrappers.h +++ b/framework/encode/vulkan_handle_wrappers.h @@ -211,7 +211,7 @@ struct BufferWrapper : public HandleWrapper, AssetWrapperBase VkBufferUsageFlags usage{ 0 }; std::set buffer_views; - + DeviceWrapper* bind_device{ nullptr }; const void* bind_pnext{ nullptr }; std::unique_ptr bind_pnext_memory; From c9b17cbb1ec2a7efdda2c4df6962c6824ab509e4 Mon Sep 17 00:00:00 2001 From: Nick Driscoll Date: Wed, 29 Jan 2025 14:42:44 -0500 Subject: [PATCH 03/10] Init sparse-resources test app commit --- .../test_apps/sparse-resources/CMakeLists.txt | 74 +++ test/test_apps/sparse-resources/app.cpp | 566 ++++++++++++++++++ .../sparse-resources/shaders/tri.frag.spv | Bin 0 -> 456 bytes .../sparse-resources/shaders/tri.vert.spv | Bin 0 -> 1288 bytes 4 files changed, 640 insertions(+) create mode 100644 test/test_apps/sparse-resources/CMakeLists.txt create mode 100644 test/test_apps/sparse-resources/app.cpp create mode 100644 test/test_apps/sparse-resources/shaders/tri.frag.spv create mode 100644 test/test_apps/sparse-resources/shaders/tri.vert.spv diff --git a/test/test_apps/sparse-resources/CMakeLists.txt b/test/test_apps/sparse-resources/CMakeLists.txt new file mode 100644 index 0000000000..cc025c1bba --- /dev/null +++ b/test/test_apps/sparse-resources/CMakeLists.txt @@ -0,0 +1,74 @@ +############################################################################### +# Copyright (c) 2018-2025 LunarG, Inc. +# All rights reserved +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# +# Author: LunarG Team +# Description: CMake script for sparse-resources test app +############################################################################### + +add_executable(gfxrecon-testapp-sparse-resources "") + +target_sources(gfxrecon-testapp-sparse-resources + PRIVATE + ${CMAKE_CURRENT_LIST_DIR}/app.cpp + ${CMAKE_CURRENT_LIST_DIR}/../common/test_app_base.cpp) + +target_include_directories(gfxrecon-testapp-sparse-resources PUBLIC + ${CMAKE_BINARY_DIR} + ${CMAKE_CURRENT_LIST_DIR}/../common) + +target_link_libraries(gfxrecon-testapp-sparse-resources + gfxrecon_application + gfxrecon_decode + gfxrecon_graphics + gfxrecon_format + gfxrecon_util + SDL3::SDL3 + platform_specific) + +if (MSVC) + # Force inclusion of "gfxrecon_disable_popup_result" variable in linking. + # On 32-bit windows, MSVC prefixes symbols with "_" but on 64-bit windows it doesn't. + if (CMAKE_SIZEOF_VOID_P EQUAL 4) + target_link_options(gfxrecon-replay PUBLIC "LINKER:/Include:_gfxrecon_disable_popup_result") + else () + target_link_options(gfxrecon-replay PUBLIC "LINKER:/Include:gfxrecon_disable_popup_result") + endif () +endif () + +common_build_directives(gfxrecon-testapp-sparse-resources) + +add_custom_command( + TARGET gfxrecon-testapp-sparse-resources + POST_BUILD + COMMAND + ${CMAKE_COMMAND} -E copy_directory + ${CMAKE_CURRENT_LIST_DIR}/shaders ${CMAKE_CURRENT_BINARY_DIR} + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}) +if (WIN32) + add_custom_command(TARGET gfxrecon-testapp-sparse-resources POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different $ $ + COMMAND_EXPAND_LISTS) +endif () + +install(TARGETS gfxrecon-testapp-sparse-resources RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + +add_dependencies(gfxrecon-testapps gfxrecon-testapp-sparse-resources) \ No newline at end of file diff --git a/test/test_apps/sparse-resources/app.cpp b/test/test_apps/sparse-resources/app.cpp new file mode 100644 index 0000000000..c067151cf0 --- /dev/null +++ b/test/test_apps/sparse-resources/app.cpp @@ -0,0 +1,566 @@ +/* +** Copyright (c) 2018-2023 Valve Corporation +** Copyright (c) 2018-2025 LunarG, Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and associated documentation files (the "Software"), +** to deal in the Software without restriction, including without limitation +** the rights to use, copy, modify, merge, publish, distribute, sublicense, +** and/or sell copies of the Software, and to permit persons to whom the +** Software is furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +** FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +** DEALINGS IN THE SOFTWARE. +*/ + +#include + +#include + +#include + +#include + +GFXRECON_BEGIN_NAMESPACE(gfxrecon) +GFXRECON_BEGIN_NAMESPACE(test_app) +GFXRECON_BEGIN_NAMESPACE(sparse_resources) + +const size_t MAX_FRAMES_IN_FLIGHT = 2; + +class App : public gfxrecon::test::TestAppBase +{ +private: + VkQueue graphics_queue_; + VkQueue present_queue_; + + std::vector framebuffers_; + + VkRenderPass render_pass_; + VkDescriptorPool descriptor_pool_; + VkDescriptorSet descriptor_set_; + VkPipelineLayout pipeline_layout_; + VkPipeline graphics_pipeline_; + + VkImage image0_; + VkImageView image0_view_; + VkDeviceMemory image_backing_memory_; + uint32_t device_memory_heap_; + uint32_t staging_memory_heap_; + + VkCommandPool command_pools_[MAX_FRAMES_IN_FLIGHT]; + + + size_t current_frame_ = 0; + + gfxrecon::test::Sync sync_; + + void create_render_pass(); + void create_graphics_pipeline(); + void create_framebuffers(); + void create_descriptor_set(); + void create_textures(); + void determine_memory_heaps(); + void configure_instance_builder(test::InstanceBuilder& instance_builder); + void configure_physical_device_selector(test::PhysicalDeviceSelector& phys_device_selector); + bool frame(const int frame_num); + void setup(); +}; + +void App::configure_instance_builder(test::InstanceBuilder& instance_builder) { + instance_builder.desire_api_version(VK_MAKE_VERSION(1, 3, 0)); +} + +void App::configure_physical_device_selector(test::PhysicalDeviceSelector& phys_device_selector) { + VkPhysicalDeviceFeatures feats = {}; + feats.sparseBinding = true; + feats.sparseResidencyBuffer = true; + feats.sparseResidencyAliased = true; + feats.sparseResidencyImage2D = true; + phys_device_selector.set_required_features(feats); + + phys_device_selector.add_required_extension("VK_KHR_maintenance2"); +} + +void App::create_render_pass() +{ + VkAttachmentDescription color_attachment = {}; + color_attachment.format = init.swapchain.image_format; + color_attachment.samples = VK_SAMPLE_COUNT_1_BIT; + color_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + color_attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + color_attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + color_attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + color_attachment.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + color_attachment.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + VkAttachmentReference color_attachment_ref = {}; + color_attachment_ref.attachment = 0; + color_attachment_ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + VkSubpassDescription subpass = {}; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.colorAttachmentCount = 1; + subpass.pColorAttachments = &color_attachment_ref; + + VkRenderPassCreateInfo render_pass_info = {}; + render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + render_pass_info.attachmentCount = 1; + render_pass_info.pAttachments = &color_attachment; + render_pass_info.subpassCount = 1; + render_pass_info.pSubpasses = &subpass; + render_pass_info.dependencyCount = 0; + + auto result = init.disp.createRenderPass(&render_pass_info, nullptr, &render_pass_); + VERIFY_VK_RESULT("failed to create render pass", result); +} + +void App::create_graphics_pipeline() +{ + auto vert_module = gfxrecon::test::readShaderFromFile(init.disp, "tri.vert.spv"); + auto frag_module = gfxrecon::test::readShaderFromFile(init.disp, "tri.frag.spv"); + + VkPipelineShaderStageCreateInfo vert_stage_info = {}; + vert_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + vert_stage_info.stage = VK_SHADER_STAGE_VERTEX_BIT; + vert_stage_info.module = vert_module; + vert_stage_info.pName = "main"; + + VkPipelineShaderStageCreateInfo frag_stage_info = {}; + frag_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + frag_stage_info.stage = VK_SHADER_STAGE_FRAGMENT_BIT; + frag_stage_info.module = frag_module; + frag_stage_info.pName = "main"; + + VkPipelineShaderStageCreateInfo shader_stages[] = { vert_stage_info, frag_stage_info }; + + VkPipelineVertexInputStateCreateInfo vertex_input_info = {}; + vertex_input_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vertex_input_info.vertexBindingDescriptionCount = 0; + vertex_input_info.vertexAttributeDescriptionCount = 0; + + VkPipelineInputAssemblyStateCreateInfo input_assembly = {}; + input_assembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + input_assembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + input_assembly.primitiveRestartEnable = VK_FALSE; + + VkViewport viewport = {}; + viewport.x = 0.0f; + viewport.y = 0.0f; + viewport.width = (float)init.swapchain.extent.width; + viewport.height = (float)init.swapchain.extent.height; + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + + VkRect2D scissor = {}; + scissor.offset = { 0, 0 }; + scissor.extent = init.swapchain.extent; + + VkPipelineViewportStateCreateInfo viewport_state = {}; + viewport_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewport_state.viewportCount = 1; + viewport_state.pViewports = &viewport; + viewport_state.scissorCount = 1; + viewport_state.pScissors = &scissor; + + VkPipelineRasterizationStateCreateInfo rasterizer = {}; + rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rasterizer.depthClampEnable = VK_FALSE; + rasterizer.rasterizerDiscardEnable = VK_FALSE; + rasterizer.polygonMode = VK_POLYGON_MODE_FILL; + rasterizer.lineWidth = 1.0f; + rasterizer.cullMode = VK_CULL_MODE_BACK_BIT; + rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; + rasterizer.depthBiasEnable = VK_FALSE; + + VkPipelineMultisampleStateCreateInfo multisampling = {}; + multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisampling.sampleShadingEnable = VK_FALSE; + multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + + VkPipelineColorBlendAttachmentState colorBlendAttachment = {}; + colorBlendAttachment.colorWriteMask = + VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + colorBlendAttachment.blendEnable = VK_FALSE; + + VkPipelineColorBlendStateCreateInfo color_blending = {}; + color_blending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + color_blending.logicOpEnable = VK_FALSE; + color_blending.logicOp = VK_LOGIC_OP_COPY; + color_blending.attachmentCount = 1; + color_blending.pAttachments = &colorBlendAttachment; + color_blending.blendConstants[0] = 0.0f; + color_blending.blendConstants[1] = 0.0f; + color_blending.blendConstants[2] = 0.0f; + color_blending.blendConstants[3] = 0.0f; + + VkPipelineLayoutCreateInfo pipeline_layout_info = {}; + pipeline_layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipeline_layout_info.setLayoutCount = 0; + pipeline_layout_info.pushConstantRangeCount = 0; + + auto result = init.disp.createPipelineLayout(&pipeline_layout_info, nullptr, &pipeline_layout_); + VERIFY_VK_RESULT("failed to create pipeline layout", result); + + std::vector dynamic_states = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; + + VkPipelineDynamicStateCreateInfo dynamic_info = {}; + dynamic_info.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + dynamic_info.dynamicStateCount = static_cast(dynamic_states.size()); + dynamic_info.pDynamicStates = dynamic_states.data(); + + VkGraphicsPipelineCreateInfo pipeline_info = {}; + pipeline_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipeline_info.stageCount = 2; + pipeline_info.pStages = shader_stages; + pipeline_info.pVertexInputState = &vertex_input_info; + pipeline_info.pInputAssemblyState = &input_assembly; + pipeline_info.pViewportState = &viewport_state; + pipeline_info.pRasterizationState = &rasterizer; + pipeline_info.pMultisampleState = &multisampling; + pipeline_info.pColorBlendState = &color_blending; + pipeline_info.pDynamicState = &dynamic_info; + pipeline_info.layout = pipeline_layout_; + pipeline_info.renderPass = render_pass_; + pipeline_info.subpass = 0; + pipeline_info.basePipelineHandle = VK_NULL_HANDLE; + + result = init.disp.createGraphicsPipelines(VK_NULL_HANDLE, 1, &pipeline_info, nullptr, &graphics_pipeline_); + VERIFY_VK_RESULT("failed to create graphics pipeline", result); + + init.disp.destroyShaderModule(frag_module, nullptr); + init.disp.destroyShaderModule(vert_module, nullptr); +} + +void App::create_framebuffers() +{ + framebuffers_.resize(init.swapchain_image_views.size()); + + for (size_t i = 0; i < init.swapchain_image_views.size(); i++) + { + VkImageView attachments[] = { init.swapchain_image_views[i] }; + + VkFramebufferCreateInfo framebuffer_info = {}; + framebuffer_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + framebuffer_info.renderPass = render_pass_; + framebuffer_info.attachmentCount = 1; + framebuffer_info.pAttachments = attachments; + framebuffer_info.width = init.swapchain.extent.width; + framebuffer_info.height = init.swapchain.extent.height; + framebuffer_info.layers = 1; + + auto result = init.disp.createFramebuffer(&framebuffer_info, nullptr, &framebuffers_[i]); + VERIFY_VK_RESULT("failed to create framebuffer", result); + } +} + +void App::create_descriptor_set() { + +} + +void App::determine_memory_heaps() { + VkPhysicalDeviceMemoryProperties props = init.physical_device.memory_properties; + + // Search for largest device-only memory heap + VkDeviceSize largest_seen = 0; + for (int i = 0; i < props.memoryTypeCount; ++i) { + VkMemoryType type = props.memoryTypes[i]; + bool device_local = type.propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + VkMemoryHeap heap = props.memoryHeaps[type.heapIndex]; + + if ( + (type.propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) && + (type.propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) && + (type.propertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) { + staging_memory_heap_ = type.heapIndex; + } else if (heap.flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) { + if (heap.size > largest_seen) { + device_memory_heap_ = type.heapIndex; + largest_seen = heap.size; + } + } + } +} + +void App::create_textures() { + // Create image object + VkImageCreateInfo image_info = {}; + image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + image_info.pNext = nullptr; + image_info.flags = VK_IMAGE_CREATE_SPARSE_BINDING_BIT | VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT; + image_info.imageType = VK_IMAGE_TYPE_2D; + image_info.format = VK_FORMAT_R8G8B8A8_SRGB; + image_info.extent.width = 64; + image_info.extent.height = 64; + image_info.extent.depth = 1; + image_info.mipLevels = 7; + image_info.arrayLayers = 1; + image_info.samples = VK_SAMPLE_COUNT_1_BIT; + image_info.tiling = VK_IMAGE_TILING_OPTIMAL; + image_info.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; + image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + image_info.queueFamilyIndexCount = 1; + uint32_t idx = init.device.get_queue_index(test::QueueType::graphics).value(); + image_info.pQueueFamilyIndices = &idx; + image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + init.disp.createImage(&image_info, nullptr, &image0_); + + // Allocate image backing memory + VkMemoryRequirements image0_reqs = {}; + init.disp.getImageMemoryRequirements(image0_, &image0_reqs); + VkMemoryAllocateInfo alloc_info = {}; + alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + alloc_info.pNext = nullptr; + alloc_info.allocationSize = image0_reqs.size; + alloc_info.memoryTypeIndex = device_memory_heap_; + init.disp.allocateMemory(&alloc_info, nullptr, &image_backing_memory_); + + // Bind image to memory + init.disp.bindImageMemory(image0_, image_backing_memory_, 0); + + // Create image view object + VkImageViewCreateInfo view_info = {}; + view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + view_info.pNext = nullptr; + view_info.flags = 0; + view_info.image = image0_; + view_info.viewType = VK_IMAGE_VIEW_TYPE_2D; + view_info.format = VK_FORMAT_R8G8B8A8_SRGB; + view_info.components = {}; + view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + view_info.subresourceRange.baseMipLevel = 0; + view_info.subresourceRange.levelCount = 7; + view_info.subresourceRange.baseArrayLayer = 0; + view_info.subresourceRange.layerCount = 1; + init.disp.createImageView(&view_info, nullptr, &image0_view_); + + // Upload image data + + + // Update descriptor set? +} + +void App::setup() { + auto graphics_queue = init.device.get_queue(gfxrecon::test::QueueType::graphics); + if (!graphics_queue.has_value()) + throw std::runtime_error("could not get graphics queue"); + graphics_queue_ = *graphics_queue; + + auto present_queue = init.device.get_queue(gfxrecon::test::QueueType::present); + if (!present_queue.has_value()) + throw std::runtime_error("could not get present queue"); + present_queue_ = *present_queue; + + determine_memory_heaps(); + + create_render_pass(); + create_graphics_pipeline(); + create_framebuffers(); + create_textures(); + + auto queue_family_index = init.device.get_queue_index(gfxrecon::test::QueueType::graphics); + if (!queue_family_index) + throw std::runtime_error("could not find graphics queue"); + for (auto& command_pool : command_pools_) + { + command_pool = gfxrecon::test::create_command_pool(init.disp, *queue_family_index); + } + + sync_ = gfxrecon::test::create_sync_objects(init.swapchain, init.disp, MAX_FRAMES_IN_FLIGHT); +} + +bool App::frame(const int frame_num) { + init.disp.waitForFences(1, &sync_.in_flight_fences[current_frame_], VK_TRUE, UINT64_MAX); + + uint32_t image_index = 0; + VkResult result = init.disp.acquireNextImageKHR( + init.swapchain, UINT64_MAX, sync_.available_semaphores[current_frame_], VK_NULL_HANDLE, &image_index); + + if (result == VK_ERROR_OUT_OF_DATE_KHR) + { + //recreate_swapchain(); + TestAppBase::recreate_swapchain(true); + //return frame_num >= NUM_FRAMES; + return true; + } + else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) + { + throw gfxrecon::test::vulkan_exception("failed to acquire next image", result); + } + + if (sync_.image_in_flight[image_index] != VK_NULL_HANDLE) + { + init.disp.waitForFences(1, &sync_.image_in_flight[image_index], VK_TRUE, UINT64_MAX); + } + sync_.image_in_flight[image_index] = sync_.in_flight_fences[current_frame_]; + + init.disp.resetCommandPool(command_pools_[current_frame_], 0); + VkCommandBufferAllocateInfo allocate_info = {}; + allocate_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocate_info.commandBufferCount = 1; + allocate_info.commandPool = command_pools_[current_frame_]; + VkCommandBuffer command_buffer; + result = init.disp.allocateCommandBuffers(&allocate_info, &command_buffer); + VERIFY_VK_RESULT("failed to allocate command buffer", result); + + { + VkCommandBufferBeginInfo begin_info = {}; + begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + result = init.disp.beginCommandBuffer(command_buffer, &begin_info); + VERIFY_VK_RESULT("failed to create command buffer", result); + + { + VkImageMemoryBarrier image_barrier = {}; + image_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + image_barrier.image = init.swapchain_images[image_index]; + image_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + image_barrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + image_barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + image_barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS; + image_barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS; + image_barrier.srcAccessMask = VK_ACCESS_NONE; + image_barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + init.disp.cmdPipelineBarrier(command_buffer, + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + 0, + 0, + nullptr, + 0, + nullptr, + 1, + &image_barrier); + } + + VkRenderPassBeginInfo render_pass_info = {}; + render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + render_pass_info.renderPass = render_pass_; + render_pass_info.framebuffer = framebuffers_[image_index]; + render_pass_info.renderArea.offset = { 0, 0 }; + render_pass_info.renderArea.extent = init.swapchain.extent; + VkClearValue clearColor{ { { 0.0f, 0.0f, 0.0f, 1.0f } } }; + render_pass_info.clearValueCount = 1; + render_pass_info.pClearValues = &clearColor; + + VkViewport viewport = {}; + viewport.x = 0.0f; + viewport.y = 0.0f; + viewport.width = (float)init.swapchain.extent.width; + viewport.height = (float)init.swapchain.extent.height; + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + + VkRect2D scissor = {}; + scissor.offset = { 0, 0 }; + scissor.extent = init.swapchain.extent; + + init.disp.cmdSetViewport(command_buffer, 0, 1, &viewport); + init.disp.cmdSetScissor(command_buffer, 0, 1, &scissor); + init.disp.cmdBeginRenderPass(command_buffer, &render_pass_info, VK_SUBPASS_CONTENTS_INLINE); + init.disp.cmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline_); + + init.disp.cmdDraw(command_buffer, 3, 1, 0, 0); + + init.disp.cmdEndRenderPass(command_buffer); + + { + VkImageMemoryBarrier image_barrier = {}; + image_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + image_barrier.image = init.swapchain_images[image_index]; + image_barrier.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + image_barrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + image_barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + image_barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS; + image_barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS; + image_barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + image_barrier.dstAccessMask = VK_ACCESS_NONE; + init.disp.cmdPipelineBarrier(command_buffer, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + 0, + 0, + nullptr, + 0, + nullptr, + 1, + &image_barrier); + } + + result = init.disp.endCommandBuffer(command_buffer); + VERIFY_VK_RESULT("failed to end command buffer", result); + } + + VkSubmitInfo submitInfo = {}; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + + VkSemaphore wait_semaphores[] = { sync_.available_semaphores[current_frame_] }; + VkPipelineStageFlags wait_stages[] = { VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT }; + submitInfo.waitSemaphoreCount = 1; + submitInfo.pWaitSemaphores = wait_semaphores; + submitInfo.pWaitDstStageMask = wait_stages; + + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &command_buffer; + + VkSemaphore signal_semaphores[] = { sync_.finished_semaphore[current_frame_] }; + submitInfo.signalSemaphoreCount = 1; + submitInfo.pSignalSemaphores = signal_semaphores; + + init.disp.resetFences(1, &sync_.in_flight_fences[current_frame_]); + + result = init.disp.queueSubmit(graphics_queue_, 1, &submitInfo, sync_.in_flight_fences[current_frame_]); + VERIFY_VK_RESULT("failed to submit queue", result); + + VkPresentInfoKHR present_info = {}; + present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + + present_info.waitSemaphoreCount = 1; + present_info.pWaitSemaphores = signal_semaphores; + + VkSwapchainKHR swapChains[] = { init.swapchain }; + present_info.swapchainCount = 1; + present_info.pSwapchains = swapChains; + + present_info.pImageIndices = &image_index; + + result = init.disp.queuePresentKHR(present_queue_, &present_info); + if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) + { + //recreate_swapchain(); + TestAppBase::recreate_swapchain(true); + //return frame_num >= NUM_FRAMES; + return true; + } + VERIFY_VK_RESULT("failed to present queue", result); + + current_frame_ = (current_frame_ + 1) % MAX_FRAMES_IN_FLIGHT; + + //return IS_RUNNING(frame_num); + return true; +} + +GFXRECON_END_NAMESPACE(sparse_resources) +GFXRECON_END_NAMESPACE(test_app) +GFXRECON_END_NAMESPACE(gfxrecon) + +int main(int argc, char* argv[]) +{ + try + { + gfxrecon::test_app::sparse_resources::App app{}; + app.run("sparse_resources"); + return 0; + } + catch (std::exception e) + { + std::cout << e.what() << std::endl; + return -1; + } +} diff --git a/test/test_apps/sparse-resources/shaders/tri.frag.spv b/test/test_apps/sparse-resources/shaders/tri.frag.spv new file mode 100644 index 0000000000000000000000000000000000000000..4dd964cdb214546be951938d7084d348aeca7bc0 GIT binary patch literal 456 zcmZ9I&r8EV5QV2n(`eP!AE38V@gfKwMG*DqHUB}$K_!PoiK*btKiiAo_pSBfhS}No zX6Eh7=Gj>p#VF1r;*fA1L>43REb@ds&!dbnIV0M-=`75NZDOB3`I5-_cF4{YeAb)p zbv<0J-`^59O>>Xk(0^1fO*d3^U$?8LzJ6{YR~zvnCQ0)@|9}fM7le0+^&xg0ZM>f* zC%-+%oC|78;)MPQS*+(Jc#Eu-y!9#By8nB@?s=DR{o%cYKJT5MarUL0y8)}GM>WOP p^=Qu(YVzLglKTRA*Dbpqd`IUikea@yoF2UQjw$uzzfrp;egH6QAEf{Q literal 0 HcmV?d00001 diff --git a/test/test_apps/sparse-resources/shaders/tri.vert.spv b/test/test_apps/sparse-resources/shaders/tri.vert.spv new file mode 100644 index 0000000000000000000000000000000000000000..6d4ac23ce8187e6c21ea1e76aa73fdb561c0d570 GIT binary patch literal 1288 zcmaKqTThcg5Qeuc1@XW^1T+!-kZ_U)H5!8wAV?yKa?yW48q*6BXp>TiCSLH*{9E3T z@H~5nz4B|`?!52J&dknkqrNf`nzgVILO20_v!NbptW%*8x%PCJjCP&>i_Te3as-aS zcd!T={2s6x-^9|_?F|RN+WoWMu-zGSE>5qzgJJjQsVTO9^w0Z)7<)hF_+RYR^(8r* zbQZ!$4r+{rhiK#A9+1ywt}eGX`+A+Zearpy+vkhO68AiE&oP&0u^dmAv;}PIRbY|* z6Rh&F%jJAQf+-FSb{S&r* zr`%}{pEFPUleUW{FFC!J9pHZB^?Qadtlp{TkblkGv&g??KFPWZ_uhP~wandr1k9n| z{l_ER{k~QGAGtU8)q$VdUzl&wy}qFtU@vmt%-jgq|0MH0{ORqaetyG!L(jlG*eAQW zyw^kOdY5ZJ%^3H4rgfm+X71MA$eQzYcIL4M@54Oa`Fo&N@;IvxSvTGuopq_ZhpkrP Zt-BA@jdy<5)pmB~(spjf-7>Wua0gywNk9Mq literal 0 HcmV?d00001 From ebba5efd5efce0809d94048b1f1e07e7bdf78029 Mon Sep 17 00:00:00 2001 From: Nick Driscoll Date: Thu, 30 Jan 2025 11:31:51 -0500 Subject: [PATCH 04/10] Add sparse-resources to cmakelists --- test/test_apps/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/test/test_apps/CMakeLists.txt b/test/test_apps/CMakeLists.txt index e5e44fe9a2..f81b2d54f7 100644 --- a/test/test_apps/CMakeLists.txt +++ b/test/test_apps/CMakeLists.txt @@ -33,3 +33,4 @@ add_subdirectory(multisample-depth) add_subdirectory(pipeline-binaries) add_subdirectory(host-image-copy) add_subdirectory(shader-objects) +add_subdirectory(sparse-resources) From 0c0ccbdbf46934e9545fb6dc528fa7ede37a0f2c Mon Sep 17 00:00:00 2001 From: Nick Driscoll Date: Thu, 30 Jan 2025 11:40:44 -0500 Subject: [PATCH 05/10] fix issue --- test/test_apps/sparse-resources/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_apps/sparse-resources/CMakeLists.txt b/test/test_apps/sparse-resources/CMakeLists.txt index cc025c1bba..0bffe109c1 100644 --- a/test/test_apps/sparse-resources/CMakeLists.txt +++ b/test/test_apps/sparse-resources/CMakeLists.txt @@ -65,7 +65,7 @@ add_custom_command( DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}) if (WIN32) add_custom_command(TARGET gfxrecon-testapp-sparse-resources POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_if_different $ $ + COMMAND ${CMAKE_COMMAND} -E copy_if_different $ $ COMMAND_EXPAND_LISTS) endif () From 0c4d22daecb3ad8025e05384675dfb256a87e5df Mon Sep 17 00:00:00 2001 From: Nick Driscoll Date: Thu, 30 Jan 2025 12:26:52 -0500 Subject: [PATCH 06/10] staging buffer --- test/test_apps/sparse-resources/app.cpp | 65 ++++++++++++++++++++----- 1 file changed, 52 insertions(+), 13 deletions(-) diff --git a/test/test_apps/sparse-resources/app.cpp b/test/test_apps/sparse-resources/app.cpp index c067151cf0..82e6b18a26 100644 --- a/test/test_apps/sparse-resources/app.cpp +++ b/test/test_apps/sparse-resources/app.cpp @@ -34,6 +34,7 @@ GFXRECON_BEGIN_NAMESPACE(test_app) GFXRECON_BEGIN_NAMESPACE(sparse_resources) const size_t MAX_FRAMES_IN_FLIGHT = 2; +const size_t STAGING_BUFFER_SIZE = 16 * 1024 * 1024; class App : public gfxrecon::test::TestAppBase { @@ -49,11 +50,14 @@ class App : public gfxrecon::test::TestAppBase VkPipelineLayout pipeline_layout_; VkPipeline graphics_pipeline_; + VkBuffer staging_buffer_; + uint8_t* staging_buffer_ptr_; VkImage image0_; VkImageView image0_view_; VkDeviceMemory image_backing_memory_; - uint32_t device_memory_heap_; - uint32_t staging_memory_heap_; + VkDeviceMemory staging_backing_memory_; + uint32_t device_memory_type_; + uint32_t staging_memory_type_; VkCommandPool command_pools_[MAX_FRAMES_IN_FLIGHT]; @@ -66,6 +70,7 @@ class App : public gfxrecon::test::TestAppBase void create_graphics_pipeline(); void create_framebuffers(); void create_descriptor_set(); + void create_staging_buffer(); void create_textures(); void determine_memory_heaps(); void configure_instance_builder(test::InstanceBuilder& instance_builder); @@ -279,28 +284,61 @@ void App::determine_memory_heaps() { (type.propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) && (type.propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) && (type.propertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) { - staging_memory_heap_ = type.heapIndex; + staging_memory_type_ = i; } else if (heap.flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) { if (heap.size > largest_seen) { - device_memory_heap_ = type.heapIndex; + device_memory_type_ = i; largest_seen = heap.size; } } } } +void App::create_staging_buffer() { + // Create buffer object + VkBufferCreateInfo buffer_info = {}; + buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + buffer_info.pNext = nullptr; + buffer_info.flags = 0; + buffer_info.size = STAGING_BUFFER_SIZE; + buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + buffer_info.queueFamilyIndexCount = 1; + uint32_t idx = init.device.get_queue_index(test::QueueType::graphics).value(); + buffer_info.pQueueFamilyIndices = &idx; + VERIFY_VK_RESULT("failed to create staging buffer", init.disp.createBuffer(&buffer_info, nullptr, &staging_buffer_)); + + // Allocate and bind buffer memory + VkMemoryRequirements mem_reqs = {}; + init.disp.getBufferMemoryRequirements(staging_buffer_, &mem_reqs); + VkMemoryAllocateInfo alloc_info = {}; + alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + alloc_info.pNext = nullptr; + alloc_info.allocationSize = mem_reqs.size; + alloc_info.memoryTypeIndex = staging_memory_type_; + VERIFY_VK_RESULT("failed to allocate staging buffer memory", init.disp.allocateMemory(&alloc_info, nullptr, &staging_backing_memory_)); + VERIFY_VK_RESULT("failed to bind staging buffer memory", init.disp.bindBufferMemory(staging_buffer_, staging_backing_memory_, 0)); + + // Map buffer + init.disp.mapMemory(staging_backing_memory_, 0, STAGING_BUFFER_SIZE, 0, (void**)&staging_buffer_ptr_); +} + void App::create_textures() { + const uint32_t size = 16; + const uint32_t mip_levels = 5; + const VkFormat format = VK_FORMAT_R8G8B8A8_SRGB; + // Create image object VkImageCreateInfo image_info = {}; image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; image_info.pNext = nullptr; image_info.flags = VK_IMAGE_CREATE_SPARSE_BINDING_BIT | VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT; image_info.imageType = VK_IMAGE_TYPE_2D; - image_info.format = VK_FORMAT_R8G8B8A8_SRGB; - image_info.extent.width = 64; - image_info.extent.height = 64; + image_info.format = format; + image_info.extent.width = size; + image_info.extent.height = size; image_info.extent.depth = 1; - image_info.mipLevels = 7; + image_info.mipLevels = mip_levels; image_info.arrayLayers = 1; image_info.samples = VK_SAMPLE_COUNT_1_BIT; image_info.tiling = VK_IMAGE_TILING_OPTIMAL; @@ -310,7 +348,7 @@ void App::create_textures() { uint32_t idx = init.device.get_queue_index(test::QueueType::graphics).value(); image_info.pQueueFamilyIndices = &idx; image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - init.disp.createImage(&image_info, nullptr, &image0_); + VERIFY_VK_RESULT("failed to create image", init.disp.createImage(&image_info, nullptr, &image0_)); // Allocate image backing memory VkMemoryRequirements image0_reqs = {}; @@ -319,11 +357,11 @@ void App::create_textures() { alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; alloc_info.pNext = nullptr; alloc_info.allocationSize = image0_reqs.size; - alloc_info.memoryTypeIndex = device_memory_heap_; + alloc_info.memoryTypeIndex = device_memory_type_; init.disp.allocateMemory(&alloc_info, nullptr, &image_backing_memory_); // Bind image to memory - init.disp.bindImageMemory(image0_, image_backing_memory_, 0); + //init.disp.bindImageMemory(image0_, image_backing_memory_, 0); // Create image view object VkImageViewCreateInfo view_info = {}; @@ -332,11 +370,11 @@ void App::create_textures() { view_info.flags = 0; view_info.image = image0_; view_info.viewType = VK_IMAGE_VIEW_TYPE_2D; - view_info.format = VK_FORMAT_R8G8B8A8_SRGB; + view_info.format = format; view_info.components = {}; view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; view_info.subresourceRange.baseMipLevel = 0; - view_info.subresourceRange.levelCount = 7; + view_info.subresourceRange.levelCount = mip_levels; view_info.subresourceRange.baseArrayLayer = 0; view_info.subresourceRange.layerCount = 1; init.disp.createImageView(&view_info, nullptr, &image0_view_); @@ -363,6 +401,7 @@ void App::setup() { create_render_pass(); create_graphics_pipeline(); create_framebuffers(); + create_staging_buffer(); create_textures(); auto queue_family_index = init.device.get_queue_index(gfxrecon::test::QueueType::graphics); From 8618bbba45ec9eaa5464b4f34cc5cc6ce5989804 Mon Sep 17 00:00:00 2001 From: Nick Driscoll Date: Thu, 30 Jan 2025 12:29:59 -0500 Subject: [PATCH 07/10] format --- test/test_apps/sparse-resources/app.cpp | 202 +++++++++++++----------- 1 file changed, 106 insertions(+), 96 deletions(-) diff --git a/test/test_apps/sparse-resources/app.cpp b/test/test_apps/sparse-resources/app.cpp index 82e6b18a26..658c58c1fe 100644 --- a/test/test_apps/sparse-resources/app.cpp +++ b/test/test_apps/sparse-resources/app.cpp @@ -34,34 +34,33 @@ GFXRECON_BEGIN_NAMESPACE(test_app) GFXRECON_BEGIN_NAMESPACE(sparse_resources) const size_t MAX_FRAMES_IN_FLIGHT = 2; -const size_t STAGING_BUFFER_SIZE = 16 * 1024 * 1024; +const size_t STAGING_BUFFER_SIZE = 16 * 1024 * 1024; class App : public gfxrecon::test::TestAppBase { -private: + private: VkQueue graphics_queue_; VkQueue present_queue_; std::vector framebuffers_; - VkRenderPass render_pass_; + VkRenderPass render_pass_; VkDescriptorPool descriptor_pool_; - VkDescriptorSet descriptor_set_; + VkDescriptorSet descriptor_set_; VkPipelineLayout pipeline_layout_; VkPipeline graphics_pipeline_; - VkBuffer staging_buffer_; - uint8_t* staging_buffer_ptr_; - VkImage image0_; - VkImageView image0_view_; + VkBuffer staging_buffer_; + uint8_t* staging_buffer_ptr_; + VkImage image0_; + VkImageView image0_view_; VkDeviceMemory image_backing_memory_; VkDeviceMemory staging_backing_memory_; - uint32_t device_memory_type_; - uint32_t staging_memory_type_; + uint32_t device_memory_type_; + uint32_t staging_memory_type_; VkCommandPool command_pools_[MAX_FRAMES_IN_FLIGHT]; - size_t current_frame_ = 0; gfxrecon::test::Sync sync_; @@ -79,18 +78,20 @@ class App : public gfxrecon::test::TestAppBase void setup(); }; -void App::configure_instance_builder(test::InstanceBuilder& instance_builder) { +void App::configure_instance_builder(test::InstanceBuilder& instance_builder) +{ instance_builder.desire_api_version(VK_MAKE_VERSION(1, 3, 0)); } -void App::configure_physical_device_selector(test::PhysicalDeviceSelector& phys_device_selector) { +void App::configure_physical_device_selector(test::PhysicalDeviceSelector& phys_device_selector) +{ VkPhysicalDeviceFeatures feats = {}; - feats.sparseBinding = true; - feats.sparseResidencyBuffer = true; - feats.sparseResidencyAliased = true; - feats.sparseResidencyImage2D = true; + feats.sparseBinding = true; + feats.sparseResidencyBuffer = true; + feats.sparseResidencyAliased = true; + feats.sparseResidencyImage2D = true; phys_device_selector.set_required_features(feats); - + phys_device_selector.add_required_extension("VK_KHR_maintenance2"); } @@ -266,126 +267,134 @@ void App::create_framebuffers() } } -void App::create_descriptor_set() { +void App::create_descriptor_set() {} -} - -void App::determine_memory_heaps() { +void App::determine_memory_heaps() +{ VkPhysicalDeviceMemoryProperties props = init.physical_device.memory_properties; - + // Search for largest device-only memory heap VkDeviceSize largest_seen = 0; - for (int i = 0; i < props.memoryTypeCount; ++i) { - VkMemoryType type = props.memoryTypes[i]; - bool device_local = type.propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; - VkMemoryHeap heap = props.memoryHeaps[type.heapIndex]; + for (int i = 0; i < props.memoryTypeCount; ++i) + { + VkMemoryType type = props.memoryTypes[i]; + bool device_local = type.propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + VkMemoryHeap heap = props.memoryHeaps[type.heapIndex]; - if ( - (type.propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) && + if ((type.propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) && (type.propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) && - (type.propertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) { - staging_memory_type_ = i; - } else if (heap.flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) { - if (heap.size > largest_seen) { + (type.propertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) + { + staging_memory_type_ = i; + } + else if (heap.flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) + { + if (heap.size > largest_seen) + { device_memory_type_ = i; - largest_seen = heap.size; + largest_seen = heap.size; } } } } -void App::create_staging_buffer() { +void App::create_staging_buffer() +{ // Create buffer object - VkBufferCreateInfo buffer_info = {}; - buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; - buffer_info.pNext = nullptr; - buffer_info.flags = 0; - buffer_info.size = STAGING_BUFFER_SIZE; - buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; - buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + VkBufferCreateInfo buffer_info = {}; + buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + buffer_info.pNext = nullptr; + buffer_info.flags = 0; + buffer_info.size = STAGING_BUFFER_SIZE; + buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; buffer_info.queueFamilyIndexCount = 1; - uint32_t idx = init.device.get_queue_index(test::QueueType::graphics).value(); - buffer_info.pQueueFamilyIndices = &idx; - VERIFY_VK_RESULT("failed to create staging buffer", init.disp.createBuffer(&buffer_info, nullptr, &staging_buffer_)); + uint32_t idx = init.device.get_queue_index(test::QueueType::graphics).value(); + buffer_info.pQueueFamilyIndices = &idx; + VERIFY_VK_RESULT("failed to create staging buffer", + init.disp.createBuffer(&buffer_info, nullptr, &staging_buffer_)); // Allocate and bind buffer memory VkMemoryRequirements mem_reqs = {}; init.disp.getBufferMemoryRequirements(staging_buffer_, &mem_reqs); VkMemoryAllocateInfo alloc_info = {}; - alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - alloc_info.pNext = nullptr; - alloc_info.allocationSize = mem_reqs.size; - alloc_info.memoryTypeIndex = staging_memory_type_; - VERIFY_VK_RESULT("failed to allocate staging buffer memory", init.disp.allocateMemory(&alloc_info, nullptr, &staging_backing_memory_)); - VERIFY_VK_RESULT("failed to bind staging buffer memory", init.disp.bindBufferMemory(staging_buffer_, staging_backing_memory_, 0)); + alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + alloc_info.pNext = nullptr; + alloc_info.allocationSize = mem_reqs.size; + alloc_info.memoryTypeIndex = staging_memory_type_; + VERIFY_VK_RESULT("failed to allocate staging buffer memory", + init.disp.allocateMemory(&alloc_info, nullptr, &staging_backing_memory_)); + VERIFY_VK_RESULT("failed to bind staging buffer memory", + init.disp.bindBufferMemory(staging_buffer_, staging_backing_memory_, 0)); // Map buffer init.disp.mapMemory(staging_backing_memory_, 0, STAGING_BUFFER_SIZE, 0, (void**)&staging_buffer_ptr_); } -void App::create_textures() { - const uint32_t size = 16; +void App::create_textures() +{ + const uint32_t size = 16; const uint32_t mip_levels = 5; - const VkFormat format = VK_FORMAT_R8G8B8A8_SRGB; + const VkFormat format = VK_FORMAT_R8G8B8A8_SRGB; // Create image object VkImageCreateInfo image_info = {}; - image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; - image_info.pNext = nullptr; - image_info.flags = VK_IMAGE_CREATE_SPARSE_BINDING_BIT | VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT; - image_info.imageType = VK_IMAGE_TYPE_2D; - image_info.format = format; - image_info.extent.width = size; - image_info.extent.height = size; - image_info.extent.depth = 1; - image_info.mipLevels = mip_levels; - image_info.arrayLayers = 1; - image_info.samples = VK_SAMPLE_COUNT_1_BIT; - image_info.tiling = VK_IMAGE_TILING_OPTIMAL; - image_info.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; - image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + image_info.pNext = nullptr; + image_info.flags = VK_IMAGE_CREATE_SPARSE_BINDING_BIT | VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT; + image_info.imageType = VK_IMAGE_TYPE_2D; + image_info.format = format; + image_info.extent.width = size; + image_info.extent.height = size; + image_info.extent.depth = 1; + image_info.mipLevels = mip_levels; + image_info.arrayLayers = 1; + image_info.samples = VK_SAMPLE_COUNT_1_BIT; + image_info.tiling = VK_IMAGE_TILING_OPTIMAL; + image_info.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; + image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; image_info.queueFamilyIndexCount = 1; - uint32_t idx = init.device.get_queue_index(test::QueueType::graphics).value(); - image_info.pQueueFamilyIndices = &idx; - image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + uint32_t idx = init.device.get_queue_index(test::QueueType::graphics).value(); + image_info.pQueueFamilyIndices = &idx; + image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; VERIFY_VK_RESULT("failed to create image", init.disp.createImage(&image_info, nullptr, &image0_)); // Allocate image backing memory VkMemoryRequirements image0_reqs = {}; init.disp.getImageMemoryRequirements(image0_, &image0_reqs); VkMemoryAllocateInfo alloc_info = {}; - alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - alloc_info.pNext = nullptr; - alloc_info.allocationSize = image0_reqs.size; - alloc_info.memoryTypeIndex = device_memory_type_; + alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + alloc_info.pNext = nullptr; + alloc_info.allocationSize = image0_reqs.size; + alloc_info.memoryTypeIndex = device_memory_type_; init.disp.allocateMemory(&alloc_info, nullptr, &image_backing_memory_); // Bind image to memory - //init.disp.bindImageMemory(image0_, image_backing_memory_, 0); + // init.disp.bindImageMemory(image0_, image_backing_memory_, 0); // Create image view object - VkImageViewCreateInfo view_info = {}; - view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - view_info.pNext = nullptr; - view_info.flags = 0; - view_info.image = image0_; - view_info.viewType = VK_IMAGE_VIEW_TYPE_2D; - view_info.format = format; - view_info.components = {}; - view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - view_info.subresourceRange.baseMipLevel = 0; - view_info.subresourceRange.levelCount = mip_levels; + VkImageViewCreateInfo view_info = {}; + view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + view_info.pNext = nullptr; + view_info.flags = 0; + view_info.image = image0_; + view_info.viewType = VK_IMAGE_VIEW_TYPE_2D; + view_info.format = format; + view_info.components = {}; + view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + view_info.subresourceRange.baseMipLevel = 0; + view_info.subresourceRange.levelCount = mip_levels; view_info.subresourceRange.baseArrayLayer = 0; - view_info.subresourceRange.layerCount = 1; + view_info.subresourceRange.layerCount = 1; init.disp.createImageView(&view_info, nullptr, &image0_view_); // Upload image data - // Update descriptor set? } -void App::setup() { +void App::setup() +{ auto graphics_queue = init.device.get_queue(gfxrecon::test::QueueType::graphics); if (!graphics_queue.has_value()) throw std::runtime_error("could not get graphics queue"); @@ -415,7 +424,8 @@ void App::setup() { sync_ = gfxrecon::test::create_sync_objects(init.swapchain, init.disp, MAX_FRAMES_IN_FLIGHT); } -bool App::frame(const int frame_num) { +bool App::frame(const int frame_num) +{ init.disp.waitForFences(1, &sync_.in_flight_fences[current_frame_], VK_TRUE, UINT64_MAX); uint32_t image_index = 0; @@ -424,9 +434,9 @@ bool App::frame(const int frame_num) { if (result == VK_ERROR_OUT_OF_DATE_KHR) { - //recreate_swapchain(); + // recreate_swapchain(); TestAppBase::recreate_swapchain(true); - //return frame_num >= NUM_FRAMES; + // return frame_num >= NUM_FRAMES; return true; } else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) @@ -572,16 +582,16 @@ bool App::frame(const int frame_num) { result = init.disp.queuePresentKHR(present_queue_, &present_info); if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) { - //recreate_swapchain(); + // recreate_swapchain(); TestAppBase::recreate_swapchain(true); - //return frame_num >= NUM_FRAMES; + // return frame_num >= NUM_FRAMES; return true; } VERIFY_VK_RESULT("failed to present queue", result); current_frame_ = (current_frame_ + 1) % MAX_FRAMES_IN_FLIGHT; - //return IS_RUNNING(frame_num); + // return IS_RUNNING(frame_num); return true; } From b455a197a72b8b6ee0808f3a61f42f203463f3d3 Mon Sep 17 00:00:00 2001 From: Nick Driscoll Date: Wed, 5 Feb 2025 15:18:02 -0500 Subject: [PATCH 08/10] Why is image only valid for first frame --- test/test_apps/sparse-resources/app.cpp | 392 ++++++++++++++++-- .../sparse-resources/shaders/tri.frag.spv | Bin 456 -> 840 bytes 2 files changed, 350 insertions(+), 42 deletions(-) diff --git a/test/test_apps/sparse-resources/app.cpp b/test/test_apps/sparse-resources/app.cpp index 658c58c1fe..1ece8db060 100644 --- a/test/test_apps/sparse-resources/app.cpp +++ b/test/test_apps/sparse-resources/app.cpp @@ -35,6 +35,7 @@ GFXRECON_BEGIN_NAMESPACE(sparse_resources) const size_t MAX_FRAMES_IN_FLIGHT = 2; const size_t STAGING_BUFFER_SIZE = 16 * 1024 * 1024; +const VkFormat IMAGE_FORMAT = VK_FORMAT_R8G8B8A8_SRGB; class App : public gfxrecon::test::TestAppBase { @@ -46,6 +47,7 @@ class App : public gfxrecon::test::TestAppBase VkRenderPass render_pass_; VkDescriptorPool descriptor_pool_; + VkDescriptorSetLayout descriptor_layout_; VkDescriptorSet descriptor_set_; VkPipelineLayout pipeline_layout_; VkPipeline graphics_pipeline_; @@ -54,23 +56,31 @@ class App : public gfxrecon::test::TestAppBase uint8_t* staging_buffer_ptr_; VkImage image0_; VkImageView image0_view_; + const uint32_t image_size_ = 16; + const uint32_t mip_levels_ = 1; + VkSparseImageMemoryRequirements image_mem_reqs; VkDeviceMemory image_backing_memory_; VkDeviceMemory staging_backing_memory_; uint32_t device_memory_type_; uint32_t staging_memory_type_; + VkExtent3D sparse_block_granularity_; + VkCommandPool command_pools_[MAX_FRAMES_IN_FLIGHT]; + VkCommandBuffer command_buffers_[MAX_FRAMES_IN_FLIGHT]; size_t current_frame_ = 0; gfxrecon::test::Sync sync_; + std::vector sparse_binding_semaphores_; + VkFence dirty_fence_; void create_render_pass(); void create_graphics_pipeline(); void create_framebuffers(); void create_descriptor_set(); void create_staging_buffer(); - void create_textures(); + void create_images(); void determine_memory_heaps(); void configure_instance_builder(test::InstanceBuilder& instance_builder); void configure_physical_device_selector(test::PhysicalDeviceSelector& phys_device_selector); @@ -209,7 +219,8 @@ void App::create_graphics_pipeline() VkPipelineLayoutCreateInfo pipeline_layout_info = {}; pipeline_layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; - pipeline_layout_info.setLayoutCount = 0; + pipeline_layout_info.setLayoutCount = 1; + pipeline_layout_info.pSetLayouts = &descriptor_layout_; pipeline_layout_info.pushConstantRangeCount = 0; auto result = init.disp.createPipelineLayout(&pipeline_layout_info, nullptr, &pipeline_layout_); @@ -267,7 +278,81 @@ void App::create_framebuffers() } } -void App::create_descriptor_set() {} +void App::create_descriptor_set() { + VkDescriptorPoolSize pool_sizes[2] = {}; + pool_sizes[0].type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + pool_sizes[0].descriptorCount = 1; + pool_sizes[1].type = VK_DESCRIPTOR_TYPE_SAMPLER; + pool_sizes[1].descriptorCount = 1; + + VkDescriptorPoolCreateInfo pool_info = {}; + pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + pool_info.pNext = nullptr; + pool_info.flags = 0; + pool_info.maxSets = 1; + pool_info.poolSizeCount = 2; + pool_info.pPoolSizes = pool_sizes; + VERIFY_VK_RESULT( + "Failed to create descriptor pool", + init.disp.createDescriptorPool(&pool_info, nullptr, &descriptor_pool_) + ); + + // Create immutable sampler + VkSamplerCreateInfo sampler_info = {}; + sampler_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + sampler_info.pNext = nullptr; + sampler_info.flags = 0; + sampler_info.magFilter = VK_FILTER_NEAREST; + sampler_info.minFilter = VK_FILTER_NEAREST; + sampler_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST; + sampler_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + sampler_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + sampler_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + sampler_info.mipLodBias = 0.0; + sampler_info.anisotropyEnable = VK_FALSE; + sampler_info.maxAnisotropy = 1.0; + sampler_info.compareEnable = VK_FALSE; + sampler_info.compareOp = VK_COMPARE_OP_NEVER; + sampler_info.minLod = 0.0; + sampler_info.maxLod = 1.0; + sampler_info.borderColor = {}; + sampler_info.unnormalizedCoordinates = VK_FALSE; + + VkSampler sampler; + VERIFY_VK_RESULT("Failed to create sampler.", init.disp.createSampler(&sampler_info, nullptr, &sampler)); + + VkDescriptorSetLayoutBinding bindings[2] = {}; + bindings[0].binding = 0; + bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + bindings[0].descriptorCount = 1; + bindings[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT; + bindings[0].pImmutableSamplers = nullptr; + bindings[1].binding = 1; + bindings[1].descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER; + bindings[1].descriptorCount = 1; + bindings[1].stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT; + bindings[1].pImmutableSamplers = &sampler; + + VkDescriptorSetLayoutCreateInfo layout_info = {}; + layout_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + layout_info.pNext = nullptr; + layout_info.flags = 0; + layout_info.bindingCount = 2; + layout_info.pBindings = bindings; + VERIFY_VK_RESULT( + "Failed to create descriptor set layout", + init.disp.createDescriptorSetLayout(&layout_info, nullptr, &descriptor_layout_) + ); + + VkDescriptorSetAllocateInfo alloc_info = {}; + alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + alloc_info.pNext = nullptr; + alloc_info.descriptorPool = descriptor_pool_; + alloc_info.descriptorSetCount = 1; + alloc_info.pSetLayouts = &descriptor_layout_; + + VERIFY_VK_RESULT("Failed to allocate descriptor sets", init.disp.allocateDescriptorSets(&alloc_info, &descriptor_set_)); +} void App::determine_memory_heaps() { @@ -331,23 +416,50 @@ void App::create_staging_buffer() init.disp.mapMemory(staging_backing_memory_, 0, STAGING_BUFFER_SIZE, 0, (void**)&staging_buffer_ptr_); } -void App::create_textures() +void App::create_images() { - const uint32_t size = 16; - const uint32_t mip_levels = 5; - const VkFormat format = VK_FORMAT_R8G8B8A8_SRGB; + // Get VkSparseImageFormatProperties for 2D R8G8B8A8_SRGB images + VkPhysicalDevice pd = init.physical_device.physical_device; + uint32_t prop_count = 0; + init.inst_disp.getPhysicalDeviceSparseImageFormatProperties( + pd, + IMAGE_FORMAT, + VK_IMAGE_TYPE_2D, + VK_SAMPLE_COUNT_1_BIT, + VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, + VK_IMAGE_TILING_OPTIMAL, + &prop_count, + nullptr + ); + std::vector props; + props.resize(prop_count); + init.inst_disp.getPhysicalDeviceSparseImageFormatProperties( + pd, + IMAGE_FORMAT, + VK_IMAGE_TYPE_2D, + VK_SAMPLE_COUNT_1_BIT, + VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, + VK_IMAGE_TILING_OPTIMAL, + &prop_count, + props.data() + ); + for (VkSparseImageFormatProperties prop : props) { + if (prop.aspectMask == VK_IMAGE_ASPECT_COLOR_BIT) { + sparse_block_granularity_ = prop.imageGranularity; + } + } // Create image object - VkImageCreateInfo image_info = {}; + VkImageCreateInfo image_info = {}; image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; image_info.pNext = nullptr; image_info.flags = VK_IMAGE_CREATE_SPARSE_BINDING_BIT | VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT; image_info.imageType = VK_IMAGE_TYPE_2D; - image_info.format = format; - image_info.extent.width = size; - image_info.extent.height = size; + image_info.format = IMAGE_FORMAT; + image_info.extent.width = image_size_; + image_info.extent.height = image_size_; image_info.extent.depth = 1; - image_info.mipLevels = mip_levels; + image_info.mipLevels = mip_levels_; image_info.arrayLayers = 1; image_info.samples = VK_SAMPLE_COUNT_1_BIT; image_info.tiling = VK_IMAGE_TILING_OPTIMAL; @@ -359,9 +471,19 @@ void App::create_textures() image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; VERIFY_VK_RESULT("failed to create image", init.disp.createImage(&image_info, nullptr, &image0_)); - // Allocate image backing memory + // Get memory requirements VkMemoryRequirements image0_reqs = {}; init.disp.getImageMemoryRequirements(image0_, &image0_reqs); + + uint32_t sparse_reqs_count = 0; + init.disp.getImageSparseMemoryRequirements(image0_, &sparse_reqs_count, nullptr); + std::vector sparse_reqs; + sparse_reqs.resize(sparse_reqs_count); + init.disp.getImageSparseMemoryRequirements(image0_, &sparse_reqs_count, sparse_reqs.data()); + assert(sparse_reqs.size() == 1); + image_mem_reqs = sparse_reqs[0]; + + // Allocate image backing memory VkMemoryAllocateInfo alloc_info = {}; alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; alloc_info.pNext = nullptr; @@ -369,9 +491,6 @@ void App::create_textures() alloc_info.memoryTypeIndex = device_memory_type_; init.disp.allocateMemory(&alloc_info, nullptr, &image_backing_memory_); - // Bind image to memory - // init.disp.bindImageMemory(image0_, image_backing_memory_, 0); - // Create image view object VkImageViewCreateInfo view_info = {}; view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; @@ -379,18 +498,136 @@ void App::create_textures() view_info.flags = 0; view_info.image = image0_; view_info.viewType = VK_IMAGE_VIEW_TYPE_2D; - view_info.format = format; + view_info.format = IMAGE_FORMAT; view_info.components = {}; view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; view_info.subresourceRange.baseMipLevel = 0; - view_info.subresourceRange.levelCount = mip_levels; + view_info.subresourceRange.levelCount = mip_levels_; view_info.subresourceRange.baseArrayLayer = 0; view_info.subresourceRange.layerCount = 1; init.disp.createImageView(&view_info, nullptr, &image0_view_); - // Upload image data + // Write image data to staging buffer + for (int y = 0; y < image_size_; ++y) { + for (int x = 0; x < image_size_; ++x) { + // Pointer to first byte of current pixel + // This is an R8G8B8A8 format + uint8_t* first_byte = staging_buffer_ptr_ + 4 * (y * image_size_ + x); + bool is_purple = x % 2 + y % 2 == 1; + first_byte[0] = is_purple ? 0xFF : 0x00; + first_byte[2] = is_purple ? 0xFF : 0x00; + } + } + + // Need to start command buffer for image upload + VkCommandBuffer cmd = command_buffers_[0]; + VkCommandBufferBeginInfo begin_info = {}; + begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + VkResult result = init.disp.beginCommandBuffer(cmd, &begin_info); + VERIFY_VK_RESULT("failed to begin command buffer", result); + + + // Memory barrier to transition into transfer dst optimal + { + VkImageMemoryBarrier image_barrier = {}; + image_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + image_barrier.image = image0_; + image_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + image_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + image_barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + image_barrier.subresourceRange.layerCount = 1; + image_barrier.subresourceRange.levelCount = 1; + image_barrier.srcAccessMask = VK_ACCESS_NONE; + image_barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + init.disp.cmdPipelineBarrier(cmd, + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + 0, + 0, + nullptr, + 0, + nullptr, + 1, + &image_barrier); + } + + // Copy image data from staging buffer + VkBufferImageCopy image_copy = {}; + image_copy.bufferOffset = 0; + image_copy.bufferRowLength = 0; + image_copy.bufferImageHeight = 0; + image_copy.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + image_copy.imageSubresource.mipLevel = 0; + image_copy.imageSubresource.baseArrayLayer = 0; + image_copy.imageSubresource.layerCount = 1; + image_copy.imageOffset.x = 0; + image_copy.imageOffset.y = 0; + image_copy.imageOffset.z = 0; + image_copy.imageExtent.width = image_size_; + image_copy.imageExtent.height = image_size_; + image_copy.imageExtent.depth = 1; + + init.disp.cmdCopyBufferToImage(cmd, staging_buffer_, image0_, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &image_copy); + + // Memory barrier to transition into read-only optimal + { + VkImageMemoryBarrier image_barrier = {}; + image_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + image_barrier.image = image0_; + image_barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + image_barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + image_barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + image_barrier.subresourceRange.layerCount = 1; + image_barrier.subresourceRange.levelCount = 1; + image_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + image_barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + init.disp.cmdPipelineBarrier(cmd, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + 0, + 0, + nullptr, + 0, + nullptr, + 1, + &image_barrier); + } - // Update descriptor set? + result = init.disp.endCommandBuffer(cmd); + VERIFY_VK_RESULT("failed to end command buffer", result); + + // Queue submit + VkSubmitInfo submit_info = {}; + submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submit_info.pNext = nullptr; + submit_info.waitSemaphoreCount = 0; + submit_info.signalSemaphoreCount = 0; + submit_info.commandBufferCount = 1; + submit_info.pCommandBuffers = &cmd; + init.disp.queueSubmit(graphics_queue_, 1, &submit_info, dirty_fence_); + + // Wait for submission + result = init.disp.waitForFences(1, &dirty_fence_, VK_TRUE, ~0); + VERIFY_VK_RESULT("failed to wait for upload fence", result); + + // Update descriptor set + VkDescriptorImageInfo desc_image = {}; + desc_image.sampler = 0; + desc_image.imageView = image0_view_; + desc_image.imageLayout = VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL; + + VkWriteDescriptorSet write = {}; + write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + write.pNext = nullptr; + write.dstSet = descriptor_set_; + write.dstBinding = 0; + write.dstArrayElement = 0; + write.descriptorCount = 1; + write.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + write.pImageInfo = &desc_image; + write.pBufferInfo = nullptr; + write.pTexelBufferView = nullptr; + init.disp.updateDescriptorSets(1, &write, 0, nullptr); } void App::setup() @@ -405,23 +642,50 @@ void App::setup() throw std::runtime_error("could not get present queue"); present_queue_ = *present_queue; - determine_memory_heaps(); + // Create semaphore for sparse binding + sparse_binding_semaphores_.resize(MAX_FRAMES_IN_FLIGHT); + for (int i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i) { + VkSemaphoreCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + info.pNext = nullptr; + info.flags = 0; + init.disp.createSemaphore(&info, nullptr, &sparse_binding_semaphores_[i]); + } - create_render_pass(); - create_graphics_pipeline(); - create_framebuffers(); - create_staging_buffer(); - create_textures(); + // Create fence + { + VkFenceCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + info.pNext = nullptr; + info.flags = 0; + init.disp.createFence(&info, nullptr, &dirty_fence_); + } auto queue_family_index = init.device.get_queue_index(gfxrecon::test::QueueType::graphics); if (!queue_family_index) throw std::runtime_error("could not find graphics queue"); - for (auto& command_pool : command_pools_) + for (int i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i) { + VkCommandPool& command_pool = command_pools_[i]; command_pool = gfxrecon::test::create_command_pool(init.disp, *queue_family_index); + + VkCommandBufferAllocateInfo allocate_info = {}; + allocate_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocate_info.commandBufferCount = 1; + allocate_info.commandPool = command_pool; + VkResult result = init.disp.allocateCommandBuffers(&allocate_info, &command_buffers_[i]); + VERIFY_VK_RESULT("failed to allocate command buffer", result); } sync_ = gfxrecon::test::create_sync_objects(init.swapchain, init.disp, MAX_FRAMES_IN_FLIGHT); + + determine_memory_heaps(); + create_descriptor_set(); + create_render_pass(); + create_graphics_pipeline(); + create_framebuffers(); + create_staging_buffer(); + create_images(); } bool App::frame(const int frame_num) @@ -434,9 +698,7 @@ bool App::frame(const int frame_num) if (result == VK_ERROR_OUT_OF_DATE_KHR) { - // recreate_swapchain(); TestAppBase::recreate_swapchain(true); - // return frame_num >= NUM_FRAMES; return true; } else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) @@ -451,13 +713,7 @@ bool App::frame(const int frame_num) sync_.image_in_flight[image_index] = sync_.in_flight_fences[current_frame_]; init.disp.resetCommandPool(command_pools_[current_frame_], 0); - VkCommandBufferAllocateInfo allocate_info = {}; - allocate_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - allocate_info.commandBufferCount = 1; - allocate_info.commandPool = command_pools_[current_frame_]; - VkCommandBuffer command_buffer; - result = init.disp.allocateCommandBuffers(&allocate_info, &command_buffer); - VERIFY_VK_RESULT("failed to allocate command buffer", result); + VkCommandBuffer command_buffer = command_buffers_[current_frame_]; { VkCommandBufferBeginInfo begin_info = {}; @@ -494,7 +750,7 @@ bool App::frame(const int frame_num) render_pass_info.framebuffer = framebuffers_[image_index]; render_pass_info.renderArea.offset = { 0, 0 }; render_pass_info.renderArea.extent = init.swapchain.extent; - VkClearValue clearColor{ { { 0.0f, 0.0f, 0.0f, 1.0f } } }; + VkClearValue clearColor{ { { 0.0f, 0.0f, 1.0f, 1.0f } } }; render_pass_info.clearValueCount = 1; render_pass_info.pClearValues = &clearColor; @@ -513,6 +769,16 @@ bool App::frame(const int frame_num) init.disp.cmdSetViewport(command_buffer, 0, 1, &viewport); init.disp.cmdSetScissor(command_buffer, 0, 1, &scissor); init.disp.cmdBeginRenderPass(command_buffer, &render_pass_info, VK_SUBPASS_CONTENTS_INLINE); + init.disp.cmdBindDescriptorSets( + command_buffer, + VK_PIPELINE_BIND_POINT_GRAPHICS, + pipeline_layout_, + 0, + 1, + &descriptor_set_, + 0, + nullptr + ); init.disp.cmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline_); init.disp.cmdDraw(command_buffer, 3, 1, 0, 0); @@ -546,17 +812,61 @@ bool App::frame(const int frame_num) VERIFY_VK_RESULT("failed to end command buffer", result); } + // Bind sparse memory + { + VkSparseImageMemoryBind bind = {}; + bind.subresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + bind.subresource.mipLevel = 0; + bind.subresource.arrayLayer = 0; + bind.offset.x = 0; + bind.offset.y = 0; + bind.offset.z = 0; + bind.extent.width = image_size_; + bind.extent.height = image_size_; + bind.extent.depth = 1; + bind.memory = image_backing_memory_; + bind.memoryOffset = 0; + bind.flags = 0; + + VkSparseImageMemoryBindInfo im_bind_info = {}; + im_bind_info.image = image0_; + im_bind_info.bindCount = 1; + im_bind_info.pBinds = &bind; + + VkBindSparseInfo sparse_info = {}; + sparse_info.sType = VK_STRUCTURE_TYPE_BIND_SPARSE_INFO; + sparse_info.pNext = nullptr; + sparse_info.waitSemaphoreCount = 0; + sparse_info.pWaitSemaphores = nullptr; + sparse_info.bufferBindCount = 0; + sparse_info.pBufferBinds = nullptr; + sparse_info.imageOpaqueBindCount = 0; + sparse_info.pImageOpaqueBinds = nullptr; + sparse_info.imageBindCount = 1; + sparse_info.pImageBinds = &im_bind_info; + sparse_info.signalSemaphoreCount = 1; + sparse_info.pSignalSemaphores = &sparse_binding_semaphores_[current_frame_]; + VERIFY_VK_RESULT("Failed to bind sparse memory", init.disp.queueBindSparse(graphics_queue_, 1, &sparse_info, nullptr)); + } + VkSubmitInfo submitInfo = {}; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - VkSemaphore wait_semaphores[] = { sync_.available_semaphores[current_frame_] }; - VkPipelineStageFlags wait_stages[] = { VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT }; - submitInfo.waitSemaphoreCount = 1; + VkSemaphore wait_semaphores[] = { + sync_.available_semaphores[current_frame_], + sparse_binding_semaphores_[current_frame_] + }; + VkPipelineStageFlags wait_stages[] = { + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + }; + submitInfo.waitSemaphoreCount = 2; submitInfo.pWaitSemaphores = wait_semaphores; submitInfo.pWaitDstStageMask = wait_stages; submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = &command_buffer; + VkSemaphore signal_semaphores[] = { sync_.finished_semaphore[current_frame_] }; submitInfo.signalSemaphoreCount = 1; @@ -582,9 +892,7 @@ bool App::frame(const int frame_num) result = init.disp.queuePresentKHR(present_queue_, &present_info); if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) { - // recreate_swapchain(); TestAppBase::recreate_swapchain(true); - // return frame_num >= NUM_FRAMES; return true; } VERIFY_VK_RESULT("failed to present queue", result); diff --git a/test/test_apps/sparse-resources/shaders/tri.frag.spv b/test/test_apps/sparse-resources/shaders/tri.frag.spv index 4dd964cdb214546be951938d7084d348aeca7bc0..24ecacd4f57b7ccc66e51dcf0a61fa769ad7088a 100644 GIT binary patch literal 840 zcmZ9KOHaa35QPV@AgDYB*>F)`}WxRRAFNfRp}v6Yn8=+2+*#>DeoS|kn` z=AL=oGt*lto>a^$*r}NvN|BZotsq;slEa)ER<$+RqBQiU5rL|7OL`z}Nt?kstfv_$Bryp`7E4yk}*{^}qiv<2vLrNsf;f&|$a}XEx&C)i}F2 zT8XnCFw)L;J9Hab^w`tcQ*0pZYExxdF#XwQE?{J!JJ%OSm)lcTE%R??esD*5by<4R zhq|ULeem~n@3t)Wk-H;HF5em4$uO_rOqf_tYW8(Yuf9&isJjw|6WjaHsRKNDJNU^( g9>khCc|*idBUrJCnuK)l5 literal 456 zcmZ9I&r8EV5QV2n(`eP!AE38V@gfKwMG*DqHUB}$K_!PoiK*btKiiAo_pSBfhS}No zX6Eh7=Gj>p#VF1r;*fA1L>43REb@ds&!dbnIV0M-=`75NZDOB3`I5-_cF4{YeAb)p zbv<0J-`^59O>>Xk(0^1fO*d3^U$?8LzJ6{YR~zvnCQ0)@|9}fM7le0+^&xg0ZM>f* zC%-+%oC|78;)MPQS*+(Jc#Eu-y!9#By8nB@?s=DR{o%cYKJT5MarUL0y8)}GM>WOP p^=Qu(YVzLglKTRA*Dbpqd`IUikea@yoF2UQjw$uzzfrp;egH6QAEf{Q From 68c9c36a51432c753737339ae24a8c0d0b4507e1 Mon Sep 17 00:00:00 2001 From: Nick Driscoll Date: Thu, 6 Feb 2025 16:14:53 -0500 Subject: [PATCH 09/10] Bit hard by VERIFY_VK_RESULT evaluating 'result' twice --- test/test_apps/sparse-resources/app.cpp | 134 ++++++++++++++---------- 1 file changed, 78 insertions(+), 56 deletions(-) diff --git a/test/test_apps/sparse-resources/app.cpp b/test/test_apps/sparse-resources/app.cpp index 1ece8db060..5479cc08db 100644 --- a/test/test_apps/sparse-resources/app.cpp +++ b/test/test_apps/sparse-resources/app.cpp @@ -33,7 +33,7 @@ GFXRECON_BEGIN_NAMESPACE(gfxrecon) GFXRECON_BEGIN_NAMESPACE(test_app) GFXRECON_BEGIN_NAMESPACE(sparse_resources) -const size_t MAX_FRAMES_IN_FLIGHT = 2; +const size_t MAX_FRAMES_IN_FLIGHT = 1; const size_t STAGING_BUFFER_SIZE = 16 * 1024 * 1024; const VkFormat IMAGE_FORMAT = VK_FORMAT_R8G8B8A8_SRGB; @@ -58,7 +58,7 @@ class App : public gfxrecon::test::TestAppBase VkImageView image0_view_; const uint32_t image_size_ = 16; const uint32_t mip_levels_ = 1; - VkSparseImageMemoryRequirements image_mem_reqs; + //VkSparseImageMemoryRequirements image_mem_reqs; VkDeviceMemory image_backing_memory_; VkDeviceMemory staging_backing_memory_; uint32_t device_memory_type_; @@ -70,10 +70,12 @@ class App : public gfxrecon::test::TestAppBase VkCommandBuffer command_buffers_[MAX_FRAMES_IN_FLIGHT]; size_t current_frame_ = 0; + size_t last_frame_ = MAX_FRAMES_IN_FLIGHT - 1; gfxrecon::test::Sync sync_; std::vector sparse_binding_semaphores_; VkFence dirty_fence_; + VkFence sparse_binding_fence_; void create_render_pass(); void create_graphics_pipeline(); @@ -103,6 +105,8 @@ void App::configure_physical_device_selector(test::PhysicalDeviceSelector& phys_ phys_device_selector.set_required_features(feats); phys_device_selector.add_required_extension("VK_KHR_maintenance2"); + + phys_device_selector.prefer_gpu_device_type(test::PreferredDeviceType::discrete); } void App::create_render_pass() @@ -292,10 +296,8 @@ void App::create_descriptor_set() { pool_info.maxSets = 1; pool_info.poolSizeCount = 2; pool_info.pPoolSizes = pool_sizes; - VERIFY_VK_RESULT( - "Failed to create descriptor pool", - init.disp.createDescriptorPool(&pool_info, nullptr, &descriptor_pool_) - ); + VkResult result = init.disp.createDescriptorPool(&pool_info, nullptr, &descriptor_pool_); + VERIFY_VK_RESULT("Failed to create descriptor pool", result); // Create immutable sampler VkSamplerCreateInfo sampler_info = {}; @@ -319,7 +321,8 @@ void App::create_descriptor_set() { sampler_info.unnormalizedCoordinates = VK_FALSE; VkSampler sampler; - VERIFY_VK_RESULT("Failed to create sampler.", init.disp.createSampler(&sampler_info, nullptr, &sampler)); + result = init.disp.createSampler(&sampler_info, nullptr, &sampler); + VERIFY_VK_RESULT("Failed to create sampler.", result); VkDescriptorSetLayoutBinding bindings[2] = {}; bindings[0].binding = 0; @@ -339,10 +342,8 @@ void App::create_descriptor_set() { layout_info.flags = 0; layout_info.bindingCount = 2; layout_info.pBindings = bindings; - VERIFY_VK_RESULT( - "Failed to create descriptor set layout", - init.disp.createDescriptorSetLayout(&layout_info, nullptr, &descriptor_layout_) - ); + result = init.disp.createDescriptorSetLayout(&layout_info, nullptr, &descriptor_layout_); + VERIFY_VK_RESULT("Failed to create descriptor set layout", result); VkDescriptorSetAllocateInfo alloc_info = {}; alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; @@ -351,7 +352,8 @@ void App::create_descriptor_set() { alloc_info.descriptorSetCount = 1; alloc_info.pSetLayouts = &descriptor_layout_; - VERIFY_VK_RESULT("Failed to allocate descriptor sets", init.disp.allocateDescriptorSets(&alloc_info, &descriptor_set_)); + result = init.disp.allocateDescriptorSets(&alloc_info, &descriptor_set_); + VERIFY_VK_RESULT("Failed to allocate descriptor sets", result); } void App::determine_memory_heaps() @@ -396,8 +398,8 @@ void App::create_staging_buffer() buffer_info.queueFamilyIndexCount = 1; uint32_t idx = init.device.get_queue_index(test::QueueType::graphics).value(); buffer_info.pQueueFamilyIndices = &idx; - VERIFY_VK_RESULT("failed to create staging buffer", - init.disp.createBuffer(&buffer_info, nullptr, &staging_buffer_)); + VkResult result = init.disp.createBuffer(&buffer_info, nullptr, &staging_buffer_); + VERIFY_VK_RESULT("failed to create staging buffer", result); // Allocate and bind buffer memory VkMemoryRequirements mem_reqs = {}; @@ -407,10 +409,10 @@ void App::create_staging_buffer() alloc_info.pNext = nullptr; alloc_info.allocationSize = mem_reqs.size; alloc_info.memoryTypeIndex = staging_memory_type_; - VERIFY_VK_RESULT("failed to allocate staging buffer memory", - init.disp.allocateMemory(&alloc_info, nullptr, &staging_backing_memory_)); - VERIFY_VK_RESULT("failed to bind staging buffer memory", - init.disp.bindBufferMemory(staging_buffer_, staging_backing_memory_, 0)); + result = init.disp.allocateMemory(&alloc_info, nullptr, &staging_backing_memory_); + VERIFY_VK_RESULT("failed to allocate staging buffer memory", result); + result = init.disp.bindBufferMemory(staging_buffer_, staging_backing_memory_, 0); + VERIFY_VK_RESULT("failed to bind staging buffer memory", result); // Map buffer init.disp.mapMemory(staging_backing_memory_, 0, STAGING_BUFFER_SIZE, 0, (void**)&staging_buffer_ptr_); @@ -453,7 +455,7 @@ void App::create_images() VkImageCreateInfo image_info = {}; image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; image_info.pNext = nullptr; - image_info.flags = VK_IMAGE_CREATE_SPARSE_BINDING_BIT | VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT; + image_info.flags = VK_IMAGE_CREATE_SPARSE_BINDING_BIT; image_info.imageType = VK_IMAGE_TYPE_2D; image_info.format = IMAGE_FORMAT; image_info.extent.width = image_size_; @@ -469,19 +471,20 @@ void App::create_images() uint32_t idx = init.device.get_queue_index(test::QueueType::graphics).value(); image_info.pQueueFamilyIndices = &idx; image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - VERIFY_VK_RESULT("failed to create image", init.disp.createImage(&image_info, nullptr, &image0_)); + VkResult result = init.disp.createImage(&image_info, nullptr, &image0_); + VERIFY_VK_RESULT("failed to create image", result); // Get memory requirements VkMemoryRequirements image0_reqs = {}; init.disp.getImageMemoryRequirements(image0_, &image0_reqs); - uint32_t sparse_reqs_count = 0; - init.disp.getImageSparseMemoryRequirements(image0_, &sparse_reqs_count, nullptr); - std::vector sparse_reqs; - sparse_reqs.resize(sparse_reqs_count); - init.disp.getImageSparseMemoryRequirements(image0_, &sparse_reqs_count, sparse_reqs.data()); - assert(sparse_reqs.size() == 1); - image_mem_reqs = sparse_reqs[0]; + // uint32_t sparse_reqs_count = 0; + // init.disp.getImageSparseMemoryRequirements(image0_, &sparse_reqs_count, nullptr); + // std::vector sparse_reqs; + // sparse_reqs.resize(sparse_reqs_count); + // init.disp.getImageSparseMemoryRequirements(image0_, &sparse_reqs_count, sparse_reqs.data()); + // assert(sparse_reqs.size() == 1); + // image_mem_reqs = sparse_reqs[0]; // Allocate image backing memory VkMemoryAllocateInfo alloc_info = {}; @@ -523,7 +526,7 @@ void App::create_images() VkCommandBuffer cmd = command_buffers_[0]; VkCommandBufferBeginInfo begin_info = {}; begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - VkResult result = init.disp.beginCommandBuffer(cmd, &begin_info); + result = init.disp.beginCommandBuffer(cmd, &begin_info); VERIFY_VK_RESULT("failed to begin command buffer", result); @@ -609,6 +612,8 @@ void App::create_images() // Wait for submission result = init.disp.waitForFences(1, &dirty_fence_, VK_TRUE, ~0); VERIFY_VK_RESULT("failed to wait for upload fence", result); + result = init.disp.resetFences(1, &dirty_fence_); + VERIFY_VK_RESULT("failed to reset upload fence", result); // Update descriptor set VkDescriptorImageInfo desc_image = {}; @@ -652,13 +657,18 @@ void App::setup() init.disp.createSemaphore(&info, nullptr, &sparse_binding_semaphores_[i]); } - // Create fence + // Create fences { VkFenceCreateInfo info = {}; info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; info.pNext = nullptr; info.flags = 0; init.disp.createFence(&info, nullptr, &dirty_fence_); + VkFenceCreateInfo info2 = {}; + info2.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + info2.pNext = nullptr; + info2.flags = 0; + init.disp.createFence(&info2, nullptr, &sparse_binding_fence_); } auto queue_family_index = init.device.get_queue_index(gfxrecon::test::QueueType::graphics); @@ -706,11 +716,11 @@ bool App::frame(const int frame_num) throw gfxrecon::test::vulkan_exception("failed to acquire next image", result); } - if (sync_.image_in_flight[image_index] != VK_NULL_HANDLE) + if (sync_.image_in_flight[current_frame_] != VK_NULL_HANDLE) { - init.disp.waitForFences(1, &sync_.image_in_flight[image_index], VK_TRUE, UINT64_MAX); + init.disp.waitForFences(1, &sync_.image_in_flight[current_frame_], VK_TRUE, UINT64_MAX); } - sync_.image_in_flight[image_index] = sync_.in_flight_fences[current_frame_]; + sync_.image_in_flight[current_frame_] = sync_.in_flight_fences[current_frame_]; init.disp.resetCommandPool(command_pools_[current_frame_], 0); VkCommandBuffer command_buffer = command_buffers_[current_frame_]; @@ -813,22 +823,30 @@ bool App::frame(const int frame_num) } // Bind sparse memory - { - VkSparseImageMemoryBind bind = {}; - bind.subresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - bind.subresource.mipLevel = 0; - bind.subresource.arrayLayer = 0; - bind.offset.x = 0; - bind.offset.y = 0; - bind.offset.z = 0; - bind.extent.width = image_size_; - bind.extent.height = image_size_; - bind.extent.depth = 1; + static bool first_frame = true; + if (first_frame) { + // VkSparseImageMemoryBind bind = {}; + // bind.subresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + // bind.subresource.mipLevel = 0; + // bind.subresource.arrayLayer = 0; + // bind.offset.x = 0; + // bind.offset.y = 0; + // bind.offset.z = 0; + // bind.extent.width = image_size_; + // bind.extent.height = image_size_; + // bind.extent.depth = 1; + // bind.memory = image_backing_memory_; + // bind.memoryOffset = 0; + // bind.flags = 0; + + VkSparseMemoryBind bind = {}; + bind.resourceOffset = 0; + bind.size = 4 * image_size_ * image_size_; bind.memory = image_backing_memory_; bind.memoryOffset = 0; bind.flags = 0; - VkSparseImageMemoryBindInfo im_bind_info = {}; + VkSparseImageOpaqueMemoryBindInfo im_bind_info = {}; im_bind_info.image = image0_; im_bind_info.bindCount = 1; im_bind_info.pBinds = &bind; @@ -836,31 +854,33 @@ bool App::frame(const int frame_num) VkBindSparseInfo sparse_info = {}; sparse_info.sType = VK_STRUCTURE_TYPE_BIND_SPARSE_INFO; sparse_info.pNext = nullptr; - sparse_info.waitSemaphoreCount = 0; - sparse_info.pWaitSemaphores = nullptr; + sparse_info.bufferBindCount = 0; sparse_info.pBufferBinds = nullptr; - sparse_info.imageOpaqueBindCount = 0; - sparse_info.pImageOpaqueBinds = nullptr; - sparse_info.imageBindCount = 1; - sparse_info.pImageBinds = &im_bind_info; - sparse_info.signalSemaphoreCount = 1; - sparse_info.pSignalSemaphores = &sparse_binding_semaphores_[current_frame_]; - VERIFY_VK_RESULT("Failed to bind sparse memory", init.disp.queueBindSparse(graphics_queue_, 1, &sparse_info, nullptr)); + sparse_info.imageOpaqueBindCount = 1; + sparse_info.pImageOpaqueBinds = &im_bind_info; + sparse_info.imageBindCount = 0; + sparse_info.pImageBinds = nullptr; + result = init.disp.queueBindSparse(graphics_queue_, 1, &sparse_info, sparse_binding_fence_); + VERIFY_VK_RESULT("Failed to bind sparse memory", result); + + result = init.disp.waitForFences(1, &sparse_binding_fence_, VK_TRUE, ~0); + VERIFY_VK_RESULT("Failed to wait for sparse binding fence.", result); + result = init.disp.resetFences(1, &sparse_binding_fence_); + VERIFY_VK_RESULT("failed to reset sparse binding fence", result); } VkSubmitInfo submitInfo = {}; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; VkSemaphore wait_semaphores[] = { - sync_.available_semaphores[current_frame_], - sparse_binding_semaphores_[current_frame_] + sync_.available_semaphores[current_frame_] }; VkPipelineStageFlags wait_stages[] = { VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, }; - submitInfo.waitSemaphoreCount = 2; + submitInfo.waitSemaphoreCount = 1; submitInfo.pWaitSemaphores = wait_semaphores; submitInfo.pWaitDstStageMask = wait_stages; @@ -897,9 +917,11 @@ bool App::frame(const int frame_num) } VERIFY_VK_RESULT("failed to present queue", result); + last_frame_ = current_frame_; current_frame_ = (current_frame_ + 1) % MAX_FRAMES_IN_FLIGHT; // return IS_RUNNING(frame_num); + first_frame = false; return true; } From 10fa69f679d5520bae2119cfa7baacd38f9db6b8 Mon Sep 17 00:00:00 2001 From: Nick Driscoll Date: Fri, 7 Feb 2025 15:03:14 -0500 Subject: [PATCH 10/10] Must bind multiple of VkMemoryRequirements::alignment --- test/test_apps/sparse-resources/app.cpp | 33 ++++++++++++++----------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/test/test_apps/sparse-resources/app.cpp b/test/test_apps/sparse-resources/app.cpp index 5479cc08db..ac1ef2e031 100644 --- a/test/test_apps/sparse-resources/app.cpp +++ b/test/test_apps/sparse-resources/app.cpp @@ -64,6 +64,7 @@ class App : public gfxrecon::test::TestAppBase uint32_t device_memory_type_; uint32_t staging_memory_type_; + uint32_t sparse_binding_granularity_; VkExtent3D sparse_block_granularity_; VkCommandPool command_pools_[MAX_FRAMES_IN_FLIGHT]; @@ -74,8 +75,7 @@ class App : public gfxrecon::test::TestAppBase gfxrecon::test::Sync sync_; std::vector sparse_binding_semaphores_; - VkFence dirty_fence_; - VkFence sparse_binding_fence_; + VkFence immediate_fence_; void create_render_pass(); void create_graphics_pipeline(); @@ -477,6 +477,7 @@ void App::create_images() // Get memory requirements VkMemoryRequirements image0_reqs = {}; init.disp.getImageMemoryRequirements(image0_, &image0_reqs); + sparse_binding_granularity_ = image0_reqs.alignment; // uint32_t sparse_reqs_count = 0; // init.disp.getImageSparseMemoryRequirements(image0_, &sparse_reqs_count, nullptr); @@ -492,7 +493,8 @@ void App::create_images() alloc_info.pNext = nullptr; alloc_info.allocationSize = image0_reqs.size; alloc_info.memoryTypeIndex = device_memory_type_; - init.disp.allocateMemory(&alloc_info, nullptr, &image_backing_memory_); + result = init.disp.allocateMemory(&alloc_info, nullptr, &image_backing_memory_); + VERIFY_VK_RESULT("Failed to allocate image memory", result); // Create image view object VkImageViewCreateInfo view_info = {}; @@ -508,7 +510,8 @@ void App::create_images() view_info.subresourceRange.levelCount = mip_levels_; view_info.subresourceRange.baseArrayLayer = 0; view_info.subresourceRange.layerCount = 1; - init.disp.createImageView(&view_info, nullptr, &image0_view_); + result = init.disp.createImageView(&view_info, nullptr, &image0_view_); + VERIFY_VK_RESULT("Failed to create image view", result); // Write image data to staging buffer for (int y = 0; y < image_size_; ++y) { @@ -607,12 +610,12 @@ void App::create_images() submit_info.signalSemaphoreCount = 0; submit_info.commandBufferCount = 1; submit_info.pCommandBuffers = &cmd; - init.disp.queueSubmit(graphics_queue_, 1, &submit_info, dirty_fence_); + init.disp.queueSubmit(graphics_queue_, 1, &submit_info, immediate_fence_); // Wait for submission - result = init.disp.waitForFences(1, &dirty_fence_, VK_TRUE, ~0); + result = init.disp.waitForFences(1, &immediate_fence_, VK_TRUE, ~0); VERIFY_VK_RESULT("failed to wait for upload fence", result); - result = init.disp.resetFences(1, &dirty_fence_); + result = init.disp.resetFences(1, &immediate_fence_); VERIFY_VK_RESULT("failed to reset upload fence", result); // Update descriptor set @@ -663,12 +666,12 @@ void App::setup() info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; info.pNext = nullptr; info.flags = 0; - init.disp.createFence(&info, nullptr, &dirty_fence_); + init.disp.createFence(&info, nullptr, &immediate_fence_); VkFenceCreateInfo info2 = {}; info2.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; info2.pNext = nullptr; info2.flags = 0; - init.disp.createFence(&info2, nullptr, &sparse_binding_fence_); + init.disp.createFence(&info2, nullptr, &immediate_fence_); } auto queue_family_index = init.device.get_queue_index(gfxrecon::test::QueueType::graphics); @@ -841,10 +844,12 @@ bool App::frame(const int frame_num) VkSparseMemoryBind bind = {}; bind.resourceOffset = 0; - bind.size = 4 * image_size_ * image_size_; + //bind.size = 4 * image_size_ * image_size_; + bind.size = sparse_binding_granularity_; bind.memory = image_backing_memory_; bind.memoryOffset = 0; bind.flags = 0; + //bind.flags = VK_SPARSE_MEMORY_BIND_METADATA_BIT; VkSparseImageOpaqueMemoryBindInfo im_bind_info = {}; im_bind_info.image = image0_; @@ -861,14 +866,15 @@ bool App::frame(const int frame_num) sparse_info.pImageOpaqueBinds = &im_bind_info; sparse_info.imageBindCount = 0; sparse_info.pImageBinds = nullptr; - result = init.disp.queueBindSparse(graphics_queue_, 1, &sparse_info, sparse_binding_fence_); + result = init.disp.queueBindSparse(graphics_queue_, 1, &sparse_info, immediate_fence_); VERIFY_VK_RESULT("Failed to bind sparse memory", result); - result = init.disp.waitForFences(1, &sparse_binding_fence_, VK_TRUE, ~0); + result = init.disp.waitForFences(1, &immediate_fence_, VK_TRUE, ~0); VERIFY_VK_RESULT("Failed to wait for sparse binding fence.", result); - result = init.disp.resetFences(1, &sparse_binding_fence_); + result = init.disp.resetFences(1, &immediate_fence_); VERIFY_VK_RESULT("failed to reset sparse binding fence", result); } + first_frame = false; VkSubmitInfo submitInfo = {}; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; @@ -921,7 +927,6 @@ bool App::frame(const int frame_num) current_frame_ = (current_frame_ + 1) % MAX_FRAMES_IN_FLIGHT; // return IS_RUNNING(frame_num); - first_frame = false; return true; }