From 7cb928b5b5807975a0d864c5796d4514c040aaaf Mon Sep 17 00:00:00 2001 From: Panagiotis Apostolou Date: Fri, 13 Sep 2024 12:00:26 +0300 Subject: [PATCH 1/7] Expose 2 symbols from android_main MainGetCurrentBlockIndex and MainGetLoadingTrimmedState whould de discoveralbe through dlopen + dlsym from the android replay native .so: - MainGetCurrentBlockIndex(): Returns the current block index as calculated from the file processor - MainGetLoadingTrimmedState(): Returns wheter file processor is between a FrameStateBegin/FrameStateEnd markers pair --- framework/decode/file_processor.cpp | 6 ++++-- framework/decode/file_processor.h | 6 ++++++ tools/replay/android_main.cpp | 22 ++++++++++++++++++---- 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/framework/decode/file_processor.cpp b/framework/decode/file_processor.cpp index ad7cba467..9c219171d 100644 --- a/framework/decode/file_processor.cpp +++ b/framework/decode/file_processor.cpp @@ -47,7 +47,7 @@ const uint32_t kFirstFrame = 0; FileProcessor::FileProcessor() : current_frame_number_(kFirstFrame), error_state_(kErrorInvalidFileDescriptor), bytes_read_(0), annotation_handler_(nullptr), compressor_(nullptr), block_index_(0), api_call_index_(0), block_limit_(0), - capture_uses_frame_markers_(false), first_frame_(kFirstFrame + 1) + capture_uses_frame_markers_(false), first_frame_(kFirstFrame + 1), loading_trimmed_capture_state_(false) {} FileProcessor::FileProcessor(uint64_t block_limit) : FileProcessor() @@ -2167,11 +2167,13 @@ bool FileProcessor::ProcessStateMarker(const format::BlockHeader& block_header, if (marker_type == format::kBeginMarker) { GFXRECON_LOG_INFO("Loading state for captured frame %" PRId64, frame_number); + loading_trimmed_capture_state_ = true; } else if (marker_type == format::kEndMarker) { GFXRECON_LOG_INFO("Finished loading state for captured frame %" PRId64, frame_number); - first_frame_ = frame_number; + first_frame_ = frame_number; + loading_trimmed_capture_state_ = false; } for (auto decoder : decoders_) diff --git a/framework/decode/file_processor.h b/framework/decode/file_processor.h index dfced5c17..ec1b3cc9f 100644 --- a/framework/decode/file_processor.h +++ b/framework/decode/file_processor.h @@ -98,6 +98,10 @@ class FileProcessor uint32_t GetCurrentFrameNumber() const { return current_frame_number_; } + uint64_t GetCurrentBlockIndex() const { return block_index_; } + + bool GetLoadingTrimmedState() const { return loading_trimmed_capture_state_; } + uint64_t GetNumBytesRead() const { return bytes_read_; } Error GetErrorState() const { return error_state_; } @@ -238,6 +242,7 @@ class FileProcessor bool enable_print_block_info_{ false }; int64_t block_index_from_{ 0 }; int64_t block_index_to_{ 0 }; + bool loading_trimmed_capture_state_; struct ActiveFiles { @@ -271,6 +276,7 @@ class FileProcessor return file_stack_.back(); } + }; GFXRECON_END_NAMESPACE(decode) diff --git a/tools/replay/android_main.cpp b/tools/replay/android_main.cpp index 1abf506ea..d95ed25bf 100644 --- a/tools/replay/android_main.cpp +++ b/tools/replay/android_main.cpp @@ -59,6 +59,21 @@ void ProcessAppCmd(struct android_app* app, int32_t cmd); int32_t ProcessInputEvent(struct android_app* app, AInputEvent* event); void DestroyActivity(struct android_app* app); +static std::unique_ptr file_processor; + +extern "C" +{ + uint64_t MainGetCurrentBlockIndex() + { + return file_processor->GetCurrentBlockIndex(); + } + + bool MainGetLoadingTrimmedState() + { + return file_processor->GetLoadingTrimmedState(); + } +} + void android_main(struct android_app* app) { gfxrecon::util::Log::Init(); @@ -102,10 +117,9 @@ void android_main(struct android_app* app) try { - std::unique_ptr file_processor = - arg_parser.IsOptionSet(kPreloadMeasurementRangeOption) - ? std::make_unique() - : std::make_unique(); + file_processor = arg_parser.IsOptionSet(kPreloadMeasurementRangeOption) + ? std::make_unique() + : std::make_unique(); if (!file_processor->Initialize(filename)) { From 8a80c414560c8599a4d0ce4eb8deb200448069d2 Mon Sep 17 00:00:00 2001 From: Panagiotis Apostolou Date: Mon, 16 Sep 2024 12:04:27 +0300 Subject: [PATCH 2/7] Limit usage of device table in vulkan replay consumer --- .../decode/vulkan_replay_consumer_base.cpp | 59 +++++++++---------- .../decode/vulkan_replay_consumer_base.h | 9 ++- .../generated_vulkan_replay_consumer.cpp | 6 +- .../vulkan_replay_consumer_body_generator.py | 2 +- 4 files changed, 38 insertions(+), 38 deletions(-) diff --git a/framework/decode/vulkan_replay_consumer_base.cpp b/framework/decode/vulkan_replay_consumer_base.cpp index 738982005..a0d68c717 100644 --- a/framework/decode/vulkan_replay_consumer_base.cpp +++ b/framework/decode/vulkan_replay_consumer_base.cpp @@ -7637,8 +7637,6 @@ VkResult VulkanReplayConsumerBase::OverrideCreateAccelerationStructureKHR( auto capture_id = (*pAccelerationStructureKHR->GetPointer()); auto replay_create_info = pCreateInfo->GetPointer(); VkDevice device = device_info->handle; - auto device_table = GetDeviceTable(device); - assert(device_table != nullptr); if (device_info->property_feature_info.feature_accelerationStructureCaptureReplay) { @@ -7657,13 +7655,11 @@ VkResult VulkanReplayConsumerBase::OverrideCreateAccelerationStructureKHR( capture_id); } - result = device_table->CreateAccelerationStructureKHR( - device, &modified_create_info, GetAllocationCallbacks(pAllocator), replay_accel_struct); + result = func(device, &modified_create_info, GetAllocationCallbacks(pAllocator), replay_accel_struct); } else { - result = device_table->CreateAccelerationStructureKHR( - device, replay_create_info, GetAllocationCallbacks(pAllocator), replay_accel_struct); + result = func(device, replay_create_info, GetAllocationCallbacks(pAllocator), replay_accel_struct); } return result; @@ -7822,7 +7818,6 @@ VkResult VulkanReplayConsumerBase::OverrideCreateRayTracingPipelinesKHR( VkResult result = VK_SUCCESS; VkDevice device = device_info->handle; - auto device_table = GetDeviceTable(device); const VkRayTracingPipelineCreateInfoKHR* in_pCreateInfos = pCreateInfos->GetPointer(); const VkAllocationCallbacks* in_pAllocator = GetAllocationCallbacks(pAllocator); VkPipeline* out_pPipelines = pPipelines->GetHandlePointer(); @@ -7916,13 +7911,13 @@ VkResult VulkanReplayConsumerBase::OverrideCreateRayTracingPipelinesKHR( created_pipelines = out_pPipelines; } - result = device_table->CreateRayTracingPipelinesKHR(device, - in_deferredOperation, - in_pipelineCache, - createInfoCount, - modified_create_infos.data(), - in_pAllocator, - created_pipelines); + result = func(device, + in_deferredOperation, + in_pipelineCache, + createInfoCount, + modified_create_infos.data(), + in_pAllocator, + created_pipelines); if ((result == VK_SUCCESS) || (result == VK_OPERATION_NOT_DEFERRED_KHR) || (result == VK_PIPELINE_COMPILE_REQUIRED_EXT)) @@ -7975,13 +7970,13 @@ VkResult VulkanReplayConsumerBase::OverrideCreateRayTracingPipelinesKHR( created_pipelines = out_pPipelines; } - result = device_table->CreateRayTracingPipelinesKHR(device, - in_deferredOperation, - in_pipelineCache, - createInfoCount, - in_pCreateInfos, - in_pAllocator, - created_pipelines); + result = func(device, + in_deferredOperation, + in_pipelineCache, + createInfoCount, + in_pCreateInfos, + in_pAllocator, + created_pipelines); if ((result == VK_SUCCESS) || (result == VK_OPERATION_NOT_DEFERRED_KHR) || (result == VK_PIPELINE_COMPILE_REQUIRED_EXT)) @@ -10088,8 +10083,9 @@ void VulkanReplayConsumerBase::OverrideDestroyShaderModule( } std::function()> VulkanReplayConsumerBase::AsyncCreateGraphicsPipelines( - const ApiCallInfo& call_info, + PFN_vkCreateGraphicsPipelines func, VkResult returnValue, + const ApiCallInfo& call_info, const VulkanDeviceInfo* device_info, const VulkanPipelineCacheInfo* pipeline_cache_info, uint32_t createInfoCount, @@ -10137,6 +10133,7 @@ std::function()> VulkanReplayConsumer auto task = [this, device_handle, pipeline_cache_handle, + func, returnValue, call_info, in_pAllocator, @@ -10153,8 +10150,7 @@ std::function()> VulkanReplayConsumer replaced_file_code = ReplaceShaders(createInfoCount, create_infos, pipelines.data()); } - auto device_table = GetDeviceTable(device_handle); - VkResult replay_result = device_table->CreateGraphicsPipelines( + VkResult replay_result = func( device_handle, pipeline_cache_handle, createInfoCount, create_infos, in_pAllocator, out_pipelines.data()); CheckResult("vkCreateGraphicsPipelines", returnValue, replay_result, call_info); @@ -10171,8 +10167,9 @@ std::function()> VulkanReplayConsumer } std::function()> VulkanReplayConsumerBase::AsyncCreateComputePipelines( - const ApiCallInfo& call_info, + PFN_vkCreateComputePipelines func, VkResult returnValue, + const ApiCallInfo& call_info, const VulkanDeviceInfo* device_info, const VulkanPipelineCacheInfo* pipeline_cache_info, uint32_t createInfoCount, @@ -10212,6 +10209,7 @@ std::function()> VulkanReplayConsumerBase::As auto task = [this, device_handle, pipeline_cache_handle, + func, returnValue, call_info, in_pAllocator, @@ -10220,8 +10218,7 @@ std::function()> VulkanReplayConsumerBase::As handle_deps = std::move(handle_deps)]() mutable -> handle_create_result_t { std::vector out_pipelines(createInfoCount); auto create_infos = reinterpret_cast(create_info_data.data()); - auto device_table = GetDeviceTable(device_handle); - VkResult replay_result = device_table->CreateComputePipelines( + VkResult replay_result = func( device_handle, pipeline_cache_handle, createInfoCount, create_infos, in_pAllocator, out_pipelines.data()); CheckResult("vkCreateComputePipelines", returnValue, replay_result, call_info); @@ -10238,8 +10235,9 @@ std::function()> VulkanReplayConsumerBase::As } std::function()> -VulkanReplayConsumerBase::AsyncCreateShadersEXT(const ApiCallInfo& call_info, +VulkanReplayConsumerBase::AsyncCreateShadersEXT(PFN_vkCreateShadersEXT func, VkResult returnValue, + const ApiCallInfo& call_info, const VulkanDeviceInfo* device_info, uint32_t createInfoCount, StructPointerDecoder* pCreateInfos, @@ -10272,6 +10270,7 @@ VulkanReplayConsumerBase::AsyncCreateShadersEXT(const ApiCallInfo& // define pipeline-creation task, assert object-lifetimes by copying/moving into closure auto task = [this, device_handle, + func, returnValue, call_info, in_pAllocator, @@ -10288,9 +10287,7 @@ VulkanReplayConsumerBase::AsyncCreateShadersEXT(const ApiCallInfo& replaced_file_code = ReplaceShaders(createInfoCount, create_infos, shaders.data()); } - auto device_table = GetDeviceTable(device_handle); - VkResult replay_result = device_table->CreateShadersEXT( - device_handle, createInfoCount, create_infos, in_pAllocator, out_shaders.data()); + VkResult replay_result = func(device_handle, createInfoCount, create_infos, in_pAllocator, out_shaders.data()); CheckResult("vkCreateShadersEXT", returnValue, replay_result, call_info); if (replay_result == VK_SUCCESS) diff --git a/framework/decode/vulkan_replay_consumer_base.h b/framework/decode/vulkan_replay_consumer_base.h index eca6c61c8..b94314ac9 100644 --- a/framework/decode/vulkan_replay_consumer_base.h +++ b/framework/decode/vulkan_replay_consumer_base.h @@ -1366,8 +1366,9 @@ class VulkanReplayConsumerBase : public VulkanConsumer const StructPointerDecoder* pAllocator); std::function()> - AsyncCreateGraphicsPipelines(const ApiCallInfo& call_info, + AsyncCreateGraphicsPipelines(PFN_vkCreateGraphicsPipelines func, VkResult returnValue, + const ApiCallInfo& call_info, const VulkanDeviceInfo* device_info, const VulkanPipelineCacheInfo* pipeline_cache_info, uint32_t createInfoCount, @@ -1376,8 +1377,9 @@ class VulkanReplayConsumerBase : public VulkanConsumer HandlePointerDecoder* pPipelines); std::function()> - AsyncCreateComputePipelines(const ApiCallInfo& call_info, + AsyncCreateComputePipelines(PFN_vkCreateComputePipelines func, VkResult returnValue, + const ApiCallInfo& call_info, const VulkanDeviceInfo* device_info, const VulkanPipelineCacheInfo* pipeline_cache_info, uint32_t createInfoCount, @@ -1386,8 +1388,9 @@ class VulkanReplayConsumerBase : public VulkanConsumer HandlePointerDecoder* pPipelines); std::function()> - AsyncCreateShadersEXT(const ApiCallInfo& call_info, + AsyncCreateShadersEXT(PFN_vkCreateShadersEXT func, VkResult returnValue, + const ApiCallInfo& call_info, const VulkanDeviceInfo* device_info, uint32_t createInfoCount, StructPointerDecoder* pCreateInfos, diff --git a/framework/generated/generated_vulkan_replay_consumer.cpp b/framework/generated/generated_vulkan_replay_consumer.cpp index 4f776e73b..265c1f3e7 100644 --- a/framework/generated/generated_vulkan_replay_consumer.cpp +++ b/framework/generated/generated_vulkan_replay_consumer.cpp @@ -987,7 +987,7 @@ void VulkanReplayConsumer::Process_vkCreateGraphicsPipelines( if (UseAsyncOperations()) { - auto task = AsyncCreateGraphicsPipelines(call_info, returnValue, in_device, in_pipelineCache, createInfoCount, pCreateInfos, pAllocator, pPipelines); + auto task = AsyncCreateGraphicsPipelines(GetDeviceTable(in_device->handle)->CreateGraphicsPipelines, returnValue, call_info, in_device, in_pipelineCache, createInfoCount, pCreateInfos, pAllocator, pPipelines); if(task) { AddHandlesAsync(device, pPipelines->GetPointer(), pPipelines->GetLength(), std::move(handle_info), &VulkanObjectInfoTable::AddVkPipelineInfo, std::move(task)); @@ -1021,7 +1021,7 @@ void VulkanReplayConsumer::Process_vkCreateComputePipelines( if (UseAsyncOperations()) { - auto task = AsyncCreateComputePipelines(call_info, returnValue, in_device, in_pipelineCache, createInfoCount, pCreateInfos, pAllocator, pPipelines); + auto task = AsyncCreateComputePipelines(GetDeviceTable(in_device->handle)->CreateComputePipelines, returnValue, call_info, in_device, in_pipelineCache, createInfoCount, pCreateInfos, pAllocator, pPipelines); if(task) { AddHandlesAsync(device, pPipelines->GetPointer(), pPipelines->GetLength(), std::move(handle_info), &VulkanObjectInfoTable::AddVkPipelineInfo, std::move(task)); @@ -10116,7 +10116,7 @@ void VulkanReplayConsumer::Process_vkCreateShadersEXT( if (UseAsyncOperations()) { - auto task = AsyncCreateShadersEXT(call_info, returnValue, in_device, createInfoCount, pCreateInfos, pAllocator, pShaders); + auto task = AsyncCreateShadersEXT(GetDeviceTable(in_device->handle)->CreateShadersEXT, returnValue, call_info, in_device, createInfoCount, pCreateInfos, pAllocator, pShaders); if(task) { AddHandlesAsync(device, pShaders->GetPointer(), pShaders->GetLength(), std::move(handle_info), &VulkanObjectInfoTable::AddVkShaderEXTInfo, std::move(task)); diff --git a/framework/generated/vulkan_generators/vulkan_replay_consumer_body_generator.py b/framework/generated/vulkan_generators/vulkan_replay_consumer_body_generator.py index f4a21b158..62cc3ed8b 100644 --- a/framework/generated/vulkan_generators/vulkan_replay_consumer_body_generator.py +++ b/framework/generated/vulkan_generators/vulkan_replay_consumer_body_generator.py @@ -341,7 +341,7 @@ def make_consumer_func_body(self, return_type, name, values): if is_async: body += ' if (UseAsyncOperations())\n' body += ' {\n' - body += ' auto task = {}(call_info, returnValue, {});\n'.format(self.REPLAY_ASYNC_OVERRIDES[name], arglist) + body += ' auto task = {}({}, returnValue, call_info, {});\n'.format(self.REPLAY_ASYNC_OVERRIDES[name], dispatchfunc, arglist) body += ' if(task)\n' body += ' {\n' body += ' {}\n'.format(postexpr[0]) From 3f96a18187e84132c22fa768549f2838cac9b2de Mon Sep 17 00:00:00 2001 From: Panagiotis Apostolou Date: Mon, 16 Sep 2024 17:29:26 +0300 Subject: [PATCH 3/7] Introducing SetInjectedCommandCallbacks(). This callback can be called by an external layer/library in order to register 2 callbacks. One that gfxrecon-replay will call each time it starts making API calls that are not part of the capture file, and the other when it is done. The callback can be detected with dlsym/GetProcAddress --- android/framework/decode/CMakeLists.txt | 2 + framework/decode/CMakeLists.txt | 2 + framework/decode/mark_injected_commands.cpp | 75 +++++++++++++++++++ framework/decode/mark_injected_commands.h | 47 ++++++++++++ .../decode/vulkan_replay_consumer_base.cpp | 25 +++++++ framework/decode/vulkan_virtual_swapchain.cpp | 33 +++++++- 6 files changed, 182 insertions(+), 2 deletions(-) create mode 100644 framework/decode/mark_injected_commands.cpp create mode 100644 framework/decode/mark_injected_commands.h diff --git a/android/framework/decode/CMakeLists.txt b/android/framework/decode/CMakeLists.txt index d7052f26b..fdc425530 100644 --- a/android/framework/decode/CMakeLists.txt +++ b/android/framework/decode/CMakeLists.txt @@ -28,6 +28,8 @@ target_sources(gfxrecon_decode ${GFXRECON_SOURCE_DIR}/framework/decode/handle_pointer_decoder.h ${GFXRECON_SOURCE_DIR}/framework/decode/json_writer.h ${GFXRECON_SOURCE_DIR}/framework/decode/json_writer.cpp + ${GFXRECON_SOURCE_DIR}/framework/decode/mark_injected_commands.h + ${GFXRECON_SOURCE_DIR}/framework/decode/mark_injected_commands.cpp ${GFXRECON_SOURCE_DIR}/framework/decode/pointer_decoder_base.h ${GFXRECON_SOURCE_DIR}/framework/decode/pointer_decoder.h ${GFXRECON_SOURCE_DIR}/framework/decode/portability.h diff --git a/framework/decode/CMakeLists.txt b/framework/decode/CMakeLists.txt index 29a910fc3..e15317943 100644 --- a/framework/decode/CMakeLists.txt +++ b/framework/decode/CMakeLists.txt @@ -74,6 +74,8 @@ target_sources(gfxrecon_decode ${CMAKE_CURRENT_LIST_DIR}/json_writer.cpp ${CMAKE_CURRENT_LIST_DIR}/decode_json_util.h ${CMAKE_CURRENT_LIST_DIR}/decode_json_util.cpp + ${CMAKE_CURRENT_LIST_DIR}/mark_injected_commands.h + ${CMAKE_CURRENT_LIST_DIR}/mark_injected_commands.cpp ${CMAKE_CURRENT_LIST_DIR}/pointer_decoder_base.h ${CMAKE_CURRENT_LIST_DIR}/pointer_decoder.h ${CMAKE_CURRENT_LIST_DIR}/portability.h diff --git a/framework/decode/mark_injected_commands.cpp b/framework/decode/mark_injected_commands.cpp new file mode 100644 index 000000000..72b1acf1a --- /dev/null +++ b/framework/decode/mark_injected_commands.cpp @@ -0,0 +1,75 @@ +/* +** Copyright (c) 2024 Valve Corporation +** Copyright (c) 2024 LunarG, Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and associated documentation files (the "Software"), +** to deal in the Software without restriction, including without limitation +** the rights to use, copy, modify, merge, publish, distribute, sublicense, +** and/or sell copies of the Software, and to permit persons to whom the +** Software is furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +** FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +** DEALINGS IN THE SOFTWARE. +*/ + +#include "util/defines.h" +#include "util/logging.h" +#include "mark_injected_commands.h" + +GFXRECON_BEGIN_NAMESPACE(gfxrecon) +GFXRECON_BEGIN_NAMESPACE(decode) + +static void BeginEndInjectedCommandsNoop(void* data) +{ + GFXRECON_UNREFERENCED_PARAMETER(data); +} + +#ifdef GFXRECON_DEBUG_BUILD +static bool injecting_api_calls_g = false; +#endif + +static PFN_BeginInjectedCommands BeginInjectCommands_fp = BeginEndInjectedCommandsNoop; +static PFN_EndInjectedCommands EndInjectCommands_fp = BeginEndInjectedCommandsNoop; +static void* InjectCommandsData_ptr = nullptr; + +extern "C" void +SetInjectedCommandCallbacks(PFN_BeginInjectedCommands begin_fp, PFN_EndInjectedCommands end_fp, void* data) +{ + BeginInjectCommands_fp = begin_fp != nullptr ? begin_fp : BeginEndInjectedCommandsNoop; + EndInjectCommands_fp = end_fp != nullptr ? end_fp : BeginEndInjectedCommandsNoop; + InjectCommandsData_ptr = data; +} + +void BeginInjectedCommands() +{ +#ifdef GFXRECON_DEBUG_BUILD + // Nested BeginInjectedCommands/EndInjectedCommands + GFXRECON_ASSERT(!injecting_api_calls_g); + injecting_api_calls_g = true; +#endif + + BeginInjectCommands_fp(InjectCommandsData_ptr); +} + +void EndInjectedCommands() +{ +#ifdef GFXRECON_DEBUG_BUILD + // Nested BeginInjectedCommands/EndInjectedCommands + GFXRECON_ASSERT(injecting_api_calls_g); + injecting_api_calls_g = false; +#endif + + EndInjectCommands_fp(InjectCommandsData_ptr); +} + +GFXRECON_END_NAMESPACE(decode) +GFXRECON_END_NAMESPACE(gfxrecon) diff --git a/framework/decode/mark_injected_commands.h b/framework/decode/mark_injected_commands.h new file mode 100644 index 000000000..d168662c5 --- /dev/null +++ b/framework/decode/mark_injected_commands.h @@ -0,0 +1,47 @@ +/* +** Copyright (c) 2024 Valve Corporation +** Copyright (c) 2024 LunarG, Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and associated documentation files (the "Software"), +** to deal in the Software without restriction, including without limitation +** the rights to use, copy, modify, merge, publish, distribute, sublicense, +** and/or sell copies of the Software, and to permit persons to whom the +** Software is furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +** FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +** DEALINGS IN THE SOFTWARE. +*/ + +#include "util/defines.h" + +GFXRECON_BEGIN_NAMESPACE(gfxrecon) +GFXRECON_BEGIN_NAMESPACE(decode) + +using PFN_BeginInjectedCommands = void (*)(void*); +using PFN_EndInjectedCommands = void (*)(void*); + +// Interface for registering callbacks so that GFXReconstruct can notify an external library about +// generated API calls that are not included in the capture file. +// Intended usage: GFXR will call PFN_BeginInjectedCommands once before it starts issuing synthesized +// API calls, and PFN_EndInjectedCommands once it is finished. +// A void * pointer allows passing optional data that will be forwared into both callbacks. +// +// SetInjectedCommandCallbacks can be discovered through dlsym/GetProcAddress. +extern "C" void +SetInjectedCommandCallbacks(PFN_BeginInjectedCommands begin_fp, PFN_EndInjectedCommands end_fp, void* data); + +void BeginInjectedCommands(); + +void EndInjectedCommands(); + +GFXRECON_END_NAMESPACE(decode) +GFXRECON_END_NAMESPACE(gfxrecon) diff --git a/framework/decode/vulkan_replay_consumer_base.cpp b/framework/decode/vulkan_replay_consumer_base.cpp index a0d68c717..b2a47a6cf 100644 --- a/framework/decode/vulkan_replay_consumer_base.cpp +++ b/framework/decode/vulkan_replay_consumer_base.cpp @@ -49,6 +49,7 @@ #include "util/platform.h" #include "util/logging.h" #include "util/linear_hashmap.h" +#include "decode/mark_injected_commands.h" #include "spirv_reflect.h" @@ -2898,7 +2899,11 @@ void VulkanReplayConsumerBase::OverrideDestroyDevice( if (screenshot_handler_ != nullptr) { + decode::BeginInjectedCommands(); + screenshot_handler_->DestroyDeviceResources(device, GetDeviceTable(device)); + + decode::EndInjectedCommands(); } device_info->allocator->Destroy(); @@ -3603,6 +3608,8 @@ VkResult VulkanReplayConsumerBase::OverrideQueueSubmit(PFN_vkQueueSubmit fu if (screenshot_handler_ != nullptr) { + decode::BeginInjectedCommands(); + VulkanCommandBufferInfo* frame_boundary_command_buffer_info = nullptr; for (uint32_t i = 0; i < submitCount; ++i) { @@ -3638,6 +3645,8 @@ VkResult VulkanReplayConsumerBase::OverrideQueueSubmit(PFN_vkQueueSubmit fu } } } + + decode::EndInjectedCommands(); } return result; @@ -3778,6 +3787,8 @@ VkResult VulkanReplayConsumerBase::OverrideQueueSubmit2(PFN_vkQueueSubmit2 f // Check whether any of the submitted command buffers are frame boundaries. if (screenshot_handler_ != nullptr) { + decode::BeginInjectedCommands(); + bool is_frame_boundary = false; for (uint32_t i = 0; i < submitCount; ++i) { @@ -3802,6 +3813,8 @@ VkResult VulkanReplayConsumerBase::OverrideQueueSubmit2(PFN_vkQueueSubmit2 f } } } + + decode::EndInjectedCommands(); } return result; @@ -4070,6 +4083,8 @@ VkResult VulkanReplayConsumerBase::OverrideAllocateDescriptorSets( if ((original_result >= 0) && (result == VK_ERROR_OUT_OF_POOL_MEMORY)) { + decode::BeginInjectedCommands(); + // Handle case where replay runs out of descriptor pool memory when capture did not by creating a new // descriptor pool and attempting the allocation a second time. VkDescriptorPool new_pool = VK_NULL_HANDLE; @@ -4116,6 +4131,8 @@ VkResult VulkanReplayConsumerBase::OverrideAllocateDescriptorSets( result = func(device_info->handle, &modified_allocate_info, pDescriptorSets->GetHandlePointer()); } + + decode::EndInjectedCommands(); } // The information gathered here is only relevant to the dump resources feature @@ -6806,6 +6823,8 @@ VulkanReplayConsumerBase::OverrideQueuePresentKHR(PFN_vkQueuePresentKHR capture_image_indices_.clear(); swapchain_infos_.clear(); + decode::BeginInjectedCommands(); + if ((screenshot_handler_ != nullptr) && (screenshot_handler_->IsScreenshotFrame())) { auto meta_info = pPresentInfo->GetMetaStructPointer(); @@ -7054,6 +7073,8 @@ VulkanReplayConsumerBase::OverrideQueuePresentKHR(PFN_vkQueuePresentKHR GetDeviceTable(device)->DeviceWaitIdle(device); } + decode::EndInjectedCommands(); + // Only attempt to find imported or shadow semaphores if we know at least one around. if ((!have_imported_semaphores_) && (shadow_semaphores_.empty()) && (modified_present_info.swapchainCount != 0)) { @@ -8680,6 +8701,8 @@ void VulkanReplayConsumerBase::OverrideFrameBoundaryANDROID(PFN_vkFrameBoundaryA if (screenshot_handler_ != nullptr) { + decode::BeginInjectedCommands(); + if (screenshot_handler_->IsScreenshotFrame() && image_info != nullptr) { const std::string filename_prefix = @@ -8716,6 +8739,8 @@ void VulkanReplayConsumerBase::OverrideFrameBoundaryANDROID(PFN_vkFrameBoundaryA } screenshot_handler_->EndFrame(); + + decode::EndInjectedCommands(); } func(device, semaphore, image); diff --git a/framework/decode/vulkan_virtual_swapchain.cpp b/framework/decode/vulkan_virtual_swapchain.cpp index 19430cf78..5f4e50f8b 100644 --- a/framework/decode/vulkan_virtual_swapchain.cpp +++ b/framework/decode/vulkan_virtual_swapchain.cpp @@ -24,7 +24,8 @@ #include "decode/vulkan_resource_allocator.h" #include "decode/decoder_util.h" - +#include "decode/mark_injected_commands.h" +#include "vulkan/vulkan_core.h" #include GFXRECON_BEGIN_NAMESPACE(gfxrecon) @@ -152,8 +153,14 @@ void VulkanVirtualSwapchain::DestroySwapchainKHR(PFN_vkDestroySwapchainKHR f { if ((device_info != nullptr) && (swapchain_info != nullptr)) { + // CleanSwapchainResourceData() makes Vulkan API calls that are not in the capture file. + // Notify any layers by calling the provided pointer to their ReportReplayGeneratedVulkanCommands + decode::BeginInjectedCommands(); + CleanSwapchainResourceData(device_info, swapchain_info); + decode::EndInjectedCommands(); + VkDevice device = device_info->handle; VkSwapchainKHR swapchain = swapchain_info->handle; func(device, swapchain, allocator); @@ -625,8 +632,16 @@ VkResult VulkanVirtualSwapchain::GetSwapchainImagesKHR(VkResult return result; } - return CreateSwapchainResourceData( + // CreateSwapchainResourceData() makes Vulkan API calls that are not in the capture file. + // Notify any layers by calling the provided pointer to their ReportReplayGeneratedVulkanCommands + decode::BeginInjectedCommands(); + + VkResult res = CreateSwapchainResourceData( device_info, swapchain_info, capture_image_count, replay_image_count, images, false); + + decode::EndInjectedCommands(); + + return res; } return result; @@ -713,6 +728,10 @@ VkResult VulkanVirtualSwapchain::QueuePresentKHR(VkResult return func(queue_info->handle, present_info); } + // Below Vulkan API calls are made that are not in the capture file. + // Notify any layers by calling the provided pointer to their ReportReplayGeneratedVulkanCommands + decode::BeginInjectedCommands(); + VkDevice device = queue_info->parent; VkQueue queue = queue_info->handle; uint32_t queue_family_index = queue_info->family_index; @@ -844,11 +863,14 @@ VkResult VulkanVirtualSwapchain::QueuePresentKHR(VkResult result = device_table_->ResetCommandBuffer(command_buffer, 0); if (result != VK_SUCCESS) { + decode::EndInjectedCommands(); + return result; } result = device_table_->BeginCommandBuffer(command_buffer, &begin_info); if (result != VK_SUCCESS) { + decode::EndInjectedCommands(); return result; } @@ -923,6 +945,8 @@ VkResult VulkanVirtualSwapchain::QueuePresentKHR(VkResult result = device_table_->EndCommandBuffer(command_buffer); if (result != VK_SUCCESS) { + decode::EndInjectedCommands(); + return result; } @@ -948,13 +972,18 @@ VkResult VulkanVirtualSwapchain::QueuePresentKHR(VkResult if (result != VK_SUCCESS) { + decode::EndInjectedCommands(); + return result; } } + decode::EndInjectedCommands(); + VkPresentInfoKHR modified_present_info = *present_info; modified_present_info.waitSemaphoreCount = static_cast(present_wait_semaphores.size()); modified_present_info.pWaitSemaphores = present_wait_semaphores.data(); + return func(queue, &modified_present_info); } From ed163cbe0a22a804ee7c120e78e75f626151c159 Mon Sep 17 00:00:00 2001 From: Panagiotis Apostolou Date: Wed, 6 Nov 2024 15:19:32 +0200 Subject: [PATCH 4/7] Add export-dynamic in gfxrecon-replay This should make the SetInjectedCommandCallbacks function visible to dlsym when attempting to find the function when re-capturing (capture layer is enabled while replaying) --- tools/replay/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/replay/CMakeLists.txt b/tools/replay/CMakeLists.txt index 01430b1c5..69d2fc20e 100644 --- a/tools/replay/CMakeLists.txt +++ b/tools/replay/CMakeLists.txt @@ -51,6 +51,8 @@ target_link_libraries(gfxrecon-replay $<$:dxgi.lib> $<$:${DXC_LIBRARY_PATH}>) +target_link_options(gfxrecon-replay PUBLIC "-export-dynamic") + if (MSVC) # Force inclusion of "gfxrecon_disable_popup_result" variable in linking. # On 32-bit windows, MSVC prefixes symbols with "_" but on 64-bit windows it doesn't. From 4227ac602486ed8d066bcf991a7e7aa3fafa0424 Mon Sep 17 00:00:00 2001 From: Panagiotis Apostolou Date: Wed, 6 Nov 2024 15:35:57 +0200 Subject: [PATCH 5/7] Declare type for SetInjectedCommandCallbacks --- framework/decode/mark_injected_commands.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/framework/decode/mark_injected_commands.h b/framework/decode/mark_injected_commands.h index d168662c5..462b3ffe4 100644 --- a/framework/decode/mark_injected_commands.h +++ b/framework/decode/mark_injected_commands.h @@ -26,8 +26,9 @@ GFXRECON_BEGIN_NAMESPACE(gfxrecon) GFXRECON_BEGIN_NAMESPACE(decode) -using PFN_BeginInjectedCommands = void (*)(void*); -using PFN_EndInjectedCommands = void (*)(void*); +using PFN_BeginInjectedCommands = void (*)(void*); +using PFN_EndInjectedCommands = void (*)(void*); +using PFN_SetInjectedCommandCallbacks = void (*)(PFN_BeginInjectedCommands, PFN_EndInjectedCommands, void*); // Interface for registering callbacks so that GFXReconstruct can notify an external library about // generated API calls that are not included in the capture file. From de694011215b260cafc43f00bcc49d2e0bdd79f1 Mon Sep 17 00:00:00 2001 From: Panagiotis Apostolou Date: Wed, 6 Nov 2024 18:45:10 +0200 Subject: [PATCH 6/7] Replace -export-dynamic with -rdynamic --- tools/replay/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/replay/CMakeLists.txt b/tools/replay/CMakeLists.txt index 69d2fc20e..58ab28e34 100644 --- a/tools/replay/CMakeLists.txt +++ b/tools/replay/CMakeLists.txt @@ -51,7 +51,7 @@ target_link_libraries(gfxrecon-replay $<$:dxgi.lib> $<$:${DXC_LIBRARY_PATH}>) -target_link_options(gfxrecon-replay PUBLIC "-export-dynamic") +target_link_options(gfxrecon-replay PUBLIC "-rdynamic") if (MSVC) # Force inclusion of "gfxrecon_disable_popup_result" variable in linking. From 4aaa6ffb57bf25d634775538381973626ab8106d Mon Sep 17 00:00:00 2001 From: Panagiotis Apostolou Date: Wed, 6 Nov 2024 18:46:11 +0200 Subject: [PATCH 7/7] Fix clang-format --- framework/decode/file_processor.h | 1 - framework/decode/vulkan_virtual_swapchain.cpp | 5 +---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/framework/decode/file_processor.h b/framework/decode/file_processor.h index ec1b3cc9f..c8cf0c12a 100644 --- a/framework/decode/file_processor.h +++ b/framework/decode/file_processor.h @@ -276,7 +276,6 @@ class FileProcessor return file_stack_.back(); } - }; GFXRECON_END_NAMESPACE(decode) diff --git a/framework/decode/vulkan_virtual_swapchain.cpp b/framework/decode/vulkan_virtual_swapchain.cpp index 5f4e50f8b..11bf37027 100644 --- a/framework/decode/vulkan_virtual_swapchain.cpp +++ b/framework/decode/vulkan_virtual_swapchain.cpp @@ -636,12 +636,10 @@ VkResult VulkanVirtualSwapchain::GetSwapchainImagesKHR(VkResult // Notify any layers by calling the provided pointer to their ReportReplayGeneratedVulkanCommands decode::BeginInjectedCommands(); - VkResult res = CreateSwapchainResourceData( + result = CreateSwapchainResourceData( device_info, swapchain_info, capture_image_count, replay_image_count, images, false); decode::EndInjectedCommands(); - - return res; } return result; @@ -983,7 +981,6 @@ VkResult VulkanVirtualSwapchain::QueuePresentKHR(VkResult VkPresentInfoKHR modified_present_info = *present_info; modified_present_info.waitSemaphoreCount = static_cast(present_wait_semaphores.size()); modified_present_info.pWaitSemaphores = present_wait_semaphores.data(); - return func(queue, &modified_present_info); }