diff --git a/USAGE_desktop_Vulkan.md b/USAGE_desktop_Vulkan.md index d56339ead4..86d9dc7dd2 100644 --- a/USAGE_desktop_Vulkan.md +++ b/USAGE_desktop_Vulkan.md @@ -768,7 +768,9 @@ Optional arguments: virtual Virtual Swapchain of images which match the swapchain in effect at capture time and which are copied to the underlying swapchain of the - implementation being replayed on. This is default. + implementation being replayed on. Also displays + offscreen frame boundaries to an additional window. + This is default. captured Use the swapchain indices stored in the capture directly on the swapchain setup for replay. offscreen Disable creating swapchains, surfaces diff --git a/framework/decode/replay_options.h b/framework/decode/replay_options.h index 8fc582f6ae..1359d51e11 100644 --- a/framework/decode/replay_options.h +++ b/framework/decode/replay_options.h @@ -58,8 +58,8 @@ struct ReplayOptions bool flush_measurement_frame_range{ false }; bool flush_inside_measurement_range{ false }; bool force_windowed{ false }; - uint32_t windowed_width{ 0 }; - uint32_t windowed_height{ 0 }; + uint32_t windowed_width{ 320 }; + uint32_t windowed_height{ 240 }; bool force_windowed_origin{ false }; int32_t window_topleft_x{ 0 }; int32_t window_topleft_y{ 0 }; diff --git a/framework/decode/vulkan_captured_swapchain.cpp b/framework/decode/vulkan_captured_swapchain.cpp index 9a2497d564..a65d10ac65 100644 --- a/framework/decode/vulkan_captured_swapchain.cpp +++ b/framework/decode/vulkan_captured_swapchain.cpp @@ -266,6 +266,29 @@ void VulkanCapturedSwapchain::CmdPipelineBarrier2(PFN_vkCmdPipelineBarrier2 func func(command_buffer, pDependencyInfo); } +void VulkanCapturedSwapchain::FrameBoundaryANDROID(PFN_vkFrameBoundaryANDROID func, + const VulkanDeviceInfo* device_info, + const VulkanSemaphoreInfo* semaphore_info, + const VulkanImageInfo* image_info, + VulkanInstanceInfo* instance_info, + const graphics::VulkanInstanceTable* instance_table, + const graphics::VulkanDeviceTable* device_table, + application::Application* application) +{ + GFXRECON_UNREFERENCED_PARAMETER(instance_info); + GFXRECON_UNREFERENCED_PARAMETER(instance_table); + GFXRECON_UNREFERENCED_PARAMETER(device_table); + GFXRECON_UNREFERENCED_PARAMETER(application); + + GFXRECON_ASSERT(device_info != nullptr); + + VkDevice device = device_info->handle; + VkSemaphore semaphore = (semaphore_info == nullptr ? VK_NULL_HANDLE : semaphore_info->handle); + VkImage image = (image_info == nullptr ? VK_NULL_HANDLE : image_info->handle); + + func(device, semaphore, image); +} + void VulkanCapturedSwapchain::ProcessSetSwapchainImageStateCommand( const VulkanDeviceInfo* device_info, VulkanSwapchainKHRInfo* swapchain_info, diff --git a/framework/decode/vulkan_captured_swapchain.h b/framework/decode/vulkan_captured_swapchain.h index bf9ddec3e8..4f63cb68a2 100644 --- a/framework/decode/vulkan_captured_swapchain.h +++ b/framework/decode/vulkan_captured_swapchain.h @@ -109,6 +109,15 @@ class VulkanCapturedSwapchain : public VulkanSwapchain VulkanCommandBufferInfo* command_buffer_info, const VkDependencyInfo* pDependencyInfo) override; + virtual void FrameBoundaryANDROID(PFN_vkFrameBoundaryANDROID func, + const VulkanDeviceInfo* device_info, + const VulkanSemaphoreInfo* semaphore_info, + const VulkanImageInfo* image_info, + VulkanInstanceInfo* instance_info, + const graphics::VulkanInstanceTable* instance_table, + const graphics::VulkanDeviceTable* device_table, + application::Application* application) override; + virtual void ProcessSetSwapchainImageStateCommand(const VulkanDeviceInfo* device_info, VulkanSwapchainKHRInfo* swapchain_info, uint32_t last_presented_image, diff --git a/framework/decode/vulkan_object_cleanup_util.cpp b/framework/decode/vulkan_object_cleanup_util.cpp index 394c90c851..8b2df1ab55 100644 --- a/framework/decode/vulkan_object_cleanup_util.cpp +++ b/framework/decode/vulkan_object_cleanup_util.cpp @@ -693,10 +693,12 @@ void FreeAllLiveObjects(CommonObjectInfoTable* &CommonObjectInfoTable::VisitVkDeviceInfo, &CommonObjectInfoTable::RemoveVkDeviceInfo, [&](const VulkanDeviceInfo* object_info) { - assert(object_info != nullptr); + GFXRECON_ASSERT(object_info != nullptr); + GFXRECON_ASSERT(swapchain != nullptr) + auto* device_table = get_device_table(object_info->handle); + swapchain->CleanDeviceResources(object_info->handle, device_table); object_info->allocator->Destroy(); - auto table = get_device_table(object_info->handle); - table->DestroyDevice(object_info->handle, nullptr); + device_table->DestroyDevice(object_info->handle, nullptr); }); // Remove the objects that are not destroyed from the table. diff --git a/framework/decode/vulkan_offscreen_swapchain.cpp b/framework/decode/vulkan_offscreen_swapchain.cpp index ca8b4bd18d..ac54653482 100644 --- a/framework/decode/vulkan_offscreen_swapchain.cpp +++ b/framework/decode/vulkan_offscreen_swapchain.cpp @@ -33,12 +33,7 @@ VkResult VulkanOffscreenSwapchain::CreateSurface(VkResult VkFlags flags, HandlePointerDecoder* surface, const graphics::VulkanInstanceTable* instance_table, - application::Application* application, - const int32_t xpos, - const int32_t ypos, - const uint32_t width, - const uint32_t height, - bool force_windowed) + application::Application* application) { GFXRECON_ASSERT(surface); @@ -291,6 +286,28 @@ VkResult VulkanOffscreenSwapchain::QueuePresentKHR(VkResult return original_result; } +void VulkanOffscreenSwapchain::FrameBoundaryANDROID(PFN_vkFrameBoundaryANDROID func, + const VulkanDeviceInfo* device_info, + const VulkanSemaphoreInfo* semaphore_info, + const VulkanImageInfo* image_info, + VulkanInstanceInfo* instance_info, + const graphics::VulkanInstanceTable* instance_table, + const graphics::VulkanDeviceTable* device_table, + application::Application* application) +{ + GFXRECON_UNREFERENCED_PARAMETER(instance_info); + GFXRECON_UNREFERENCED_PARAMETER(instance_table); + GFXRECON_UNREFERENCED_PARAMETER(device_table); + GFXRECON_UNREFERENCED_PARAMETER(application); + + GFXRECON_ASSERT(device_info != nullptr); + + VkSemaphore semaphore = (semaphore_info == nullptr ? VK_NULL_HANDLE : semaphore_info->handle); + VkImage image = (image_info == nullptr ? VK_NULL_HANDLE : image_info->handle); + + func(device_info->handle, semaphore, image); +} + // queue_info could be nullptr. It means it doesn't specify a VkQueue and use default_queue. Its purpose is to singal // semaphores or fence. All VkQueue should work. VkResult VulkanOffscreenSwapchain::SignalSemaphoresFence(const VulkanQueueInfo* queue_info, diff --git a/framework/decode/vulkan_offscreen_swapchain.h b/framework/decode/vulkan_offscreen_swapchain.h index d3f3d439fc..6bdec10929 100644 --- a/framework/decode/vulkan_offscreen_swapchain.h +++ b/framework/decode/vulkan_offscreen_swapchain.h @@ -41,12 +41,7 @@ class VulkanOffscreenSwapchain : public VulkanVirtualSwapchain VkFlags flags, HandlePointerDecoder* surface, const graphics::VulkanInstanceTable* instance_table, - application::Application* application, - const int32_t xpos, - const int32_t ypos, - const uint32_t width, - const uint32_t height, - bool force_windowed = false) override; + application::Application* application) override; virtual void DestroySurface(PFN_vkDestroySurfaceKHR func, const VulkanInstanceInfo* instance_info, @@ -99,6 +94,15 @@ class VulkanOffscreenSwapchain : public VulkanVirtualSwapchain const VulkanQueueInfo* queue_info, const VkPresentInfoKHR* present_info) override; + virtual void FrameBoundaryANDROID(PFN_vkFrameBoundaryANDROID func, + const VulkanDeviceInfo* device_info, + const VulkanSemaphoreInfo* semaphore_info, + const VulkanImageInfo* image_info, + VulkanInstanceInfo* instance_info, + const graphics::VulkanInstanceTable* instance_table, + const graphics::VulkanDeviceTable* device_table, + application::Application* application) override; + private: const uint32_t default_queue_family_index_{ 0 }; VkQueue default_queue_{ VK_NULL_HANDLE }; // default_queue_family_index_,0 diff --git a/framework/decode/vulkan_replay_consumer_base.cpp b/framework/decode/vulkan_replay_consumer_base.cpp index 17653c3b57..79dc45f90a 100644 --- a/framework/decode/vulkan_replay_consumer_base.cpp +++ b/framework/decode/vulkan_replay_consumer_base.cpp @@ -234,9 +234,16 @@ VulkanReplayConsumerBase::VulkanReplayConsumerBase(std::shared_ptrSetOptions(swapchain_options); if (options_.enable_debug_device_lost) @@ -3490,20 +3497,23 @@ void VulkanReplayConsumerBase::OverrideDestroyDevice( if (device_info != nullptr && device_info->duplicate_source_id == format::kNullHandleId) { - device = device_info->handle; + device = device_info->handle; + const auto device_table = GetDeviceTable(device); if (screenshot_handler_ != nullptr) { util::BeginInjectedCommands(); - - screenshot_handler_->DestroyDeviceResources(device, GetDeviceTable(device)); - + screenshot_handler_->DestroyDeviceResources(device, device_table); util::EndInjectedCommands(); } // free replacer internal vulkan-resources for the device _device_address_replacers.erase(device_info); + // free potential swapchain-resources for the device + GFXRECON_ASSERT(swapchain_) + swapchain_->CleanDeviceResources(device_info->handle, device_table); + device_info->allocator->Destroy(); } @@ -8772,7 +8782,6 @@ VkResult VulkanReplayConsumerBase::OverrideCreateAndroidSurfaceKHR( HandlePointerDecoder* pSurface) { GFXRECON_UNREFERENCED_PARAMETER(func); - GFXRECON_UNREFERENCED_PARAMETER(original_result); GFXRECON_UNREFERENCED_PARAMETER(pAllocator); assert((instance_info != nullptr) && (pCreateInfo != nullptr)); @@ -8787,12 +8796,7 @@ VkResult VulkanReplayConsumerBase::OverrideCreateAndroidSurfaceKHR( replay_create_info->flags, pSurface, GetInstanceTable(instance_info->handle), - application_.get(), - options_.window_topleft_x, - options_.window_topleft_y, - kDefaultWindowWidth, - kDefaultWindowHeight, - options_.force_windowed || options_.force_windowed_origin); + application_.get()); } VkResult VulkanReplayConsumerBase::OverrideCreateWin32SurfaceKHR( @@ -8804,7 +8808,6 @@ VkResult VulkanReplayConsumerBase::OverrideCreateWin32SurfaceKHR( HandlePointerDecoder* pSurface) { GFXRECON_UNREFERENCED_PARAMETER(func); - GFXRECON_UNREFERENCED_PARAMETER(original_result); GFXRECON_UNREFERENCED_PARAMETER(pAllocator); assert((instance_info != nullptr) && (pCreateInfo != nullptr)); @@ -8819,12 +8822,7 @@ VkResult VulkanReplayConsumerBase::OverrideCreateWin32SurfaceKHR( replay_create_info->flags, pSurface, GetInstanceTable(instance_info->handle), - application_.get(), - options_.window_topleft_x, - options_.window_topleft_y, - kDefaultWindowWidth, - kDefaultWindowHeight, - options_.force_windowed || options_.force_windowed_origin); + application_.get()); } VkBool32 VulkanReplayConsumerBase::OverrideGetPhysicalDeviceWin32PresentationSupportKHR( @@ -8853,7 +8851,6 @@ VkResult VulkanReplayConsumerBase::OverrideCreateXcbSurfaceKHR( HandlePointerDecoder* pSurface) { GFXRECON_UNREFERENCED_PARAMETER(func); - GFXRECON_UNREFERENCED_PARAMETER(original_result); GFXRECON_UNREFERENCED_PARAMETER(pAllocator); assert((instance_info != nullptr) && (pCreateInfo != nullptr)); @@ -8868,12 +8865,7 @@ VkResult VulkanReplayConsumerBase::OverrideCreateXcbSurfaceKHR( replay_create_info->flags, pSurface, GetInstanceTable(instance_info->handle), - application_.get(), - options_.window_topleft_x, - options_.window_topleft_y, - kDefaultWindowWidth, - kDefaultWindowHeight, - options_.force_windowed || options_.force_windowed_origin); + application_.get()); } VkBool32 VulkanReplayConsumerBase::OverrideGetPhysicalDeviceXcbPresentationSupportKHR( @@ -8906,7 +8898,6 @@ VkResult VulkanReplayConsumerBase::OverrideCreateXlibSurfaceKHR( HandlePointerDecoder* pSurface) { GFXRECON_UNREFERENCED_PARAMETER(func); - GFXRECON_UNREFERENCED_PARAMETER(original_result); GFXRECON_UNREFERENCED_PARAMETER(pAllocator); assert((instance_info != nullptr) && (pCreateInfo != nullptr)); @@ -8921,12 +8912,7 @@ VkResult VulkanReplayConsumerBase::OverrideCreateXlibSurfaceKHR( replay_create_info->flags, pSurface, GetInstanceTable(instance_info->handle), - application_.get(), - options_.window_topleft_x, - options_.window_topleft_y, - kDefaultWindowWidth, - kDefaultWindowHeight, - options_.force_windowed || options_.force_windowed_origin); + application_.get()); } VkBool32 VulkanReplayConsumerBase::OverrideGetPhysicalDeviceXlibPresentationSupportKHR( @@ -8959,7 +8945,6 @@ VkResult VulkanReplayConsumerBase::OverrideCreateWaylandSurfaceKHR( HandlePointerDecoder* pSurface) { GFXRECON_UNREFERENCED_PARAMETER(func); - GFXRECON_UNREFERENCED_PARAMETER(original_result); GFXRECON_UNREFERENCED_PARAMETER(pAllocator); assert((instance_info != nullptr) && (pCreateInfo != nullptr)); @@ -8974,12 +8959,7 @@ VkResult VulkanReplayConsumerBase::OverrideCreateWaylandSurfaceKHR( replay_create_info->flags, pSurface, GetInstanceTable(instance_info->handle), - application_.get(), - options_.window_topleft_x, - options_.window_topleft_y, - kDefaultWindowWidth, - kDefaultWindowHeight, - options_.force_windowed || options_.force_windowed_origin); + application_.get()); } VkResult VulkanReplayConsumerBase::OverrideCreateDisplayPlaneSurfaceKHR( @@ -8991,7 +8971,6 @@ VkResult VulkanReplayConsumerBase::OverrideCreateDisplayPlaneSurfaceKHR( HandlePointerDecoder* pSurface) { GFXRECON_UNREFERENCED_PARAMETER(func); - GFXRECON_UNREFERENCED_PARAMETER(original_result); GFXRECON_UNREFERENCED_PARAMETER(pAllocator); assert((instance_info != nullptr) && (pCreateInfo != nullptr)); @@ -9006,12 +8985,7 @@ VkResult VulkanReplayConsumerBase::OverrideCreateDisplayPlaneSurfaceKHR( replay_create_info->flags, pSurface, GetInstanceTable(instance_info->handle), - application_.get(), - options_.window_topleft_x, - options_.window_topleft_y, - kDefaultWindowWidth, - kDefaultWindowHeight, - options_.force_windowed || options_.force_windowed_origin); + application_.get()); } VkResult VulkanReplayConsumerBase::OverrideCreateHeadlessSurfaceEXT( @@ -9023,7 +8997,6 @@ VkResult VulkanReplayConsumerBase::OverrideCreateHeadlessSurfaceEXT( HandlePointerDecoder* pSurface) { GFXRECON_UNREFERENCED_PARAMETER(func); - GFXRECON_UNREFERENCED_PARAMETER(original_result); GFXRECON_UNREFERENCED_PARAMETER(pAllocator); assert((instance_info != nullptr) && (pCreateInfo != nullptr)); @@ -9038,12 +9011,7 @@ VkResult VulkanReplayConsumerBase::OverrideCreateHeadlessSurfaceEXT( replay_create_info->flags, pSurface, GetInstanceTable(instance_info->handle), - application_.get(), - options_.window_topleft_x, - options_.window_topleft_y, - kDefaultWindowWidth, - kDefaultWindowHeight, - options_.force_windowed || options_.force_windowed_origin); + application_.get()); } VkBool32 VulkanReplayConsumerBase::OverrideGetPhysicalDeviceWaylandPresentationSupportKHR( @@ -9074,7 +9042,6 @@ VkResult VulkanReplayConsumerBase::OverrideCreateMetalSurfaceEXT( HandlePointerDecoder* pSurface) { GFXRECON_UNREFERENCED_PARAMETER(func); - GFXRECON_UNREFERENCED_PARAMETER(original_result); GFXRECON_UNREFERENCED_PARAMETER(pAllocator); assert((instance_info != nullptr) && (pCreateInfo != nullptr)); @@ -9089,12 +9056,7 @@ VkResult VulkanReplayConsumerBase::OverrideCreateMetalSurfaceEXT( replay_create_info->flags, pSurface, GetInstanceTable(instance_info->handle), - application_.get(), - options_.window_topleft_x, - options_.window_topleft_y, - kDefaultWindowWidth, - kDefaultWindowHeight, - options_.force_windowed || options_.force_windowed_origin); + application_.get()); } void VulkanReplayConsumerBase::OverrideDestroySurfaceKHR( @@ -10452,11 +10414,9 @@ void VulkanReplayConsumerBase::OverrideFrameBoundaryANDROID(PFN_vkFrameBoundaryA const VulkanSemaphoreInfo* semaphore_info, const VulkanImageInfo* image_info) { - GFXRECON_ASSERT((device_info != nullptr)); + GFXRECON_ASSERT(device_info != nullptr); - VkDevice device = device_info->handle; - VkSemaphore semaphore = semaphore_info ? semaphore_info->handle : VK_NULL_HANDLE; - VkImage image = image_info ? image_info->handle : VK_NULL_HANDLE; + const graphics::VulkanDeviceTable* device_table = GetDeviceTable(device_info->handle); if (screenshot_handler_ != nullptr && !options_.screenshot_ignore_frameBoundaryAndroid) { @@ -10485,10 +10445,10 @@ void VulkanReplayConsumerBase::OverrideFrameBoundaryANDROID(PFN_vkFrameBoundaryA screenshot_handler_->WriteImage(filename_prefix, device_info, - GetDeviceTable(device), + device_table, memory_properties, device_info->allocator.get(), - image, + image_info->handle, image_info->format, image_info->extent.width, image_info->extent.height, @@ -10502,7 +10462,18 @@ void VulkanReplayConsumerBase::OverrideFrameBoundaryANDROID(PFN_vkFrameBoundaryA util::EndInjectedCommands(); } - func(device, semaphore, image); + CommonObjectInfoTable& object_info_table = GetObjectInfoTable(); + + VulkanPhysicalDeviceInfo* physical_device_info = object_info_table.GetVkPhysicalDeviceInfo(device_info->parent_id); + GFXRECON_ASSERT(physical_device_info != nullptr); + + VulkanInstanceInfo* instance_info = object_info_table.GetVkInstanceInfo(physical_device_info->parent_id); + GFXRECON_ASSERT(instance_info != nullptr); + + const graphics::VulkanInstanceTable* instance_table = GetInstanceTable(instance_info->handle); + + swapchain_->FrameBoundaryANDROID( + func, device_info, semaphore_info, image_info, instance_info, instance_table, device_table, application_.get()); } // We want to allow skipping the query for tool properties because the capture layer actually adds this extension diff --git a/framework/decode/vulkan_swapchain.cpp b/framework/decode/vulkan_swapchain.cpp index 132ee146d3..1c41310863 100644 --- a/framework/decode/vulkan_swapchain.cpp +++ b/framework/decode/vulkan_swapchain.cpp @@ -52,12 +52,7 @@ VkResult VulkanSwapchain::CreateSurface(VkResult ori VkFlags flags, HandlePointerDecoder* surface, const graphics::VulkanInstanceTable* instance_table, - application::Application* application, - const int32_t xpos, - const int32_t ypos, - const uint32_t width, - const uint32_t height, - bool force_windowed) + application::Application* application) { assert(instance_info != nullptr); @@ -98,7 +93,12 @@ VkResult VulkanSwapchain::CreateSurface(VkResult ori // By default, the created window will be automatically in full screen mode, and its location will be set to 0,0 // if the requested size exceeds or equals the current screen size. If the user specifies "--fw" or "--fwo" this // behavior will change, and replay will instead render in windowed mode. - auto* window = window_factory->Create(xpos, ypos, width, height, force_windowed); + auto* window = + window_factory->Create(swapchain_options_.window_topleft_x, + swapchain_options_.window_topleft_y, + swapchain_options_.windowed_width, + swapchain_options_.windowed_height, + swapchain_options_.force_windowed || swapchain_options_.force_windowed_origin); if (window == nullptr) { @@ -115,9 +115,8 @@ VkResult VulkanSwapchain::CreateSurface(VkResult ori if ((result == VK_SUCCESS) && (replay_surface != nullptr)) { - auto surface_id = surface->GetPointer(); auto surface_info = reinterpret_cast(surface->GetConsumerData(0)); - assert((surface_id != nullptr) && (surface_info != nullptr)); + assert(surface_info != nullptr); assert(!surface_info->surface_creation_skipped); surface_info->window = window; diff --git a/framework/decode/vulkan_swapchain.h b/framework/decode/vulkan_swapchain.h index 7ac04e0f0d..b98b877b9c 100644 --- a/framework/decode/vulkan_swapchain.h +++ b/framework/decode/vulkan_swapchain.h @@ -39,27 +39,29 @@ GFXRECON_BEGIN_NAMESPACE(gfxrecon) GFXRECON_BEGIN_NAMESPACE(decode) -const int32_t kDefaultWindowPositionX = 0; -const int32_t kDefaultWindowPositionY = 0; -const uint32_t kDefaultWindowWidth = 320; -const uint32_t kDefaultWindowHeight = 240; - -class ScreenshotHandler; - struct VulkanSwapchainOptions { - bool virtual_swapchain_skip_blit{ false }; - int32_t surface_index{ -1 }; - bool offscreen_swapchain_frame_boundary{ false }; + bool force_windowed{ false }; + uint32_t windowed_width{ 320 }; + uint32_t windowed_height{ 240 }; + bool force_windowed_origin{ false }; + int32_t window_topleft_x{ 0 }; + int32_t window_topleft_y{ 0 }; + bool virtual_swapchain_skip_blit{ false }; + int32_t surface_index{ -1 }; + bool offscreen_swapchain_frame_boundary{ false }; + util::PresentModeOption present_mode_option{ util::PresentModeOption::kCapture }; }; class VulkanSwapchain { public: - virtual ~VulkanSwapchain() {} + virtual ~VulkanSwapchain() = default; virtual void Clean(); + virtual void CleanDeviceResources(VkDevice, const graphics::VulkanDeviceTable*) {} + void SetOptions(const VulkanSwapchainOptions& options) { swapchain_options_ = options; } virtual VkResult CreateSurface(VkResult original_result, @@ -68,12 +70,7 @@ class VulkanSwapchain VkFlags flags, HandlePointerDecoder* surface, const graphics::VulkanInstanceTable* instance_table, - application::Application* application, - const int32_t xpos, - const int32_t ypos, - const uint32_t width, - const uint32_t height, - bool force_windowed = false); + application::Application* application); virtual void DestroySurface(PFN_vkDestroySurfaceKHR func, const VulkanInstanceInfo* instance_info, @@ -166,6 +163,15 @@ class VulkanSwapchain VulkanCommandBufferInfo* command_buffer_info, const VkDependencyInfo* pDependencyInfo) = 0; + virtual void FrameBoundaryANDROID(PFN_vkFrameBoundaryANDROID func, + const VulkanDeviceInfo* device_info, + const VulkanSemaphoreInfo* semaphore_info, + const VulkanImageInfo* image_info, + VulkanInstanceInfo* instance_info, + const graphics::VulkanInstanceTable* instance_table, + const graphics::VulkanDeviceTable* device_table, + application::Application* application) = 0; + virtual void ProcessSetSwapchainImageStateCommand(const VulkanDeviceInfo* device_info, VulkanSwapchainKHRInfo* swapchain_info, uint32_t last_presented_image, diff --git a/framework/decode/vulkan_virtual_swapchain.cpp b/framework/decode/vulkan_virtual_swapchain.cpp index d996c5a68c..3879654a8a 100644 --- a/framework/decode/vulkan_virtual_swapchain.cpp +++ b/framework/decode/vulkan_virtual_swapchain.cpp @@ -27,11 +27,47 @@ #include "util/callbacks.h" #include "graphics/vulkan_resources_util.h" #include "vulkan/vulkan_core.h" -#include GFXRECON_BEGIN_NAMESPACE(gfxrecon) GFXRECON_BEGIN_NAMESPACE(decode) +void VulkanVirtualSwapchain::CleanDeviceResources(VkDevice device, const graphics::VulkanDeviceTable* device_table) +{ + GFXRECON_ASSERT(device != VK_NULL_HANDLE); + GFXRECON_ASSERT(device_table != nullptr); + + // cleanup offscreen-frame-boundary (OFB) assets + if (auto it = ofb_data_.find(device); it != ofb_data_.end()) + { + const auto& ofb_data = it->second; + + for (auto semaphore : ofb_data.acquire_semaphores) + { + device_table->DestroySemaphore(device, semaphore, nullptr); + } + + for (auto& img_data : ofb_data.image_datas) + { + if (img_data.copy_command_buffer != VK_NULL_HANDLE) + { + device_table->FreeCommandBuffers(device, ofb_data.command_pool, 1, &img_data.copy_command_buffer); + } + + if (img_data.copy_semaphore != VK_NULL_HANDLE) + { + device_table->DestroySemaphore(device, img_data.copy_semaphore, nullptr); + } + } + + if (ofb_data.command_pool != VK_NULL_HANDLE) + { + device_table->DestroyCommandPool(device, ofb_data.command_pool, nullptr); + } + + ofb_data_.erase(device); + } +} + bool VulkanVirtualSwapchain::AddSwapchainResourceData(VkSwapchainKHR swapchain) { auto data = std::make_unique(); @@ -58,7 +94,7 @@ VkResult VulkanVirtualSwapchain::CreateSwapchainKHR(VkResult if (device_info != nullptr) { - device = device_info->handle; + device = device_info->handle; physical_device = device_info->parent; } device_table_ = device_table; @@ -806,10 +842,10 @@ VkResult VulkanVirtualSwapchain::QueuePresentKHR(VkResult uint32_t capture_image_index = capture_image_indices[i]; uint32_t replay_image_index = present_info->pImageIndices[i]; - auto aspect_mask = graphics::GetFormatAspects(swapchain_info->format); - subresource.aspectMask = aspect_mask; - initial_barrier_virtual_image.subresourceRange.aspectMask = aspect_mask; - final_barrier_virtual_image.subresourceRange.aspectMask = aspect_mask; + auto aspect_mask = graphics::GetFormatAspects(swapchain_info->format); + subresource.aspectMask = aspect_mask; + initial_barrier_virtual_image.subresourceRange.aspectMask = aspect_mask; + final_barrier_virtual_image.subresourceRange.aspectMask = aspect_mask; initial_barrier_swapchain_image.subresourceRange.aspectMask = aspect_mask; final_barrier_swapchain_image.subresourceRange.aspectMask = aspect_mask; @@ -1081,6 +1117,303 @@ void VulkanVirtualSwapchain::CmdPipelineBarrier2(PFN_vkCmdPipelineBarrier2 func, func(command_buffer, pDependencyInfo); } +void VulkanVirtualSwapchain::FrameBoundaryANDROID(PFN_vkFrameBoundaryANDROID func, + const VulkanDeviceInfo* device_info, + const VulkanSemaphoreInfo* semaphore_info, + const VulkanImageInfo* image_info, + VulkanInstanceInfo* instance_info, + const graphics::VulkanInstanceTable* instance_table, + const graphics::VulkanDeviceTable* device_table, + application::Application* application) +{ + GFXRECON_ASSERT(instance_info != nullptr && instance_table != nullptr && device_info != nullptr && + device_table != nullptr && application != nullptr); + + VkResult result = VK_SUCCESS; + VkDevice device = device_info->handle; + VkImage image = (image_info == nullptr ? VK_NULL_HANDLE : image_info->handle); + VkSemaphore semaphore = (semaphore_info == nullptr ? VK_NULL_HANDLE : semaphore_info->handle); + + // If there is no image to present, do nothing + if (image == VK_NULL_HANDLE) + { + return; + } + + util::BeginInjectedCommands(); + + // Create a new surface if necessary + GFXRECON_ASSERT(device != VK_NULL_HANDLE); + auto& ofb_data = ofb_data_[device]; + + if (ofb_data.surface_info.handle == VK_NULL_HANDLE) + { + // Create a window and surface + + ofb_data.surface_ptr.SetHandleLength(1); + ofb_data.surface_ptr.SetConsumerData(0, &ofb_data.surface_info); + + result = CreateSurface(VK_SUCCESS, + instance_info, + VK_KHR_ANDROID_SURFACE_EXTENSION_NAME, + 0, + &ofb_data.surface_ptr, + instance_table, + application); + GFXRECON_ASSERT(result == VK_SUCCESS); + + ofb_data.surface_info.handle = *ofb_data.surface_ptr.GetHandlePointer(); + ofb_data.surface_info.window->SetSize(image_info->extent.width, image_info->extent.height); + + // Retrieve the queue that will be used for presentation/image copy and create a command pool + + device_table->GetDeviceQueue(device, 0, 0, &ofb_data.queue); + + VkCommandPoolCreateInfo command_pool_create_info; + command_pool_create_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + command_pool_create_info.pNext = nullptr; + command_pool_create_info.flags = + VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + command_pool_create_info.queueFamilyIndex = 0; + + result = device_table->CreateCommandPool(device, &command_pool_create_info, nullptr, &ofb_data.command_pool); + GFXRECON_ASSERT(result == VK_SUCCESS); + } + + // Create/Re-create a swapchain if necessary + + VkExtent2D window_size = ofb_data.surface_info.window->GetSize(); + if (image_info->extent.width != window_size.width || image_info->extent.height != window_size.height || + ofb_data.swapchain == VK_NULL_HANDLE) + { + // Create a swapchain + + VkSwapchainCreateInfoKHR swapchain_create_info; + swapchain_create_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + swapchain_create_info.pNext = nullptr; + swapchain_create_info.flags = 0; + swapchain_create_info.surface = ofb_data.surface_info.handle; + swapchain_create_info.minImageCount = 3; + swapchain_create_info.imageFormat = VK_FORMAT_B8G8R8A8_UNORM; + swapchain_create_info.imageColorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; + swapchain_create_info.imageExtent.width = image_info->extent.width; + swapchain_create_info.imageExtent.height = image_info->extent.height; + swapchain_create_info.imageArrayLayers = 1; + swapchain_create_info.imageUsage = VK_IMAGE_USAGE_TRANSFER_DST_BIT; + swapchain_create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + swapchain_create_info.queueFamilyIndexCount = 0; + swapchain_create_info.pQueueFamilyIndices = nullptr; + swapchain_create_info.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; + swapchain_create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + swapchain_create_info.clipped = VK_TRUE; + swapchain_create_info.oldSwapchain = ofb_data.swapchain; + + switch (swapchain_options_.present_mode_option) + { + case util::PresentModeOption::kCapture: + // There is no corresponding present-mode for "capture", so we fall back to FIFO. + swapchain_create_info.presentMode = VK_PRESENT_MODE_FIFO_KHR; + break; + case util::PresentModeOption::kImmediate: + swapchain_create_info.presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR; + break; + case util::PresentModeOption::kMailbox: + swapchain_create_info.presentMode = VK_PRESENT_MODE_MAILBOX_KHR; + break; + case util::PresentModeOption::kFifo: + swapchain_create_info.presentMode = VK_PRESENT_MODE_FIFO_KHR; + break; + case util::PresentModeOption::kFifoRelaxed: + swapchain_create_info.presentMode = VK_PRESENT_MODE_FIFO_RELAXED_KHR; + break; + default: + // Falling here means a case has been forgotten, it is an implementation error. + assert(false); + break; + } + + VkSwapchainKHR swapchain; + result = device_table->CreateSwapchainKHR(device, &swapchain_create_info, nullptr, &swapchain); + GFXRECON_ASSERT(result == VK_SUCCESS); + + // Destroy old swapchain resources if necessary + + if (ofb_data.swapchain != VK_NULL_HANDLE) + { + // We need to be sure that swapchain resources are not in use anymore + result = device_table->QueueWaitIdle(ofb_data.queue); + GFXRECON_ASSERT(result == VK_SUCCESS); + + for (VkSemaphore acquire_semaphore : ofb_data.acquire_semaphores) + { + device_table->DestroySemaphore(device, acquire_semaphore, nullptr); + } + for (auto& image_data : ofb_data.image_datas) + { + device_table->DestroySemaphore(device, image_data.copy_semaphore, nullptr); + device_table->FreeCommandBuffers(device, ofb_data.command_pool, 1, &image_data.copy_command_buffer); + } + device_table->DestroySwapchainKHR(device, ofb_data.swapchain, nullptr); + + ofb_data.acquire_semaphores.clear(); + ofb_data.image_datas.clear(); + } + + ofb_data.swapchain = swapchain; + + // Get swapchain images and create swapchain resources + + uint32_t image_count = 0; + result = device_table->GetSwapchainImagesKHR(device, ofb_data.swapchain, &image_count, nullptr); + GFXRECON_ASSERT(result == VK_SUCCESS); + + std::vector swapchain_images(image_count, VK_NULL_HANDLE); + result = device_table->GetSwapchainImagesKHR(device, ofb_data.swapchain, &image_count, swapchain_images.data()); + GFXRECON_ASSERT((result == VK_SUCCESS) && (swapchain_images.size() == image_count)); + + ofb_data.acquire_semaphores.resize(image_count); + ofb_data.image_datas.resize(image_count); + + VkSemaphoreCreateInfo semaphore_create_info; + semaphore_create_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + semaphore_create_info.pNext = nullptr; + semaphore_create_info.flags = 0; + + VkCommandBufferAllocateInfo command_buffer_alloc_info; + command_buffer_alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + command_buffer_alloc_info.pNext = nullptr; + command_buffer_alloc_info.commandPool = ofb_data.command_pool; + command_buffer_alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + command_buffer_alloc_info.commandBufferCount = 1; + + for (uint32_t i = 0; i < image_count; ++i) + { + ofb_data.image_datas[i].image = swapchain_images[i]; + + result = + device_table->CreateSemaphore(device, &semaphore_create_info, nullptr, &ofb_data.acquire_semaphores[i]); + GFXRECON_ASSERT(result == VK_SUCCESS); + + result = device_table->CreateSemaphore( + device, &semaphore_create_info, nullptr, &ofb_data.image_datas[i].copy_semaphore); + GFXRECON_ASSERT(result == VK_SUCCESS); + + result = device_table->AllocateCommandBuffers( + device, &command_buffer_alloc_info, &ofb_data.image_datas[i].copy_command_buffer); + GFXRECON_ASSERT(result == VK_SUCCESS); + } + } + + // Acquire next image from the swapchain + + VkSemaphore& acquire_semaphore = ofb_data.acquire_semaphores[ofb_data.acquire_index]; + uint32_t swapchain_image_index = 0; + + result = device_table->AcquireNextImageKHR( + device, ofb_data.swapchain, UINT64_MAX, acquire_semaphore, VK_NULL_HANDLE, &swapchain_image_index); + + auto& image_data = ofb_data.image_datas[swapchain_image_index]; + + ofb_data.acquire_index = (ofb_data.acquire_index + 1) % ofb_data.acquire_semaphores.size(); + + std::vector submit_wait_semaphores = { acquire_semaphore }; + if (semaphore != VK_NULL_HANDLE) + { + submit_wait_semaphores.push_back(semaphore); + } + + // Copy frame boundary image to present image + + if (!swapchain_options_.virtual_swapchain_skip_blit) + { + // Record command buffer for copy + + result = device_table->ResetCommandBuffer(image_data.copy_command_buffer, 0); + GFXRECON_ASSERT(result == VK_SUCCESS); + + VkCommandBufferBeginInfo command_buffer_begin_info; + command_buffer_begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + command_buffer_begin_info.pNext = nullptr; + command_buffer_begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + command_buffer_begin_info.pInheritanceInfo = nullptr; + + result = device_table->BeginCommandBuffer(image_data.copy_command_buffer, &command_buffer_begin_info); + GFXRECON_ASSERT(result == VK_SUCCESS); + + VkImageCopy image_copy; + image_copy.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + image_copy.srcSubresource.mipLevel = 0; + image_copy.srcSubresource.baseArrayLayer = 0; + image_copy.srcSubresource.layerCount = 1; + image_copy.srcOffset.x = 0; + image_copy.srcOffset.y = 0; + image_copy.srcOffset.z = 0; + image_copy.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + image_copy.dstSubresource.mipLevel = 0; + image_copy.dstSubresource.baseArrayLayer = 0; + image_copy.dstSubresource.layerCount = 1; + image_copy.dstOffset.x = 0; + image_copy.dstOffset.y = 0; + image_copy.dstOffset.z = 0; + image_copy.extent = image_info->extent; + + device_table->CmdCopyImage(image_data.copy_command_buffer, + image_info->handle, + VK_IMAGE_LAYOUT_GENERAL, + image_data.image, + VK_IMAGE_LAYOUT_GENERAL, + 1, + &image_copy); + + result = device_table->EndCommandBuffer(image_data.copy_command_buffer); + GFXRECON_ASSERT(result == VK_SUCCESS); + + // Submit copy command buffer + + std::vector submit_wait_stages(submit_wait_semaphores.size(), + VK_PIPELINE_STAGE_TRANSFER_BIT); + + VkSubmitInfo submit_info; + submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submit_info.pNext = nullptr; + submit_info.waitSemaphoreCount = submit_wait_semaphores.size(); + submit_info.pWaitSemaphores = submit_wait_semaphores.data(); + submit_info.pWaitDstStageMask = submit_wait_stages.data(); + submit_info.commandBufferCount = 1; + submit_info.pCommandBuffers = &image_data.copy_command_buffer; + submit_info.signalSemaphoreCount = 1; + submit_info.pSignalSemaphores = &image_data.copy_semaphore; + + result = device_table->QueueSubmit(ofb_data.queue, 1, &submit_info, VK_NULL_HANDLE); + GFXRECON_ASSERT(result == VK_SUCCESS); + } + + // Present image + + VkPresentInfoKHR present_info; + present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + present_info.pNext = nullptr; + present_info.swapchainCount = 1; + present_info.pSwapchains = &ofb_data.swapchain; + present_info.pImageIndices = &swapchain_image_index; + present_info.pResults = nullptr; + + if (swapchain_options_.virtual_swapchain_skip_blit) + { + present_info.waitSemaphoreCount = submit_wait_semaphores.size(); + present_info.pWaitSemaphores = submit_wait_semaphores.data(); + } + else + { + present_info.waitSemaphoreCount = 1; + present_info.pWaitSemaphores = &image_data.copy_semaphore; + } + + result = device_table->QueuePresentKHR(ofb_data.queue, &present_info); + + util::EndInjectedCommands(); +} + VkResult VulkanVirtualSwapchain::CreateVirtualSwapchainImage(const VulkanDeviceInfo* device_info, const VkImageCreateInfo& image_create_info, VirtualImage& image) diff --git a/framework/decode/vulkan_virtual_swapchain.h b/framework/decode/vulkan_virtual_swapchain.h index 1a00a98b39..21840fc23c 100644 --- a/framework/decode/vulkan_virtual_swapchain.h +++ b/framework/decode/vulkan_virtual_swapchain.h @@ -31,7 +31,9 @@ GFXRECON_BEGIN_NAMESPACE(decode) class VulkanVirtualSwapchain : public VulkanSwapchain { public: - virtual ~VulkanVirtualSwapchain() override {} + ~VulkanVirtualSwapchain() override = default; + + void CleanDeviceResources(VkDevice device, const graphics::VulkanDeviceTable* device_table) override; virtual VkResult CreateSwapchainKHR(VkResult original_result, PFN_vkCreateSwapchainKHR func, @@ -109,6 +111,15 @@ class VulkanVirtualSwapchain : public VulkanSwapchain VulkanCommandBufferInfo* command_buffer_info, const VkDependencyInfo* pDependencyInfo) override; + virtual void FrameBoundaryANDROID(PFN_vkFrameBoundaryANDROID func, + const VulkanDeviceInfo* device_info, + const VulkanSemaphoreInfo* semaphore_info, + const VulkanImageInfo* image_info, + VulkanInstanceInfo* instance_info, + const graphics::VulkanInstanceTable* instance_table, + const graphics::VulkanDeviceTable* device_table, + application::Application* application) override; + virtual void ProcessSetSwapchainImageStateCommand(const VulkanDeviceInfo* device_info, VulkanSwapchainKHRInfo* swapchain_info, uint32_t last_presented_image, @@ -170,6 +181,32 @@ class VulkanVirtualSwapchain : public VulkanSwapchain // Create an unordered map to associate the swapchain resource data with a particular Vulkan swapchain std::unordered_map> swapchain_resources_; + + // This structure contains the data tied to a swapchain image created for presenting offscreen frame boundaries + struct OFBSwapchainImageData + { + VkImage image{ VK_NULL_HANDLE }; + VkCommandBuffer copy_command_buffer{ VK_NULL_HANDLE }; + VkSemaphore copy_semaphore{ VK_NULL_HANDLE }; + }; + + // This structure contains the custom surface, swapchain, and swapchain images data created and used by the virtual + // swapchain when encountering an offscreen frame boundary (like vkFrameBoundaryANDROID) + struct OFBData + { + VulkanSurfaceKHRInfo surface_info{}; + HandlePointerDecoder surface_ptr{}; + VkQueue queue{ VK_NULL_HANDLE }; + VkCommandPool command_pool{ VK_NULL_HANDLE }; + VkSwapchainKHR swapchain{ VK_NULL_HANDLE }; + + std::vector acquire_semaphores{}; + uint32_t acquire_index{ 0 }; + + std::vector image_datas{}; + }; + + std::unordered_map ofb_data_; }; GFXRECON_END_NAMESPACE(decode) diff --git a/tools/replay/replay_settings.h b/tools/replay/replay_settings.h index 20f444281b..e3ae86b508 100644 --- a/tools/replay/replay_settings.h +++ b/tools/replay/replay_settings.h @@ -247,7 +247,9 @@ static void PrintUsage(const char* exe_name) GFXRECON_WRITE_CONSOLE(" \t\t %s\tVirtual Swapchain of images which match", kSwapchainVirtual); GFXRECON_WRITE_CONSOLE(" \t\t \tthe swapchain in effect at capture time and"); GFXRECON_WRITE_CONSOLE(" \t\t \twhich are copied to the underlying swapchain of the"); - GFXRECON_WRITE_CONSOLE(" \t\t \timplementation being replayed on. This is default."); + GFXRECON_WRITE_CONSOLE(" \t\t \timplementation being replayed on. Also displays"); + GFXRECON_WRITE_CONSOLE(" \t\t \toffscreen frame boundaries to an additional window."); + GFXRECON_WRITE_CONSOLE(" \t\t \tThis is only available and default for Vulkan."); GFXRECON_WRITE_CONSOLE(" \t\t %s\tUse the swapchain indices stored in the ", kSwapchainCaptured); GFXRECON_WRITE_CONSOLE(" \t\t \tcapture directly on the swapchain setup for replay."); GFXRECON_WRITE_CONSOLE(" \t\t %s\tDisable creating swapchains, surfaces", kSwapchainOffscreen);