diff --git a/.gitignore b/.gitignore index 550568a71d..818a0a7ffa 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,3 @@ __pycache__/ .vscode/ *.db *.pyc - diff --git a/framework/decode/vulkan_captured_swapchain.cpp b/framework/decode/vulkan_captured_swapchain.cpp index c4e176319b..659c120c49 100644 --- a/framework/decode/vulkan_captured_swapchain.cpp +++ b/framework/decode/vulkan_captured_swapchain.cpp @@ -279,7 +279,6 @@ void VulkanCapturedSwapchain::ProcessSetSwapchainImageStateCommand( const VulkanObjectInfoTable& object_info_table, SwapchainImageTracker& swapchain_image_tracker) { - VkDevice device = device_info->handle; VkSwapchainKHR swapchain = swapchain_info->handle; diff --git a/framework/decode/vulkan_object_info.h b/framework/decode/vulkan_object_info.h index dd1f552eb7..3945f89d83 100644 --- a/framework/decode/vulkan_object_info.h +++ b/framework/decode/vulkan_object_info.h @@ -266,6 +266,7 @@ struct DeviceInfo : public VulkanObjectInfo graphics::VulkanDevicePropertyFeatureInfo property_feature_info; std::unordered_map queue_family_creation_flags; + std::vector queue_family_index_enabled; std::vector replay_device_group; }; @@ -273,6 +274,8 @@ struct DeviceInfo : public VulkanObjectInfo struct QueueInfo : public VulkanObjectInfo { std::unordered_map array_counts; + uint32_t family_index; + uint32_t queue_index; }; struct SemaphoreInfo : public VulkanObjectInfo @@ -401,31 +404,13 @@ struct SwapchainKHRInfo : public VulkanObjectInfo // When replay is restricted to a specific surface, a dummy swapchain is created for the omitted surfaces, requiring // backing images. + uint32_t replay_image_count{ 0 }; std::vector image_infos; VkSwapchainCreateFlagsKHR image_flags{ 0 }; VkFormat image_format{ VK_FORMAT_UNDEFINED }; uint32_t image_array_layers{ 0 }; VkImageUsageFlags image_usage{ 0 }; VkSharingMode image_sharing_mode{ VK_SHARING_MODE_EXCLUSIVE }; - - // TODO: These values are used by the virtual swapchain. They should be replaced with an opaque handle, similar to - // DeviceMemoryInfo::allocator_data, which is really a pointer to a struct that contains the virtual swapchain's - // internal info. The memory for the struct referenced by the opaque handle would be managed by the virtual - // swapchain class, similar to the way that the VulkanRebindAllocator works. - struct VirtualImage - { - VkDeviceMemory memory{ VK_NULL_HANDLE }; - VkImage image{ VK_NULL_HANDLE }; - VulkanResourceAllocator::MemoryData memory_allocator_data{ 0 }; - VulkanResourceAllocator::ResourceData resource_allocator_data{ 0 }; - }; - uint32_t replay_image_count{ 0 }; - std::vector virtual_images; // Images created by replay, returned in place of the swapchain images. - std::vector swapchain_images; // The real swapchain images. - VkQueue blit_queue{ VK_NULL_HANDLE }; - VkCommandPool blit_command_pool{ VK_NULL_HANDLE }; - std::vector blit_command_buffers; - std::vector blit_semaphores; }; struct ValidationCacheEXTInfo : public VulkanObjectInfo diff --git a/framework/decode/vulkan_replay_consumer_base.cpp b/framework/decode/vulkan_replay_consumer_base.cpp old mode 100644 new mode 100755 index 1adbd9948d..d9b28717d3 --- a/framework/decode/vulkan_replay_consumer_base.cpp +++ b/framework/decode/vulkan_replay_consumer_base.cpp @@ -41,8 +41,10 @@ #include "generated/generated_vulkan_enum_to_string.h" +#include #include #include +#include #include #include @@ -2145,12 +2147,12 @@ void VulkanReplayConsumerBase::WriteScreenshots(const Decoded_VkPresentInfoKHR* // If both copy_scale and copy_width are provided, use copy_scale. const uint32_t screenshot_width = options_.screenshot_scale - ? (options_.screenshot_scale * swapchain_info->width) + ? static_cast(options_.screenshot_scale * swapchain_info->width) : (options_.screenshot_width ? options_.screenshot_width : swapchain_info->width); const uint32_t screenshot_height = options_.screenshot_scale - ? (options_.screenshot_scale * swapchain_info->height) + ? static_cast(options_.screenshot_scale * swapchain_info->height) : (options_.screenshot_height ? options_.screenshot_height : swapchain_info->height); screenshot_handler_->WriteImage(filename_prefix, @@ -2222,12 +2224,12 @@ bool VulkanReplayConsumerBase::CheckCommandBufferInfoForFrameBoundary(const Comm // If both copy_scale and copy_width are provided, use copy_scale. const uint32_t screenshot_width = options_.screenshot_scale - ? (options_.screenshot_scale * image_info->extent.width) + ? static_cast(options_.screenshot_scale * image_info->extent.width) : (options_.screenshot_width ? options_.screenshot_width : image_info->extent.width); const uint32_t screenshot_height = options_.screenshot_scale - ? (options_.screenshot_scale * image_info->extent.height) + ? static_cast(options_.screenshot_scale * image_info->extent.height) : (options_.screenshot_height ? options_.screenshot_height : image_info->extent.height); screenshot_handler_->WriteImage(filename_prefix, @@ -2664,6 +2666,19 @@ VulkanReplayConsumerBase::OverrideCreateDevice(VkResult original_resu // Track state of physical device properties and features at device creation device_info->property_feature_info = property_feature_info; + // Keep track of what queue families this device is planning on using. This information is + // very important if we end up using the VulkanVirtualSwapchain path. + auto max = [](uint32_t current_max, const VkDeviceQueueCreateInfo& dqci) { + return std::max(current_max, dqci.queueFamilyIndex); + }; + uint32_t max_queue_family = + std::accumulate(modified_create_info.pQueueCreateInfos, + modified_create_info.pQueueCreateInfos + modified_create_info.queueCreateInfoCount, + 0, + max); + device_info->queue_family_index_enabled.clear(); + device_info->queue_family_index_enabled.resize(max_queue_family + 1, false); + for (uint32_t q = 0; q < modified_create_info.queueCreateInfoCount; ++q) { const VkDeviceQueueCreateInfo* queue_create_info = &modified_create_info.pQueueCreateInfos[q]; @@ -2671,6 +2686,7 @@ VulkanReplayConsumerBase::OverrideCreateDevice(VkResult original_resu device_info->queue_family_creation_flags.end()); device_info->queue_family_creation_flags[queue_create_info->queueFamilyIndex] = queue_create_info->flags; + device_info->queue_family_index_enabled[queue_create_info->queueFamilyIndex] = true; } } @@ -2907,6 +2923,52 @@ VkResult VulkanReplayConsumerBase::OverrideEnumeratePhysicalDeviceGroups( return result; } +void VulkanReplayConsumerBase::OverrideGetDeviceQueue(PFN_vkGetDeviceQueue func, + DeviceInfo* device_info, + uint32_t queueFamilyIndex, + uint32_t queueIndex, + HandlePointerDecoder* pQueue) +{ + VkDevice device = device_info->handle; + if (!pQueue->IsNull()) + { + pQueue->SetHandleLength(1); + } + VkQueue* out_pQueue = pQueue->GetHandlePointer(); + + func(device, queueFamilyIndex, queueIndex, out_pQueue); + + // Add tracking for which VkQueue objects are associated with what queue family and index. + // This is necessary for the virtual swapchain to determine which command buffer to use when + // Bliting the images on the Presenting Queue. + auto queue_info = reinterpret_cast(pQueue->GetConsumerData(0)); + queue_info->family_index = queueFamilyIndex; + queue_info->queue_index = queueIndex; +} + +void VulkanReplayConsumerBase::OverrideGetDeviceQueue2(PFN_vkGetDeviceQueue2 func, + DeviceInfo* device_info, + StructPointerDecoder* pQueueInfo, + HandlePointerDecoder* pQueue) +{ + VkDevice device = device_info->handle; + const VkDeviceQueueInfo2* in_pQueueInfo = pQueueInfo->GetPointer(); + if (!pQueue->IsNull()) + { + pQueue->SetHandleLength(1); + } + VkQueue* out_pQueue = pQueue->GetHandlePointer(); + + func(device, in_pQueueInfo, out_pQueue); + + // Add tracking for which VkQueue objects are associated with what queue family and index. + // This is necessary for the virtual swapchain to determine which command buffer to use when + // Bliting the images on the Presenting Queue. + auto queue_info = reinterpret_cast(pQueue->GetConsumerData(0)); + queue_info->family_index = in_pQueueInfo->queueFamilyIndex; + queue_info->queue_index = in_pQueueInfo->queueIndex; +} + void VulkanReplayConsumerBase::OverrideGetPhysicalDeviceProperties( PFN_vkGetPhysicalDeviceProperties func, PhysicalDeviceInfo* physical_device_info, @@ -5294,9 +5356,13 @@ VkResult VulkanReplayConsumerBase::OverrideAcquireNextImageKHR(PFN_vkAcquireNext // If expected result is VK_TIMEOUT, try to get a timeout by using a timeout of 0. // If expected result is anything else, use the passed in timeout value. if (original_result == VK_SUCCESS) + { timeout = std::numeric_limits::max(); + } else if (original_result == VK_TIMEOUT) + { timeout = 0; + } result = swapchain_->AcquireNextImageKHR( func, device_info, swapchain_info, timeout, semaphore_info, fence_info, captured_index, replay_index); @@ -5412,9 +5478,13 @@ VkResult VulkanReplayConsumerBase::OverrideAcquireNextImage2KHR( // If expected result is anything else, use the passed in timeout value. VkAcquireNextImageInfoKHR modified_acquire_info = *replay_acquire_info; if (original_result == VK_SUCCESS) + { modified_acquire_info.timeout = std::numeric_limits::max(); + } else if (original_result == VK_TIMEOUT) + { modified_acquire_info.timeout = 0; + } result = swapchain_->AcquireNextImage2KHR( func, device_info, swapchain_info, &modified_acquire_info, captured_index, replay_index); @@ -6641,6 +6711,90 @@ VkResult VulkanReplayConsumerBase::OverrideGetPhysicalDeviceToolProperties( } } +VkResult +VulkanReplayConsumerBase::OverrideWaitSemaphores(PFN_vkWaitSemaphores func, + VkResult original_result, + const DeviceInfo* device_info, + const StructPointerDecoder* pInfo, + uint64_t timeout) +{ + assert((device_info != nullptr) && (pInfo != nullptr) && !pInfo->IsNull() && (pInfo->GetPointer() != nullptr)); + VkDevice device = device_info->handle; + const VkSemaphoreWaitInfo* wait_info = pInfo->GetPointer(); + VkResult result; + + // If expected result is VK_SUCCESS, ensure that vkWaitSemaphores waits until semaphores + // are available by using a timeout of UINT64_MAX. + // If expected result is VK_TIMEOUT, try to get a timeout by using a timeout of 0. + // If expected result is anything else, use the passed in timeout value. + if (original_result == VK_SUCCESS) + { + timeout = std::numeric_limits::max(); + } + else if (original_result == VK_TIMEOUT) + { + timeout = 0; + } + result = func(device, wait_info, timeout); + return result; +} + +VkResult VulkanReplayConsumerBase::OverrideAcquireProfilingLockKHR( + PFN_vkAcquireProfilingLockKHR func, + VkResult original_result, + const DeviceInfo* device_info, + const StructPointerDecoder* pInfo) +{ + assert((device_info != nullptr) && (pInfo != nullptr) && !pInfo->IsNull() && (pInfo->GetPointer() != nullptr)); + VkDevice device = device_info->handle; + const VkAcquireProfilingLockInfoKHR* acquire_info = pInfo->GetPointer(); + VkResult result; + + // If expected result is VK_SUCCESS, ensure that vkAcquireProfilingLockKHR waits for locks + // using a timeout of UINT64_MAX. + // If expected result is VK_TIMEOUT, try to get a timeout by using a timeout of 0. + // If expected result is anything else, use the passed in timeout value. + VkAcquireProfilingLockInfoKHR modified_acquire_info = *acquire_info; + if (original_result == VK_SUCCESS) + { + modified_acquire_info.timeout = std::numeric_limits::max(); + } + else if (original_result == VK_TIMEOUT) + { + modified_acquire_info.timeout = 0; + } + result = func(device, &modified_acquire_info); + return result; +} + +VkResult VulkanReplayConsumerBase::OverrideWaitForPresentKHR(PFN_vkWaitForPresentKHR func, + VkResult original_result, + const DeviceInfo* device_info, + SwapchainKHRInfo* swapchain_info, + uint64_t presentid, + uint64_t timeout) +{ + assert((device_info != nullptr) && (swapchain_info != nullptr)); + VkDevice device = device_info->handle; + VkSwapchainKHR swapchain = swapchain_info->handle; + VkResult result; + + // If expected result is VK_SUCCESS, ensure that vkWaitForPresent waits for present by + // using a timeout of UINT64_MAX. + // If expected result is VK_TIMEOUT, try to get a timeout by using a timeout of 0. + // If expected result is anything else, use the passed in timeout value. + if (original_result == VK_SUCCESS) + { + timeout = std::numeric_limits::max(); + } + else if (original_result == VK_TIMEOUT) + { + timeout = 0; + } + result = func(device, swapchain, presentid, timeout); + return result; +} + void VulkanReplayConsumerBase::MapDescriptorUpdateTemplateHandles( const DescriptorUpdateTemplateInfo* update_template_info, DescriptorUpdateTemplateDecoder* decoder) { diff --git a/framework/decode/vulkan_replay_consumer_base.h b/framework/decode/vulkan_replay_consumer_base.h index ebe26e565f..fc6fc5e539 100644 --- a/framework/decode/vulkan_replay_consumer_base.h +++ b/framework/decode/vulkan_replay_consumer_base.h @@ -483,6 +483,16 @@ class VulkanReplayConsumerBase : public VulkanConsumer PointerDecoder* pToolCount, StructPointerDecoder* pToolProperties); + void OverrideGetDeviceQueue(PFN_vkGetDeviceQueue func, + DeviceInfo* device_info, + uint32_t queueFamilyIndex, + uint32_t queueIndex, + HandlePointerDecoder* pQueue); + void OverrideGetDeviceQueue2(PFN_vkGetDeviceQueue2 func, + DeviceInfo* device_info, + StructPointerDecoder* pQueueInfo, + HandlePointerDecoder* pQueue); + VkResult OverrideWaitForFences(PFN_vkWaitForFences func, VkResult original_result, const DeviceInfo* device_info, @@ -935,6 +945,23 @@ class VulkanReplayConsumerBase : public VulkanConsumer void OverrideCmdDebugMarkerInsertEXT(PFN_vkCmdDebugMarkerInsertEXT func, CommandBufferInfo* command_buffer_info, StructPointerDecoder* marker_info_decoder); + VkResult OverrideWaitSemaphores(PFN_vkWaitSemaphores func, + VkResult original_result, + const DeviceInfo* device_info, + const StructPointerDecoder* pInfo, + uint64_t timeout); + + VkResult OverrideAcquireProfilingLockKHR(PFN_vkAcquireProfilingLockKHR func, + VkResult original_result, + const DeviceInfo* device_info, + const StructPointerDecoder* pInfo); + + VkResult OverrideWaitForPresentKHR(PFN_vkWaitForPresentKHR func, + VkResult original_result, + const DeviceInfo* device_info, + SwapchainKHRInfo* swapchain_info, + uint64_t presentid, + uint64_t timeout); void OverrideCmdBeginRenderPass(PFN_vkCmdBeginRenderPass func, CommandBufferInfo* command_buffer_info, @@ -1115,7 +1142,6 @@ class VulkanReplayConsumerBase : public VulkanConsumer ActiveWindows active_windows_; const VulkanReplayOptions options_; bool loading_trim_state_; - bool have_imported_semaphores_; SwapchainImageTracker swapchain_image_tracker_; HardwareBufferMap hardware_buffers_; HardwareBufferMemoryMap hardware_buffer_memory_info_; @@ -1125,7 +1151,14 @@ class VulkanReplayConsumerBase : public VulkanConsumer int32_t create_surface_count_; graphics::FpsInfo* fps_info_; - // Used to track if any shadow sync objects are active to avoid checking if not needed + // Imported semaphores are semaphores that are used to track external memory. + // During replay, the external memory is not present (we have no Fds or handles to valid + // data), so we ignore those semaphores when they are encountered. + bool have_imported_semaphores_; + + // Used to track if any shadow sync objects are active to avoid checking if not needed. + // SHadowed objects are ignored when they would have been unsignaled (waited on). + // [Currently set during a call to AcquireNextImage if the VkSurfaceKHR is VK_NULL_HANDLE. std::unordered_set shadow_semaphores_; std::unordered_set shadow_fences_; diff --git a/framework/decode/vulkan_virtual_swapchain.cpp b/framework/decode/vulkan_virtual_swapchain.cpp index 9eb1a34363..aba86a2dde 100644 --- a/framework/decode/vulkan_virtual_swapchain.cpp +++ b/framework/decode/vulkan_virtual_swapchain.cpp @@ -38,7 +38,8 @@ VkResult VulkanVirtualSwapchain::CreateSwapchainKHR(PFN_vkCreateSwapchainKHR const encode::InstanceTable* instance_table, const encode::DeviceTable* device_table) { - VkDevice device = VK_NULL_HANDLE; + VkDevice device = VK_NULL_HANDLE; + VkSurfaceCapabilitiesKHR surfCapabilities{}; if (device_info != nullptr) { @@ -51,8 +52,7 @@ VkResult VulkanVirtualSwapchain::CreateSwapchainKHR(PFN_vkCreateSwapchainKHR modified_create_info.imageUsage = modified_create_info.imageUsage | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; - VkSurfaceCapabilitiesKHR surfCapabilities; - auto result = instance_table_->GetPhysicalDeviceSurfaceCapabilitiesKHR( + VkResult result = instance_table_->GetPhysicalDeviceSurfaceCapabilitiesKHR( physical_device, create_info->surface, &surfCapabilities); GFXRECON_ASSERT(result == VK_SUCCESS); @@ -64,7 +64,19 @@ VkResult VulkanVirtualSwapchain::CreateSwapchainKHR(PFN_vkCreateSwapchainKHR { modified_create_info.minImageCount = surfCapabilities.maxImageCount; } - return func(device, &modified_create_info, allocator, swapchain); + + result = func(device, &modified_create_info, allocator, swapchain); + if (result == VK_SUCCESS && *swapchain != VK_NULL_HANDLE) + { + auto data = std::make_unique(); + if (data == nullptr) + { + GFXRECON_LOG_ERROR("Virtual swapchain failed creating swapchain resource data during vkCreateSwapchainKHR"); + return VK_ERROR_OUT_OF_HOST_MEMORY; + } + swapchain_resources_.emplace(*swapchain, std::move(data)); + } + return result; } void VulkanVirtualSwapchain::DestroySwapchainKHR(PFN_vkDestroySwapchainKHR func, @@ -89,20 +101,34 @@ void VulkanVirtualSwapchain::DestroySwapchainKHR(PFN_vkDestroySwapchainKHR fu allocator->FreeMemoryDirect(image_info.memory, nullptr, image_info.memory_allocator_data); } - device_table_->FreeCommandBuffers(device, - swapchain_info->blit_command_pool, - static_cast(swapchain_info->blit_command_buffers.size()), - swapchain_info->blit_command_buffers.data()); - device_table_->DestroyCommandPool(device, swapchain_info->blit_command_pool, nullptr); - - for (const SwapchainKHRInfo::VirtualImage& image_info : swapchain_info->virtual_images) - { - allocator->DestroyImageDirect(image_info.image, nullptr, image_info.resource_allocator_data); - allocator->FreeMemoryDirect(image_info.memory, nullptr, image_info.memory_allocator_data); - } - for (const auto semaphore : swapchain_info->blit_semaphores) + // Delete the virtual swapchain-specific swapchain resource data + if (swapchain_resources_.find(swapchain) != swapchain_resources_.end()) { - device_table_->DestroySemaphore(device, semaphore, nullptr); + auto& swapchain_resources = swapchain_resources_[swapchain]; + for (const VirtualImage& image_info : swapchain_resources->virtual_swapchain_images) + { + allocator->DestroyImageDirect(image_info.image, nullptr, image_info.resource_allocator_data); + allocator->FreeMemoryDirect(image_info.memory, nullptr, image_info.memory_allocator_data); + } + + for (auto& copy_cmd_data : swapchain_resources->copy_cmd_data) + { + if (copy_cmd_data.second.command_pool != VK_NULL_HANDLE) + { + device_table_->FreeCommandBuffers( + device, + copy_cmd_data.second.command_pool, + static_cast(copy_cmd_data.second.command_buffers.size()), + copy_cmd_data.second.command_buffers.data()); + device_table_->DestroyCommandPool(device, copy_cmd_data.second.command_pool, nullptr); + } + for (auto& semaphore : copy_cmd_data.second.semaphores) + { + device_table_->DestroySemaphore(device, semaphore, nullptr); + } + } + + swapchain_resources_.erase(swapchain); } } func(device, swapchain, allocator); @@ -115,10 +141,11 @@ VkResult VulkanVirtualSwapchain::GetSwapchainImagesKHR(PFN_vkGetSwapchainImagesK uint32_t* image_count, VkImage* images) { - VkDevice device = VK_NULL_HANDLE; - VkSwapchainKHR swapchain = VK_NULL_HANDLE; - uint32_t* replay_image_count = nullptr; - std::vector replay_swapchain_images; + VkDevice device = VK_NULL_HANDLE; + VkSwapchainKHR swapchain = VK_NULL_HANDLE; + uint32_t* replay_image_count = nullptr; + VkResult result; + VkImage* replay_images = images; if (device_info != nullptr) { @@ -129,201 +156,377 @@ VkResult VulkanVirtualSwapchain::GetSwapchainImagesKHR(PFN_vkGetSwapchainImagesK { swapchain = swapchain_info->handle; replay_image_count = &swapchain_info->replay_image_count; + } - if (images != nullptr) - { - replay_swapchain_images.resize(*replay_image_count); - } + // Get the swapchain resource data so we have access to the virtual swapchain-specific information. + if (swapchain == VK_NULL_HANDLE || swapchain_resources_.find(swapchain) == swapchain_resources_.end()) + { + GFXRECON_LOG_ERROR( + "Virtual swapchain vkGetSwapchainImagesKHR missing swapchain resource data for swapchain (ID = %" PRIu64 + ")", + swapchain_info->capture_id); + } + else if (images != nullptr) + { + auto& swapchain_resources = swapchain_resources_[swapchain]; + swapchain_resources->replay_swapchain_images.resize(*replay_image_count); + + // Use the resized replay images vector to contain the replay device swapchain images. + replay_images = swapchain_resources->replay_swapchain_images.data(); } // TODO: Adjust the swapchain image format if the specified format is not supported by the replay device. - auto result = func(device, swapchain, replay_image_count, replay_swapchain_images.data()); + result = func(device, swapchain, replay_image_count, replay_images); if ((result == VK_SUCCESS) && (image_count != nullptr)) { - if (swapchain_info->blit_command_pool == VK_NULL_HANDLE) + // Return the capture count. The virtual swapchain will create a number of virtual images equal to the capture + // count. The virtual images will be returned to the caller in place of the real swapchain images. + (*image_count) = capture_image_count; + + if (images == nullptr || device_info == nullptr || swapchain_info == nullptr) { - device_table_->GetDeviceQueue( - device, swapchain_info->queue_family_indices[0], 0, &swapchain_info->blit_queue); - - VkCommandPoolCreateInfo command_pool_create_info = { VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO }; - command_pool_create_info.flags = - VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; - command_pool_create_info.queueFamilyIndex = swapchain_info->queue_family_indices[0]; - VkResult result = device_table_->CreateCommandPool( - device, &command_pool_create_info, nullptr, &swapchain_info->blit_command_pool); - if (result != VK_SUCCESS) + return result; + } + + bool found_copy_queue_family = false; + uint32_t copy_queue_family_index = VK_QUEUE_FAMILY_IGNORED; + bool found_transfer_queue_family_index = false; + uint32_t transfer_queue_family_index = 0; + + // Determine what queue to use for the initial virtual image setup + VkQueue initial_copy_queue = VK_NULL_HANDLE; + uint32_t property_count = 0; + std::vector props; + + instance_table_->GetPhysicalDeviceQueueFamilyProperties(device_info->parent, &property_count, nullptr); + props.resize(property_count); + instance_table_->GetPhysicalDeviceQueueFamilyProperties(device_info->parent, &property_count, props.data()); + + for (uint32_t queue_family_index = 0; queue_family_index < property_count; ++queue_family_index) + { + // If we're past the point of enabled queues, then stop looking because we really can't enable + // a queue that isn't flagged during device creation. + if (queue_family_index >= static_cast(device_info->queue_family_index_enabled.size())) { - return result; + break; } - for (uint32_t i = 0; i < capture_image_count; ++i) + if (!device_info->queue_family_index_enabled[queue_family_index]) { - VkCommandBufferAllocateInfo allocate_info = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO }; - allocate_info.pNext = nullptr; - allocate_info.commandPool = swapchain_info->blit_command_pool; - allocate_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - allocate_info.commandBufferCount = 1; - VkCommandBuffer command_buffer = VK_NULL_HANDLE; - result = device_table_->AllocateCommandBuffers(device, &allocate_info, &command_buffer); - if (result != VK_SUCCESS) - { - return result; - } - swapchain_info->blit_command_buffers.emplace_back(command_buffer); + continue; + } - VkSemaphoreCreateInfo semaphore_create_info = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO }; - semaphore_create_info.pNext = nullptr; - semaphore_create_info.flags = 0; - VkSemaphore semaphore = VK_NULL_HANDLE; - result = device_table_->CreateSemaphore(device, &semaphore_create_info, nullptr, &semaphore); - if (result != VK_SUCCESS) - { - return result; - } - swapchain_info->blit_semaphores.emplace_back(semaphore); + // If we find a graphics queue, we're good, so grab it and bail + if (props[queue_family_index].queueFlags & VK_QUEUE_GRAPHICS_BIT) + { + copy_queue_family_index = queue_family_index; + found_copy_queue_family = true; + break; } - } - // Return the capture count. The virtual swapchain will create a number of virtual images equal to the capture - // count. The virtual images will be returned to the caller in place of the real swapchain images. - (*image_count) = capture_image_count; - if ((device_info != nullptr) && (swapchain_info != nullptr) && (images != nullptr)) + // Find a transfer queue as an alternative, just in case + if (!found_transfer_queue_family_index && props[queue_family_index].queueFlags & VK_QUEUE_TRANSFER_BIT) + { + transfer_queue_family_index = queue_family_index; + found_transfer_queue_family_index = true; + } + } + if (!found_copy_queue_family) { - // Store the retrieved images and create new images to return to the caller. The replay call always - // retrieves the full swapchain image count, so this only needs to be done once. It does not need to handle - // the VK_INCOMPLETE case that the virtual image creation must handle. - if (swapchain_info->swapchain_images.empty()) + if (!found_transfer_queue_family_index) { - swapchain_info->swapchain_images = std::vector( - replay_swapchain_images.data(), std::next(replay_swapchain_images.data(), *replay_image_count)); + GFXRECON_LOG_ERROR("Virtual swapchain failed finding a queue to create initial virtual swapchain " + "images for swapchain (ID = %" PRIu64 ")", + swapchain_info->capture_id); + return VK_ERROR_INITIALIZATION_FAILED; } + copy_queue_family_index = transfer_queue_family_index; + GFXRECON_LOG_INFO("Virtual swapchain using transfer queue %d to create initial virtual swapchain " + "images for swapchain (ID = %" PRIu64 ")", + transfer_queue_family_index, + swapchain_info->capture_id); + } + device_table_->GetDeviceQueue(device, copy_queue_family_index, 0, &initial_copy_queue); + if (initial_copy_queue == VK_NULL_HANDLE) + { + GFXRECON_LOG_ERROR("Virtual swapchain failed getting device queue %d to create initial virtual swapchain " + "images for swapchain (ID = %" PRIu64 ")", + copy_queue_family_index, + swapchain_info->capture_id); + return VK_ERROR_INITIALIZATION_FAILED; + } + + auto& swapchain_resources = swapchain_resources_[swapchain]; - // If the call was made more than once because the first call returned VK_INCOMPLETE, only the new images - // returned by the second call will have virtual images created and appended to the end of the virtual image - // array. - if (capture_image_count != swapchain_info->virtual_images.size()) + for (uint32_t queue_family_index = 0; queue_family_index < property_count; ++queue_family_index) + { + if (swapchain_resources->copy_cmd_data.find(queue_family_index) == swapchain_resources->copy_cmd_data.end()) { - uint32_t start_index = static_cast(swapchain_info->virtual_images.size()); - - // TODO: This is the same code used in VulkanReplayConsumerBase::OverrideGetSwapchainImagesKHR, which - // should be moved to a shared graphics utility function. - - // Create an image for the virtual swapchain. Based on vkspec.html#swapchain-wsi-image-create-info. - VkImageCreateInfo image_create_info = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; - image_create_info.pNext = nullptr; - image_create_info.flags = 0; - image_create_info.imageType = VK_IMAGE_TYPE_2D; - image_create_info.format = swapchain_info->format; - image_create_info.extent = { swapchain_info->width, swapchain_info->height, 1 }; - image_create_info.mipLevels = 1; - image_create_info.arrayLayers = swapchain_info->image_array_layers; - image_create_info.samples = VK_SAMPLE_COUNT_1_BIT; - image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL; - image_create_info.usage = swapchain_info->image_usage | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; - image_create_info.sharingMode = swapchain_info->image_sharing_mode; - image_create_info.queueFamilyIndexCount = - static_cast(swapchain_info->queue_family_indices.size()); - image_create_info.pQueueFamilyIndices = swapchain_info->queue_family_indices.data(); - image_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - - if ((swapchain_info->image_flags & VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR) == - VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR) + VkBool32 supported = VK_FALSE; + + // We only want to look at a given queue if it was enabled during device creation time + // and if it supports present. Otherwise, we don't need to create a command pool, + // command buffers, and semaphores for performing the swapchain copy. + if (device_info->queue_family_index_enabled.size() <= queue_family_index || + !device_info->queue_family_index_enabled[queue_family_index]) { - image_create_info.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT; + GFXRECON_LOG_DEBUG("Virtual swapchain skipping creating blit info for queue family %d because it " + "was not enabled by the device", + queue_family_index); + continue; } - VkCommandBufferBeginInfo begin_info = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; - begin_info.pNext = nullptr; - begin_info.flags = 0; - begin_info.pInheritanceInfo = nullptr; - - auto command_buffer = swapchain_info->blit_command_buffers[0]; - result = device_table_->ResetCommandBuffer(command_buffer, 0); - if (result != VK_SUCCESS) + result = instance_table_->GetPhysicalDeviceSurfaceSupportKHR( + device_info->parent, queue_family_index, swapchain_info->surface, &supported); + if (result != VK_SUCCESS || supported == VK_FALSE) { - return result; + GFXRECON_LOG_DEBUG( + "Virtual swapchain skipping queue family %d since present support is not present " + "for swapchain (ID = %" PRIu64 ")", + queue_family_index, + swapchain_info->capture_id); + continue; } - result = device_table_->BeginCommandBuffer(command_buffer, &begin_info); + // Create one command pool per queue. + VkCommandPoolCreateInfo command_pool_create_info = { + VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, // sType + nullptr, // pNext + VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, // flags + static_cast(queue_family_index) // queueFamilyIndex + }; + + CopyCmdData copy_cmd_data = {}; + result = device_table_->CreateCommandPool( + device, &command_pool_create_info, nullptr, ©_cmd_data.command_pool); if (result != VK_SUCCESS) { + GFXRECON_LOG_ERROR("Virtual swapchain failed creating command pool %d for swapchain (ID = %" PRIu64 + ")", + queue_family_index, + swapchain_info->capture_id); return result; } + swapchain_resources->copy_cmd_data.emplace(queue_family_index, std::move(copy_cmd_data)); + } - for (uint32_t i = start_index; i < capture_image_count; ++i) - { - SwapchainKHRInfo::VirtualImage image; - - result = CreateSwapchainImage(device_info, image_create_info, image); + auto& copy_cmd_data = swapchain_resources->copy_cmd_data[queue_family_index]; + // Make sure we have enough storage for each of our tracked components (Command pools, + // Command Buffers, Semaphores, etc) as many queue families that are available. + // This is because at any point, the application may get a Device queue from that family and + // use it during the present. + uint32_t start_size = static_cast(copy_cmd_data.command_buffers.size()); + uint32_t new_count = property_count; + if (start_size < new_count) + { + // Create one command buffer per queue per swapchain image so that we don't reset a command buffer that + // may be in active use. + uint32_t command_buffer_count = static_cast(copy_cmd_data.command_buffers.size()); + if (command_buffer_count < capture_image_count) + { + copy_cmd_data.command_buffers.resize(capture_image_count); + + uint32_t new_count = capture_image_count - command_buffer_count; + VkCommandBufferAllocateInfo allocate_info = { + VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, // sType + nullptr, // pNext + copy_cmd_data.command_pool, // commandPool + VK_COMMAND_BUFFER_LEVEL_PRIMARY, // level + new_count // commandBufferCount + }; + + result = device_table_->AllocateCommandBuffers( + device, &allocate_info, ©_cmd_data.command_buffers[command_buffer_count]); if (result != VK_SUCCESS) { - GFXRECON_LOG_ERROR("Failed to create virtual swapchain image for swapchain (ID = %" PRIu64 ")", + GFXRECON_LOG_ERROR("Virtual swapchain failed allocating internal command buffer %d for " + "swapchain (ID = %" PRIu64 ")", + queue_family_index, swapchain_info->capture_id); - break; + return result; } - swapchain_info->virtual_images.emplace_back(std::move(image)); } - VkImageMemoryBarrier barrier = { - VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - nullptr, - VK_ACCESS_NONE, - VK_ACCESS_NONE, - VK_IMAGE_LAYOUT_UNDEFINED, - VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, - VK_QUEUE_FAMILY_IGNORED, - VK_QUEUE_FAMILY_IGNORED, - VK_NULL_HANDLE, - VkImageSubresourceRange{ - VK_IMAGE_ASPECT_COLOR_BIT, - 0, - image_create_info.mipLevels, - 0, - image_create_info.arrayLayers, - }, - }; - - for (uint32_t i = 0; i < *replay_image_count; ++i) + uint32_t semaphore_count = static_cast(copy_cmd_data.semaphores.size()); + if (semaphore_count < capture_image_count) { - barrier.image = replay_swapchain_images[i]; - device_table_->CmdPipelineBarrier(command_buffer, - VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, - VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, - 0, - 0, - nullptr, - 0, - nullptr, - 1, - &barrier); + copy_cmd_data.semaphores.resize(capture_image_count); + + for (uint32_t ii = semaphore_count; ii < capture_image_count; ++ii) + { + VkSemaphoreCreateInfo semaphore_create_info = { + VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, // sType + nullptr, // pNext + 0 // flags + }; + + VkSemaphore semaphore = 0; + result = device_table_->CreateSemaphore(device, &semaphore_create_info, nullptr, &semaphore); + if (result != VK_SUCCESS) + { + GFXRECON_LOG_ERROR( + "Virtual swapchain failed creating internal copy semaphore for swapchain (ID = %" PRIu64 + ")", + swapchain_info->capture_id); + return result; + } + copy_cmd_data.semaphores[ii] = semaphore; + } } + } + } - result = device_table_->EndCommandBuffer(command_buffer); + uint32_t virtual_swapchain_count = static_cast(swapchain_resources->virtual_swapchain_images.size()); - VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_TRANSFER_BIT; + // If the call was made more than once because the first call returned VK_INCOMPLETE, only the new images + // returned by the second call will have virtual images created and appended to the end of the virtual image + // array. + if (virtual_swapchain_count < capture_image_count) + { + // TODO: This is the same code used in VulkanReplayConsumerBase::OverrideGetSwapchainImagesKHR, which + // should be moved to a shared graphics utility function. + + // Create an image for the virtual swapchain. Based on vkspec.html#swapchain-wsi-image-create-info. + VkImageCreateInfo image_create_info = { + VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // sType, + nullptr, // pNext + 0, // flags + VK_IMAGE_TYPE_2D, // imageType + swapchain_info->format, // format + VkExtent3D{ swapchain_info->width, swapchain_info->height, 1 }, // extent + 1, // mipLevels + swapchain_info->image_array_layers, // arrayLayers + VK_SAMPLE_COUNT_1_BIT, // samples + VK_IMAGE_TILING_OPTIMAL, // tiling + swapchain_info->image_usage | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, // usage + swapchain_info->image_sharing_mode, // sharingMode + static_cast(swapchain_info->queue_family_indices.size()), // queueFamilyIndexCount + swapchain_info->queue_family_indices.data(), // pQueueFamilyIndices + VK_IMAGE_LAYOUT_UNDEFINED // initialLayout + }; + + if ((swapchain_info->image_flags & VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR) == + VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR) + { + image_create_info.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT; + } - VkSubmitInfo submit_info = { VK_STRUCTURE_TYPE_SUBMIT_INFO }; - submit_info.commandBufferCount = 1; - submit_info.pCommandBuffers = &command_buffer; + VkCommandBufferBeginInfo begin_info = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; + begin_info.pNext = nullptr; + begin_info.flags = 0; + begin_info.pInheritanceInfo = nullptr; + + auto command_buffer = swapchain_resources->copy_cmd_data[copy_queue_family_index].command_buffers[0]; + + result = device_table_->ResetCommandBuffer(command_buffer, 0); + if (result != VK_SUCCESS) + { + GFXRECON_LOG_ERROR( + "Virtual swapchain failed resetting internal command buffer %d for swapchain (ID = %" PRIu64 ")", + copy_queue_family_index, + swapchain_info->capture_id); + return result; + } + + result = device_table_->BeginCommandBuffer(command_buffer, &begin_info); + if (result != VK_SUCCESS) + { + GFXRECON_LOG_ERROR( + "Virtual swapchain failed starting internal command buffer %d for swapchain (ID = %" PRIu64 ")", + copy_queue_family_index, + swapchain_info->capture_id); + return result; + } + + for (uint32_t i = virtual_swapchain_count; i < capture_image_count; ++i) + { + VirtualImage image; + + result = CreateVirtualSwapchainImage(device_info, image_create_info, image); - result = device_table_->QueueSubmit(swapchain_info->blit_queue, 1, &submit_info, VK_NULL_HANDLE); - if (result != VK_SUCCESS) - { - return result; - } - result = device_table_->QueueWaitIdle(swapchain_info->blit_queue); if (result != VK_SUCCESS) { - return result; + GFXRECON_LOG_ERROR("Failed to create virtual swapchain image for swapchain (ID = %" PRIu64 ")", + swapchain_info->capture_id); + break; } + swapchain_resources->virtual_swapchain_images.emplace_back(std::move(image)); } - for (uint32_t i = 0; i < capture_image_count; ++i) + + VkImageMemoryBarrier barrier = { + VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // sType + nullptr, // pNext + VK_ACCESS_NONE, // srcAccessMask + VK_ACCESS_NONE, // dstAccessMask + VK_IMAGE_LAYOUT_UNDEFINED, // oldLayout + VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, // newLayout + VK_QUEUE_FAMILY_IGNORED, // srcQueueFamilyIndex + VK_QUEUE_FAMILY_IGNORED, // dstQueueFamilyIndex + VK_NULL_HANDLE, // image + VkImageSubresourceRange{ + VK_IMAGE_ASPECT_COLOR_BIT, + 0, + image_create_info.mipLevels, + 0, + image_create_info.arrayLayers, + }, // subResourceRange + }; + + for (uint32_t i = 0; i < *replay_image_count; ++i) { - images[i] = swapchain_info->virtual_images[i].image; + barrier.image = swapchain_resources->replay_swapchain_images[i]; + device_table_->CmdPipelineBarrier(command_buffer, + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + 0, + 0, + nullptr, + 0, + nullptr, + 1, + &barrier); } + + result = device_table_->EndCommandBuffer(command_buffer); + if (result != VK_SUCCESS) + { + GFXRECON_LOG_ERROR( + "Virtual swapchain failed ending internal command buffer %d for swapchain (ID = %" PRIu64 ")", + copy_queue_family_index, + swapchain_info->capture_id); + return result; + } + + VkSubmitInfo submit_info = { VK_STRUCTURE_TYPE_SUBMIT_INFO }; + submit_info.commandBufferCount = 1; + submit_info.pCommandBuffers = &command_buffer; + + result = device_table_->QueueSubmit(initial_copy_queue, 1, &submit_info, VK_NULL_HANDLE); + if (result != VK_SUCCESS) + { + GFXRECON_LOG_ERROR( + "Virtual swapchain failed submitting internal command buffer %d for swapchain (ID = %" PRIu64 ")", + copy_queue_family_index, + swapchain_info->capture_id); + return result; + } + result = device_table_->QueueWaitIdle(initial_copy_queue); + if (result != VK_SUCCESS) + { + GFXRECON_LOG_ERROR( + "Virtual swapchain failed waiting for internal command buffer %d for swapchain (ID = %" PRIu64 ")", + copy_queue_family_index, + swapchain_info->capture_id); + return result; + } + } + + for (uint32_t i = 0; i < capture_image_count; ++i) + { + images[i] = swapchain_resources->virtual_swapchain_images[i].image; } } @@ -367,6 +570,7 @@ VkResult VulkanVirtualSwapchain::AcquireNextImageKHR(PFN_vkAcquireNextImageKHR f { VkDevice device = VK_NULL_HANDLE; VkSwapchainKHR swapchain = VK_NULL_HANDLE; + VkResult result = VK_NOT_READY; if (device_info != nullptr) { @@ -378,7 +582,14 @@ VkResult VulkanVirtualSwapchain::AcquireNextImageKHR(PFN_vkAcquireNextImageKHR f swapchain = swapchain_info->handle; } - return func(device, swapchain, timeout, semaphore, fence, image_index); + result = func(device, swapchain, timeout, semaphore, fence, image_index); + if (result != VK_SUCCESS) + { + GFXRECON_LOG_ERROR("Virtual swapchain failed AcquireNextImageKHR 0x%08x for swapchain (ID = %" PRIu64 ")", + result, + swapchain_info->capture_id); + } + return result; } VkResult VulkanVirtualSwapchain::AcquireNextImage2KHR(PFN_vkAcquireNextImage2KHR func, @@ -395,7 +606,14 @@ VkResult VulkanVirtualSwapchain::AcquireNextImage2KHR(PFN_vkAcquireNextImage2KHR device = device_info->handle; } - return func(device, acquire_info, image_index); + VkResult result = func(device, acquire_info, image_index); + if (result != VK_SUCCESS) + { + GFXRECON_LOG_ERROR("Virtual swapchain failed AcquireNextImage2KHR 0x%08x for swapchain (ID = %" PRIu64 ")", + result, + swapchain_info->capture_id); + } + return result; } VkResult VulkanVirtualSwapchain::QueuePresentKHR(PFN_vkQueuePresentKHR func, @@ -404,22 +622,21 @@ VkResult VulkanVirtualSwapchain::QueuePresentKHR(PFN_vkQueuePresentKHR const QueueInfo* queue_info, const VkPresentInfoKHR* present_info) { - VkQueue queue = VK_NULL_HANDLE; + VkResult result = VK_ERROR_UNKNOWN; if (queue_info == nullptr) { return VK_ERROR_FEATURE_NOT_PRESENT; } - queue = queue_info->handle; - // TODO: Note that this blit could also be used to scale the image, which would allow replay to support an option + VkQueue queue = queue_info->handle; + uint32_t queue_family_index = queue_info->family_index; + + // TODO: Note that this copy could also be used to scale the image, which would allow replay to support an option // for changing the window/swapchain size when the virtual swapchain mode is active. The virtual image would // continue to use the captured swapchain image size, and be scaled to the replay swapchain image size with // vkCmdBlitImage. - VkCommandBufferBeginInfo begin_info = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; - begin_info.pNext = nullptr; - begin_info.flags = 0; - begin_info.pInheritanceInfo = nullptr; + VkCommandBufferBeginInfo begin_info = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, nullptr, 0, nullptr }; VkImageMemoryBarrier initial_barrier_virtual_image; VkImageMemoryBarrier initial_barrier_swapchain_image; @@ -458,56 +675,89 @@ VkResult VulkanVirtualSwapchain::QueuePresentKHR(PFN_vkQueuePresentKHR final_barrier_swapchain_image = final_barrier_virtual_image; final_barrier_swapchain_image.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - VkImageSubresourceLayers subresource = { - VK_IMAGE_ASPECT_COLOR_BIT, - 0, - 0, - 0, - }; + VkImageSubresourceLayers subresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 0 }; + VkOffset3D offset = { 0, 0, 0 }; + auto swapchainCount = present_info->swapchainCount; + std::vector present_wait_semaphores; + + // TODO: There is a potential issue here where a vkQueuePresent comes in on a queue (let's call + // it QueueX) which does not support vkCmdCopyImage (i.e. a video-only queue). In that case, + // we would need to insert an emtpy command buffer into the command stream of QueueX which + // triggers a semaphore (let's say SemA), then we would need to submit the vkCmdCopyImage in a + // command buffer on a queue that supports it (let's say QueueY) which will wait on SemA to + // start and signaling another semaphore (SemB) when it is done. Then, we need to add the + // QueuePresent to QueueX, but waiting on SemB before it executes. And that is assuming that + // the buffer image is even accessible on both Queues! + + for (uint32_t i = 0; i < swapchainCount; ++i) + { + const auto* swapchain_info = swapchain_infos[i]; + uint32_t capture_image_index = capture_image_indices[i]; + uint32_t replay_image_index = present_info->pImageIndices[i]; - VkOffset3D offsets[2] = { + // Get the per swapchain resource data so we have access to the virtual swapchain-specific information. + if (swapchain_resources_.find(swapchain_info->handle) == swapchain_resources_.end()) { - 0, - 0, - 0, - }, + GFXRECON_LOG_ERROR( + "Virtual swapchain vkQueuePresentKHR missing swapchain resource data for swapchain (ID = %" PRIu64 ")", + swapchain_info->capture_id); + continue; + } + + auto& swapchain_resources = swapchain_resources_[swapchain_info->handle]; + assert(swapchain_resources != nullptr); + + // Find the appropriate CommandCopyData struct for this queue family + if (swapchain_resources->copy_cmd_data.find(queue_family_index) == swapchain_resources->copy_cmd_data.end()) { - 0, - 0, - 1, - }, - }; + GFXRECON_LOG_ERROR("Virtual swapchain vkQueuePresentKHR missing swapchain resource copy command data for " + "queue (Handle %" PRIu64 ") in swapchain (ID = %" PRIu64 ")", + queue, + swapchain_info->capture_id); + continue; + } - auto length = present_info->swapchainCount; - std::vector semaphores; + const auto& virtual_image = swapchain_resources->virtual_swapchain_images[capture_image_index]; + const auto& replay_image = swapchain_resources->replay_swapchain_images[replay_image_index]; - for (uint32_t i = 0; i < length; ++i) - { - uint32_t capture_image_index = capture_image_indices[i]; - uint32_t replay_image_index = present_info->pImageIndices[i]; - const auto* swapchain_info = swapchain_infos[i]; - const auto& virtual_image = swapchain_info->virtual_images[capture_image_index]; - const auto& swapchain_image = swapchain_info->swapchain_images[replay_image_index]; + // Use a command buffer and semaphore from the same queue index + auto& copy_cmd_data = swapchain_resources->copy_cmd_data[queue_family_index]; + auto command_buffer = copy_cmd_data.command_buffers[capture_image_index]; + auto copy_semaphore = copy_cmd_data.semaphores[capture_image_index]; - auto command_buffer = swapchain_info->blit_command_buffers[capture_image_index]; - VkResult result = device_table_->ResetCommandBuffer(command_buffer, 0); + std::vector wait_semaphores; + std::vector signal_semaphores; + + // Only wait for the present semaphore dependencies on the first copy command buffer. + // The others will automatically inherit that dependency because of their order in the + // command buffer. + if (i == 0 && present_info->waitSemaphoreCount > 0) + { + wait_semaphores.assign(present_info->pWaitSemaphores, + present_info->pWaitSemaphores + present_info->waitSemaphoreCount); + } + + // Only trigger a semaphore on the last copy + if (i == swapchainCount - 1) + { + signal_semaphores.push_back(copy_semaphore); + present_wait_semaphores.emplace_back(copy_semaphore); + } + + result = device_table_->ResetCommandBuffer(command_buffer, 0); if (result != VK_SUCCESS) { return result; } - result = device_table_->BeginCommandBuffer(command_buffer, &begin_info); if (result != VK_SUCCESS) { return result; } - auto semaphore = swapchain_info->blit_semaphores[capture_image_index]; - semaphores.emplace_back(semaphore); - initial_barrier_virtual_image.image = virtual_image.image; initial_barrier_virtual_image.subresourceRange.layerCount = swapchain_info->image_array_layers; - initial_barrier_swapchain_image.image = swapchain_image; + initial_barrier_swapchain_image.image = replay_image; initial_barrier_swapchain_image.subresourceRange.layerCount = swapchain_info->image_array_layers; device_table_->CmdPipelineBarrier(command_buffer, @@ -532,29 +782,23 @@ VkResult VulkanVirtualSwapchain::QueuePresentKHR(PFN_vkQueuePresentKHR 1, &initial_barrier_swapchain_image); - subresource.layerCount = swapchain_info->image_array_layers; - - offsets[1].x = static_cast(swapchain_info->width); - offsets[1].y = static_cast(swapchain_info->height); - VkImageBlit blit = { - subresource, - { offsets[0], offsets[1] }, - subresource, - { offsets[0], offsets[1] }, - }; + subresource.layerCount = swapchain_info->image_array_layers; + VkExtent3D image_extent = { swapchain_info->width, swapchain_info->height, 1 }; + VkImageCopy image_copy = { subresource, offset, subresource, offset, image_extent }; - device_table_->CmdBlitImage(command_buffer, + // NOTE: vkCmdCopyImage works on Queues of types including Graphics, Compute + // and Transfer. So should work on any queues we get a vkQueuePresentKHR from. + device_table_->CmdCopyImage(command_buffer, virtual_image.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - swapchain_image, + replay_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, - &blit, - VK_FILTER_NEAREST); + &image_copy); final_barrier_virtual_image.image = virtual_image.image; final_barrier_virtual_image.subresourceRange.layerCount = swapchain_info->image_array_layers; - final_barrier_swapchain_image.image = swapchain_image; + final_barrier_swapchain_image.image = replay_image; final_barrier_swapchain_image.subresourceRange.layerCount = swapchain_info->image_array_layers; device_table_->CmdPipelineBarrier(command_buffer, @@ -587,16 +831,24 @@ VkResult VulkanVirtualSwapchain::QueuePresentKHR(PFN_vkQueuePresentKHR VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_TRANSFER_BIT; - VkSubmitInfo submit_info = { VK_STRUCTURE_TYPE_SUBMIT_INFO }; - submit_info.waitSemaphoreCount = present_info->waitSemaphoreCount; - submit_info.pWaitSemaphores = present_info->pWaitSemaphores; + VkSubmitInfo submit_info = { VK_STRUCTURE_TYPE_SUBMIT_INFO }; + submit_info.waitSemaphoreCount = static_cast(wait_semaphores.size()); + if (present_info->waitSemaphoreCount > 0) + { + submit_info.pWaitSemaphores = wait_semaphores.data(); + } + else + { + submit_info.pWaitSemaphores = nullptr; + } submit_info.pWaitDstStageMask = &wait_stage; + submit_info.signalSemaphoreCount = static_cast(signal_semaphores.size()); + submit_info.pSignalSemaphores = signal_semaphores.data(); submit_info.commandBufferCount = 1; submit_info.pCommandBuffers = &command_buffer; - submit_info.signalSemaphoreCount = 1; - submit_info.pSignalSemaphores = &semaphore; - result = device_table_->QueueSubmit(swapchain_info->blit_queue, 1, &submit_info, VK_NULL_HANDLE); + result = device_table_->QueueSubmit(queue, 1, &submit_info, VK_NULL_HANDLE); + if (result != VK_SUCCESS) { return result; @@ -604,8 +856,8 @@ VkResult VulkanVirtualSwapchain::QueuePresentKHR(PFN_vkQueuePresentKHR } VkPresentInfoKHR modified_present_info = *present_info; - modified_present_info.waitSemaphoreCount = static_cast(semaphores.size()); - modified_present_info.pWaitSemaphores = semaphores.data(); + modified_present_info.waitSemaphoreCount = static_cast(present_wait_semaphores.size()); + modified_present_info.pWaitSemaphores = present_wait_semaphores.data(); return func(queue, &modified_present_info); } @@ -672,9 +924,9 @@ void VulkanVirtualSwapchain::CmdPipelineBarrier(PFN_vkCmdPipelineBarrier fun image_memory_barriers); } -VkResult VulkanVirtualSwapchain::CreateSwapchainImage(const DeviceInfo* device_info, - const VkImageCreateInfo& image_create_info, - SwapchainKHRInfo::VirtualImage& image) +VkResult VulkanVirtualSwapchain::CreateVirtualSwapchainImage(const DeviceInfo* device_info, + const VkImageCreateInfo& image_create_info, + VirtualImage& image) { // TODO: This is the same code used in VulkanReplayConsumerBase::CreateSwapchainImage, which // should be moved to a shared graphics utility function. @@ -732,70 +984,15 @@ VkResult VulkanVirtualSwapchain::CreateSwapchainImage(const DeviceInfo* if (image.memory != VK_NULL_HANDLE) { allocator->FreeMemoryDirect(image.memory, nullptr, image.memory_allocator_data); + image.memory = VK_NULL_HANDLE; } allocator->DestroyImageDirect(image.image, nullptr, image.resource_allocator_data); + image.image = VK_NULL_HANDLE; } } return result; } -int32_t VulkanVirtualSwapchain::FindFirstPresentSrcLayout(const VkRenderPassCreateInfo* create_info) const -{ - if ((create_info != nullptr) && (create_info->pAttachments != nullptr)) - { - uint32_t count = create_info->attachmentCount; - auto descriptions = create_info->pAttachments; - - for (uint32_t i = 0; i < count; ++i) - { - // TODO: This should also look at the initialLayout values. - if (descriptions[i].finalLayout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR) - { - return i; - } - } - } - - return -1; -} - -int32_t VulkanVirtualSwapchain::FindFirstPresentSrcLayout(const VkRenderPassCreateInfo2* create_info) const -{ - if ((create_info != nullptr) && (create_info->pAttachments != nullptr)) - { - uint32_t count = create_info->attachmentCount; - auto descriptions = create_info->pAttachments; - - for (uint32_t i = 0; i < count; ++i) - { - // TODO: This should also look at the initialLayout values. - if (descriptions[i].finalLayout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR) - { - return i; - } - } - } - - return -1; -} - -int32_t VulkanVirtualSwapchain::FindFirstPresentSrcLayout(uint32_t count, const VkImageMemoryBarrier* barriers) const -{ - if (barriers != nullptr) - { - for (uint32_t i = 0; i < count; ++i) - { - // TODO: This should also look at the oldLayout values. - if (barriers[i].newLayout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR) - { - return i; - } - } - } - - return -1; -} - GFXRECON_END_NAMESPACE(decode) GFXRECON_END_NAMESPACE(gfxrecon) diff --git a/framework/decode/vulkan_virtual_swapchain.h b/framework/decode/vulkan_virtual_swapchain.h index e939bee3d5..baaa6707e0 100644 --- a/framework/decode/vulkan_virtual_swapchain.h +++ b/framework/decode/vulkan_virtual_swapchain.h @@ -118,15 +118,43 @@ class VulkanVirtualSwapchain : public VulkanSwapchain {} private: - VkResult CreateSwapchainImage(const DeviceInfo* device_info, - const VkImageCreateInfo& image_create_info, - SwapchainKHRInfo::VirtualImage& image); - - int32_t FindFirstPresentSrcLayout(const VkRenderPassCreateInfo* create_info) const; - - int32_t FindFirstPresentSrcLayout(const VkRenderPassCreateInfo2* create_info) const; - - int32_t FindFirstPresentSrcLayout(uint32_t count, const VkImageMemoryBarrier* barriers) const; + // Structure necessary to track the necessary information related to the virtual swapchain images + struct VirtualImage + { + VkDeviceMemory memory{ VK_NULL_HANDLE }; + VkImage image{ VK_NULL_HANDLE }; + VulkanResourceAllocator::MemoryData memory_allocator_data{ 0 }; + VulkanResourceAllocator::ResourceData resource_allocator_data{ 0 }; + }; + + // Structure to store data required per device queue family to properly handle creating the + // virtual swapchain images and copying that data to the actual swapchain images. + struct CopyCmdData + { + VkCommandPool command_pool; + std::vector command_buffers; + std::vector semaphores; + }; + + // Structure to track VulkanVirtualSwapchain private data specific to a particular VkSwapchainKHR handle. + struct SwapchainResourceData + { + // Create a map that correlates copy command data with a queue family index. + std::unordered_map copy_cmd_data; + + // Swapchain images, these include the virtual ones created by this + // class that are returned in place of the hardware ones, as well + // as a vector of the actual hardware ones used during replay. + std::vector virtual_swapchain_images; + std::vector replay_swapchain_images; + }; + + VkResult CreateVirtualSwapchainImage(const DeviceInfo* device_info, + const VkImageCreateInfo& image_create_info, + VirtualImage& image); + + // Create an unordered map to associate the swapchain resource data with a particular Vulkan swapchain + std::unordered_map> swapchain_resources_; }; GFXRECON_END_NAMESPACE(decode) diff --git a/framework/generated/generated_vulkan_replay_consumer.cpp b/framework/generated/generated_vulkan_replay_consumer.cpp index 3bff45ecd0..3ed2b2452d 100644 --- a/framework/generated/generated_vulkan_replay_consumer.cpp +++ b/framework/generated/generated_vulkan_replay_consumer.cpp @@ -206,13 +206,14 @@ void VulkanReplayConsumer::Process_vkGetDeviceQueue( uint32_t queueIndex, HandlePointerDecoder* pQueue) { - VkDevice in_device = MapHandle(device, &VulkanObjectInfoTable::GetDeviceInfo); + auto in_device = GetObjectInfoTable().GetDeviceInfo(device); if (!pQueue->IsNull()) { pQueue->SetHandleLength(1); } - VkQueue* out_pQueue = pQueue->GetHandlePointer(); + QueueInfo handle_info; + pQueue->SetConsumerData(0, &handle_info); - GetDeviceTable(in_device)->GetDeviceQueue(in_device, queueFamilyIndex, queueIndex, out_pQueue); + OverrideGetDeviceQueue(GetDeviceTable(in_device->handle)->GetDeviceQueue, in_device, queueFamilyIndex, queueIndex, pQueue); - AddHandle(device, pQueue->GetPointer(), out_pQueue, &VulkanObjectInfoTable::AddQueueInfo); + AddHandle(device, pQueue->GetPointer(), pQueue->GetHandlePointer(), std::move(handle_info), &VulkanObjectInfoTable::AddQueueInfo); } void VulkanReplayConsumer::Process_vkQueueSubmit( @@ -2308,14 +2309,14 @@ void VulkanReplayConsumer::Process_vkGetDeviceQueue2( StructPointerDecoder* pQueueInfo, HandlePointerDecoder* pQueue) { - VkDevice in_device = MapHandle(device, &VulkanObjectInfoTable::GetDeviceInfo); - const VkDeviceQueueInfo2* in_pQueueInfo = pQueueInfo->GetPointer(); + auto in_device = GetObjectInfoTable().GetDeviceInfo(device); if (!pQueue->IsNull()) { pQueue->SetHandleLength(1); } - VkQueue* out_pQueue = pQueue->GetHandlePointer(); + QueueInfo handle_info; + pQueue->SetConsumerData(0, &handle_info); - GetDeviceTable(in_device)->GetDeviceQueue2(in_device, in_pQueueInfo, out_pQueue); + OverrideGetDeviceQueue2(GetDeviceTable(in_device->handle)->GetDeviceQueue2, in_device, pQueueInfo, pQueue); - AddHandle(device, pQueue->GetPointer(), out_pQueue, &VulkanObjectInfoTable::AddQueueInfo); + AddHandle(device, pQueue->GetPointer(), pQueue->GetHandlePointer(), std::move(handle_info), &VulkanObjectInfoTable::AddQueueInfo); } void VulkanReplayConsumer::Process_vkCreateSamplerYcbcrConversion( @@ -2569,11 +2570,11 @@ void VulkanReplayConsumer::Process_vkWaitSemaphores( StructPointerDecoder* pWaitInfo, uint64_t timeout) { - VkDevice in_device = MapHandle(device, &VulkanObjectInfoTable::GetDeviceInfo); - const VkSemaphoreWaitInfo* in_pWaitInfo = pWaitInfo->GetPointer(); + auto in_device = GetObjectInfoTable().GetDeviceInfo(device); + MapStructHandles(pWaitInfo->GetMetaStructPointer(), GetObjectInfoTable()); - VkResult replay_result = GetDeviceTable(in_device)->WaitSemaphores(in_device, in_pWaitInfo, timeout); + VkResult replay_result = OverrideWaitSemaphores(GetDeviceTable(in_device->handle)->WaitSemaphores, returnValue, in_device, pWaitInfo, timeout); CheckResult("vkWaitSemaphores", returnValue, replay_result); } @@ -4435,10 +4436,9 @@ void VulkanReplayConsumer::Process_vkAcquireProfilingLockKHR( format::HandleId device, StructPointerDecoder* pInfo) { - VkDevice in_device = MapHandle(device, &VulkanObjectInfoTable::GetDeviceInfo); - const VkAcquireProfilingLockInfoKHR* in_pInfo = pInfo->GetPointer(); + auto in_device = GetObjectInfoTable().GetDeviceInfo(device); - VkResult replay_result = GetDeviceTable(in_device)->AcquireProfilingLockKHR(in_device, in_pInfo); + VkResult replay_result = OverrideAcquireProfilingLockKHR(GetDeviceTable(in_device->handle)->AcquireProfilingLockKHR, returnValue, in_device, pInfo); CheckResult("vkAcquireProfilingLockKHR", returnValue, replay_result); } @@ -4810,11 +4810,10 @@ void VulkanReplayConsumer::Process_vkWaitForPresentKHR( uint64_t presentId, uint64_t timeout) { - VkDevice in_device = MapHandle(device, &VulkanObjectInfoTable::GetDeviceInfo); - VkSwapchainKHR in_swapchain = MapHandle(swapchain, &VulkanObjectInfoTable::GetSwapchainKHRInfo); - if (GetObjectInfoTable().GetSurfaceKHRInfo(GetObjectInfoTable().GetSwapchainKHRInfo(swapchain)->surface_id) == nullptr || GetObjectInfoTable().GetSurfaceKHRInfo(GetObjectInfoTable().GetSwapchainKHRInfo(swapchain)->surface_id)->surface_creation_skipped) { return; } + auto in_device = GetObjectInfoTable().GetDeviceInfo(device); + auto in_swapchain = GetObjectInfoTable().GetSwapchainKHRInfo(swapchain); - VkResult replay_result = GetDeviceTable(in_device)->WaitForPresentKHR(in_device, in_swapchain, presentId, timeout); + VkResult replay_result = OverrideWaitForPresentKHR(GetDeviceTable(in_device->handle)->WaitForPresentKHR, returnValue, in_device, in_swapchain, presentId, timeout); CheckResult("vkWaitForPresentKHR", returnValue, replay_result); } diff --git a/framework/generated/vulkan_generators/replay_overrides.json b/framework/generated/vulkan_generators/replay_overrides.json index 2152821035..dc62dc1ed4 100644 --- a/framework/generated/vulkan_generators/replay_overrides.json +++ b/framework/generated/vulkan_generators/replay_overrides.json @@ -14,6 +14,8 @@ "vkGetPhysicalDeviceMemoryProperties2KHR": "OverrideGetPhysicalDeviceMemoryProperties2", "vkGetPhysicalDeviceSurfaceCapabilitiesKHR": "OverrideGetPhysicalDeviceSurfaceCapabilitiesKHR", "vkGetPhysicalDeviceSurfaceCapabilities2KHR": "OverrideGetPhysicalDeviceSurfaceCapabilities2KHR", + "vkGetDeviceQueue": "OverrideGetDeviceQueue", + "vkGetDeviceQueue2": "OverrideGetDeviceQueue2", "vkWaitForFences": "OverrideWaitForFences", "vkGetFenceStatus": "OverrideGetFenceStatus", "vkGetEventStatus": "OverrideGetEventStatus", @@ -93,6 +95,9 @@ "vkCmdDebugMarkerInsertEXT": "OverrideCmdDebugMarkerInsertEXT", "vkCmdBeginRenderPass": "OverrideCmdBeginRenderPass", "vkCreateImageView": "OverrideCreateImageView", - "vkCreateFramebuffer": "OverrideCreateFramebuffer" + "vkCreateFramebuffer": "OverrideCreateFramebuffer", + "vkAcquireProfilingLockKHR": "OverrideAcquireProfilingLockKHR", + "vkWaitForPresentKHR": "OverrideWaitForPresentKHR", + "vkWaitSemaphores": "OverrideWaitSemaphores" } } diff --git a/tools/launcher/CMakeLists.txt b/tools/launcher/CMakeLists.txt index d8f6e1c773..bfb1b40f2b 100644 --- a/tools/launcher/CMakeLists.txt +++ b/tools/launcher/CMakeLists.txt @@ -42,4 +42,5 @@ if(BUILD_LAUNCHER_AND_INTERCEPTOR) common_build_directives(gfxrecon-launcher) install(TARGETS gfxrecon-launcher RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + install(FILES ${CMAKE_BINARY_DIR}/layer/gfxrecon_interceptor/$/gfxrecon_interceptor.dll DESTINATION ${CMAKE_INSTALL_BINDIR}) endif() diff --git a/tools/tool_settings.h b/tools/tool_settings.h index bcac446c0c..9e59473232 100644 --- a/tools/tool_settings.h +++ b/tools/tool_settings.h @@ -480,7 +480,7 @@ static void GetScreenshotSize(const gfxrecon::util::ArgumentParser& arg_parser, width = std::stoul(value.substr(0, x)); height = std::stoul(value.substr(x + 1)); } - catch (std::exception& e) + catch (std::exception&) { GFXRECON_LOG_WARNING("Ignoring invalid screenshot width x height option. Expected format is " "--screenshot-size [width]x[height]"); @@ -510,7 +510,7 @@ static float GetScreenshotScale(const gfxrecon::util::ArgumentParser& arg_parser { scale = std::stof(value); } - catch (std::exception& e) + catch (std::exception&) { GFXRECON_LOG_WARNING( "Ignoring invalid screenshot scale option. Expected format is --screenshot-scale [scale]");