From fb5d0f8e8dbad8311568ef4a8ac2ad7f10f68366 Mon Sep 17 00:00:00 2001 From: Oleh Kuznetsov Date: Tue, 11 Aug 2026 17:08:22 +0100 Subject: [PATCH] vulkan: Fix state snapshot capture on Android - Initialize DeviceWrapper queue_family_indices from VkDeviceCreateInfo to track valid queue families during state snapshot. - Add GetValidQueueFamilyIndex validator to prevent VK_QUEUE_FAMILY_IGNORED / invalid indices from being passed to VulkanResourcesUtil. - Add VulkanResourcesUtil::SetQueue and register existing child queue handles during state snapshotting and import fd processing, avoiding unneeded vkGetDeviceQueue calls that crash the Android Vulkan loader. - Add RegisterDeviceQueues helper to deduplicate queue registration across state writer and capture manager. --- framework/encode/vulkan_capture_manager.cpp | 13 +++- framework/encode/vulkan_handle_wrapper_util.h | 65 +++++++++++++++++++ .../vulkan_state_tracker_initializers.h | 12 +++- framework/encode/vulkan_state_writer.cpp | 31 +++++---- framework/graphics/vulkan_resources_util.cpp | 23 ++++++- framework/graphics/vulkan_resources_util.h | 12 ++++ 6 files changed, 140 insertions(+), 16 deletions(-) diff --git a/framework/encode/vulkan_capture_manager.cpp b/framework/encode/vulkan_capture_manager.cpp index 9a37f209f2..1c0056f04b 100644 --- a/framework/encode/vulkan_capture_manager.cpp +++ b/framework/encode/vulkan_capture_manager.cpp @@ -2239,12 +2239,18 @@ void VulkanCaptureManager::ProcessImportFdForBuffer(VkDevice device, VkBuffer bu device_wrapper->property_feature_info, device_wrapper->physical_device->memory_properties); + vulkan_wrappers::RegisterDeviceQueues(resource_util, device_wrapper); + VkResult result = resource_util.CreateStagingBuffer(buffer_wrapper->size); if (result == VK_SUCCESS) { std::vector data; result = resource_util.ReadFromBufferResource( - buffer, buffer_wrapper->size, memoryOffset, buffer_wrapper->queue_family_index, data); + buffer, + buffer_wrapper->size, + memoryOffset, + vulkan_wrappers::GetValidQueueFamilyIndex(device_wrapper, buffer_wrapper->queue_family_index), + data); if (result == VK_SUCCESS) { WriteBeginResourceInitCmd(device_wrapper->handle_id, buffer_wrapper->size, buffer_wrapper->size); @@ -2273,6 +2279,8 @@ void VulkanCaptureManager::ProcessImportFdForImage(VkDevice device, VkImage imag device_wrapper->property_feature_info, device_wrapper->physical_device->memory_properties); + vulkan_wrappers::RegisterDeviceQueues(resource_util, device_wrapper); + std::vector aspects; graphics::GetFormatAspects(image_wrapper->format, &aspects); @@ -2316,7 +2324,8 @@ void VulkanCaptureManager::ProcessImportFdForImage(VkDevice device, VkImage imag image_resource.tiling = image_wrapper->tiling; image_resource.sample_count = image_wrapper->samples; image_resource.layout = image_wrapper->current_layout; - image_resource.queue_family_index = image_wrapper->queue_family_index; + image_resource.queue_family_index = + vulkan_wrappers::GetValidQueueFamilyIndex(device_wrapper, image_wrapper->queue_family_index); image_resource.external_format = image_wrapper->external_format; image_resource.size = image_wrapper->size; image_resource.aspect = aspect; diff --git a/framework/encode/vulkan_handle_wrapper_util.h b/framework/encode/vulkan_handle_wrapper_util.h index 138741e037..0f2d7fbd41 100644 --- a/framework/encode/vulkan_handle_wrapper_util.h +++ b/framework/encode/vulkan_handle_wrapper_util.h @@ -32,6 +32,7 @@ #include "generated/generated_vulkan_dispatch_table.h" #include "generated/generated_vulkan_state_table.h" #include "util/defines.h" +#include "graphics/vulkan_resources_util.h" #include "graphics/vulkan_util.h" #include @@ -43,6 +44,70 @@ GFXRECON_BEGIN_NAMESPACE(gfxrecon) GFXRECON_BEGIN_NAMESPACE(encode) GFXRECON_BEGIN_NAMESPACE(vulkan_wrappers) +const uint32_t kDefaultQueueFamilyIndex = 0; + +inline bool IsSpecialQueueFamilyIndex(uint32_t queue_family_index) +{ + return (queue_family_index == VK_QUEUE_FAMILY_IGNORED) || (queue_family_index == VK_QUEUE_FAMILY_EXTERNAL) || + (queue_family_index == VK_QUEUE_FAMILY_FOREIGN_EXT); +} + +/** + * @brief Validates a queue family index against the valid queue family indices of a device wrapper. + * + * If the provided queue_family_index is a special index (e.g. VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_EXTERNAL, + * or VK_QUEUE_FAMILY_FOREIGN_EXT) or not present in the device's configured queue family indices, this function + * falls back to the device's primary queue family or the default queue family index (0). + * + * @param device_wrapper The device wrapper containing valid queue family indices from device creation. + * @param queue_family_index The queue family index to validate. + * @return A valid queue family index that belongs to the device. + */ +inline uint32_t GetValidQueueFamilyIndex(const DeviceWrapper* device_wrapper, uint32_t queue_family_index) +{ + if ((device_wrapper != nullptr) && !device_wrapper->queue_family_indices.empty()) + { + if (!IsSpecialQueueFamilyIndex(queue_family_index)) + { + const auto& indices = device_wrapper->queue_family_indices; + if (std::find(indices.begin(), indices.end(), queue_family_index) != indices.end()) + { + return queue_family_index; + } + } + return device_wrapper->queue_family_indices.front(); + } + return !IsSpecialQueueFamilyIndex(queue_family_index) ? queue_family_index : kDefaultQueueFamilyIndex; +} + +/** + * @brief Registers active child queues from a DeviceWrapper into a VulkanResourcesUtil instance. + * + * @param resource_util The resource utility instance to configure. + * @param device_wrapper The device wrapper holding active child queues. + */ +inline void RegisterDeviceQueues(graphics::VulkanResourcesUtil& resource_util, const DeviceWrapper* device_wrapper) +{ + if (device_wrapper == nullptr) + { + return; + } + + for (size_t i = 0; i < device_wrapper->child_queues.size(); ++i) + { + const auto* queue_wrapper = device_wrapper->child_queues[i]; + if ((queue_wrapper != nullptr) && (queue_wrapper->handle != VK_NULL_HANDLE)) + { + uint32_t qfi = + (i < device_wrapper->queue_family_indices.size()) + ? device_wrapper->queue_family_indices[i] + : ((!device_wrapper->queue_family_indices.empty()) ? device_wrapper->queue_family_indices.front() + : kDefaultQueueFamilyIndex); + resource_util.SetQueue(qfi, queue_wrapper->handle); + } + } +} + // Temporary resource IDs for state processing. static const format::HandleId kTempQueueId = std::numeric_limits::max() - 1; static const VkCommandPool kTempCommandPool = diff --git a/framework/encode/vulkan_state_tracker_initializers.h b/framework/encode/vulkan_state_tracker_initializers.h index df9d4efb75..9949ffc3c0 100644 --- a/framework/encode/vulkan_state_tracker_initializers.h +++ b/framework/encode/vulkan_state_tracker_initializers.h @@ -107,12 +107,22 @@ inline void InitializeStatecreate_call_id = create_call_id; wrapper->create_parameters = std::move(create_parameters); wrapper->physical_device = vulkan_wrappers::GetWrapper(parent_handle); + + wrapper->queue_family_indices.clear(); + if ((create_info != nullptr) && (create_info->pQueueCreateInfos != nullptr)) + { + wrapper->queue_family_indices.reserve(create_info->queueCreateInfoCount); + for (uint32_t q = 0; q < create_info->queueCreateInfoCount; ++q) + { + wrapper->queue_family_indices.push_back(create_info->pQueueCreateInfos[q].queueFamilyIndex); + } + } } template <> diff --git a/framework/encode/vulkan_state_writer.cpp b/framework/encode/vulkan_state_writer.cpp index a3d3d7ad0e..2feaeaa314 100644 --- a/framework/encode/vulkan_state_writer.cpp +++ b/framework/encode/vulkan_state_writer.cpp @@ -49,8 +49,6 @@ GFXRECON_BEGIN_NAMESPACE(gfxrecon) GFXRECON_BEGIN_NAMESPACE(encode) -const uint32_t kDefaultQueueFamilyIndex = 0; - static bool IsMemoryCoherent(VkMemoryPropertyFlags property_flags) { return ((property_flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) == VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); @@ -2312,7 +2310,8 @@ void VulkanStateWriter::ProcessBufferMemory(const vulkan_wrappers::DeviceWrapper buffer_resource.buffer = buffer_wrapper->handle; buffer_resource.size = buffer_wrapper->size; buffer_resource.offset = 0; - buffer_resource.queue_family_index = buffer_wrapper->queue_family_index; + buffer_resource.queue_family_index = + vulkan_wrappers::GetValidQueueFamilyIndex(device_wrapper, buffer_wrapper->queue_family_index); if (snapshot_entry.need_staging_copy) { @@ -2439,7 +2438,8 @@ void VulkanStateWriter::ProcessBufferMemoryWithAssetFile(const vulkan_wrappers:: buffer_resource.buffer = buffer_wrapper->handle; buffer_resource.size = buffer_wrapper->size; buffer_resource.offset = 0; - buffer_resource.queue_family_index = buffer_wrapper->queue_family_index; + buffer_resource.queue_family_index = + vulkan_wrappers::GetValidQueueFamilyIndex(device_wrapper, buffer_wrapper->queue_family_index); if (snapshot_entry.need_staging_copy) { @@ -2570,7 +2570,8 @@ void VulkanStateWriter::ProcessImageMemory(const vulkan_wrappers::DeviceWrapper* image_resource.tiling = image_wrapper->tiling; image_resource.sample_count = image_wrapper->samples; image_resource.layout = image_wrapper->current_layout; - image_resource.queue_family_index = image_wrapper->queue_family_index; + image_resource.queue_family_index = + vulkan_wrappers::GetValidQueueFamilyIndex(device_wrapper, image_wrapper->queue_family_index); image_resource.external_format = image_wrapper->external_format; image_resource.size = image_wrapper->size; image_resource.resource_size = snapshot_entry.resource_size; @@ -2750,7 +2751,8 @@ void VulkanStateWriter::ProcessImageMemoryWithAssetFile(const vulkan_wrappers::D image_resource.tiling = image_wrapper->tiling; image_resource.sample_count = image_wrapper->samples; image_resource.layout = image_wrapper->current_layout; - image_resource.queue_family_index = image_wrapper->queue_family_index; + image_resource.queue_family_index = + vulkan_wrappers::GetValidQueueFamilyIndex(device_wrapper, image_wrapper->queue_family_index); image_resource.size = image_wrapper->size; image_resource.resource_size = snapshot_entry.resource_size; image_resource.level_sizes = &snapshot_entry.level_sizes; @@ -2895,7 +2897,8 @@ void VulkanStateWriter::WriteBufferMemoryState(const VulkanStateTable& state_tab // 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]; + ResourceSnapshotInfo& snapshot_entry = snapshot_table[vulkan_wrappers::GetValidQueueFamilyIndex( + device_wrapper, wrapper->queue_family_index)]; BufferSnapshotInfo snapshot_info; snapshot_info.buffer_wrapper = wrapper; @@ -3003,7 +3006,8 @@ void VulkanStateWriter::WriteBufferMemoryState(const VulkanStateTable& state_tab // 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]; + ResourceSnapshotInfo& snapshot_entry = + snapshot_table[vulkan_wrappers::GetValidQueueFamilyIndex(device_wrapper, wrapper->queue_family_index)]; BufferSnapshotInfo snapshot_info; snapshot_info.buffer_wrapper = wrapper; @@ -3199,14 +3203,17 @@ void VulkanStateWriter::WriteImageMemoryState(const VulkanStateTable& state_tabl { // Group images with memory bindings by device for memory snapshot. ResourceSnapshotQueueFamilyTable& snapshot_table = (*resources)[device_wrapper]; - ResourceSnapshotInfo& snapshot_entry = snapshot_table[wrapper->queue_family_index]; - graphics::VulkanResourcesUtil resource_util(device_wrapper->handle, + ResourceSnapshotInfo& snapshot_entry = snapshot_table[vulkan_wrappers::GetValidQueueFamilyIndex( + device_wrapper, wrapper->queue_family_index)]; + graphics::VulkanResourcesUtil resource_util(device_wrapper->handle, device_wrapper->physical_device->handle, device_wrapper->layer_table, *device_wrapper->physical_device->layer_table_ref, device_wrapper->property_feature_info, device_wrapper->physical_device->memory_properties); + vulkan_wrappers::RegisterDeviceQueues(resource_util, device_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. @@ -3349,6 +3356,8 @@ void VulkanStateWriter::WriteResourceMemoryState(const VulkanStateTable& state_t device_wrapper->property_feature_info, device_wrapper->physical_device->memory_properties); + vulkan_wrappers::RegisterDeviceQueues(resource_util, device_wrapper); + if (max_staging_copy_size > 0) { assert(device_wrapper != nullptr); @@ -4021,7 +4030,7 @@ void VulkanStateWriter::WriteQueryPoolReset( { // Retrieve a queue and create a command buffer for query pool reset. WriteCommandProcessingCreateCommands(device_id, - kDefaultQueueFamilyIndex, + vulkan_wrappers::kDefaultQueueFamilyIndex, vulkan_wrappers::kTempQueueId, vulkan_wrappers::kTempCommandPool, vulkan_wrappers::kTempCommandBufferId); diff --git a/framework/graphics/vulkan_resources_util.cpp b/framework/graphics/vulkan_resources_util.cpp index 26cc70e411..b97c180d8b 100644 --- a/framework/graphics/vulkan_resources_util.cpp +++ b/framework/graphics/vulkan_resources_util.cpp @@ -1506,19 +1506,38 @@ void VulkanResourcesUtil::CopyBuffer(VkCommandBuffer command_buffer, nullptr); } +void VulkanResourcesUtil::SetQueue(uint32_t queue_family_index, VkQueue queue) +{ + if (queue != VK_NULL_HANDLE) + { + queue_map_[queue_family_index] = queue; + } +} + VkQueue VulkanResourcesUtil::GetQueue(uint32_t queue_family_index, uint32_t queue_index) { + auto it = queue_map_.find(queue_family_index); + if (it != queue_map_.end() && it->second != VK_NULL_HANDLE) + { + return it->second; + } + VkQueue queue = VK_NULL_HANDLE; - device_table_.GetDeviceQueue(device_, queue_family_index, queue_index, &queue); + if (device_table_.GetDeviceQueue != nullptr) + { + device_table_.GetDeviceQueue(device_, queue_family_index, queue_index, &queue); + } if (queue != VK_NULL_HANDLE) { // Because this queue was not allocated through the loader, it must be assigned a dispatch table. *reinterpret_cast(queue) = *reinterpret_cast(device_); + queue_map_[queue_family_index] = queue; } else { - GFXRECON_LOG_ERROR("Failed to retrieve a queue for resource memory snapshot"); + GFXRECON_LOG_ERROR("Failed to retrieve a queue for queue family %u during resource memory snapshot", + queue_family_index); } return queue; diff --git a/framework/graphics/vulkan_resources_util.h b/framework/graphics/vulkan_resources_util.h index 87c163d8cd..0f36093d2b 100644 --- a/framework/graphics/vulkan_resources_util.h +++ b/framework/graphics/vulkan_resources_util.h @@ -52,6 +52,17 @@ class VulkanResourcesUtil ~VulkanResourcesUtil(); + /** + * @brief Registers an existing VkQueue handle for a specified queue family index. + * + * When set, GetQueue will prefer using registered queues instead of querying vkGetDeviceQueue, + * avoiding redundant driver/loader calls and potential issues during state snapshotting or replay. + * + * @param queue_family_index The Vulkan queue family index associated with the queue. + * @param queue The active VkQueue handle to use for commands on this queue family. + */ + void SetQueue(uint32_t queue_family_index, VkQueue queue); + // This function creates a staging buffer that will be used by the ReadFromImageResourceStaging() and // ReadFromBufferResource() functions. It is not necessary to do so but can be useful when dumping multiple // resource and the size of the biggest staging buffer necessary is known in advance. @@ -374,6 +385,7 @@ class VulkanResourcesUtil // map queue-family index -> command-pool/buffer std::unordered_map command_asset_map_; + std::unordered_map queue_map_; StagingBufferContext staging_buffer_; PFN_vkSetDebugUtilsObjectNameEXT set_debug_utils_object_name_fn_ = nullptr;