diff --git a/android/framework/encode/CMakeLists.txt b/android/framework/encode/CMakeLists.txt index 935bbfde6a..b19e98b372 100644 --- a/android/framework/encode/CMakeLists.txt +++ b/android/framework/encode/CMakeLists.txt @@ -8,6 +8,8 @@ target_sources(gfxrecon_encode ${GFXRECON_SOURCE_DIR}/framework/encode/capture_manager.cpp ${GFXRECON_SOURCE_DIR}/framework/encode/capture_settings.h ${GFXRECON_SOURCE_DIR}/framework/encode/capture_settings.cpp + ${GFXRECON_SOURCE_DIR}/framework/encode/command_writer.h + ${GFXRECON_SOURCE_DIR}/framework/encode/command_writer.cpp ${GFXRECON_SOURCE_DIR}/framework/encode/custom_vulkan_encoder_commands.h ${GFXRECON_SOURCE_DIR}/framework/encode/custom_vulkan_api_call_encoders.h ${GFXRECON_SOURCE_DIR}/framework/encode/custom_vulkan_api_call_encoders.cpp @@ -19,6 +21,7 @@ target_sources(gfxrecon_encode ${GFXRECON_SOURCE_DIR}/framework/encode/custom_vulkan_struct_handle_wrappers.cpp ${GFXRECON_SOURCE_DIR}/framework/encode/descriptor_update_template_info.h ${GFXRECON_SOURCE_DIR}/framework/encode/handle_unwrap_memory.h + ${GFXRECON_SOURCE_DIR}/framework/encode/output_stream_writer.h ${GFXRECON_SOURCE_DIR}/framework/encode/parameter_buffer.h ${GFXRECON_SOURCE_DIR}/framework/encode/parameter_encoder.h ${GFXRECON_SOURCE_DIR}/framework/encode/scoped_destroy_lock.h diff --git a/android/framework/util/CMakeLists.txt b/android/framework/util/CMakeLists.txt index b840d13f8a..c8e16335bf 100644 --- a/android/framework/util/CMakeLists.txt +++ b/android/framework/util/CMakeLists.txt @@ -45,6 +45,8 @@ target_sources(gfxrecon_util ${GFXRECON_SOURCE_DIR}/framework/util/spirv_parsing_util.cpp ${GFXRECON_SOURCE_DIR}/framework/util/strings.h ${GFXRECON_SOURCE_DIR}/framework/util/strings.cpp + ${GFXRECON_SOURCE_DIR}/framework/util/thread_data.h + ${GFXRECON_SOURCE_DIR}/framework/util/thread_data.cpp ${GFXRECON_SOURCE_DIR}/framework/util/to_string.h ${GFXRECON_SOURCE_DIR}/framework/util/to_string.cpp ${GFXRECON_SOURCE_DIR}/framework/util/options.h diff --git a/framework/encode/CMakeLists.txt b/framework/encode/CMakeLists.txt index c8ab2a72b0..5e46d925c1 100644 --- a/framework/encode/CMakeLists.txt +++ b/framework/encode/CMakeLists.txt @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2018-2022 LunarG, Inc. +# Copyright (c) 2018-2025 LunarG, Inc. # Copyright (c) 2019-2023 Advanced Micro Devices, Inc. # All rights reserved # @@ -36,6 +36,8 @@ target_sources(gfxrecon_encode ${CMAKE_CURRENT_LIST_DIR}/capture_manager.cpp ${CMAKE_CURRENT_LIST_DIR}/capture_settings.h ${CMAKE_CURRENT_LIST_DIR}/capture_settings.cpp + ${CMAKE_CURRENT_LIST_DIR}/command_writer.h + ${CMAKE_CURRENT_LIST_DIR}/command_writer.cpp ${CMAKE_CURRENT_LIST_DIR}/custom_vulkan_encoder_commands.h ${CMAKE_CURRENT_LIST_DIR}/custom_vulkan_api_call_encoders.h ${CMAKE_CURRENT_LIST_DIR}/custom_vulkan_api_call_encoders.cpp diff --git a/framework/encode/api_capture_manager.h b/framework/encode/api_capture_manager.h index 67e5b95330..3321982f88 100644 --- a/framework/encode/api_capture_manager.h +++ b/framework/encode/api_capture_manager.h @@ -1,6 +1,6 @@ /* ** Copyright (c) 2018-2022 Valve Corporation -** Copyright (c) 2018-2024 LunarG, Inc. +** Copyright (c) 2018-2025 LunarG, Inc. ** Copyright (c) 2019-2023 Advanced Micro Devices, Inc. All rights reserved. ** ** Permission is hereby granted, free of charge, to any person obtaining a @@ -43,16 +43,16 @@ class ApiCaptureManager static auto AcquireExclusiveApiCallLock() { return std::move(CommonCaptureManager::AcquireExclusiveApiCallLock()); } // Virtual interface - virtual void CreateStateTracker() = 0; - virtual void DestroyStateTracker() = 0; - virtual void WriteTrackedState(util::FileOutputStream* file_stream, format::ThreadId thread_id) = 0; + virtual void CreateStateTracker() = 0; + virtual void DestroyStateTracker() = 0; + virtual void WriteTrackedState(util::FileOutputStream* file_stream, util::ThreadData* thread_data) = 0; virtual void WriteTrackedStateWithAssetFile(util::FileOutputStream* file_stream, - format::ThreadId thread_id, + util::ThreadData* thread_data, util::FileOutputStream* asset_file_stream, - const std::string* asset_file_name) = 0; + const std::string* asset_file_name) = 0; virtual void WriteAssets(util::FileOutputStream* asset_file_stream, const std::string* asset_file_name, - format::ThreadId thread_id) = 0; + util::ThreadData* thread_data) = 0; virtual CaptureSettings::TraceSettings GetDefaultTraceSettings(); @@ -212,12 +212,6 @@ class ApiCaptureManager common_manager_->WriteEndResourceInitCmd(api_family_, device_id); } - void WriteInitBufferCmd( - format::HandleId device_id, format::HandleId buffer_id, uint64_t offset, uint64_t size, const void* data) - { - common_manager_->WriteInitBufferCmd(api_family_, device_id, buffer_id, offset, size, data); - } - void WriteCreateHeapAllocationCmd(uint64_t allocation_id, uint64_t allocation_size) { common_manager_->WriteCreateHeapAllocationCmd(api_family_, allocation_id, allocation_size); @@ -230,14 +224,15 @@ class ApiCaptureManager common_manager_->CombineAndWriteToFile(buffers); } - CommonCaptureManager::ThreadData* GetThreadData() { return common_manager_->GetThreadData(); } - util::Compressor* GetCompressor() { return common_manager_->GetCompressor(); } - std::mutex& GetMappedMemoryLock() { return common_manager_->GetMappedMemoryLock(); } - util::Keyboard& GetKeyboard() { return common_manager_->GetKeyboard(); } - const std::string& GetScreenshotPrefix() const { return common_manager_->GetScreenshotPrefix(); } - util::ScreenshotFormat GetScreenshotFormat() { return common_manager_->GetScreenshotFormat(); } - auto GetTrimBoundary() const { return common_manager_->GetTrimBoundary(); } - auto GetTrimDrawCalls() const { return common_manager_->GetTrimDrawCalls(); } + util::ThreadData* GetThreadData() { return common_manager_->GetThreadData(); } + util::Compressor* GetCompressor() { return common_manager_->GetCompressor(); } + std::mutex& GetMappedMemoryLock() { return common_manager_->GetMappedMemoryLock(); } + util::Keyboard& GetKeyboard() { return common_manager_->GetKeyboard(); } + const std::string& GetScreenshotPrefix() const { return common_manager_->GetScreenshotPrefix(); } + util::ScreenshotFormat GetScreenshotFormat() { return common_manager_->GetScreenshotFormat(); } + auto GetTrimBoundary() const { return common_manager_->GetTrimBoundary(); } + auto GetTrimDrawCalls() const { return common_manager_->GetTrimDrawCalls(); } + CommandWriter* GetCommandWriter() { return common_manager_->GetCommandWriter(); } protected: const format::ApiFamilyId api_family_; diff --git a/framework/encode/capture_manager.cpp b/framework/encode/capture_manager.cpp index 1de5d32423..2ba452e0d3 100644 --- a/framework/encode/capture_manager.cpp +++ b/framework/encode/capture_manager.cpp @@ -1,6 +1,6 @@ /* ** Copyright (c) 2018-2022 Valve Corporation -** Copyright (c) 2018-2022 LunarG, Inc. +** Copyright (c) 2018-2025 LunarG, Inc. ** Copyright (c) 2019-2023 Advanced Micro Devices, Inc. All rights reserved. ** ** Permission is hereby granted, free of charge, to any person obtaining a @@ -55,46 +55,13 @@ GFXRECON_BEGIN_NAMESPACE(encode) const uint32_t kFirstFrame = 1; const size_t kFileStreamBufferSize = 256 * 1024; -std::mutex CommonCaptureManager::ThreadData::count_lock_; -format::ThreadId CommonCaptureManager::ThreadData::thread_count_ = 0; -std::unordered_map CommonCaptureManager::ThreadData::id_map_; - -CommonCaptureManager* CommonCaptureManager::singleton_; -std::mutex CommonCaptureManager::instance_lock_; -thread_local std::unique_ptr CommonCaptureManager::thread_data_; -CommonCaptureManager::ApiCallMutexT CommonCaptureManager::api_call_mutex_; +CommonCaptureManager* CommonCaptureManager::singleton_; +std::mutex CommonCaptureManager::instance_lock_; +thread_local std::unique_ptr CommonCaptureManager::thread_data_; +CommonCaptureManager::ApiCallMutexT CommonCaptureManager::api_call_mutex_; std::atomic CommonCaptureManager::unique_id_counter_{ format::kNullHandleId }; -CommonCaptureManager::ThreadData::ThreadData() : - thread_id_(GetThreadId()), object_id_(format::kNullHandleId), call_id_(format::ApiCallId::ApiCall_Unknown), - block_index_(0) -{ - parameter_buffer_ = std::make_unique(); - parameter_encoder_ = std::make_unique(parameter_buffer_.get()); -} - -format::ThreadId CommonCaptureManager::ThreadData::GetThreadId() -{ - format::ThreadId id = 0; - uint64_t tid = util::platform::GetCurrentThreadId(); - - // Using a uint64_t sequence number associated with the thread ID. - std::lock_guard lock(count_lock_); - auto entry = id_map_.find(tid); - if (entry != id_map_.end()) - { - id = entry->second; - } - else - { - id = ++thread_count_; - id_map_.insert(std::make_pair(tid, id)); - } - - return id; -} - CommonCaptureManager::CommonCaptureManager() : force_file_flush_(false), timestamp_filename_(true), memory_tracking_mode_(CaptureSettings::MemoryTrackingMode::kPageGuard), page_guard_align_buffer_sizes_(false), @@ -491,14 +458,19 @@ bool CommonCaptureManager::Initialize(format::ApiFamilyId api_ capture_mode_ = kModeDisabled; } + if (success) + { + command_writer_ = std::make_unique(this, compressor_.get()); + } + return success; } -CommonCaptureManager::ThreadData* CommonCaptureManager::GetThreadData() +util::ThreadData* CommonCaptureManager::GetThreadData() { if (!thread_data_) { - thread_data_ = std::make_unique(); + thread_data_ = std::make_unique(); } return thread_data_.get(); } @@ -834,15 +806,15 @@ void CommonCaptureManager::CheckStartCaptureForTrackMode(format::ApiFamilyId { capture_mode_ |= kModeWrite; - auto thread_data = GetThreadData(); - assert(thread_data != nullptr); + auto* thread_data = GetThreadData(); + GFXRECON_ASSERT(thread_data != nullptr); std::unique_ptr asset_file_stream = CreateAssetFile(); if (asset_file_stream) { for (auto& manager : api_capture_managers_) { - manager.first->WriteAssets(asset_file_stream.get(), &asset_file_name_, thread_data->thread_id_); + manager.first->WriteAssets(asset_file_stream.get(), &asset_file_name_, thread_data); } } @@ -1226,22 +1198,22 @@ void CommonCaptureManager::ActivateTrimming(std::shared_lock& cur capture_mode_ |= kModeWrite; - auto thread_data = GetThreadData(); - assert(thread_data != nullptr); + auto* thread_data = GetThreadData(); + GFXRECON_ASSERT(thread_data != nullptr); if (use_asset_file_) { std::unique_ptr asset_file_stream = CreateAssetFile(); for (auto& manager : api_capture_managers_) { manager.first->WriteTrackedStateWithAssetFile( - file_stream_.get(), thread_data->thread_id_, asset_file_stream.get(), &asset_file_name_); + file_stream_.get(), thread_data, asset_file_stream.get(), &asset_file_name_); } } else { for (auto& manager : api_capture_managers_) { - manager.first->WriteTrackedState(file_stream_.get(), thread_data->thread_id_); + manager.first->WriteTrackedState(file_stream_.get(), thread_data); } } } @@ -1507,69 +1479,6 @@ void CommonCaptureManager::WriteEndResourceInitCmd(format::ApiFamilyId api_famil WriteToFile(&init_cmd, sizeof(init_cmd)); } -void CommonCaptureManager::WriteInitBufferCmd(format::ApiFamilyId api_family, - format::HandleId device_id, - format::HandleId buffer_id, - uint64_t offset, - uint64_t size, - const void* data) -{ - if ((capture_mode_ & kModeWrite) != kModeWrite) - { - return; - } - - GFXRECON_CHECK_CONVERSION_DATA_LOSS(size_t, size); - - format::InitBufferCommandHeader init_cmd; - size_t header_size = sizeof(format::InitBufferCommandHeader); - const uint8_t* uncompressed_data = (static_cast(data) + offset); - size_t uncompressed_size = static_cast(size); - - auto thread_data = GetThreadData(); - assert(thread_data != nullptr); - - init_cmd.meta_header.block_header.type = format::BlockType::kMetaDataBlock; - init_cmd.meta_header.meta_data_id = format::MakeMetaDataId(api_family, format::MetaDataType::kInitBufferCommand); - init_cmd.thread_id = thread_data->thread_id_; - init_cmd.device_id = device_id; - init_cmd.buffer_id = buffer_id; - init_cmd.data_size = size; - - bool not_compressed = true; - - if (compressor_ != nullptr) - { - size_t compressed_size = - compressor_->Compress(uncompressed_size, uncompressed_data, &thread_data->compressed_buffer_, header_size); - - if ((compressed_size > 0) && (compressed_size < uncompressed_size)) - { - not_compressed = false; - - // We don't have a special header for compressed fill commands because the header always includes - // the uncompressed size, so we just change the type to indicate the data is compressed. - init_cmd.meta_header.block_header.type = format::BlockType::kCompressedMetaDataBlock; - - // Calculate size of packet with uncompressed data size. - init_cmd.meta_header.block_header.size = format::GetMetaDataBlockBaseSize(init_cmd) + compressed_size; - - // Copy header to beginning of compressed_buffer_ - util::platform::MemoryCopy(thread_data->compressed_buffer_.data(), header_size, &init_cmd, header_size); - - WriteToFile(thread_data->compressed_buffer_.data(), header_size + compressed_size); - } - } - - if (not_compressed) - { - // Calculate size of packet with compressed data size. - init_cmd.meta_header.block_header.size = format::GetMetaDataBlockBaseSize(init_cmd) + uncompressed_size; - - CombineAndWriteToFile({ { &init_cmd, header_size }, { uncompressed_data, uncompressed_size } }); - } -} - void CommonCaptureManager::WriteCreateHeapAllocationCmd(format::ApiFamilyId api_family, uint64_t allocation_id, uint64_t allocation_size) diff --git a/framework/encode/capture_manager.h b/framework/encode/capture_manager.h index cc11ad8750..78cd1fb1f8 100644 --- a/framework/encode/capture_manager.h +++ b/framework/encode/capture_manager.h @@ -1,6 +1,6 @@ /* ** Copyright (c) 2018-2022 Valve Corporation -** Copyright (c) 2018-2022 LunarG, Inc. +** Copyright (c) 2018-2025 LunarG, Inc. ** Copyright (c) 2019-2023 Advanced Micro Devices, Inc. All rights reserved. ** ** Permission is hereby granted, free of charge, to any person obtaining a @@ -26,9 +26,11 @@ #define GFXRECON_ENCODE_CAPTURE_MANAGER_H #include "encode/capture_settings.h" +#include "encode/command_writer.h" #include "encode/handle_unwrap_memory.h" #include "encode/parameter_buffer.h" #include "encode/parameter_encoder.h" +#include "encode/output_stream_writer.h" #include "format/api_call_id.h" #include "format/format.h" #include "format/platform_types.h" @@ -36,6 +38,7 @@ #include "util/defines.h" #include "util/file_output_stream.h" #include "util/keyboard.h" +#include "util/thread_data.h" #include #include @@ -52,7 +55,7 @@ GFXRECON_BEGIN_NAMESPACE(encode) class ApiCaptureManager; // The CommonCaptureManager provides common functionality referenced API specific capture managers -class CommonCaptureManager +class CommonCaptureManager : public OutputStreamWriter { public: typedef std::shared_mutex ApiCallMutexT; @@ -203,41 +206,9 @@ class CommonCaptureManager typedef uint32_t CaptureMode; - class ThreadData - { - public: - ThreadData(); - - ~ThreadData() {} - - std::vector& GetScratchBuffer() { return scratch_buffer_; } - - public: - const format::ThreadId thread_id_; - format::ApiCallId call_id_; - format::HandleId object_id_; - std::unique_ptr parameter_buffer_; - std::unique_ptr parameter_encoder_; - std::vector compressed_buffer_; - HandleUnwrapMemory handle_unwrap_memory_; - uint64_t block_index_; - - private: - static format::ThreadId GetThreadId(); - - private: - static std::mutex count_lock_; - static format::ThreadId thread_count_; - static std::unordered_map id_map_; - - private: - // Used for combining multiple buffers for a single file write. - std::vector scratch_buffer_; - }; - - ThreadData* GetThreadData(); - bool IsCaptureModeTrack() const; - bool IsCaptureModeWrite() const; + util::ThreadData* GetThreadData() override; + bool IsCaptureModeTrack() const; + bool IsCaptureModeWrite() const; void DestroyInstance(ApiCaptureManager* singleton); @@ -275,6 +246,7 @@ class CommonCaptureManager util::Keyboard& GetKeyboard() { return keyboard_; } const std::string& GetScreenshotPrefix() const { return screenshot_prefix_; } util::ScreenshotFormat GetScreenShotFormat() const { return screenshot_format_; } + CommandWriter* GetCommandWriter() { return command_writer_.get(); } std::string CreateTrimFilename(const std::string& base_filename, const util::UintRange& trim_range); std::string CreateTrimDrawCallsFilename(const std::string& base_filename, @@ -306,15 +278,14 @@ class CommonCaptureManager void WriteEndResourceInitCmd(format::ApiFamilyId api_family, format::HandleId device_id); - void WriteInitBufferCmd(format::ApiFamilyId api_family, - format::HandleId device_id, - format::HandleId buffer_id, - uint64_t offset, - uint64_t size, - const void* data); - void WriteCreateHeapAllocationCmd(format::ApiFamilyId api_family, uint64_t allocation_id, uint64_t allocation_size); + bool OutputStreamWrite(const void* data, size_t len) override + { + WriteToFile(data, len); + return true; + } + void WriteToFile(const void* data, size_t size, util::FileOutputStream* file_stream = nullptr); template @@ -365,11 +336,11 @@ class CommonCaptureManager static void AtExit(); private: - static std::mutex instance_lock_; - static CommonCaptureManager* singleton_; - static thread_local std::unique_ptr thread_data_; - static std::atomic unique_id_counter_; - static ApiCallMutexT api_call_mutex_; + static std::mutex instance_lock_; + static CommonCaptureManager* singleton_; + static thread_local std::unique_ptr thread_data_; + static std::atomic unique_id_counter_; + static ApiCallMutexT api_call_mutex_; uint32_t instance_count_ = 0; struct ApiInstanceRecord @@ -438,6 +409,8 @@ class CommonCaptureManager uint16_t descriptor_mask{ RvAnnotationUtil::kDescriptorMask }; uint64_t shaderid_mask{ RvAnnotationUtil::kShaderIDMask }; } rv_annotation_info_; + + std::unique_ptr command_writer_; }; GFXRECON_END_NAMESPACE(encode) diff --git a/framework/encode/command_writer.cpp b/framework/encode/command_writer.cpp new file mode 100644 index 0000000000..1a7e43dce1 --- /dev/null +++ b/framework/encode/command_writer.cpp @@ -0,0 +1,167 @@ +/* + ** Copyright (c) 2025 LunarG, Inc. + ** + ** Permission is hereby granted, free of charge, to any person obtaining a + ** copy of this software and associated documentation files (the "Software"), + ** to deal in the Software without restriction, including without limitation + ** the rights to use, copy, modify, merge, publish, distribute, sublicense, + ** and/or sell copies of the Software, and to permit persons to whom the + ** Software is furnished to do so, subject to the following conditions: + ** + ** The above copyright notice and this permission notice shall be included in + ** all copies or substantial portions of the Software. + ** + ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + ** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + ** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + ** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + ** FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + ** DEALINGS IN THE SOFTWARE. + */ + +#include "encode/command_writer.h" + +GFXRECON_BEGIN_NAMESPACE(gfxrecon) +GFXRECON_BEGIN_NAMESPACE(encode) + +CommandWriter::CommandWriter(OutputStreamWriter* p_output_stream_writer, util::Compressor* p_compressor) : + output_stream_writer_(p_output_stream_writer), compressor_(p_compressor) +{} + +void CommandWriter::WriteInitBufferCmd(format::ApiFamilyId p_api_family, + format::HandleId p_device_id, + format::HandleId p_buffer_id, + uint64_t p_offset, + uint64_t p_size, + const void* p_data) +{ + GFXRECON_CHECK_CONVERSION_DATA_LOSS(size_t, p_size); + + format::InitBufferCommandHeader init_cmd = {}; + + size_t header_size = sizeof(format::InitBufferCommandHeader); + const uint8_t* uncompressed_data = static_cast(p_data) + p_offset; + size_t uncompressed_size = static_cast(p_size); + + auto* thread_data = output_stream_writer_->GetThreadData(); + GFXRECON_ASSERT(thread_data != nullptr); + + init_cmd.meta_header.block_header.type = format::BlockType::kMetaDataBlock; + init_cmd.meta_header.meta_data_id = format::MakeMetaDataId(p_api_family, format::MetaDataType::kInitBufferCommand); + init_cmd.thread_id = thread_data->thread_id_; + init_cmd.device_id = p_device_id; + init_cmd.buffer_id = p_buffer_id; + init_cmd.data_size = p_size; + + bool compressed = false; + + if (compressor_ != nullptr) + { + std::vector& compressed_buffer = thread_data->compressed_buffer_; + + size_t compressed_size = + compressor_->Compress(uncompressed_size, uncompressed_data, &compressed_buffer, header_size); + + if ((compressed_size > 0) && (compressed_size < uncompressed_size)) + { + compressed = true; + + // We don't have a special header for compressed fill commands because the header always includes + // the uncompressed size, so we just change the type to indicate the data is compressed. + init_cmd.meta_header.block_header.type = format::BlockType::kCompressedMetaDataBlock; + + // Calculate size of packet with uncompressed data size. + init_cmd.meta_header.block_header.size = format::GetMetaDataBlockBaseSize(init_cmd) + compressed_size; + + // Copy header to beginning of compressed_buffer. + util::platform::MemoryCopy(compressed_buffer.data(), header_size, &init_cmd, header_size); + + output_stream_writer_->OutputStreamWrite(compressed_buffer.data(), header_size + compressed_size); + } + } + + if (!compressed) + { + // Calculate size of packet with uncompressed data size. + init_cmd.meta_header.block_header.size = format::GetMetaDataBlockBaseSize(init_cmd) + uncompressed_size; + + output_stream_writer_->OutputStreamCombineAndWrite( + { { &init_cmd, header_size }, { uncompressed_data, uncompressed_size } }); + } +} + +void CommandWriter::WriteInitImageCmd(format::ApiFamilyId p_api_family, + format::HandleId p_device_id, + format::HandleId p_image_id, + uint32_t p_aspect, + uint32_t p_layout, + uint32_t p_mip_levels, + const std::vector& p_level_sizes, + uint64_t p_size, + const void* p_data) +{ + + format::InitImageCommandHeader upload_cmd; + + auto* thread_data = output_stream_writer_->GetThreadData(); + GFXRECON_ASSERT(thread_data != nullptr); + + // Packet size without the resource data. + upload_cmd.meta_header.block_header.size = format::GetMetaDataBlockBaseSize(upload_cmd); + upload_cmd.meta_header.block_header.type = format::kMetaDataBlock; + upload_cmd.meta_header.meta_data_id = format::MakeMetaDataId(p_api_family, format::MetaDataType::kInitImageCommand); + upload_cmd.thread_id = thread_data->thread_id_; + upload_cmd.device_id = p_device_id; + upload_cmd.image_id = p_image_id; + upload_cmd.aspect = p_aspect; + upload_cmd.layout = p_layout; + + auto* bytes = reinterpret_cast(p_data); + + if (bytes != nullptr) + { + GFXRECON_CHECK_CONVERSION_DATA_LOSS(size_t, p_size); + size_t data_size = static_cast(p_size); + // Store uncompressed data size in packet. + upload_cmd.data_size = data_size; + + GFXRECON_ASSERT(p_mip_levels > 0); + upload_cmd.level_count = p_mip_levels; + + if (compressor_ != nullptr) + { + std::vector& compressed_buffer = thread_data->compressed_buffer_; + size_t compressed_size = compressor_->Compress(data_size, bytes, &compressed_buffer, 0); + + if ((compressed_size > 0) && (compressed_size < data_size)) + { + upload_cmd.meta_header.block_header.type = format::BlockType::kCompressedMetaDataBlock; + + bytes = compressed_buffer.data(); + data_size = compressed_size; + } + } + + // Calculate size of packet with compressed or uncompressed data size. + size_t levels_size = p_level_sizes.size() * sizeof(p_level_sizes[0]); + + upload_cmd.meta_header.block_header.size += levels_size + data_size; + + output_stream_writer_->OutputStreamWrite(&upload_cmd, sizeof(upload_cmd)); + output_stream_writer_->OutputStreamWrite(p_level_sizes.data(), levels_size); + output_stream_writer_->OutputStreamWrite(bytes, data_size); + } + else + { + // Write a packet without resource data; replay must still perform a layout transition at image + // initialization. + upload_cmd.data_size = 0; + upload_cmd.level_count = 0; + + output_stream_writer_->OutputStreamWrite(&upload_cmd, sizeof(upload_cmd)); + } +} + +GFXRECON_END_NAMESPACE(encode) +GFXRECON_END_NAMESPACE(gfxrecon) diff --git a/framework/encode/command_writer.h b/framework/encode/command_writer.h new file mode 100644 index 0000000000..5f36872680 --- /dev/null +++ b/framework/encode/command_writer.h @@ -0,0 +1,65 @@ +/* + ** Copyright (c) 2025 LunarG, Inc. + ** + ** Permission is hereby granted, free of charge, to any person obtaining a + ** copy of this software and associated documentation files (the "Software"), + ** to deal in the Software without restriction, including without limitation + ** the rights to use, copy, modify, merge, publish, distribute, sublicense, + ** and/or sell copies of the Software, and to permit persons to whom the + ** Software is furnished to do so, subject to the following conditions: + ** + ** The above copyright notice and this permission notice shall be included in + ** all copies or substantial portions of the Software. + ** + ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + ** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + ** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + ** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + ** FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + ** DEALINGS IN THE SOFTWARE. + */ + +#ifndef GFXRECON_ENCODE_COMMAND_WRITER_H +#define GFXRECON_ENCODE_COMMAND_WRITER_H + +#include "encode/output_stream_writer.h" +#include "format/format.h" +#include "util/compressor.h" +#include "util/defines.h" +#include "util/thread_data.h" + +GFXRECON_BEGIN_NAMESPACE(gfxrecon) +GFXRECON_BEGIN_NAMESPACE(encode) + +class CommandWriter +{ + public: + CommandWriter(OutputStreamWriter* p_output_stream_writer, util::Compressor* p_compressor); + + void WriteInitBufferCmd(format::ApiFamilyId p_api_family, + format::HandleId p_device_id, + format::HandleId p_buffer_id, + uint64_t p_offset, + uint64_t p_size, + const void* p_data); + + void WriteInitImageCmd(format::ApiFamilyId p_api_family, + format::HandleId p_device_id, + format::HandleId p_image_id, + uint32_t p_aspect, + uint32_t p_layout, + uint32_t p_mip_levels, + const std::vector& p_level_sizes, + uint64_t p_size, + const void* p_data); + + private: + OutputStreamWriter* output_stream_writer_; + util::Compressor* compressor_; +}; + +GFXRECON_END_NAMESPACE(encode) +GFXRECON_END_NAMESPACE(gfxrecon) + +#endif // GFXRECON_ENCODE_COMMAND_WRITER_H diff --git a/framework/encode/output_stream_writer.h b/framework/encode/output_stream_writer.h new file mode 100644 index 0000000000..0704336133 --- /dev/null +++ b/framework/encode/output_stream_writer.h @@ -0,0 +1,71 @@ +/* +** Copyright (c) 2025 LunarG, Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and associated documentation files (the "Software"), +** to deal in the Software without restriction, including without limitation +** the rights to use, copy, modify, merge, publish, distribute, sublicense, +** and/or sell copies of the Software, and to permit persons to whom the +** Software is furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +** FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +** DEALINGS IN THE SOFTWARE. +*/ + +#ifndef GFXRECON_ENCODE_OUTPUT_STREAM_WRITER_H +#define GFXRECON_ENCODE_OUTPUT_STREAM_WRITER_H + +#include "util/defines.h" +#include "util/compressor.h" +#include "util/thread_data.h" + +GFXRECON_BEGIN_NAMESPACE(gfxrecon) +GFXRECON_BEGIN_NAMESPACE(encode) + +/** + * @brief Common class with utilities to write to an output stream. + */ +class OutputStreamWriter +{ + public: + OutputStreamWriter() = default; + OutputStreamWriter(const OutputStreamWriter&) = delete; + OutputStreamWriter(OutputStreamWriter&&) = delete; + OutputStreamWriter& operator=(const OutputStreamWriter&) = delete; + OutputStreamWriter& operator=(OutputStreamWriter&&) noexcept = delete; + virtual ~OutputStreamWriter() = default; + + virtual util::ThreadData* GetThreadData() = 0; + virtual bool OutputStreamWrite(const void* data, size_t size) = 0; + + template + bool OutputStreamCombineAndWrite(const std::pair (&buffers)[N]) + { + static_assert(N != 1, "Use OutputStreamWrite(void*, size) when writing a single buffer."); + + // Combine buffers for a single write. + std::vector& scratch_buffer = GetThreadData()->GetScratchBuffer(); + scratch_buffer.clear(); + for (size_t i = 0; i < N; ++i) + { + const uint8_t* const data = reinterpret_cast(buffers[i].first); + const size_t size = buffers[i].second; + scratch_buffer.insert(scratch_buffer.end(), data, data + size); + } + + return OutputStreamWrite(scratch_buffer.data(), scratch_buffer.size()); + } +}; + +GFXRECON_END_NAMESPACE(encode) +GFXRECON_END_NAMESPACE(gfxrecon) + +#endif // GFXRECON_ENCODE_OUTPUT_STREAM_WRITER_H diff --git a/framework/encode/vulkan_capture_manager.cpp b/framework/encode/vulkan_capture_manager.cpp index 026d9041b8..2f908a7247 100644 --- a/framework/encode/vulkan_capture_manager.cpp +++ b/framework/encode/vulkan_capture_manager.cpp @@ -1,6 +1,6 @@ /* ** Copyright (c) 2018-2021 Valve Corporation - ** Copyright (c) 2018-2023 LunarG, Inc. + ** Copyright (c) 2018-2025 LunarG, Inc. ** Copyright (c) 2019-2023 Advanced Micro Devices, Inc. All rights reserved. ** ** Permission is hereby granted, free of charge, to any person obtaining a @@ -102,22 +102,22 @@ void VulkanCaptureManager::DestroyInstance() singleton_->common_manager_->DestroyInstance(singleton_); } -void VulkanCaptureManager::WriteTrackedState(util::FileOutputStream* file_stream, format::ThreadId thread_id) +void VulkanCaptureManager::WriteTrackedState(util::FileOutputStream* file_stream, util::ThreadData* thread_data) { uint64_t n_blocks = state_tracker_->WriteState( - file_stream, thread_id, [] { return GetUniqueId(); }, GetCompressor(), GetCurrentFrame(), nullptr, nullptr); + file_stream, thread_data, [] { return GetUniqueId(); }, GetCompressor(), GetCurrentFrame(), nullptr, nullptr); common_manager_->IncrementBlockIndex(n_blocks); } void VulkanCaptureManager::WriteTrackedStateWithAssetFile(util::FileOutputStream* file_stream, - format::ThreadId thread_id, + util::ThreadData* thread_data, util::FileOutputStream* asset_file_stream, const std::string* asset_file_name) { uint64_t n_blocks = state_tracker_->WriteState( file_stream, - thread_id, + thread_data, [] { return GetUniqueId(); }, GetCompressor(), GetCurrentFrame(), @@ -129,11 +129,11 @@ void VulkanCaptureManager::WriteTrackedStateWithAssetFile(util::FileOutputStream void VulkanCaptureManager::WriteAssets(util::FileOutputStream* asset_file_stream, const std::string* asset_file_name, - format::ThreadId thread_id) + util::ThreadData* thread_data) { assert(state_tracker_ != nullptr); state_tracker_->WriteAssets( - asset_file_stream, asset_file_name, thread_id, [] { return GetUniqueId(); }, GetCompressor()); + asset_file_stream, asset_file_name, thread_data, [] { return GetUniqueId(); }, GetCompressor()); } void VulkanCaptureManager::SetLayerFuncs(PFN_vkCreateInstance create_instance, PFN_vkCreateDevice create_device) @@ -1808,7 +1808,7 @@ void VulkanCaptureManager::PostProcess_vkEnumeratePhysicalDeviceGroups( } } -void VulkanCaptureManager::ProcessImportFd(VkDevice device, VkBuffer buffer, VkDeviceSize memoryOffset) +void VulkanCaptureManager::ProcessImportFdForBuffer(VkDevice device, VkBuffer buffer, VkDeviceSize memoryOffset) { auto* device_wrapper = vulkan_wrappers::GetWrapper(device); auto* buffer_wrapper = vulkan_wrappers::GetWrapper(buffer); @@ -1829,8 +1829,89 @@ void VulkanCaptureManager::ProcessImportFd(VkDevice device, VkBuffer buffer, VkD if (result == VK_SUCCESS) { WriteBeginResourceInitCmd(device_wrapper->handle_id, buffer_wrapper->size); - WriteInitBufferCmd( - device_wrapper->handle_id, buffer_wrapper->handle_id, memoryOffset, buffer_wrapper->size, data.data()); + + GetCommandWriter()->WriteInitBufferCmd(api_family_, + device_wrapper->handle_id, + buffer_wrapper->handle_id, + memoryOffset, + buffer_wrapper->size, + data.data()); + WriteEndResourceInitCmd(device_wrapper->handle_id); + } + } +} + +void VulkanCaptureManager::ProcessImportFdForImage(VkDevice device, VkImage image, VkDeviceSize memoryOffset) +{ + auto* device_wrapper = vulkan_wrappers::GetWrapper(device); + auto* image_wrapper = vulkan_wrappers::GetWrapper(image); + + // create staging buffer, bind this memory, write init image command + graphics::VulkanResourcesUtil resource_util(device_wrapper->handle, + device_wrapper->physical_device->handle, + device_wrapper->layer_table, + *device_wrapper->physical_device->layer_table_ref, + device_wrapper->physical_device->memory_properties); + + VkResult result = resource_util.CreateStagingBuffer(image_wrapper->size); + if (result != VK_SUCCESS) + { + return; + } + + std::vector aspects; + graphics::GetFormatAspects(image_wrapper->format, &aspects); + + for (auto aspect : aspects) + { + std::vector data; + std::vector subresource_offsets; + std::vector subresource_sizes; + bool scaling_supported; + + result = resource_util.ReadFromImageResourceStaging(image, + image_wrapper->format, + image_wrapper->image_type, + image_wrapper->extent, + image_wrapper->mip_levels, + image_wrapper->array_layers, + image_wrapper->tiling, + image_wrapper->samples, + image_wrapper->current_layout, + image_wrapper->queue_family_index, + aspect, + data, + subresource_offsets, + subresource_sizes, + scaling_supported, + true); + if (result == VK_SUCCESS) + { + // Combined size of all layers in a mip level. + std::vector level_sizes; + + uint64_t resource_size = resource_util.GetImageResourceSizesOptimal(image_wrapper->handle, + image_wrapper->format, + image_wrapper->image_type, + image_wrapper->extent, + image_wrapper->mip_levels, + image_wrapper->array_layers, + image_wrapper->tiling, + aspect, + nullptr, + &level_sizes, + true); + + WriteBeginResourceInitCmd(device_wrapper->handle_id, resource_size); + GetCommandWriter()->WriteInitImageCmd(api_family_, + device_wrapper->handle_id, + image_wrapper->handle_id, + aspect, + image_wrapper->current_layout, + image_wrapper->mip_levels, + level_sizes, + resource_size, + data.data()); WriteEndResourceInitCmd(device_wrapper->handle_id); } } @@ -1849,7 +1930,7 @@ void VulkanCaptureManager::PostProcess_vkBindBufferMemory( auto* memory_wrapper = vulkan_wrappers::GetWrapper(memory); if (memory_wrapper->imported_fd >= 0) { - ProcessImportFd(device, buffer, memoryOffset); + ProcessImportFdForBuffer(device, buffer, memoryOffset); } } } @@ -1877,7 +1958,7 @@ void VulkanCaptureManager::PostProcess_vkBindBufferMemory2(VkResult vulkan_wrappers::GetWrapper(pBindInfos[i].memory); if (memory_wrapper->imported_fd >= 0) { - ProcessImportFd(device, pBindInfos[i].buffer, pBindInfos[i].memoryOffset); + ProcessImportFdForBuffer(device, pBindInfos[i].buffer, pBindInfos[i].memoryOffset); } } } @@ -1891,6 +1972,14 @@ void VulkanCaptureManager::PostProcess_vkBindImageMemory( GFXRECON_ASSERT(state_tracker_ != nullptr); state_tracker_->TrackImageMemoryBinding(device, image, memory, memoryOffset); } + else if (IsCaptureModeWrite() && (result == VK_SUCCESS)) + { + auto* memory_wrapper = vulkan_wrappers::GetWrapper(memory); + if (memory_wrapper->imported_fd >= 0) + { + ProcessImportFdForImage(device, image, memoryOffset); + } + } } void VulkanCaptureManager::PostProcess_vkBindImageMemory2(VkResult result, @@ -1908,6 +1997,18 @@ void VulkanCaptureManager::PostProcess_vkBindImageMemory2(VkResult device, pBindInfos[i].image, pBindInfos[i].memory, pBindInfos[i].memoryOffset, pBindInfos[i].pNext); } } + else if (IsCaptureModeWrite() && (result == VK_SUCCESS) && (pBindInfos != nullptr)) + { + for (uint32_t i = 0; i < bindInfoCount; ++i) + { + auto* memory_wrapper = + vulkan_wrappers::GetWrapper(pBindInfos[i].memory); + if (memory_wrapper->imported_fd >= 0) + { + ProcessImportFdForImage(device, pBindInfos[i].image, pBindInfos[i].memoryOffset); + } + } + } } void VulkanCaptureManager::PreProcess_vkCreateXlibSurfaceKHR(VkInstance instance, diff --git a/framework/encode/vulkan_capture_manager.h b/framework/encode/vulkan_capture_manager.h index 5bf8bce7cb..5f515d441a 100644 --- a/framework/encode/vulkan_capture_manager.h +++ b/framework/encode/vulkan_capture_manager.h @@ -1,6 +1,6 @@ /* ** Copyright (c) 2018-2021 Valve Corporation - ** Copyright (c) 2018-2023 LunarG, Inc. + ** Copyright (c) 2018-2025 LunarG, Inc. ** Copyright (c) 2019-2023 Advanced Micro Devices, Inc. All rights reserved. ** ** Permission is hereby granted, free of charge, to any person obtaining a @@ -769,7 +769,8 @@ class VulkanCaptureManager : public ApiCaptureManager } } - void ProcessImportFd(VkDevice device, VkBuffer buffer, VkDeviceSize memoryOffset); + void ProcessImportFdForBuffer(VkDevice device, VkBuffer buffer, VkDeviceSize memoryOffset); + void ProcessImportFdForImage(VkDevice device, VkImage image, VkDeviceSize memoryOffset); void PostProcess_vkBindBufferMemory( VkResult result, VkDevice device, VkBuffer buffer, VkDeviceMemory memory, VkDeviceSize memoryOffset); @@ -1571,16 +1572,16 @@ class VulkanCaptureManager : public ApiCaptureManager state_tracker_ = nullptr; } - virtual void WriteTrackedState(util::FileOutputStream* file_stream, format::ThreadId thread_id) override; + virtual void WriteTrackedState(util::FileOutputStream* file_stream, util::ThreadData* thread_data) override; virtual void WriteTrackedStateWithAssetFile(util::FileOutputStream* file_stream, - format::ThreadId thread_id, + util::ThreadData* thread_data, util::FileOutputStream* asset_file_stream, const std::string* asset_file_name) override; virtual void WriteAssets(util::FileOutputStream* asset_file_stream, const std::string* asset_file_name, - format::ThreadId thread_id) override; + util::ThreadData* thread_data) override; private: struct HardwareBufferInfo diff --git a/framework/encode/vulkan_state_tracker.h b/framework/encode/vulkan_state_tracker.h index 05f7056d5a..3208cc1638 100644 --- a/framework/encode/vulkan_state_tracker.h +++ b/framework/encode/vulkan_state_tracker.h @@ -1,5 +1,5 @@ /* -** Copyright (c) 2019-2021 LunarG, Inc. +** Copyright (c) 2019-2025 LunarG, Inc. ** ** Permission is hereby granted, free of charge, to any person obtaining a ** copy of this software and associated documentation files (the "Software"), @@ -56,7 +56,7 @@ class VulkanStateTracker ~VulkanStateTracker(); uint64_t WriteState(util::FileOutputStream* file_stream, - format::ThreadId thread_id, + util::ThreadData* thread_data, std::function get_unique_id_fn, util::Compressor* compressor, uint64_t frame_number, @@ -65,7 +65,7 @@ class VulkanStateTracker { VulkanStateWriter state_writer(file_stream, compressor, - thread_id, + thread_data, get_unique_id_fn, asset_file_stream, asset_file_name, @@ -77,15 +77,20 @@ class VulkanStateTracker uint64_t WriteAssets(util::FileOutputStream* asset_file_stream, const std::string* asset_file_name, - format::ThreadId thread_id, + util::ThreadData* thread_data, std::function get_unique_id_fn, util::Compressor* compressor) { assert(asset_file_stream != nullptr); assert(asset_file_name != nullptr); - VulkanStateWriter state_writer( - nullptr, compressor, thread_id, get_unique_id_fn, asset_file_stream, asset_file_name, &asset_file_offsets_); + VulkanStateWriter state_writer(nullptr, + compressor, + thread_data, + get_unique_id_fn, + asset_file_stream, + asset_file_name, + &asset_file_offsets_); std::unique_lock lock(state_table_mutex_); return state_writer.WriteAssets(state_table_); diff --git a/framework/encode/vulkan_state_writer.cpp b/framework/encode/vulkan_state_writer.cpp index afe054fb97..cb3da0c60f 100644 --- a/framework/encode/vulkan_state_writer.cpp +++ b/framework/encode/vulkan_state_writer.cpp @@ -1,5 +1,5 @@ /* - ** Copyright (c) 2019-2020 LunarG, Inc. + ** Copyright (c) 2019-2025 LunarG, Inc. ** Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. ** ** Permission is hereby granted, free of charge, to any person obtaining a @@ -85,15 +85,14 @@ static bool IsImageReadable(VkMemoryPropertyFlags property VulkanStateWriter::VulkanStateWriter(util::FileOutputStream* output_stream, util::Compressor* compressor, - format::ThreadId thread_id, + util::ThreadData* thread_data, std::function get_unique_id_fn, util::FileOutputStream* asset_file_stream, const std::string* asset_file_name, VulkanStateWriter::AssetFileOffsetsInfo* asset_file_offsets) : - output_stream_(output_stream), - compressor_(compressor), thread_id_(thread_id), encoder_(¶meter_stream_), + output_stream_(output_stream), compressor_(compressor), thread_data_(thread_data), encoder_(¶meter_stream_), get_unique_id_(std::move(get_unique_id_fn)), asset_file_stream_(asset_file_stream), - asset_file_offsets_(asset_file_offsets) + asset_file_offsets_(asset_file_offsets), command_writer_(CommandWriter(this, compressor_)) { assert(output_stream != nullptr || asset_file_stream != nullptr); @@ -1355,7 +1354,7 @@ void VulkanStateWriter::WriteDeviceMemoryState(const VulkanStateTable& state_tab for (auto hardware_buffer : hardware_buffers) { const vulkan_wrappers::DeviceMemoryWrapper* wrapper = hardware_buffer.second; - CommonProcessHardwareBuffer(thread_id_, + CommonProcessHardwareBuffer(thread_data_->thread_id_, wrapper->hardware_buffer_memory_id, wrapper->hardware_buffer, wrapper->allocation_size, @@ -1427,7 +1426,7 @@ void VulkanStateWriter::BeginAccelerationStructuresSection(format::HandleId devi begin_cmd.meta_header.block_header.type = format::kMetaDataBlock; begin_cmd.meta_header.meta_data_id = format::MakeMetaDataId(format::ApiFamilyId::ApiFamily_Vulkan, format::MetaDataType::kBeginResourceInitCommand); - begin_cmd.thread_id = thread_id_; + begin_cmd.thread_id = thread_data_->thread_id_; begin_cmd.device_id = device_id; begin_cmd.max_resource_size = max_resource_size; // Our buffers should not need staging copy as the memroy should be host visible and coherent @@ -1563,7 +1562,7 @@ void VulkanStateWriter::InitializeASInputBuffer(ASInputBuffer& buffer) upload_cmd.meta_header.block_header.type = format::kMetaDataBlock; upload_cmd.meta_header.meta_data_id = format::MakeMetaDataId(format::ApiFamilyId::ApiFamily_Vulkan, format::MetaDataType::kInitBufferCommand); - upload_cmd.thread_id = thread_id_; + upload_cmd.thread_id = thread_data_->thread_id_; upload_cmd.device_id = device_id; upload_cmd.buffer_id = buffer.handle_id; upload_cmd.data_size = data_size; @@ -1621,7 +1620,7 @@ void VulkanStateWriter::EndAccelerationStructureSection(format::HandleId device_ end_cmd.meta_header.block_header.type = format::kMetaDataBlock; end_cmd.meta_header.meta_data_id = format::MakeMetaDataId(format::ApiFamilyId::ApiFamily_Vulkan, format::MetaDataType::kEndResourceInitCommand); - end_cmd.thread_id = thread_id_; + end_cmd.thread_id = thread_data_->thread_id_; end_cmd.device_id = device_id; output_stream_->Write(&end_cmd, sizeof(end_cmd)); @@ -1643,7 +1642,7 @@ void VulkanStateWriter::WriteTlasToBlasDependenciesMetadata(const VulkanStateTab format::GetMetaDataBlockBaseSize(tlas_to_blas) + blas_count * sizeof(format::HandleId); tlas_to_blas.meta_header.meta_data_id = format::MakeMetaDataId( format::ApiFamilyId::ApiFamily_Vulkan, format::MetaDataType::kParentToChildDependency); - tlas_to_blas.thread_id = thread_id_; + tlas_to_blas.thread_id = thread_data_->thread_id_; tlas_to_blas.dependency_type = format::ParentToChildDependencyType::kAccelerationStructuresDependency; tlas_to_blas.parent_id = tlas->handle_id; tlas_to_blas.child_count = static_cast(blas_count); @@ -2062,7 +2061,7 @@ void VulkanStateWriter::ProcessBufferMemory(const vulkan_wrappers::DeviceWrapper upload_cmd.meta_header.block_header.type = format::kMetaDataBlock; upload_cmd.meta_header.meta_data_id = format::MakeMetaDataId(format::ApiFamilyId::ApiFamily_Vulkan, format::MetaDataType::kInitBufferCommand); - upload_cmd.thread_id = thread_id_; + upload_cmd.thread_id = thread_data_->thread_id_; upload_cmd.device_id = device_wrapper->handle_id; upload_cmd.buffer_id = buffer_wrapper->handle_id; upload_cmd.data_size = data_size; @@ -2178,7 +2177,7 @@ void VulkanStateWriter::ProcessBufferMemoryWithAssetFile(const vulkan_wrappers:: upload_cmd.meta_header.block_header.type = format::kMetaDataBlock; upload_cmd.meta_header.meta_data_id = format::MakeMetaDataId(format::ApiFamilyId::ApiFamily_Vulkan, format::MetaDataType::kInitBufferCommand); - upload_cmd.thread_id = thread_id_; + upload_cmd.thread_id = thread_data_->thread_id_; upload_cmd.device_id = device_wrapper->handle_id; upload_cmd.buffer_id = buffer_wrapper->handle_id; upload_cmd.data_size = data_size; @@ -2314,66 +2313,19 @@ void VulkanStateWriter::ProcessImageMemory(const vulkan_wrappers::DeviceWrapper* if (!image_wrapper->is_swapchain_image) { - format::InitImageCommandHeader upload_cmd; + command_writer_.WriteInitImageCmd(format::ApiFamilyId::ApiFamily_Vulkan, + device_wrapper->handle_id, + image_wrapper->handle_id, + snapshot_entry.aspect, + image_wrapper->current_layout, + image_wrapper->mip_levels, + snapshot_entry.level_sizes, + snapshot_entry.resource_size, + bytes); - // Packet size without the resource data. - upload_cmd.meta_header.block_header.size = format::GetMetaDataBlockBaseSize(upload_cmd); - upload_cmd.meta_header.block_header.type = format::kMetaDataBlock; - upload_cmd.meta_header.meta_data_id = - format::MakeMetaDataId(format::ApiFamilyId::ApiFamily_Vulkan, format::MetaDataType::kInitImageCommand); - upload_cmd.thread_id = thread_id_; - upload_cmd.device_id = device_wrapper->handle_id; - upload_cmd.image_id = image_wrapper->handle_id; - upload_cmd.aspect = snapshot_entry.aspect; - upload_cmd.layout = image_wrapper->current_layout; - - if (bytes != nullptr) - { - GFXRECON_CHECK_CONVERSION_DATA_LOSS(size_t, snapshot_entry.resource_size); - - size_t data_size = static_cast(snapshot_entry.resource_size); - - // Store uncompressed data size in packet. - upload_cmd.data_size = data_size; - upload_cmd.level_count = image_wrapper->mip_levels; - - if (compressor_ != nullptr) - { - size_t compressed_size = compressor_->Compress(data_size, bytes, &compressed_parameter_buffer_, 0); - - if ((compressed_size > 0) && (compressed_size < data_size)) - { - upload_cmd.meta_header.block_header.type = format::BlockType::kCompressedMetaDataBlock; - - bytes = compressed_parameter_buffer_.data(); - data_size = compressed_size; - } - } - - // Calculate size of packet with compressed or uncompressed data size. - assert(!snapshot_entry.level_sizes.empty() && - (snapshot_entry.level_sizes.size() == upload_cmd.level_count)); - size_t levels_size = snapshot_entry.level_sizes.size() * sizeof(snapshot_entry.level_sizes[0]); - - upload_cmd.meta_header.block_header.size += levels_size + data_size; - - output_stream_->Write(&upload_cmd, sizeof(upload_cmd)); - output_stream_->Write(snapshot_entry.level_sizes.data(), levels_size); - output_stream_->Write(bytes, data_size); - - if (!snapshot_entry.need_staging_copy && memory_wrapper->mapped_data == nullptr) - { - device_table->UnmapMemory(device_wrapper->handle, memory_wrapper->handle); - } - } - else + if (!snapshot_entry.need_staging_copy && memory_wrapper->mapped_data == nullptr) { - // Write a packet without resource data; replay must still perform a layout transition at image - // initialization. - upload_cmd.data_size = 0; - upload_cmd.level_count = 0; - - output_stream_->Write(&upload_cmd, sizeof(upload_cmd)); + device_table->UnmapMemory(device_wrapper->handle, memory_wrapper->handle); } ++blocks_written_; @@ -2479,7 +2431,7 @@ void VulkanStateWriter::ProcessImageMemoryWithAssetFile(const vulkan_wrappers::D upload_cmd.meta_header.block_header.type = format::kMetaDataBlock; upload_cmd.meta_header.meta_data_id = format::MakeMetaDataId(format::ApiFamilyId::ApiFamily_Vulkan, format::MetaDataType::kInitImageCommand); - upload_cmd.thread_id = thread_id_; + upload_cmd.thread_id = thread_data_->thread_id_; upload_cmd.device_id = device_wrapper->handle_id; upload_cmd.image_id = image_wrapper->handle_id; upload_cmd.aspect = snapshot_entry.aspect; @@ -2883,7 +2835,7 @@ void VulkanStateWriter::WriteResourceMemoryState(const VulkanStateTable& state_t begin_cmd.meta_header.block_header.type = format::kMetaDataBlock; begin_cmd.meta_header.meta_data_id = format::MakeMetaDataId( format::ApiFamilyId::ApiFamily_Vulkan, format::MetaDataType::kBeginResourceInitCommand); - begin_cmd.thread_id = thread_id_; + begin_cmd.thread_id = thread_data_->thread_id_; begin_cmd.device_id = device_wrapper->handle_id; begin_cmd.max_resource_size = max_resource_size; begin_cmd.max_copy_size = max_staging_copy_size; @@ -2913,7 +2865,7 @@ void VulkanStateWriter::WriteResourceMemoryState(const VulkanStateTable& state_t end_cmd.meta_header.block_header.type = format::kMetaDataBlock; end_cmd.meta_header.meta_data_id = format::MakeMetaDataId( format::ApiFamilyId::ApiFamily_Vulkan, format::MetaDataType::kEndResourceInitCommand); - end_cmd.thread_id = thread_id_; + end_cmd.thread_id = thread_data_->thread_id_; end_cmd.device_id = device_wrapper->handle_id; output_stream_->Write(&end_cmd, sizeof(end_cmd)); @@ -2980,7 +2932,7 @@ void VulkanStateWriter::WriteSwapchainImageState(const VulkanStateTable& state_t // Initialize block data for set-swapchain-image-state meta-data command. header.meta_header.meta_data_id = format::MakeMetaDataId(format::ApiFamilyId::ApiFamily_Vulkan, format::MetaDataType::kSetSwapchainImageStateCommand); - header.thread_id = thread_id_; + header.thread_id = thread_data_->thread_id_; header.device_id = device_wrapper->handle_id; header.swapchain_id = wrapper->handle_id; header.last_presented_image = wrapper->last_presented_image; @@ -3746,7 +3698,7 @@ void VulkanStateWriter::WriteFunctionCall(format::ApiCallId call_id, compressed_header.block_header.type = format::BlockType::kCompressedFunctionCallBlock; compressed_header.api_call_id = call_id; - compressed_header.thread_id = thread_id_; + compressed_header.thread_id = thread_data_->thread_id_; compressed_header.uncompressed_size = uncompressed_size; packet_size += sizeof(compressed_header.api_call_id) + sizeof(compressed_header.uncompressed_size) + @@ -3767,7 +3719,7 @@ void VulkanStateWriter::WriteFunctionCall(format::ApiCallId call_id, uncompressed_header.block_header.type = format::BlockType::kFunctionCallBlock; uncompressed_header.api_call_id = call_id; - uncompressed_header.thread_id = thread_id_; + uncompressed_header.thread_id = thread_data_->thread_id_; packet_size += sizeof(uncompressed_header.api_call_id) + sizeof(uncompressed_header.thread_id) + data_size; @@ -3804,7 +3756,7 @@ void VulkanStateWriter::WriteFillMemoryCmd(format::HandleId memory_id, fill_cmd.meta_header.block_header.type = format::BlockType::kMetaDataBlock; fill_cmd.meta_header.meta_data_id = format::MakeMetaDataId(format::ApiFamilyId::ApiFamily_Vulkan, format::MetaDataType::kFillMemoryCommand); - fill_cmd.thread_id = thread_id_; + fill_cmd.thread_id = thread_data_->thread_id_; fill_cmd.memory_id = memory_id; fill_cmd.memory_offset = offset; fill_cmd.memory_size = size; @@ -3842,7 +3794,7 @@ void VulkanStateWriter::WriteResizeWindowCmd(format::HandleId surface_id, uint32 resize_cmd.meta_header.block_header.size = format::GetMetaDataBlockBaseSize(resize_cmd); resize_cmd.meta_header.meta_data_id = format::MakeMetaDataId(format::ApiFamilyId::ApiFamily_Vulkan, format::MetaDataType::kResizeWindowCommand); - resize_cmd.thread_id = thread_id_; + resize_cmd.thread_id = thread_data_->thread_id_; resize_cmd.surface_id = surface_id; resize_cmd.width = width; @@ -3865,7 +3817,7 @@ void VulkanStateWriter::WriteResizeWindowCmd2(format::HandleId surf resize_cmd2.meta_header.block_header.size = format::GetMetaDataBlockBaseSize(resize_cmd2); resize_cmd2.meta_header.meta_data_id = format::MakeMetaDataId(format::ApiFamilyId::ApiFamily_Vulkan, format::MetaDataType::kResizeWindowCommand2); - resize_cmd2.thread_id = thread_id_; + resize_cmd2.thread_id = thread_data_->thread_id_; resize_cmd2.surface_id = surface_id; resize_cmd2.width = width; @@ -3909,7 +3861,7 @@ void VulkanStateWriter::WriteSetDevicePropertiesCommand(format::HandleId properties_cmd.meta_header.block_header.size = format::GetMetaDataBlockBaseSize(properties_cmd) + device_name_len; properties_cmd.meta_header.meta_data_id = format::MakeMetaDataId(format::ApiFamilyId::ApiFamily_Vulkan, format::MetaDataType::kSetDevicePropertiesCommand); - properties_cmd.thread_id = thread_id_; + properties_cmd.thread_id = thread_data_->thread_id_; properties_cmd.physical_device_id = physical_device_id; properties_cmd.api_version = properties.apiVersion; properties_cmd.driver_version = properties.driverVersion; @@ -3938,7 +3890,7 @@ void VulkanStateWriter::WriteSetDeviceMemoryPropertiesCommand(format::HandleId p (sizeof(format::DeviceMemoryHeap) * memory_properties.memoryHeapCount); memory_properties_cmd.meta_header.meta_data_id = format::MakeMetaDataId( format::ApiFamilyId::ApiFamily_Vulkan, format::MetaDataType::kSetDeviceMemoryPropertiesCommand); - memory_properties_cmd.thread_id = thread_id_; + memory_properties_cmd.thread_id = thread_data_->thread_id_; memory_properties_cmd.physical_device_id = physical_device_id; memory_properties_cmd.memory_type_count = memory_properties.memoryTypeCount; memory_properties_cmd.memory_heap_count = memory_properties.memoryHeapCount; @@ -3976,7 +3928,7 @@ void VulkanStateWriter::WriteSetOpaqueAddressCommand(format::HandleId device_id, opaque_address_cmd.meta_header.block_header.size = format::GetMetaDataBlockBaseSize(opaque_address_cmd); opaque_address_cmd.meta_header.meta_data_id = format::MakeMetaDataId(format::ApiFamilyId::ApiFamily_Vulkan, format::MetaDataType::kSetOpaqueAddressCommand); - opaque_address_cmd.thread_id = thread_id_; + opaque_address_cmd.thread_id = thread_data_->thread_id_; opaque_address_cmd.device_id = device_id; opaque_address_cmd.object_id = object_id; opaque_address_cmd.address = address; @@ -3997,7 +3949,7 @@ void VulkanStateWriter::WriteSetRayTracingShaderGroupHandlesCommand(format::Hand set_handles_cmd.meta_header.block_header.size = format::GetMetaDataBlockBaseSize(set_handles_cmd) + data_size; set_handles_cmd.meta_header.meta_data_id = format::MakeMetaDataId( format::ApiFamilyId::ApiFamily_Vulkan, format::MetaDataType::kSetRayTracingShaderGroupHandlesCommand); - set_handles_cmd.thread_id = thread_id_; + set_handles_cmd.thread_id = thread_data_->thread_id_; set_handles_cmd.device_id = device_id; set_handles_cmd.pipeline_id = pipeline_id; set_handles_cmd.data_size = data_size; @@ -4333,7 +4285,7 @@ void VulkanStateWriter::WriteExecuteFromFile(const std::string& filename, uint32 execute_from_file.meta_header.block_header.type = format::kMetaDataBlock; execute_from_file.meta_header.meta_data_id = format::MakeMetaDataId(format::ApiFamilyId::ApiFamily_Vulkan, format::MetaDataType::kExecuteBlocksFromFile); - execute_from_file.thread_id = thread_id_; + execute_from_file.thread_id = thread_data_->thread_id_; execute_from_file.n_blocks = n_blocks; execute_from_file.offset = offset; execute_from_file.filename_length = filename_length; diff --git a/framework/encode/vulkan_state_writer.h b/framework/encode/vulkan_state_writer.h index 2e53a2f351..7fd8d01085 100644 --- a/framework/encode/vulkan_state_writer.h +++ b/framework/encode/vulkan_state_writer.h @@ -1,5 +1,5 @@ /* - ** Copyright (c) 2019-2021 LunarG, Inc. + ** Copyright (c) 2019-2025 LunarG, Inc. ** Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. ** ** Permission is hereby granted, free of charge, to any person obtaining a @@ -24,8 +24,10 @@ #ifndef GFXRECON_ENCODE_VULKAN_STATE_WRITER_H #define GFXRECON_ENCODE_VULKAN_STATE_WRITER_H +#include "encode/command_writer.h" #include "encode/parameter_encoder.h" #include "encode/vulkan_handle_wrappers.h" +#include "encode/output_stream_writer.h" #include "generated/generated_vulkan_state_table.h" #include "format/format.h" #include "format/platform_types.h" @@ -35,6 +37,7 @@ #include "util/defines.h" #include "util/file_output_stream.h" #include "util/memory_output_stream.h" +#include "util/thread_data.h" #include "vulkan/vulkan.h" @@ -46,26 +49,28 @@ GFXRECON_BEGIN_NAMESPACE(gfxrecon) GFXRECON_BEGIN_NAMESPACE(encode) -class VulkanStateWriter +class VulkanStateWriter : public OutputStreamWriter { public: using AssetFileOffsetsInfo = std::unordered_map; VulkanStateWriter(util::FileOutputStream* output_stream, util::Compressor* compressor, - format::ThreadId thread_id, + util::ThreadData* thread_data, std::function get_unique_id_fn, util::FileOutputStream* asset_file_stream = nullptr, const std::string* asset_file_name = nullptr, VulkanStateWriter::AssetFileOffsetsInfo* asset_file_offsets = nullptr); + util::ThreadData* GetThreadData() override { return thread_data_; } + + bool OutputStreamWrite(const void* data, size_t len) override; + // Returns number of blocks written to the output_stream. uint64_t WriteState(const VulkanStateTable& state_table, uint64_t frame_number); uint64_t WriteAssets(const VulkanStateTable& state_table); - bool OutputStreamWrite(const void* data, size_t len); - void WriteFillMemoryCmd(format::HandleId memory_id, VkDeviceSize offset, VkDeviceSize size, const void* data); private: @@ -421,7 +426,7 @@ class VulkanStateWriter util::FileOutputStream* output_stream_; util::Compressor* compressor_; std::vector compressed_parameter_buffer_; - format::ThreadId thread_id_; + util::ThreadData* thread_data_; util::MemoryOutputStream parameter_stream_; ParameterEncoder encoder_; uint64_t blocks_written_{ 0 }; @@ -432,6 +437,8 @@ class VulkanStateWriter util::FileOutputStream* asset_file_stream_; std::string asset_file_name_; AssetFileOffsetsInfo* asset_file_offsets_; + + CommandWriter command_writer_; }; GFXRECON_END_NAMESPACE(encode) diff --git a/framework/util/CMakeLists.txt b/framework/util/CMakeLists.txt index 5289d49de4..ce8baad4e4 100644 --- a/framework/util/CMakeLists.txt +++ b/framework/util/CMakeLists.txt @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2018-2020,2023 LunarG, Inc. +# Copyright (c) 2018-2020,2023-2025 LunarG, Inc. # Copyright (c) 2018-2023 Advanced Micro Devices, Inc. All rights reserved # All rights reserved # @@ -100,8 +100,8 @@ target_sources(gfxrecon_util ${CMAKE_CURRENT_LIST_DIR}/strings.cpp ${CMAKE_CURRENT_LIST_DIR}/to_string.h ${CMAKE_CURRENT_LIST_DIR}/to_string.cpp - ${CMAKE_CURRENT_LIST_DIR}/options.h - ${CMAKE_CURRENT_LIST_DIR}/options.cpp + ${CMAKE_CURRENT_LIST_DIR}/thread_data.h + ${CMAKE_CURRENT_LIST_DIR}/thread_data.cpp ${CMAKE_CURRENT_LIST_DIR}/custom_common_to_string.h ${CMAKE_CURRENT_LIST_DIR}/custom_common_to_string.cpp ${CMAKE_SOURCE_DIR}/framework/generated/generated_vulkan_enum_to_string.h diff --git a/framework/util/thread_data.cpp b/framework/util/thread_data.cpp new file mode 100644 index 0000000000..0abc398cb5 --- /dev/null +++ b/framework/util/thread_data.cpp @@ -0,0 +1,62 @@ +/* + ** Copyright (c) 2025 LunarG, Inc. + ** + ** Permission is hereby granted, free of charge, to any person obtaining a + ** copy of this software and associated documentation files (the "Software"), + ** to deal in the Software without restriction, including without limitation + ** the rights to use, copy, modify, merge, publish, distribute, sublicense, + ** and/or sell copies of the Software, and to permit persons to whom the + ** Software is furnished to do so, subject to the following conditions: + ** + ** The above copyright notice and this permission notice shall be included in + ** all copies or substantial portions of the Software. + ** + ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + ** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + ** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + ** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + ** FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + ** DEALINGS IN THE SOFTWARE. + */ + +#include "util/thread_data.h" + +GFXRECON_BEGIN_NAMESPACE(gfxrecon) +GFXRECON_BEGIN_NAMESPACE(util) + +std::mutex ThreadData::count_lock_; +format::ThreadId ThreadData::thread_count_ = 0; +std::unordered_map ThreadData::id_map_; + +ThreadData::ThreadData() : + thread_id_(GetThreadId()), object_id_(format::kNullHandleId), call_id_(format::ApiCallId::ApiCall_Unknown), + block_index_(0) +{ + parameter_buffer_ = std::make_unique(); + parameter_encoder_ = std::make_unique(parameter_buffer_.get()); +} + +format::ThreadId ThreadData::GetThreadId() +{ + format::ThreadId id = 0; + uint64_t tid = util::platform::GetCurrentThreadId(); + + // Using a uint64_t sequence number associated with the thread ID. + std::lock_guard lock(count_lock_); + auto entry = id_map_.find(tid); + if (entry != id_map_.end()) + { + id = entry->second; + } + else + { + id = ++thread_count_; + id_map_.insert(std::make_pair(tid, id)); + } + + return id; +} + +GFXRECON_END_NAMESPACE(util) +GFXRECON_END_NAMESPACE(gfxrecon) diff --git a/framework/util/thread_data.h b/framework/util/thread_data.h new file mode 100644 index 0000000000..c14fefafe3 --- /dev/null +++ b/framework/util/thread_data.h @@ -0,0 +1,70 @@ +/* +** Copyright (c) 2025 LunarG, Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and associated documentation files (the "Software"), +** to deal in the Software without restriction, including without limitation +** the rights to use, copy, modify, merge, publish, distribute, sublicense, +** and/or sell copies of the Software, and to permit persons to whom the +** Software is furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +** FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +** DEALINGS IN THE SOFTWARE. +*/ + +#ifndef GFXRECON_UTIL_THREAD_DATA_H +#define GFXRECON_UTIL_THREAD_DATA_H + +#include "encode/handle_unwrap_memory.h" +#include "encode/parameter_buffer.h" +#include "encode/parameter_encoder.h" +#include "format/format.h" +#include "util/defines.h" + +GFXRECON_BEGIN_NAMESPACE(gfxrecon) +GFXRECON_BEGIN_NAMESPACE(util) + +class ThreadData +{ + public: + ThreadData(); + + ~ThreadData() {} + + std::vector& GetScratchBuffer() { return scratch_buffer_; } + + public: + const format::ThreadId thread_id_; + format::ApiCallId call_id_; + format::HandleId object_id_; + std::unique_ptr parameter_buffer_; + std::unique_ptr parameter_encoder_; + std::vector compressed_buffer_; + encode::HandleUnwrapMemory handle_unwrap_memory_; + uint64_t block_index_; + + private: + static format::ThreadId GetThreadId(); + + private: + static std::mutex count_lock_; + static format::ThreadId thread_count_; + static std::unordered_map id_map_; + + private: + // Used for combining multiple buffers for a single file write. + std::vector scratch_buffer_; +}; + +GFXRECON_END_NAMESPACE(util) +GFXRECON_END_NAMESPACE(gfxrecon) + +#endif // GFXRECON_UTIL_THREAD_DATA_H