From 20769859078428082c785d3c1ba0e0a959b44b89 Mon Sep 17 00:00:00 2001 From: ahosier Date: Mon, 13 Nov 2023 15:02:53 -0500 Subject: [PATCH] Update source code for RMV v1.8 release --- CMakeLists.txt | 12 +- build/dependency_map.py | 8 +- build/fetch_dependencies.py | 15 +- build/pre_build.py | 3 - cmake/dev_tools.cmake | 4 - source/backend/CMakeLists.txt | 11 +- source/backend/rmt_data_set.cpp | 101 ++++- source/backend/rmt_data_set.h | 14 + .../backend/rmt_memory_aliasing_timeline.cpp | 218 +++++++++++ source/backend/rmt_memory_aliasing_timeline.h | 348 ++++++++++++++++++ source/backend/rmt_rdf_file_parser.cpp | 11 +- source/backend/rmt_resource_list.cpp | 77 +++- source/backend/rmt_resource_list.h | 16 +- source/backend/rmt_trace_loader.cpp | 2 +- .../backend/rmt_virtual_allocation_list.cpp | 198 ++++++---- source/backend/rmt_virtual_allocation_list.h | 1 + source/frontend/CMakeLists.txt | 116 +----- .../managers/load_animation_manager.cpp | 15 +- .../managers/load_animation_manager.h | 16 +- source/frontend/managers/message_manager.h | 5 + source/frontend/managers/snapshot_manager.cpp | 2 +- source/frontend/models/colorizer_base.cpp | 3 +- .../snapshot/resource_details_model.cpp | 2 +- .../snapshot/resource_overview_model.cpp | 2 +- .../models/timeline/timeline_model.cpp | 47 ++- .../frontend/models/timeline/timeline_model.h | 12 +- source/frontend/stylesheet.qss | 5 + source/frontend/util/constants.h | 5 +- source/frontend/util/thread_controller.cpp | 22 +- source/frontend/util/thread_controller.h | 20 +- .../rmv_cancellable_loading_widget.cpp | 78 ++++ .../rmv_cancellable_loading_widget.h | 51 +++ .../custom_widgets/rmv_resource_details.cpp | 2 +- .../custom_widgets/rmv_timeline_graph.cpp | 2 +- source/frontend/views/main_window.cpp | 10 + source/frontend/views/main_window.h | 5 + .../frontend/views/timeline/timeline_pane.cpp | 30 +- .../frontend/views/timeline/timeline_pane.h | 3 + source/parser/CMakeLists.txt | 10 +- 39 files changed, 1220 insertions(+), 282 deletions(-) create mode 100644 source/backend/rmt_memory_aliasing_timeline.cpp create mode 100644 source/backend/rmt_memory_aliasing_timeline.h create mode 100644 source/frontend/views/custom_widgets/rmv_cancellable_loading_widget.cpp create mode 100644 source/frontend/views/custom_widgets/rmv_cancellable_loading_widget.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 14d4066..b297c4d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,7 @@ project(RMV) # Define version information set(RMV_MAJOR_VERSION 1) -set(RMV_MINOR_VERSION 7) +set(RMV_MINOR_VERSION 8) if (NOT RMV_BUGFIX_NUMBER) set(RMV_BUGFIX_NUMBER 0) endif () @@ -32,14 +32,14 @@ option(RDF_STATIC "Build RDF as a static library" ON) ## For RMV we only care about the Debug and Release configuration types set(CMAKE_CONFIGURATION_TYPES Debug Release) -## Determine build suffixes based on configuration, bitness and internal status +## Determine build suffixes based on configuration and bitness ## These values will be inherited by all child projects set(ADT_PLATFORM_POSTFIX "-x86") IF(CMAKE_SIZEOF_VOID_P EQUAL 8) set(ADT_PLATFORM_POSTFIX "-x64") ENDIF() -# As default for RMV, include the debug & internal status in filename - but not the platform bitness +# As default for RMV, include the debug status in filename - but not the platform bitness set (CMAKE_DEBUG_POSTFIX -d) set (CMAKE_RELEASE_POSTFIX) @@ -63,11 +63,10 @@ include_directories("${PROJECT_SOURCE_DIR}/external/third_party/") # Global compiler options IF(WIN32) - add_compile_options(/MP) # bump the stack size add_link_options(/STACK:16777216) ELSEIF(UNIX) - add_compile_options(-D_LINUX -mno-avx2) + add_compile_options(-mno-avx2) # Allow executable to be double clicked. add_link_options(-no-pie) # Use _DEBUG on Unix for Debug Builds (defined automatically on Windows) @@ -101,9 +100,6 @@ MACRO(SOURCE_GROUP_BY_FOLDER target) ENDIF (files) ENDMACRO(SOURCE_GROUP_BY_FOLDER) -# Define C++ standard for RMV -set(CMAKE_CXX_STANDARD 17) - add_subdirectory(external/qt_common qt_common) add_subdirectory(external/rdf/imported/zstd) add_subdirectory(external/rdf/rdf) diff --git a/build/dependency_map.py b/build/dependency_map.py index 1893df0..92b7de0 100644 --- a/build/dependency_map.py +++ b/build/dependency_map.py @@ -21,8 +21,8 @@ # Define a set of dependencies that exist as separate git projects. # each git dependency has a desired directory where it will be cloned - along with a commit to checkout git_mapping = { - github_tools + "QtCommon" : ["../external/qt_common", "v3.12.0"], - github_tools + "UpdateCheckApi" : ["../external/update_check_api", "v2.0.1"], - github_tools + "system_info_utils" : ["../external/system_info_utils", "88a338a01949f8d8bad60a30b78b65300fd13a9f"], - github_root + "GPUOpen-Drivers/libamdrdf" : ["../external/rdf", "v1.1.2"], + github_tools + "QtCommon" : ["../external/qt_common", "v3.12.0", True], + github_tools + "UpdateCheckApi" : ["../external/update_check_api", "v2.1.0", True], + github_tools + "system_info_utils" : ["../external/system_info_utils", "88a338a01949f8d8bad60a30b78b65300fd13a9f", False], + github_root + "GPUOpen-Drivers/libamdrdf" : ["../external/rdf", "v1.1.2", True], } diff --git a/build/fetch_dependencies.py b/build/fetch_dependencies.py index 79d5fbb..e64dc13 100644 --- a/build/fetch_dependencies.py +++ b/build/fetch_dependencies.py @@ -17,6 +17,11 @@ import platform import argparse +# Indices for fields in the git_mapping struct. +kDestinationIndex = 0 +kCommitIndex = 1 +kShallowCloneIndex = 2 + # Check for the python 3.x name and import it as the 2.x name try: import urllib.request as urllib @@ -43,19 +48,23 @@ def log_print(message): def update_git_dependencies(git_mapping, update): for git_repo in git_mapping: # add script directory to path - tmp_path = os.path.join(script_root, git_mapping[git_repo][0]) + tmp_path = os.path.join(script_root, git_mapping[git_repo][kDestinationIndex]) # clean up path, collapsing any ../ and converting / to \ for Windows path = os.path.normpath(tmp_path) # required commit - reqd_commit = git_mapping[git_repo][1] + reqd_commit = git_mapping[git_repo][kCommitIndex] + shallow_clone = git_mapping[git_repo][kShallowCloneIndex] do_checkout = False if not os.path.isdir(path): # directory doesn't exist - clone from git log_print("Directory %s does not exist, using 'git clone' to get latest from %s" % (path, git_repo)) - p = subprocess.Popen((["git", "clone", git_repo ,path]), stderr=subprocess.STDOUT) + if (shallow_clone): + p = subprocess.Popen((["git", "clone", "--depth", "1", "--branch", reqd_commit, git_repo ,path]), stderr=subprocess.STDOUT) + else: + p = subprocess.Popen((["git", "clone", git_repo ,path]), stderr=subprocess.STDOUT) p.wait() if(p.returncode == 0): do_checkout = True diff --git a/build/pre_build.py b/build/pre_build.py index c6969a5..34c77dc 100644 --- a/build/pre_build.py +++ b/build/pre_build.py @@ -62,7 +62,6 @@ parser.add_argument("--qt-root", default="~/Qt", help="specify the root directory for locating QT on this system (default: ~/Qt) ") else: parser.add_argument("--qt-root", default="~/Qt", help="specify the root directory for locating QT on this system (default: ~/Qt) ") - parser.add_argument("--disable-extra-qt-lib-deploy", action="store_true", help="prevent extra Qt library files (XCB and ICU libs) from being copied during post build step") parser.add_argument("--qt-system", action="store_true", help="use the system-installed version of QT") parser.add_argument("--qt", default="5.15.2", help="specify the version of QT to be used with the script (default: 5.15.2)" ) parser.add_argument("--clean", action="store_true", help="delete any directories created by this script") @@ -261,8 +260,6 @@ def generate_config(config): cmake_args.extend(["-Tv141"]) if sys.platform.startswith('linux'): - if args.disable_extra_qt_lib_deploy: - cmake_args.extend(["-DDISABLE_EXTRA_QT_LIB_DEPLOY:BOOL=TRUE"]) if args.qt_system: cmake_args.extend(["-DQT_SYSTEM:BOOL=TRUE"]) diff --git a/cmake/dev_tools.cmake b/cmake/dev_tools.cmake index 1464a28..8bc2f68 100644 --- a/cmake/dev_tools.cmake +++ b/cmake/dev_tools.cmake @@ -30,10 +30,6 @@ function(devtools_target_options name) -Wall -Werror -Wextra - -Wno-deprecated-declarations - -Wno-unused-variable - -Wno-missing-field-initializers - -Wno-unknown-pragmas ) elseif (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") target_compile_options(${name} diff --git a/source/backend/CMakeLists.txt b/source/backend/CMakeLists.txt index 57160fd..bfe3e8f 100644 --- a/source/backend/CMakeLists.txt +++ b/source/backend/CMakeLists.txt @@ -6,17 +6,9 @@ include_directories(AFTER ../backend ../parser ../../external/rdf/rdf/inc) add_definitions(-DSYSTEM_INFO_ENABLE_RDF) add_definitions(-DRDF_CXX_BINDINGS) -IF(WIN32) - # Warnings as errors for Windows - add_compile_options(/W4 /WX) -ELSEIF(UNIX) - # Remove warnings for Linux in backend - add_compile_options(-D_LINUX -Wall -Wextra -Werror) -ENDIF(WIN32) # List of all source files. It may be possible to have the build process call cmake to update the makefiles # only when this file has changed (ie source files have been added or removed) - set( SOURCES "rmt_adapter_info.cpp" "rmt_adapter_info.h" @@ -37,6 +29,8 @@ set( SOURCES "rmt_linear_buffer.h" "rmt_legacy_snapshot_writer.cpp" "rmt_legacy_snapshot_writer.h" + "rmt_memory_aliasing_timeline.cpp" + "rmt_memory_aliasing_timeline.h" "rmt_memory_event_history.cpp" "rmt_memory_event_history.h" "rmt_memory_event_history_impl.cpp" @@ -101,3 +95,4 @@ foreach(source IN LISTS SOURCES) endforeach() ENDIF(WIN32) +devtools_target_options(${PROJECT_NAME}) diff --git a/source/backend/rmt_data_set.cpp b/source/backend/rmt_data_set.cpp index f9e3d68..a10b173 100644 --- a/source/backend/rmt_data_set.cpp +++ b/source/backend/rmt_data_set.cpp @@ -9,6 +9,7 @@ #include "rmt_adapter_info.h" #include "rmt_address_helper.h" +#include "rmt_memory_aliasing_timeline.h" #include "rmt_assert.h" #include "rmt_constants.h" #include "rmt_data_snapshot.h" @@ -166,6 +167,8 @@ RmtErrorCode DestroySnapshotWriter(RmtDataSet* data_set) return kRmtOk; } +using namespace RmtMemoryAliasingTimelineAlgorithm; + // Map used to lookup unique resource ID hash using the original resource ID as the key. static std::unordered_map unique_resource_id_lookup_map; @@ -653,7 +656,7 @@ static void PerformFree(RmtDataSet* data_set, void* pointer) } // Allocate memory for a snapshot. -static RmtErrorCode AllocateMemoryForSnapshot(RmtDataSet* data_set, RmtDataSnapshot* out_snapshot) +static RmtErrorCode AllocateMemoryForSnapshot(RmtDataSet* data_set, RmtDataSnapshot* out_snapshot, bool enable_aliased_resource_usage_sizes) { // Set a pointer to parent data set. out_snapshot->data_set = data_set; @@ -687,7 +690,8 @@ static RmtErrorCode AllocateMemoryForSnapshot(RmtDataSet* data_set, RmtDataSnaps out_snapshot->resource_list_buffer, resource_list_buffer_size, &out_snapshot->virtual_allocation_list, - data_set->data_profile.max_concurrent_resources); + data_set->data_profile.max_concurrent_resources, + enable_aliased_resource_usage_sizes); RMT_ASSERT(error_code == kRmtOk); RMT_RETURN_ON_ERROR(error_code == kRmtOk, error_code); } @@ -710,7 +714,25 @@ static RmtErrorCode ProcessTokenForSnapshot(RmtDataSet* data_set, RmtToken* curr { case kRmtTokenTypeVirtualFree: { - + const RmtVirtualAllocation* virtual_allocation = nullptr; + RmtErrorCode result = RmtVirtualAllocationListGetAllocationForAddress( + &(out_snapshot->virtual_allocation_list), current_token->virtual_free_token.virtual_address, &virtual_allocation); + // Remove the virtual allocation if it is being tracked and a virtual allocation could be found. + if ((result == kRmtOk) && (out_snapshot->resource_list.enable_aliased_resource_usage_sizes)) + { + // Update memory sizes grouped by resource usage types taking into account overlapped aliased resources. + RmtMemoryAliasingCalculator* memory_aliasing_calculator = RmtMemoryAliasingCalculatorInstance(); + RMT_ASSERT(memory_aliasing_calculator != nullptr); + memory_aliasing_calculator->DestroyAllocation(virtual_allocation->allocation_identifier); + SizePerResourceUsageType sizes_per_resource_usage_type; + SizeType unbound_size; + memory_aliasing_calculator->CalculateSizes(sizes_per_resource_usage_type, unbound_size); + for (int usage_index = 0; usage_index < RmtResourceUsageType::kRmtResourceUsageTypeCount; usage_index++) + { + out_snapshot->resource_list.total_resource_usage_aliased_size[usage_index] = sizes_per_resource_usage_type.size_[usage_index]; + } + out_snapshot->resource_list.total_resource_usage_aliased_size[kRmtResourceUsageTypeFree] = unbound_size; + } error_code = RmtVirtualAllocationListRemoveAllocation(&out_snapshot->virtual_allocation_list, current_token->virtual_free_token.virtual_address); RMT_ASSERT(error_code == kRmtOk); //RMT_RETURN_ON_ERROR(error_code == kRmtOk, error_code); @@ -826,6 +848,13 @@ static RmtErrorCode ProcessTokenForSnapshot(RmtDataSet* data_set, RmtToken* curr kDummyHeapPref, RmtOwnerType::kRmtOwnerTypeClientDriver, allocation_identifier); + + if (out_snapshot->resource_list.enable_aliased_resource_usage_sizes) + { + RmtMemoryAliasingCalculator* memory_aliasing_calculator = RmtMemoryAliasingCalculatorInstance(); + RMT_ASSERT(memory_aliasing_calculator != nullptr); + memory_aliasing_calculator->CreateAllocation(allocation_identifier, current_token->resource_bind_token.size_in_bytes); + } } else if (error_code == kRmtErrorResourceAlreadyBound) { @@ -925,9 +954,11 @@ static RmtErrorCode ProcessTokenForSnapshot(RmtDataSet* data_set, RmtToken* curr case kRmtTokenTypeVirtualAllocate: { // The byte offset of the token in the data stream is used to uniquely identify this allocation. - // The offset is used rather than the virtual allocation address in case there are allocations/frees then another allocation with the same base address. + // The offset is used rather than the virtual allocation address in case there are allocations/frees + // and then another allocation is made with the same base address. uint64_t allocation_identifier = current_token->common.offset; - error_code = RmtVirtualAllocationListAddAllocation(&out_snapshot->virtual_allocation_list, + + error_code = RmtVirtualAllocationListAddAllocation(&out_snapshot->virtual_allocation_list, current_token->common.timestamp, current_token->virtual_allocate_token.virtual_address, (int32_t)(current_token->virtual_allocate_token.size_in_bytes >> 12), @@ -936,6 +967,14 @@ static RmtErrorCode ProcessTokenForSnapshot(RmtDataSet* data_set, RmtToken* curr allocation_identifier); RMT_ASSERT(error_code == kRmtOk); RMT_RETURN_ON_ERROR(error_code == kRmtOk, error_code); + + if (out_snapshot->resource_list.enable_aliased_resource_usage_sizes) + { + // Track virtual allocation for aliased resource size calculation. + RmtMemoryAliasingCalculator* memory_aliasing_calculator = RmtMemoryAliasingCalculatorInstance(); + RMT_ASSERT(memory_aliasing_calculator != nullptr); + memory_aliasing_calculator->CreateAllocation(allocation_identifier, current_token->virtual_allocate_token.size_in_bytes); + } } break; @@ -1397,9 +1436,13 @@ static int32_t UpdateSeriesValuesFromCurrentSnapshot(const RmtDataSnapshot* curr case kRmtDataTimelineTypeResourceUsageVirtualSize: { + // For Resource Usage Virtual Size timeline type, aliased sizing should be enabled + // (disabled for all other timeline types). + RMT_ASSERT(current_snapshot->resource_list.enable_aliased_resource_usage_sizes); + for (int32_t current_resource_index = 0; current_resource_index < kRmtResourceUsageTypeCount; ++current_resource_index) { - const uint64_t resource_size_for_usage_type = current_snapshot->resource_list.resource_usage_size[current_resource_index]; + const uint64_t resource_size_for_usage_type = current_snapshot->resource_list.total_resource_usage_aliased_size[current_resource_index]; // Write this to the correct slot in the series. if (current_resource_index == kRmtResourceUsageTypeHeap) @@ -1488,11 +1531,15 @@ static RmtErrorCode TimelineGeneratorParseData(RmtDataSet* data_set, RmtDataTime { RMT_ASSERT(data_set); + // Reset the cancel flag. + data_set->flags.cancel_background_task_flag = false; + // Allocate temporary snapshot. RmtDataSnapshot* temp_snapshot = (RmtDataSnapshot*)PerformAllocation(data_set, sizeof(RmtDataSnapshot), alignof(RmtDataSnapshot)); RMT_ASSERT(temp_snapshot); RMT_RETURN_ON_ERROR(temp_snapshot, kRmtErrorOutOfMemory); - RmtErrorCode error_code = AllocateMemoryForSnapshot(data_set, temp_snapshot); + RmtErrorCode error_code = + AllocateMemoryForSnapshot(data_set, temp_snapshot, timeline_type == RmtDataTimelineType::kRmtDataTimelineTypeResourceUsageVirtualSize); RMT_ASSERT(error_code == kRmtOk); RMT_RETURN_ON_ERROR(error_code == kRmtOk, error_code); @@ -1534,7 +1581,7 @@ static RmtErrorCode TimelineGeneratorParseData(RmtDataSet* data_set, RmtDataTime // if the heap has something there, then add it. int32_t last_value_index = -1; - while (!RmtStreamMergerIsEmpty(&data_set->stream_merger)) + while (!RmtStreamMergerIsEmpty(&data_set->stream_merger) && !RmtDataSetIsBackgroundTaskCancelled(data_set)) { // grab the next token from the heap. RmtToken current_token = {}; @@ -1561,6 +1608,10 @@ static RmtErrorCode TimelineGeneratorParseData(RmtDataSet* data_set, RmtDataTime } // clean up temporary structures we allocated to construct the timeline. + if (timeline_type == RmtDataTimelineType::kRmtDataTimelineTypeResourceUsageVirtualSize) + { + RmtMemoryAliasingCalculatorCleanup(); + } RmtDataSnapshotDestroy(temp_snapshot); PerformFree(data_set, temp_snapshot); return kRmtOk; @@ -1745,8 +1796,13 @@ static RmtErrorCode MergeResourceMemoryRegions(const RmtVirtualAllocation* virtu { const RmtGpuAddress allocation_base_address = virtual_allocation->base_address; const RmtResource* current_resource = virtual_allocation->resources[current_resource_index]; - bound_memory_regions.push_back(RegionOffsets{current_resource->address - allocation_base_address, - (current_resource->address - allocation_base_address) + current_resource->size_in_bytes}); + + // Skip over Heap type resources. + if (current_resource->resource_type != RmtResourceType::kRmtResourceTypeHeap) + { + bound_memory_regions.push_back(RegionOffsets{current_resource->address - allocation_base_address, + (current_resource->address - allocation_base_address) + current_resource->size_in_bytes}); + } } // Sort the bound memory regions by starting offsets. @@ -1800,7 +1856,6 @@ static RmtErrorCode SnapshotGeneratorAddUnboundResources(RmtDataSnapshot* snapsh std::vector unbound_regions; ///< The list of unbound memory regions. uint64_t allocation_size_in_bytes = RmtGetAllocationSizeInBytes(virtual_allocation->size_in_4kb_page, kRmtPageSize4Kb); ///< The virtual allocation size in bytes. - if (bound_regions.size() < 1) { // Create an unbound region covering the entire virtual allocation. @@ -1866,7 +1921,7 @@ static RmtErrorCode SnapshotGeneratorAddUnboundResources(RmtDataSnapshot* snapsh return kRmtOk; } -// Calculate the aliased size for each resource. +// Calculate the size after aliasing for each resource. static RmtErrorCode SnapshotGeneratorCalculateAliasedResourceSizes(RmtDataSnapshot* snapshot) { uint64_t resource_usage_mask = (1ULL << (kRmtResourceUsageTypeCount - 1)) - 1; @@ -2007,7 +2062,7 @@ RmtErrorCode RmtDataSetGenerateSnapshot(RmtDataSet* data_set, RmtSnapshotPoint* // set up the snapshot. memcpy(out_snapshot->name, snapshot_point->name, RMT_MINIMUM(strlen(snapshot_point->name), sizeof(out_snapshot->name))); out_snapshot->timestamp = snapshot_point->timestamp; - RmtErrorCode error_code = AllocateMemoryForSnapshot(data_set, out_snapshot); + RmtErrorCode error_code = AllocateMemoryForSnapshot(data_set, out_snapshot, false); out_snapshot->maximum_physical_memory_in_bytes = RmtDataSetGetTotalVideoMemoryInBytes(data_set); @@ -2247,3 +2302,23 @@ uint64_t RmtDataSetGetTotalVideoMemoryInBytes(const RmtDataSet* data_set) { return data_set->segment_info[kRmtHeapTypeLocal].size + data_set->segment_info[kRmtHeapTypeInvisible].size; } + +void RmtDataSetCancelBackgroundTask(RmtDataSet* data_set) +{ + RMT_ASSERT(data_set != nullptr); + data_set->flags.cancel_background_task_flag = true; +} + +bool RmtDataSetIsBackgroundTaskCancelled(const RmtDataSet* data_set) +{ + RMT_ASSERT(data_set != nullptr); + + bool result = false; + + if (data_set->flags.cancel_background_task_flag) + { + result = true; + } + + return result; +} diff --git a/source/backend/rmt_data_set.h b/source/backend/rmt_data_set.h index 0908446..f28823d 100644 --- a/source/backend/rmt_data_set.h +++ b/source/backend/rmt_data_set.h @@ -62,6 +62,7 @@ struct RmtDataSetFlags bool is_rdf_trace : 1; ///< Whether the dataset is generated from an RDF file. bool userdata_processed : 1; ///< Whether the userdata tokens have been processed yet. bool contains_correlation_tokens : 1; ///< Whether the dataset contains any correlation tokens. + bool cancel_background_task_flag : 1; ///< If true, indicates a background task has been cancelled. }; /// A structure encapsulating a single RMT dataset. @@ -271,6 +272,19 @@ int32_t RmtDataSetGetSeriesIndexForTimestamp(RmtDataSet* data_set, uint64_t time uint64_t RmtDataSetGetTotalVideoMemoryInBytes(const RmtDataSet* data_set); +/// Check the cancel flag for the datasets background task. +/// +/// @param [in] data_set A pointer to a RmtDataSet structure. +/// +/// @returns True if the cancel flag is set, otherwise false. +bool RmtDataSetIsBackgroundTaskCancelled(const RmtDataSet* data_set); + +/// Set the cancel flag for a dataset background task. +/// +/// @param [in] data_set A pointer to a RmtDataSet structure. +/// +void RmtDataSetCancelBackgroundTask(RmtDataSet* data_set); + #ifdef __cplusplus } #endif // #ifdef __cplusplus diff --git a/source/backend/rmt_memory_aliasing_timeline.cpp b/source/backend/rmt_memory_aliasing_timeline.cpp new file mode 100644 index 0000000..ad07fb7 --- /dev/null +++ b/source/backend/rmt_memory_aliasing_timeline.cpp @@ -0,0 +1,218 @@ +//============================================================================= +// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. +/// @author AMD Developer Tools Team +/// @file +/// @brief Implementation of the aliasing resource memory algorithm. +//============================================================================= + +#include "rmt_memory_aliasing_timeline.h" + +namespace RmtMemoryAliasingTimelineAlgorithm +{ + // Global instance of the aliased resource memory calculator. + static RmtMemoryAliasingCalculator* memory_aliasing_calculator_ = nullptr; + + void Allocation::Init(const SizeType size) + { + allocation_size_ = size; + regions_slow_part_.resize(1); + regions_slow_part_.front() = AllocationRegionSlowPart{}; // Clear + regions_fast_part_.resize(1); + regions_fast_part_.front().non_zero_counters_ = 0; + RMT_ASSERT(regions_fast_part_.front().begin_offset_ == 0); + total_sizes_valid_ = false; + } + + void Allocation::CreateResource(const SizeType offset, const SizeType size, const RmtResourceUsageType res_usage_type) + { + const SizeType end_offset = offset + size; + RMT_ASSERT(end_offset <= allocation_size_); + + // Binary search + const auto it = + std::lower_bound(regions_fast_part_.begin(), regions_fast_part_.end(), offset, [](const AllocationRegionFastPart& lhs, SizeType rhsOffset) { + return lhs.begin_offset_ < rhsOffset; + }); + size_t index = std::distance(regions_fast_part_.begin(), it); + + if (index == regions_fast_part_.size() || regions_fast_part_[index].begin_offset_ > offset) + { + // Previous region needs to be split. + RMT_ASSERT(index > 0); + --index; + AllocationRegionSlowPart new_region_slow = regions_slow_part_[index]; // Copy + AllocationRegionFastPart new_region_fast = regions_fast_part_[index]; // Copy + new_region_fast.begin_offset_ = offset; + ++index; + regions_slow_part_.insert(regions_slow_part_.begin() + index, new_region_slow); // Insert `new_region` after `it`. + regions_fast_part_.insert(regions_fast_part_.begin() + index, new_region_fast); + } + + while (GetRegionEndOffset(index) <= end_offset) + { + IncrementRegion(index, res_usage_type); + ++index; + if (index == regions_fast_part_.size()) + { + break; + } + } + + if (index != regions_fast_part_.size() && end_offset > regions_fast_part_[index].begin_offset_) + { + // `end_offset` of the new region falls inside the current region `it`. + // We need to split it into 2 parts: first will be existing `it`, second will be `new_region`. + AllocationRegionSlowPart new_region_slow = regions_slow_part_[index]; // Copy + AllocationRegionFastPart new_region_fast = regions_fast_part_[index]; // Copy + new_region_fast.begin_offset_ = end_offset; + IncrementRegion(index, res_usage_type); + ++index; + regions_slow_part_.insert(regions_slow_part_.begin() + index, new_region_slow); // Insert `new_region` after `it`. + regions_fast_part_.insert(regions_fast_part_.begin() + index, new_region_fast); + } + + total_sizes_valid_ = false; + } + + void Allocation::DestroyResource(const SizeType offset, const SizeType size, const RmtResourceUsageType res_usage_type) + { + const SizeType end_offset = offset + size; + RMT_ASSERT(end_offset <= allocation_size_); + + // Binary search. + const auto it = + std::lower_bound(regions_fast_part_.begin(), regions_fast_part_.end(), offset, [](const AllocationRegionFastPart& lhs, SizeType rhsOffset) { + return lhs.begin_offset_ < rhsOffset; + }); + RMT_ASSERT(it != regions_fast_part_.end() && it->begin_offset_ == offset); + size_t index = std::distance(regions_fast_part_.begin(), it); + + do + { + DecrementRegion(index, res_usage_type); + ++index; + } while (index != regions_fast_part_.size() && regions_fast_part_[index].begin_offset_ < end_offset); + + total_sizes_valid_ = false; + } + + void Allocation::AddSizes(SizePerResourceUsageType& inout_sizes, SizeType& inout_unbound_size) + { + EnsureTotalSizes(); + inout_sizes += total_sizes_per_resource_; + inout_unbound_size += allocation_size_ - total_bound_size_; + } + + void Allocation::EnsureTotalSizes() + { + if (total_sizes_valid_) + { + return; + } + + total_sizes_per_resource_ = SizePerResourceUsageType{}; // Clear + total_bound_size_ = 0; + + for (size_t index = 0, count = regions_fast_part_.size(); index < count; ++index) + { + const AllocationRegionFastPart& region = regions_fast_part_[index]; + const SizeType region_size = GetRegionEndOffset(index) - region.begin_offset_; + const RmtResourceUsageType res_type = GetHighestPriorityType(index); + if (res_type < RmtResourceUsageType::kRmtResourceUsageTypeCount) + { + total_sizes_per_resource_.size_[res_type] += region_size; + total_bound_size_ += region_size; + } + } + + total_sizes_valid_ = true; + } + + AllocationPool::~AllocationPool() + { + for (auto alloc : all_allocations_) + { + delete alloc; + } + } + + Allocation* AllocationPool::CreateAllocation() + { + Allocation* alloc; + + if (unused_allocations_.empty()) + { + alloc = new Allocation{}; + all_allocations_.push_back(alloc); + } + else + { + alloc = unused_allocations_.back(); + unused_allocations_.pop_back(); + } + + return alloc; + } + + void AllocationPool::DestroyAllocation(Allocation* alloc) + { + unused_allocations_.push_back(alloc); + } + + RmtMemoryAliasingCalculator::RmtMemoryAliasingCalculator() + { + } + + RmtMemoryAliasingCalculator::~RmtMemoryAliasingCalculator() + { + } + + void RmtMemoryAliasingCalculator::CreateAllocation(const AllocationIdType id, const SizeType size) + { + Allocation* alloc = allocation_pool_.CreateAllocation(); + alloc->Init(size); + allocations_.insert({id, alloc}); + } + + void RmtMemoryAliasingCalculator::DestroyAllocation(const AllocationIdType id) + { + auto alloc_it = allocations_.find(id); + RMT_ASSERT(alloc_it != allocations_.end()); + allocation_pool_.DestroyAllocation(alloc_it->second); + allocations_.erase(alloc_it); + } + + Allocation* RmtMemoryAliasingCalculator::FindAllocation(const AllocationIdType id) const + { + const auto it = allocations_.find(id); + return it != allocations_.end() ? it->second : nullptr; + } + + void RmtMemoryAliasingCalculator::CalculateSizes(SizePerResourceUsageType& out_sizes, SizeType& out_unbound_size) const + { + out_sizes = SizePerResourceUsageType{}; // Zero-initalize. + out_unbound_size = 0; + + for (const auto& alloc_it : allocations_) + { + alloc_it.second->AddSizes(out_sizes, out_unbound_size); + } + } + + void RmtMemoryAliasingCalculatorCleanup() + { + delete memory_aliasing_calculator_; + memory_aliasing_calculator_ = nullptr; + } + + RmtMemoryAliasingCalculator* RmtMemoryAliasingCalculatorInstance() + { + if (memory_aliasing_calculator_ == nullptr) + { + memory_aliasing_calculator_ = new RmtMemoryAliasingCalculator; + } + + return memory_aliasing_calculator_; + } + +} // namespace RmtMemoryAliasingTimelineAlgorithm diff --git a/source/backend/rmt_memory_aliasing_timeline.h b/source/backend/rmt_memory_aliasing_timeline.h new file mode 100644 index 0000000..0481989 --- /dev/null +++ b/source/backend/rmt_memory_aliasing_timeline.h @@ -0,0 +1,348 @@ +//============================================================================= +// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. +/// @author AMD Developer Tools Team +/// @file +/// @brief Definitions for the aliasing memory aliasing algorithm. +//============================================================================= + +#ifndef RMV_BACKEND_RMT_MEMORY_ALIASING_TIMELINE_H_ +#define RMV_BACKEND_RMT_MEMORY_ALIASING_TIMELINE_H_ + +#include "rmt_assert.h" +#include "rmt_format.h" + +#include +#include +#include +#include + +namespace RmtMemoryAliasingTimelineAlgorithm +{ + /// Type to be used as an identifier for allocations. + typedef uint64_t AllocationIdType; + + /// Type to be used for all memory sizes and offsets, expressed in bytes. + typedef uint64_t SizeType; + + /// Type to be used for counting the number of resources of a certain type with aliasing at the same place in memory. + /// 16 bits should be enough. We don't expect more than 65535 resources exist in the same place at the same time. + typedef uint16_t ResCountUsageType; + + /// Type to be used to store bit flags per resource type. + /// Must have enough bits to accommodate for memory types defined by enum RmtResourceUsageType. + typedef uint32_t ResBitmaskUsageType; + + static_assert(sizeof(ResBitmaskUsageType) * 8 >= RmtResourceUsageType::kRmtResourceUsageTypeCount); + + /// @brief Scans integer for index of first nonzero value from the Most Significant Bit (MSB). If mask is 0 then returns UINT8_MAX. + /// + /// Implementation taken from Vulkan Memory Allocator by AMD. + /// + /// @param [in] mask The bit mask to scan. + inline uint8_t BitScanMSB(uint32_t mask) + { +#ifdef _MSC_VER + unsigned long pos; + if (_BitScanReverse(&pos, mask)) + { + return static_cast(pos); + } + +#elif defined __GNUC__ || defined __clang__ + if (mask) + { + return 31 - static_cast(__builtin_clz(mask)); + } +#else + uint8_t pos = 31; + uint32_t bit = 1UL << 31; + do + { + if (mask & bit) + { + return pos; + } + bit >>= 1; + } while (pos-- > 0); +#endif + return UINT8_MAX; + } + + /// Stores array of counters per resource type. + /// For given resource type as index, it stores the number of resources of that type. + struct CounterPerResourceUsageType + { + ResCountUsageType counter_[RmtResourceUsageType::kRmtResourceUsageTypeCount] = {}; + + bool operator==(const CounterPerResourceUsageType& rhs) const + { + return memcmp(counter_, rhs.counter_, sizeof(ResCountUsageType) * RmtResourceUsageType::kRmtResourceUsageTypeCount) == 0; + } + }; + + /// A structure used to calculate memory sizes based on overlapped aliased resources. + struct SizePerResourceUsageType + { + SizeType size_[RmtResourceUsageType::kRmtResourceUsageTypeCount] = {}; + + SizePerResourceUsageType& operator+=(const SizePerResourceUsageType& rhs) + { + for (size_t i = 0; i < RmtResourceUsageType::kRmtResourceUsageTypeCount; ++i) + this->size_[i] += rhs.size_[i]; + return *this; + } + }; + + /// Stores part of the information for a distinct region of memory. + /// This part is intended to be small and accessed frequently - on each recalculation of the result. + struct AllocationRegionFastPart + { + /// Offset from the beginning of the allocation to the beginning of this region, in bytes. + SizeType begin_offset_ = 0; + + /// The non_zero_counters bit i is set when equivalent regions_slow_part[same_index].counters.counter[i] > 0. + ResBitmaskUsageType non_zero_counters_ = 0; + + bool operator<(const AllocationRegionFastPart& rhs) const + { + return begin_offset_ < rhs.begin_offset_; + } + }; + + /// Stores part of the information for a distinct region of memory. + /// This part is intended to be large and accessed infrequently - only when an allocation is added or removed. + struct AllocationRegionSlowPart + { + CounterPerResourceUsageType counters_; ///< Counter for each resource usage type. + }; + + /// @brief Represents a single virtual allocation with specific size in bytes. + /// + /// It stores a sequence of regions. Regions follow each other. Each region spans + /// from its offset to the offset of the next region, or the end of the allocation + /// if it is the last region. First region always starts at offset 0. Each region + /// stores counters to remember how many resources of given type exist in that + /// region. Begin or end of a resource enforces begin of a new region. After + /// removing resources, regions with equal counters are not merged back into one. + /// Such merge would be incorrect in certain cases. + struct Allocation + { + /// @brief Initializes this allocation object. + /// + /// Should be always called after the object is constructed. + /// + /// @param [in] size The size of the allocation being tracked. + void Init(const SizeType size); + + /// @brief Adds a resource to the allocation. + /// + /// Internal data structures are updated accordingly, using the main algorithm. + /// + /// @param [in] offset The offset in bytes from the start of the virtual allocation the resource is bound to. + /// @param [in] size The size in bytes of the resource. + /// @param [in] res_usage_type The usage type of the resource. + void CreateResource(const SizeType offset, const SizeType size, const RmtResourceUsageType res_usage_type); + + /// @brief Removes a resource from the allocation. + /// + /// Internal data structures are updated accordingly, using the main algorithm. + /// Parameters of the resource must exactly match former call to CreateResource() on the same allocation! + /// Otherwise, effects are undefined. + /// + /// @param [in] offset The starting offset of the region to be deleted. + /// @param [in] size The size in bytes of the region to be deleted. + /// @param [in] res_usage_type The resource usage type. + void DestroyResource(const SizeType offset, const SizeType size, const RmtResourceUsageType res_usage_type); + + /// @brief Increments resource usage sizes. + /// + /// @param [in/out] inout_sizes The array of sizes to add for each of the resource usage types. Updated with the new sizes on exit. + /// @param [in/out] inout_unbound_size The size in bytes of unbound virtual allocation space to add. Updated with the new unbound size on exit. + void AddSizes(SizePerResourceUsageType& inout_sizes, SizeType& inout_unbound_size); + + private: + /// Size of this allocation, in bytes. + SizeType allocation_size_ = 0; + + /// Arrays of structures that keep information about regions. + /// Both vectors always have equal length. + /// They are always sorted by regions_fast_part_[i].begin_offset. + /// Full information about the region is stored in two parts: regions_fast_part_[i] plus regions_slow_part_[i]. + /// It is split into two as a performance optimization - to optimize memory access + /// pattern for better cache utilization. + std::vector regions_fast_part_; ///< Used when a resource is bound or destroyed. + std::vector regions_slow_part_; ///< Used when an allocation is added or removed. + + /// Calculated and cached total sizes per resource type, which consider aliasing. + /// Valid only when total_sizes_valid_ is true. + SizePerResourceUsageType total_sizes_per_resource_; + + /// Calculated and cached total size of regions where any resource is bound. + /// (allocation_size_ - total_bound_size_) gives the unbound size of this allocation. + /// Valid only when total_sizes_valid_ is true. + SizeType total_bound_size_ = 0; + + /// True when total_sizes_per_resource and total_bound_size are calculated and up-to-date. + /// False if they are uninitialized or outdated and should not be used, but recalculated if needed. + bool total_sizes_valid_ = false; + + /// @brief Returns end offset (one byte past the end) of the region with given index. + /// + /// It is calculated based on the begin offset of the next region or the size + /// of the entire allocation, if it is the last region. + /// + /// @param [in] index The index of a region. + /// + /// @returns The offset to the next byte after the end of a region. + SizeType GetRegionEndOffset(const size_t index) const + { + return index + 1 == regions_fast_part_.size() ? allocation_size_ : regions_fast_part_[index + 1].begin_offset_; + } + + /// @brief Returns type of the resource that exist (has non-zero counter) in the region with given index. + /// + /// If multiple resource types exist in the region, returns the type that has the highest priority. + /// If all counters are zero (region is empty, no resources exist there), returns kRmtResourceUsageTypeCount. + /// + /// @param [in] region_index The index of the region to examine. + /// + /// @returns The resource usage type with the highest priority for this region. + RmtResourceUsageType GetHighestPriorityType(const size_t region_index) const + { + const ResBitmaskUsageType bitmask = regions_fast_part_[region_index].non_zero_counters_; + + if (bitmask == 0) + { + return RmtResourceUsageType::kRmtResourceUsageTypeCount; + } + + return (RmtResourceUsageType)BitScanMSB(bitmask); + } + + /// @brief Internal, ancillary function that updates data structures accordingly. + /// + /// Increments counters to reflect adding new resource of given type to the region with given index. + /// + /// @param [in] region_index The index of the region being updated. + /// @param [in] res_usage_type The resource usage type of the counter being incremented. + void IncrementRegion(const size_t region_index, const RmtResourceUsageType res_usage_type) + { + RMT_ASSERT(regions_slow_part_[region_index].counters_.counter_[res_usage_type] < std::numeric_limits::max()); + ++regions_slow_part_[region_index].counters_.counter_[res_usage_type]; + regions_fast_part_[region_index].non_zero_counters_ |= (ResBitmaskUsageType)(1u << res_usage_type); // Set the bit. + } + + /// @brief Internal, ancillary function that updates data structures accordingly. + /// + /// Decrements counters to reflect removing a resource of given type from the region with given index. + /// + /// @param [in] region_index The index of the region to update. + /// @param [in] res_usage_type The resource usage type of the counter being decremented. + void DecrementRegion(const size_t region_index, const RmtResourceUsageType res_usage_type) + { + RMT_ASSERT(regions_slow_part_[region_index].counters_.counter_[res_usage_type] > 0); + if (--regions_slow_part_[region_index].counters_.counter_[res_usage_type] == 0) + { + regions_fast_part_[region_index].non_zero_counters_ &= (ResBitmaskUsageType) ~(1u << res_usage_type); // Clear the bit. + } + } + + /// @brief Ensures that total_sizes_per_resource_ and total_bound_size_ are valid. + /// + /// If total_sizes_valid_ is false, recalculates them by traversing all the + /// regions in this allocation and summing them up. This is the main algorithm + /// for result calculation. + void EnsureTotalSizes(); + }; + + /// @brief Internal class used to own and create objects of Allocation class. + /// + /// Each object of class AliasingResMemCalculator has one object of this class. + /// It saves Allocation objects that were freed as unused instead of deleting them so they can be reused, + /// as a performance optimization. + class AllocationPool + { + public: + /// @brief AllocationPool destructor. + ~AllocationPool(); + + /// @brief Creates a new Allocation object. + /// + /// In reality, it may return one from currently existing but unused objects, but this is opaque to the user. + /// + /// @returns + Allocation* CreateAllocation(); + + /// @brief Destroys given Allocation object. + /// + /// In reality, it just saves it to the list of unused objects, but this is opaque to the user. + /// + /// @param [in] alloc A pointer to the allocation tracker to be destroyed. + /// + void DestroyAllocation(Allocation* alloc); + + private: + std::vector all_allocations_; ///< All of the allocation objects being tracked. Objects are freed by the AllocationPool destructor. + std::vector unused_allocations_; ///< List of free allocation objects that can be reused. + }; + + /// @brief Main class for calculating total resource usage type sizes. + /// + /// Represents a data structure and algorithm to be used for calculating total sizes of resources of various + /// types in an entire set of allocations. Create one object of this class for the time you do the calculation. + /// It doesn't have a notion of time. It just stores the current state. As you process events that tell about + /// new allocations created, freed, resources inside allocations created, destroyed, call appropriate methods + /// of this class to submit this event and update the current state. + /// + /// You can also call method CalculateSizes() at any given moment to calculate and get the result - resource + /// sizes summed up per resource type, based on the current state, as well as unbound size. + class RmtMemoryAliasingCalculator + { + public: + /// @brief Constructor for the RmtMemoryAliasingCalculator class. + RmtMemoryAliasingCalculator(); + + /// @brief Destructor for the RmtMemoryAliasingCalculator class. + ~RmtMemoryAliasingCalculator(); + + /// @brief Updates current state to reflect that a new allocation has been created with given size. + /// + /// @param [in] id A unique identifier for the allocation. + /// @param [in] size The new size in bytes of the allocation. + void CreateAllocation(const AllocationIdType id, const SizeType size); + + /// @brief Updates current state to reflect that an allocation with given id has been freed. + /// + /// @param [in] id The allocation identifier of the allocation being destroyed. + void DestroyAllocation(const AllocationIdType id); + + /// @brief Remember returned pointer if you can instead of searching for it every time. + /// + /// @param [in] id A unique ID representing an allocation. + /// + /// @returns A pointer to the allocation found or nullptr if the id was not found. + Allocation* FindAllocation(const AllocationIdType id) const; + + /// @brief Calculates and returns total sizes per resource type and unbound size based on the current state. + /// + /// @param [out] out_sizes The array of calculated resource usage sizes for all allocations. + /// @param [out] out_unbound_size The amount of unbound memory for all allocations. + void CalculateSizes(SizePerResourceUsageType& out_sizes, SizeType& out_unbound_size) const; + + private: + AllocationPool allocation_pool_; ///< A pool of Allocation objects and a mechanism to create them. + std::unordered_map allocations_; ///< A map of allocations that currently exist. + }; + + /// @brief Retrieves the global instance of the aliasing resource memory calculator. + /// + /// A new instance is created if one doesn't already exist. + /// + /// @returns A pointer to the RmtMemoryAliasingCalculator object. + RmtMemoryAliasingCalculator* RmtMemoryAliasingCalculatorInstance(); + + /// @brief Deletes the global instance of the aliasing resource memory calculator. + void RmtMemoryAliasingCalculatorCleanup(); + +} // namespace RmtMemoryAliasingTimelineAlgorithm +#endif // #ifdef RMV_BACKEND_RMT_MEMORY_ALIASING_TIMELINE_H_ diff --git a/source/backend/rmt_rdf_file_parser.cpp b/source/backend/rmt_rdf_file_parser.cpp index 2d7dcda..c52495a 100644 --- a/source/backend/rmt_rdf_file_parser.cpp +++ b/source/backend/rmt_rdf_file_parser.cpp @@ -130,8 +130,9 @@ static RmtErrorCode LoadSegmentChunk(rdfChunkFile* chunk_file, RmtDataSet* data_ { data_set->segment_info[count].base_address = data.physical_base_address; data_set->segment_info[count].heap_type = static_cast(data.type); - data_set->segment_info[count].index = 0; - data_set->segment_info[count].size = data.size; + data_set->segment_info[count].index = + 0; /// TODO: This is missing from RDF spec (should be memory index, exposed by the Vulkan software stack) + data_set->segment_info[count].size = data.size; count++; } else @@ -416,8 +417,10 @@ static RmtErrorCode LoadSystemInfoChunk(rdfChunkFile* chunk_file, RmtDataSet* da system_info.driver.software_version.c_str(), kRmtMaxDriverSoftwareVersionNameLength - 1); - strncpy_s( - data_set->system_info.system_memory_type_name, kRmtMaxMemoryTypeNameLength, system_info.os.memory.type.c_str(), kRmtMaxMemoryTypeNameLength - 1); + strncpy_s(data_set->system_info.system_memory_type_name, + kRmtMaxMemoryTypeNameLength, + system_info.os.memory.type.c_str(), + kRmtMaxMemoryTypeNameLength - 1); result = kRmtOk; } diff --git a/source/backend/rmt_resource_list.cpp b/source/backend/rmt_resource_list.cpp index 1506b0a..9084eb7 100644 --- a/source/backend/rmt_resource_list.cpp +++ b/source/backend/rmt_resource_list.cpp @@ -6,6 +6,8 @@ //============================================================================= #include "rmt_resource_list.h" + +#include "rmt_memory_aliasing_timeline.h" #include "rmt_virtual_allocation_list.h" #include "rmt_assert.h" #include "rmt_page_table.h" @@ -19,6 +21,8 @@ #include "linux/safe_crt.h" #endif +using namespace RmtMemoryAliasingTimelineAlgorithm; + RmtResourceUsageType RmtResourceGetUsageType(const RmtResource* resource) { RMT_ASSERT(resource); @@ -512,13 +516,32 @@ static RmtErrorCode DestroyResource(RmtResourceList* resource_list, RmtResource* bool is_shareable = (resource->resource_type == kRmtResourceTypeImage) && ((resource->image.create_flags & kRmtImageCreationFlagShareable) == kRmtImageCreationFlagShareable); - if (!is_shareable) + RmtResourceUsageType resource_type = RmtResourceGetUsageType(resource); + if ((!is_shareable && (resource_list->enable_aliased_resource_usage_sizes) && resource->bound_allocation != nullptr) && + (RmtResourceGetUsageType(resource) != RmtResourceUsageType::kRmtResourceUsageTypeHeap)) { - RmtResourceUsageType resource_type = RmtResourceGetUsageType(resource); - RMT_ASSERT(resource_list->resource_usage_size[resource_type] >= resource->size_in_bytes); - resource_list->resource_usage_size[RmtResourceGetUsageType(resource)] -= resource->size_in_bytes; + RmtMemoryAliasingCalculator* memory_aliasing_calculator = RmtMemoryAliasingCalculatorInstance(); + RMT_ASSERT(memory_aliasing_calculator != nullptr); + Allocation* aliased_resource_allocation = memory_aliasing_calculator->FindAllocation(resource->bound_allocation->allocation_identifier); + if (aliased_resource_allocation != nullptr) + { + aliased_resource_allocation->DestroyResource( + resource->address - resource->bound_allocation->base_address, resource->size_in_bytes, RmtResourceGetUsageType(resource)); + + SizePerResourceUsageType sizes_per_resource_usage_type; + SizeType unbound_size; + memory_aliasing_calculator->CalculateSizes(sizes_per_resource_usage_type, unbound_size); + for (int usage_index = 0; usage_index < RmtResourceUsageType::kRmtResourceUsageTypeCount; usage_index++) + { + resource_list->total_resource_usage_aliased_size[usage_index] = sizes_per_resource_usage_type.size_[usage_index]; + } + resource_list->total_resource_usage_aliased_size[kRmtResourceUsageTypeFree] = unbound_size; + } } + RMT_ASSERT(resource_list->resource_usage_size[resource_type] >= resource->size_in_bytes); + resource_list->resource_usage_size[RmtResourceGetUsageType(resource)] -= resource->size_in_bytes; + const int64_t hashed_identifier = GenerateResourceHandle(resource->identifier); // get a pointer to the last resource, if its the one we're trying to delete then adjust the count. @@ -547,7 +570,8 @@ RmtErrorCode RmtResourceListInitialize(RmtResourceList* resource_ void* buffer, size_t buffer_size, const RmtVirtualAllocationList* virtual_allocation_list, - int32_t maximum_concurrent_resources) + int32_t maximum_concurrent_resources, + bool enable_aliased_resource_usage_sizes) { RMT_ASSERT(resource_list); RMT_RETURN_ON_ERROR(resource_list, kRmtErrorInvalidPointer); @@ -556,10 +580,11 @@ RmtErrorCode RmtResourceListInitialize(RmtResourceList* resource_ RMT_RETURN_ON_ERROR(RmtResourceListGetBufferSize(maximum_concurrent_resources) <= buffer_size, kRmtErrorInvalidSize); // initialize the resource storage - resource_list->resources = (RmtResource*)buffer; - resource_list->resource_count = 0; - resource_list->virtual_allocation_list = virtual_allocation_list; - resource_list->maximum_concurrent_resources = maximum_concurrent_resources; + resource_list->resources = (RmtResource*)buffer; + resource_list->resource_count = 0; + resource_list->virtual_allocation_list = virtual_allocation_list; + resource_list->maximum_concurrent_resources = maximum_concurrent_resources; + resource_list->enable_aliased_resource_usage_sizes = enable_aliased_resource_usage_sizes; // initialize the acceleration structure. const uintptr_t resource_node_buffer = ((uintptr_t)buffer) + (maximum_concurrent_resources * sizeof(RmtResource)); @@ -573,6 +598,7 @@ RmtErrorCode RmtResourceListInitialize(RmtResourceList* resource_ memset(resource_list->resource_usage_count, 0, sizeof(resource_list->resource_usage_count)); memset(resource_list->resource_usage_size, 0, sizeof(resource_list->resource_usage_size)); + memset(resource_list->total_resource_usage_aliased_size, 0, sizeof(resource_list->total_resource_usage_aliased_size)); return kRmtOk; } @@ -783,8 +809,10 @@ RmtErrorCode RmtResourceListAddResourceBind(RmtResourceList* resource_list, cons resource->size_in_bytes = resource_bind->size_in_bytes; // find the bound allocation - const RmtErrorCode error_code = - RmtVirtualAllocationListGetAllocationForAddress(resource_list->virtual_allocation_list, resource_bind->virtual_address, &resource->bound_allocation); + const RmtVirtualAllocation* allocation = nullptr; + const RmtErrorCode error_code = + RmtVirtualAllocationListGetAllocationForAddress(resource_list->virtual_allocation_list, resource_bind->virtual_address, &allocation); + resource->bound_allocation = allocation; // look for externally shared resources. if ((error_code == kRmtErrorNoAllocationFound) && (resource->resource_type == kRmtResourceTypeImage) && @@ -799,6 +827,8 @@ RmtErrorCode RmtResourceListAddResourceBind(RmtResourceList* resource_list, cons // only external shared can fail to find the allocation. RMT_ASSERT(error_code == kRmtOk); + RmtResourceUsageType usage_type = RmtResourceGetUsageType(resource); + // Count the resources on each allocation. We fill in pointers later // when we do the fix up pass in snapshot generation. if (resource->bound_allocation != nullptr) @@ -814,10 +844,31 @@ RmtErrorCode RmtResourceListAddResourceBind(RmtResourceList* resource_list, cons // update the commit type of the allocation UpdateCommitType(resource_list, resource); + + if ((resource_list->enable_aliased_resource_usage_sizes) && (usage_type != RmtResourceUsageType::kRmtResourceUsageTypeHeap)) + { + RmtMemoryAliasingCalculator* memory_aliasing_calculator = RmtMemoryAliasingCalculatorInstance(); + RMT_ASSERT(memory_aliasing_calculator); + Allocation* aliased_resource_allocation = memory_aliasing_calculator->FindAllocation(resource->bound_allocation->allocation_identifier); + if (aliased_resource_allocation != nullptr) + { + aliased_resource_allocation->CreateResource( + resource->address - resource->bound_allocation->base_address, resource->size_in_bytes, RmtResourceGetUsageType(resource)); + + SizePerResourceUsageType sizes_per_resource_type; + SizeType unbound_size; + memory_aliasing_calculator->CalculateSizes(sizes_per_resource_type, unbound_size); + for (int usage_index = 0; usage_index < RmtResourceUsageType::kRmtResourceUsageTypeCount; usage_index++) + { + resource_list->total_resource_usage_aliased_size[usage_index] = sizes_per_resource_type.size_[usage_index]; + } + resource_list->total_resource_usage_aliased_size[kRmtResourceUsageTypeFree] = unbound_size; + } + } } // track the bytes bound - resource_list->resource_usage_size[RmtResourceGetUsageType(resource)] += resource->size_in_bytes; + resource_list->resource_usage_size[usage_type] += resource->size_in_bytes; return kRmtOk; } @@ -875,7 +926,7 @@ bool RmtResourceIsAliased(const RmtResource* resource) const RmtVirtualAllocation* virtual_allocation = resource->bound_allocation; const RmtGpuAddress resource_start_address = resource->address; const RmtGpuAddress resource_end_address = resource->address + resource->size_in_bytes; - bool is_aliased = false; + bool is_aliased = false; for (int32_t current_resource_index = 0; current_resource_index < virtual_allocation->resource_count; ++current_resource_index) { diff --git a/source/backend/rmt_resource_list.h b/source/backend/rmt_resource_list.h index 3f84944..beefa2a 100644 --- a/source/backend/rmt_resource_list.h +++ b/source/backend/rmt_resource_list.h @@ -50,11 +50,11 @@ typedef struct RmtResource uint64_t address; ///< The virtual address of the resource. uint64_t size_in_bytes; ///< The total size of the resource. const RmtVirtualAllocation* - bound_allocation; ///< An pointers to a RmtAllocation structure containing the virtual address allocation containing this resource. This is set to NULL if the resource isn't bound to a virtual address. - uint32_t flags; ///< Flags on the resource. - RmtCommitType commit_type; ///< The commit type of the resource. - RmtResourceType resource_type; ///< The type of the resource. - RmtOwnerType owner_type; ///< The owner of the resource. + bound_allocation; ///< An pointers to a RmtAllocation structure containing the virtual address allocation containing this resource. This is set to NULL if the resource isn't bound to a virtual address. + uint32_t flags; ///< Flags on the resource. + RmtCommitType commit_type; ///< The commit type of the resource. + RmtResourceType resource_type; ///< The type of the resource. + RmtOwnerType owner_type; ///< The owner of the resource. union { @@ -173,6 +173,8 @@ typedef struct RmtResourceList int32_t resource_usage_count[kRmtResourceUsageTypeCount]; ///< The number of each resource usage currently in the list. uint64_t resource_usage_size[kRmtResourceUsageTypeCount]; + uint64_t total_resource_usage_aliased_size[kRmtResourceUsageTypeCount]; ///< Resource usage sizes for all resources in the list. + bool enable_aliased_resource_usage_sizes; ///< A flag used to indicate that, if true, aliased usage sizes are being calculated. } RmtResourceList; @@ -191,6 +193,7 @@ size_t RmtResourceListGetBufferSize(int32_t maximum_concurrent_resources); /// @param [in] buffer_size The buffer size, in bytes. /// @param [in] virtual_allocation_list A pointer to a RmtVirtualAllocationList structure. /// @param [in] maximum_concurrent_resources The maximum number of resources that can be in flight at once. +/// @param [in] enable_aliased_resource_sizing A flag that indicates, if true, that aliased resource usage sizes are to be calculated. /// /// @retval /// kRmtOk The operation completed successfully. @@ -202,7 +205,8 @@ RmtErrorCode RmtResourceListInitialize(RmtResourceList* resource_ void* buffer, size_t buffer_size, const RmtVirtualAllocationList* virtual_allocation_list, - int32_t maximum_concurrent_resources); + int32_t maximum_concurrent_resources, + const bool enable_aliased_resource_sizing); /// Add a resource create to the list. /// diff --git a/source/backend/rmt_trace_loader.cpp b/source/backend/rmt_trace_loader.cpp index 8916f6e..e1bccb4 100644 --- a/source/backend/rmt_trace_loader.cpp +++ b/source/backend/rmt_trace_loader.cpp @@ -56,7 +56,7 @@ RmtErrorCode RmtTraceLoaderTraceLoad(const char* trace_file_name) } // Create the default timeline for the data set. - error_code = RmtDataSetGenerateTimeline(&data_set_, kRmtDataTimelineTypeResourceUsageVirtualSize, &timeline_); + error_code = RmtDataSetGenerateTimeline(&data_set_, kRmtDataTimelineTypeCommitted, &timeline_); return error_code; } diff --git a/source/backend/rmt_virtual_allocation_list.cpp b/source/backend/rmt_virtual_allocation_list.cpp index d19b3d6..75dd930 100644 --- a/source/backend/rmt_virtual_allocation_list.cpp +++ b/source/backend/rmt_virtual_allocation_list.cpp @@ -20,6 +20,49 @@ #include #include // memcpy #include +#include + +// The ResourcePriorityComparator handles ordering resources in a std::set based on aliased resource priority. +// A comparison is first made on the resource usage type enum value (highest to lowest). If the two resources +// being compared have the same usage type, the resources are compared by size (smallest size is highest priority). +// If the size is also the same, then resource IDs are compared (largest value is highest priority). +struct ResourcePriorityComparator +{ + bool operator()(const RmtResource* lhs, const RmtResource* rhs) const + { + bool result = false; + + const RmtResourceUsageType lhs_usage_type = RmtResourceGetUsageType(lhs); + const RmtResourceUsageType rhs_usage_type = RmtResourceGetUsageType(rhs); + + if (lhs_usage_type != rhs_usage_type) + { + // Compare by usage type. + result = lhs_usage_type > rhs_usage_type; + } + else + { + if (lhs->size_in_bytes != rhs->size_in_bytes) + { + // Compare by size. + result = lhs->size_in_bytes < rhs->size_in_bytes; + } + else + { + // Compare by resource identifier (last resort, other attributes are the same). + result = lhs->identifier > rhs->identifier; + } + } + + return result; + } +}; + +typedef std::set PrioritizedResourceSetType; // Set of resource objects sorted by priority. + +typedef std::map ResourceMemoryRegionsMap; // Map resource object sets split into segments using bind offset as a key. +static std::unordered_map + aliased_resource_memory_regions_for_allocations_; // Map of all allocations, allocation id is used as the key. // Helper function to improve tree balance by hashing the handles. static RmtGpuAddress HashGpuAddress(RmtGpuAddress address) @@ -383,6 +426,8 @@ RmtErrorCode RmtVirtualAllocationListInitialize(RmtVirtualAllocationList* virtua virtual_allocation_list->root = NULL; memset(virtual_allocation_list->allocations_per_preferred_heap, 0, sizeof(virtual_allocation_list->allocations_per_preferred_heap)); + + aliased_resource_memory_regions_for_allocations_.clear(); return kRmtOk; } @@ -429,6 +474,7 @@ RmtErrorCode RmtVirtualAllocationListAddAllocation(RmtVirtualAllocationList* vir allocation_details->resource_count = 0; allocation_details->next_resource_index = 0; allocation_details->allocation_identifier = allocation_identifier; + memset(allocation_details->resource_usage_aliased_size, 0, sizeof(allocation_details->resource_usage_aliased_size)); for (int32_t current_heap_preference_index = 0; current_heap_preference_index < RMT_NUM_HEAP_PREFERENCES; ++current_heap_preference_index) { @@ -769,46 +815,89 @@ RmtErrorCode RmtVirtualAllocationGetBackingStorageHistogram(const RmtDataSnapsho return kRmtOk; } -// The ResourcePriorityComparator handles ordering resources in a std::set based on aliased resource priority. -// A comparison is first made on the resource usage type enum value (highest to lowest). If the two resources -// being compared have the same usage type, the resources are compared by size (smallest size is highest priority). -// If the size is also the same, then resource IDs are compared (largest value is highest priority). -struct ResourcePriorityComparator +static RmtErrorCode TrackResourceAdd(const RmtResource* resource, std::map& out_resource_memory_segments) { - bool operator()(const RmtResource* lhs, const RmtResource* rhs) const + RMT_ASSERT(resource != nullptr); + + // Skip this resource if it isn't bound to an allocation. + if (resource->bound_allocation == nullptr) { - bool result = false; + return kRmtErrorNoResourceFound; + } - const RmtResourceUsageType lhs_usage_type = RmtResourceGetUsageType(lhs); - const RmtResourceUsageType rhs_usage_type = RmtResourceGetUsageType(rhs); + if (RmtResourceGetUsageType(resource) == kRmtResourceUsageTypeHeap) + { + return kRmtErrorNoResourceFound; + } - if (lhs_usage_type != rhs_usage_type) + // Skip this resource if it is implicit. + if (RmtResourceUserDataIsResourceImplicit(resource->identifier) == true) + + { + return kRmtErrorNoResourceFound; + } + + uint64_t allocation_size_in_bytes = RmtGetAllocationSizeInBytes(resource->bound_allocation->size_in_4kb_page, kRmtPageSize4Kb); + + if (allocation_size_in_bytes < resource->size_in_bytes) + { + return kRmtErrorInvalidSize; + } + + // Add a segment at the resource's starting offset (if one doesn't already exist) and add the resource to the segment's set of resources. + const uint64_t start_offset = resource->address - resource->bound_allocation->base_address; + out_resource_memory_segments[start_offset].insert(resource); + + // Add a segment with an empty list of resources (if one doesn't already exist). This marks the end of the resource's memory block. + const uint64_t end_offset = start_offset + resource->size_in_bytes; + out_resource_memory_segments[end_offset]; + + // Second pass: For each segment except the first one, copy resources to the next segment's resource set if the resource's ending offset is greater than the segment's offset key. + for (std::map::iterator current_segment_iterator = out_resource_memory_segments.begin(); + current_segment_iterator != out_resource_memory_segments.end(); + current_segment_iterator++) + { + // Get the next segment. If there are no more segments then exit the loop. + auto next_segment_iterator = std::next(current_segment_iterator); + if (next_segment_iterator == out_resource_memory_segments.end()) { - // Compare by usage type. - result = lhs_usage_type > rhs_usage_type; + break; } - else + + // Copy resources from previous segment to next segment if the resource's ending range extends into this segment. + for (const auto& resource_from_segment : current_segment_iterator->second) { - if (lhs->size_in_bytes != rhs->size_in_bytes) + if (resource_from_segment->bound_allocation != nullptr) { - // Compare by size. - result = lhs->size_in_bytes < rhs->size_in_bytes; - } - else - { - // Compare by resource identifier (last resort, other attributes are the same). - result = lhs->identifier > rhs->identifier; + const uint64_t resource_start_range = resource_from_segment->address - resource_from_segment->bound_allocation->base_address; + const uint64_t resource_end_range = resource_start_range + resource_from_segment->size_in_bytes; + if (resource_end_range > next_segment_iterator->first) + { + // The resource range extends into this segment. Add it to the next segment's list of resources. + next_segment_iterator->second.insert(resource_from_segment); + } } } + } - return result; + return kRmtOk; +} + +static RmtErrorCode AliasedResourceTrackerAddResource(const RmtResource* resource) +{ + const RmtVirtualAllocation* allocation = resource->bound_allocation; + if (allocation == nullptr) + { + return kRmtErrorNoAllocationFound; } -}; -// Algorithm to adjust the size of overlapping resources (i.e. resources that share part or all of the same allocated memory). + return TrackResourceAdd(resource, aliased_resource_memory_regions_for_allocations_[allocation->allocation_identifier]); +} + // The resource_memory_segments map is used to split the memory allocation into segments at the start and end offsets of each bound resource. // Each segment contains a set of resources sorted by the alias priority. The size of each segment is applied to the size of the top resource -// in each segment's set. These sizes are totaled up for each resource to obtain the resource's aliased size. +// in each segment's set. These sizes are totaled up for each resource to obtain the resource's size after aliasing. + static RmtErrorCode RmtVirtualAllocationUpdateAliasedResourceSizes(const RmtVirtualAllocation* allocation, const RmtResourceList* resource_list, uint64_t resource_usage_mask) @@ -816,11 +905,6 @@ static RmtErrorCode RmtVirtualAllocationUpdateAliasedResourceSizes(const RmtVirt RMT_RETURN_ON_ERROR(allocation != nullptr, kRmtErrorInvalidPointer); RMT_RETURN_ON_ERROR(resource_list != nullptr, kRmtErrorInvalidPointer); - typedef std::set PrioritizedResourceSetType; // Set of resource objects sorted by priority. - std::map resource_memory_segments; // Map resource object sets split into segments using bind offset as a key. - uint64_t bind_offset = 0; - uint64_t bind_end_offset = 0; - // First pass: Split resource memory into segments for each of the resources starting offset. // Add the resource to the segment's list. Also create an empty segment for the resource's memory block ending offset. for (int i = 0; i < allocation->resource_count; i++) @@ -831,18 +915,6 @@ static RmtErrorCode RmtVirtualAllocationUpdateAliasedResourceSizes(const RmtVirt // Reset the alias size. RmtResourceUpdateAliasSize(resource->identifier, resource_list, 0); - // Skip this resource if it isn't bound to an allocation. - if (resource->bound_allocation == nullptr) - { - continue; - } - - // Skip this resource if it is implicit. - if (RmtResourceUserDataIsResourceImplicit(resource->identifier) == true) - { - continue; - } - // Skip this resource if it is disabled by the usage filter. RmtResourceUsageType usage_type = RmtResourceGetUsageType(resource); if (!(RmtResourceGetUsageTypeMask(usage_type) & resource_usage_mask)) @@ -850,45 +922,15 @@ static RmtErrorCode RmtVirtualAllocationUpdateAliasedResourceSizes(const RmtVirt continue; } - // Add a segment at the resource's starting offset (if one doesn't already exist) and add the resource to the segment's set of resources. - bind_offset = resource->address - resource->bound_allocation->base_address; - resource_memory_segments[bind_offset].insert(resource); - - // Add a segment with an empty list of resources (if one doesn't already exist). This marks the end of the resource's memory block. - bind_end_offset = bind_offset + resource->size_in_bytes; - resource_memory_segments[bind_end_offset]; - } - - // Second pass: For each segment except the first one, copy resources to the next segment's resource set if the resource's ending offset is greater than the segment's offset key. - for (std::map::iterator current_segment_iterator = resource_memory_segments.begin(); - current_segment_iterator != resource_memory_segments.end(); - current_segment_iterator++) - { - // Get the next segment. If there are no more segments then exit the loop. - auto next_segment_iterator = std::next(current_segment_iterator); - if (next_segment_iterator == resource_memory_segments.end()) - { - break; - } - - // Copy resources from previous segment to next segment if the resource's ending range extends into this segment. - for (auto resource : current_segment_iterator->second) - { - const uint64_t resource_start_range = resource->address - resource->bound_allocation->base_address; - const uint64_t resource_end_range = resource_start_range + resource->size_in_bytes; - if (resource_end_range > next_segment_iterator->first) - { - // The resource range extends into this segment. Add it to the next segment's list of resources. - next_segment_iterator->second.insert(resource); - } - } + AliasedResourceTrackerAddResource(resource); } // Total up the segmented sizes of each resource. std::unordered_map resource_alias_sizes; - std::map::iterator segment_iterator = resource_memory_segments.begin(); + std::map::iterator segment_iterator = + aliased_resource_memory_regions_for_allocations_[allocation->allocation_identifier].begin(); - for (; segment_iterator != resource_memory_segments.end(); segment_iterator++) + for (; segment_iterator != aliased_resource_memory_regions_for_allocations_[allocation->allocation_identifier].end(); segment_iterator++) { if (segment_iterator->second.empty()) { @@ -902,7 +944,7 @@ static RmtErrorCode RmtVirtualAllocationUpdateAliasedResourceSizes(const RmtVirt // Get the offset of the next segment so that the size of the current segment can be determined. auto next_segment_iterator = std::next(segment_iterator); - if (next_segment_iterator == resource_memory_segments.end()) + if (next_segment_iterator == aliased_resource_memory_regions_for_allocations_[allocation->allocation_identifier].end()) { // End of segments reached. break; @@ -917,7 +959,7 @@ static RmtErrorCode RmtVirtualAllocationUpdateAliasedResourceSizes(const RmtVirt resource_alias_sizes[top_resource] += segment_size; } - // Copy the aliased sizes to the resource objects. + // Copy the size after aliasing to the resource objects. for (auto alias_resource : resource_alias_sizes) { RmtResourceUpdateAliasSize(alias_resource.first->identifier, resource_list, alias_resource.second); diff --git a/source/backend/rmt_virtual_allocation_list.h b/source/backend/rmt_virtual_allocation_list.h index 2fd4648..af7aa70 100644 --- a/source/backend/rmt_virtual_allocation_list.h +++ b/source/backend/rmt_virtual_allocation_list.h @@ -66,6 +66,7 @@ typedef struct RmtVirtualAllocation unbound_memory_regions; ///< An array of RmtUnboundMemoryRegion structures representing the unbound memory inside this virtual allocation. int32_t unbound_memory_region_count; ///< The number of RmtUnboundMemoryRegion structures inside unboundMemoryRegions. uint64_t allocation_identifier; ///< Uniquely identifies this virtual memory allocation. + uint64_t resource_usage_aliased_size[kRmtResourceUsageTypeCount]; ///< Aliased resource usage sizes for resource bound to this allocation. } RmtVirtualAllocation; diff --git a/source/frontend/CMakeLists.txt b/source/frontend/CMakeLists.txt index 7f2e7e1..b774ef4 100644 --- a/source/frontend/CMakeLists.txt +++ b/source/frontend/CMakeLists.txt @@ -5,8 +5,6 @@ project(RadeonMemoryVisualizer) add_definitions(-DSYSTEM_INFO_ENABLE_RDF) IF(WIN32) - # Warnings as errors for Windows - add_compile_options(/W4 /WX) # this warning is caused by the QT header files - use pragma to disable at source # disable warning C4127: conditional expression is constant add_compile_options(/wd4127) @@ -35,11 +33,8 @@ ENDIF(WIN32) set(CMAKE_INCLUDE_CURRENT_DIR ON) include_directories(AFTER ../backend ../parser ../frontend) -find_package(Qt5 COMPONENTS Core Gui Widgets Svg REQUIRED) - IF(UNIX) find_package(Threads) - add_compile_options(-D_LINUX -Wall -Wextra -Werror) ENDIF(UNIX) # List of all source files. It may be possible to have the build process call cmake to update the makefiles @@ -159,6 +154,8 @@ set( SOURCES "views/main_window.cpp" "views/main_window.h" "views/main_window.ui" + "views/custom_widgets/rmv_cancellable_loading_widget.cpp" + "views/custom_widgets/rmv_cancellable_loading_widget.h" "views/custom_widgets/rmv_allocation_bar.cpp" "views/custom_widgets/rmv_allocation_bar.h" "views/custom_widgets/rmv_camera_snapshot_widget.cpp" @@ -300,7 +297,7 @@ set( WINDOWS_SOURCES # Filter out the UI files and get the list of generated files set(UI_FILES ${SOURCES}) list(FILTER UI_FILES INCLUDE REGEX "\.ui$") -qt5_wrap_ui (GENERATED_UI ${UI_FILES}) +qt_wrap_ui (GENERATED_UI ${UI_FILES}) set(SOURCES ${SOURCES} ${GENERATED_UI}) # searching for library file @@ -348,9 +345,9 @@ ENDIF(CMAKE_RELEASE_POSTFIX) # executable file library dependency list IF(WIN32) - target_link_libraries(${PROJECT_NAME} RmvBackend RmvParser system_info rdf Qt5::Widgets QtCustomWidgets QtUtils wsock32 winmm ${UPDATECHECKAPI_LIBS}) + target_link_libraries(${PROJECT_NAME} RmvBackend RmvParser system_info rdf Qt::Widgets QtCustomWidgets QtUtils wsock32 winmm ${UPDATECHECKAPI_LIBS}) ELSEIF(UNIX) - target_link_libraries(${PROJECT_NAME} RmvBackend RmvParser system_info rdf Qt5::Widgets QtCustomWidgets QtUtils Threads::Threads ${UPDATECHECKAPI_LIBS}) + target_link_libraries(${PROJECT_NAME} RmvBackend RmvParser system_info rdf Qt::Widgets QtCustomWidgets QtUtils Threads::Threads ${UPDATECHECKAPI_LIBS}) ENDIF() add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD @@ -370,32 +367,6 @@ IF(APPLE) ENDIF(APPLE) IF(WIN32) - ## Not using windeployqt currently as it copies more files than necessary. Leaving the command - ## here for future reference - # - # Run windeployqt if it can be found - # find_program(WINDEPLOYQT_EXECUTABLE NAMES windeployqt) - # add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD - # COMMAND ${WINDEPLOYQT_EXECUTABLE} $ --no-translations --no-angle --no-opengl-sw --no-system-d3d-compiler --no-compiler-runtime --no-plugins - # ) - - # Copy the QT files to the output directory - add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD - # TBD - can windows use the same subdirectory layout as Linux (i.e. files in qt subfolder references via qt.conf)" - COMMAND ${CMAKE_COMMAND} -E make_directory $/imageformats - COMMAND ${CMAKE_COMMAND} -E make_directory $/platforms - COMMAND ${CMAKE_COMMAND} -E make_directory $/iconengines - COMMAND ${CMAKE_COMMAND} -E make_directory $/styles - COMMAND ${CMAKE_COMMAND} -E copy_if_different $ $ - COMMAND ${CMAKE_COMMAND} -E copy_if_different $ $ - COMMAND ${CMAKE_COMMAND} -E copy_if_different $ $ - COMMAND ${CMAKE_COMMAND} -E copy_if_different $ $ - COMMAND ${CMAKE_COMMAND} -E copy_if_different $ $/imageformats - COMMAND ${CMAKE_COMMAND} -E copy_if_different $ $/iconengines - COMMAND ${CMAKE_COMMAND} -E copy_if_different $ $/platforms - COMMAND ${CMAKE_COMMAND} -E copy_if_different $ $/styles - ) - # Copy the VisualStudio redist files # the list of all redist files is contained in the variable CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS. We only want to copy # a subset of these so we extract the directory from the first entry and then manually copy the files we want @@ -424,69 +395,20 @@ IF(WIN32) ENDIF(WIN32) -IF(UNIX AND NOT APPLE) - # define some variables for the source and destination of the QT lib copy - set(QT_LIB_SRC "$") - set(QT_LIB_DST "$/lib") - set(QT_PLATFORM_SRC "$") - set(QT_PLATFORM_DST "$/plugins/platforms") - set(QT_IMAGEFORMATS_SRC "$") - set(QT_IMAGEFORMATS_DST "$/plugins/imageformats") - set(QT_ICONENGINES_SRC "$") - set(QT_ICONENGINES_DST "$/plugins/iconengines") - - add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD - COMMAND ${CMAKE_COMMAND} -E echo "copying QT libs from ${QT_LIB_SRC} to ${QT_LIB_DST}" - COMMAND ${CMAKE_COMMAND} -E make_directory ${QT_LIB_DST} - COMMAND ${CMAKE_COMMAND} -E make_directory $/plugins/iconengines - COMMAND ${CMAKE_COMMAND} -E make_directory $/plugins/imageformats - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${QT_LIB_SRC}/libQt5Core.so.5 ${QT_LIB_DST} - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${QT_LIB_SRC}/libQt5Gui.so.5 ${QT_LIB_DST} - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${QT_LIB_SRC}/libQt5Svg.so.5 ${QT_LIB_DST} - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${QT_LIB_SRC}/libQt5Widgets.so.5 ${QT_LIB_DST} - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${QT_LIB_SRC}/libQt5XcbQpa.so.5 ${QT_LIB_DST} - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${QT_LIB_SRC}/libQt5DBus.so.5 ${QT_LIB_DST} - ) - - # Copy Additional Qt 5.15.2 library files if needed. - IF(NOT DISABLE_EXTRA_QT_LIB_DEPLOY) - add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${QT_LIB_SRC}/libicudata.so.50 ${QT_LIB_DST} - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${QT_LIB_SRC}/libicui18n.so.50 ${QT_LIB_DST} - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${QT_LIB_SRC}/libicuuc.so.50 ${QT_LIB_DST} - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${QT_LIB_SRC}/libxcb.so.1 ${QT_LIB_DST} - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${QT_LIB_SRC}/libxcb-icccm.so.4 ${QT_LIB_DST} - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${QT_LIB_SRC}/libxcb-image.so.0 ${QT_LIB_DST} - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${QT_LIB_SRC}/libxcb-keysyms.so.1 ${QT_LIB_DST} - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${QT_LIB_SRC}/libxcb-randr.so.0 ${QT_LIB_DST} - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${QT_LIB_SRC}/libxcb-render.so.0 ${QT_LIB_DST} - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${QT_LIB_SRC}/libxcb-render-util.so.0 ${QT_LIB_DST} - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${QT_LIB_SRC}/libxcb-shape.so.0 ${QT_LIB_DST} - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${QT_LIB_SRC}/libxcb-shm.so.0 ${QT_LIB_DST} - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${QT_LIB_SRC}/libxcb-sync.so.1 ${QT_LIB_DST} - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${QT_LIB_SRC}/libxcb-util.so.1 ${QT_LIB_DST} - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${QT_LIB_SRC}/libxcb-xfixes.so.0 ${QT_LIB_DST} - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${QT_LIB_SRC}/libxcb-xinerama.so.0 ${QT_LIB_DST} - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${QT_LIB_SRC}/libxcb-xkb.so.1 ${QT_LIB_DST} - ) - ENDIF(NOT DISABLE_EXTRA_QT_LIB_DEPLOY) - - set(SO_POSTFIX $<$:${CMAKE_DEBUG_POSTFIX}>$<$:${CMAKE_RELEASE_POSTFIX}>) - - add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD - COMMAND ${CMAKE_COMMAND} -E echo "copying QT platform plugins from ${QT_PLATFORM_SRC} to ${QT_PLATFORM_DST}" - COMMAND ${CMAKE_COMMAND} -E make_directory ${QT_PLATFORM_DST} - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${QT_PLATFORM_SRC}/libqxcb.so ${QT_PLATFORM_DST} - COMMAND ${CMAKE_COMMAND} -E echo "copying QT imageformat plugins from ${QT_IMAGEFORMATS_SRC} to ${QT_IMAGEFORMATS_DST}" - COMMAND ${CMAKE_COMMAND} -E make_directory ${QT_IMAGEFORMATS_DST} - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${QT_IMAGEFORMATS_SRC}/libqsvg.so ${QT_IMAGEFORMATS_DST} - COMMAND ${CMAKE_COMMAND} -E echo "copying QT iconengine plugins from ${QT_ICONENGINES_SRC} to ${QT_ICONENGINES_DST}" - COMMAND ${CMAKE_COMMAND} -E make_directory ${QT_ICONENGINES_DST} - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${QT_ICONENGINES_SRC}/libqsvgicon.so ${QT_ICONENGINES_DST} - COMMAND ${CMAKE_COMMAND} -E echo "copying qt.conf to $" - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/../../build/qt.conf $ - ) +# Copy QT components into appropriate location in build destination +if (WIN32) + DEPLOY_QT_BUILD(${PROJECT_NAME} TRUE ${PROJECT_BINARY_DIR}) +elseif (UNIX AND NOT APPLE) + DEPLOY_QT_BUILD(${PROJECT_NAME} TRUE ${PROJECT_BINARY_DIR}) +elseif (APPLE) + if (CMAKE_BUILD_TYPE STREQUAL "Debug") + DEPLOY_QT_BUILD(${PROJECT_NAME} TRUE ${CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG}) + elseif(CMAKE_BUILD_TYPE STREQUAL "Release") + DEPLOY_QT_BUILD(${PROJECT_NAME} TRUE ${CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE}) + endif() +endif() +IF(UNIX AND NOT APPLE) IF(NOT QT_SYSTEM) # Call chrpath on system to override executable file RPATH. find_program(CHRPATH_EXECUTABLE NAMES chrpath) @@ -497,3 +419,5 @@ IF(UNIX AND NOT APPLE) ENDIF(NOT QT_SYSTEM) ENDIF(UNIX AND NOT APPLE) + +devtools_target_options(${PROJECT_NAME}) diff --git a/source/frontend/managers/load_animation_manager.cpp b/source/frontend/managers/load_animation_manager.cpp index 39aa35f..b8763ed 100644 --- a/source/frontend/managers/load_animation_manager.cpp +++ b/source/frontend/managers/load_animation_manager.cpp @@ -7,6 +7,8 @@ #include "managers/load_animation_manager.h" +#include "managers/message_manager.h" + #include #include "qt_common/utils/scaling_manager.h" @@ -51,24 +53,30 @@ namespace rmv } } - void LoadAnimationManager::StartAnimation(QWidget* parent, int height_offset) + void LoadAnimationManager::StartAnimation(QWidget* parent, int height_offset, const bool can_cancel) { if (file_load_animation_ == nullptr) { - file_load_animation_ = new FileLoadingWidget(parent); + file_load_animation_ = new RmvCancellableLoadingWidget(parent, can_cancel); Resize(parent, height_offset); file_load_animation_->show(); tab_widget_->setDisabled(true); file_menu_->setDisabled(true); + emit rmv::MessageManager::Get().ChangeActionsRequested(false); qApp->setOverrideCursor(Qt::BusyCursor); + + if (can_cancel) + { + connect(file_load_animation_, &RmvCancellableLoadingWidget::CancelClicked, this, &LoadAnimationManager::AnimationCancelled); + } } } void LoadAnimationManager::StartAnimation() { - StartAnimation(tab_widget_, tab_widget_->TabHeight()); + StartAnimation(tab_widget_, tab_widget_->TabHeight(), false); } void LoadAnimationManager::StopAnimation() @@ -80,6 +88,7 @@ namespace rmv tab_widget_->setEnabled(true); file_menu_->setEnabled(true); + emit rmv::MessageManager::Get().ChangeActionsRequested(true); qApp->restoreOverrideCursor(); } diff --git a/source/frontend/managers/load_animation_manager.h b/source/frontend/managers/load_animation_manager.h index 1d21aff..b31d371 100644 --- a/source/frontend/managers/load_animation_manager.h +++ b/source/frontend/managers/load_animation_manager.h @@ -13,10 +13,11 @@ #ifndef RMV_MANAGERS_LOAD_ANIMATION_MANAGER_H_ #define RMV_MANAGERS_LOAD_ANIMATION_MANAGER_H_ +#include "views/custom_widgets/rmv_cancellable_loading_widget.h" + #include #include -#include "qt_common/custom_widgets/file_loading_widget.h" #include "qt_common/custom_widgets/tab_widget.h" namespace rmv @@ -52,7 +53,8 @@ namespace rmv /// /// @param [in] parent The parent window. /// @param [in] height_offset The offset from the top of the parent widget. - void StartAnimation(QWidget* parent, int height_offset); + /// @param [in] can_cancel If true, a cancel button is added allowing the user to abort. + void StartAnimation(QWidget* parent, int height_offset, const bool can_cancel = false); /// @brief Start the loading animation. /// @@ -72,6 +74,10 @@ namespace rmv /// Make sure that the load animation is also resized. void ResizeAnimation(); + signals: + /// @brief Indicates that the animation was cancelled by the user. + void AnimationCancelled(); + private: /// @brief Resize the loading animation. /// @@ -79,9 +85,9 @@ namespace rmv /// @param [in] height_offset The offset from the top of the parent widget. void Resize(QWidget* parent, int height_offset); - TabWidget* tab_widget_; ///< The tab widget from the main window. - QMenu* file_menu_; ///< The file menu widget from the main window. - FileLoadingWidget* file_load_animation_; ///< Widget to show animation. + TabWidget* tab_widget_; ///< The tab widget from the main window. + QMenu* file_menu_; ///< The file menu widget from the main window. + RmvCancellableLoadingWidget* file_load_animation_; ///< Widget to show animation. }; } // namespace rmv diff --git a/source/frontend/managers/message_manager.h b/source/frontend/managers/message_manager.h index 37468f7..e758eba 100644 --- a/source/frontend/managers/message_manager.h +++ b/source/frontend/managers/message_manager.h @@ -63,6 +63,11 @@ namespace rmv /// @brief Signal for when the hash values changed. void HashesChanged(); + + /// @brief Signal to request enabling or disabling UI actions. + /// + /// @param [in] enable If true, actions should be enabled. Otherwise, actions should be disabled. + void ChangeActionsRequested(const bool enable); }; } // namespace rmv diff --git a/source/frontend/managers/snapshot_manager.cpp b/source/frontend/managers/snapshot_manager.cpp index 5c18764..882deb4 100644 --- a/source/frontend/managers/snapshot_manager.cpp +++ b/source/frontend/managers/snapshot_manager.cpp @@ -24,7 +24,7 @@ class SnapshotWorker : public rmv::BackgroundTask /// @param [in] snapshot_base_point The object containing the snapshot information for the base snapshot. /// @param [in] snapshot_diff_point The object containing the snapshot information for the diff snapshot. explicit SnapshotWorker(RmtDataSet* data_set, RmtSnapshotPoint* snapshot_base_point, RmtSnapshotPoint* snapshot_diff_point) - : BackgroundTask() + : BackgroundTask(false) { data_set_ = data_set; snapshot_point_[rmv::kSnapshotCompareBase] = snapshot_base_point; diff --git a/source/frontend/models/colorizer_base.cpp b/source/frontend/models/colorizer_base.cpp index b627801..3af48df 100644 --- a/source/frontend/models/colorizer_base.cpp +++ b/source/frontend/models/colorizer_base.cpp @@ -359,7 +359,8 @@ namespace rmv case kColorModeResourceUsageType: { - for (int32_t index = 0; index < kRmtResourceUsageTypeCount; ++index) + // Note: Usage types on the legend are drawn in reverse order so that highest aliased priority usage are on the left, lowest on the right. + for (int32_t index = kRmtResourceUsageTypeCount - 1; index > -1; --index) { if (index != kRmtResourceUsageTypeUnknown) { diff --git a/source/frontend/models/snapshot/resource_details_model.cpp b/source/frontend/models/snapshot/resource_details_model.cpp index fa24267..da08887 100644 --- a/source/frontend/models/snapshot/resource_details_model.cpp +++ b/source/frontend/models/snapshot/resource_details_model.cpp @@ -32,7 +32,7 @@ namespace rmv public: /// @brief Constructor. ResourceWorker(rmv::ResourceDetailsModel* model, RmtResourceIdentifier resource_identifier) - : BackgroundTask() + : BackgroundTask(false) , model_(model) , resource_identifier_(resource_identifier) { diff --git a/source/frontend/models/snapshot/resource_overview_model.cpp b/source/frontend/models/snapshot/resource_overview_model.cpp index b1f3598..2fdb186 100644 --- a/source/frontend/models/snapshot/resource_overview_model.cpp +++ b/source/frontend/models/snapshot/resource_overview_model.cpp @@ -107,7 +107,7 @@ namespace rmv if (RmtResourceGetAliasCount(resource) > 0) { - text_string += "\nAliased size: " + rmv::string_util::LocalizedValueMemory(resource->adjusted_size_in_bytes, false, false); + text_string += "\nSize after aliasing: " + rmv::string_util::LocalizedValueMemory(resource->adjusted_size_in_bytes, false, false); } const uint64_t offset = RmtResourceGetOffsetFromBoundAllocation(resource); diff --git a/source/frontend/models/timeline/timeline_model.cpp b/source/frontend/models/timeline/timeline_model.cpp index 35bbee6..38a0277 100644 --- a/source/frontend/models/timeline/timeline_model.cpp +++ b/source/frontend/models/timeline/timeline_model.cpp @@ -39,7 +39,7 @@ namespace rmv /// @param [in] model The model containing the timeline data to work with. /// @param [in] timeline_type The type of timeline being generated. explicit TimelineWorker(rmv::TimelineModel* model, RmtDataTimelineType timeline_type) - : BackgroundTask() + : BackgroundTask(timeline_type == RmtDataTimelineType::kRmtDataTimelineTypeResourceUsageVirtualSize) , model_(model) , timeline_type_(timeline_type) { @@ -56,6 +56,11 @@ namespace rmv model_->GenerateTimeline(timeline_type_); } + virtual void Cancel() + { + model_->CancelBackgroundTask(); + } + private: rmv::TimelineModel* model_; ///< Pointer to the model data. RmtDataTimelineType timeline_type_; ///< The timeline type. @@ -257,6 +262,22 @@ namespace rmv } } + void TimelineModel::CancelBackgroundTask() + { + TraceManager& trace_manager = TraceManager::Get(); + RmtDataSet* data_set = trace_manager.GetDataSet(); + RMT_ASSERT(data_set != nullptr); + RmtDataSetCancelBackgroundTask(data_set); + } + + bool TimelineModel::IsBackgroundTaskCancelled() const + { + TraceManager& trace_manager = TraceManager::Get(); + RmtDataSet* data_set = trace_manager.GetDataSet(); + RMT_ASSERT(data_set != nullptr); + return RmtDataSetIsBackgroundTaskCancelled(data_set); + } + void TimelineModel::UpdateMemoryGraph(uint64_t min_visible, uint64_t max_visible) { min_visible_ = min_visible; @@ -403,7 +424,7 @@ namespace rmv return true; } - bool TimelineModel::GetHistogramData(int bucket_group_index, int bucket_index, qreal& out_y_pos, qreal& out_height) + bool TimelineModel::GetHistogramData(int bucket_group_index, int bucket_index, const int bucket_count, qreal& out_y_pos, qreal& out_height) { if (bucket_index < kNumBuckets) { @@ -415,11 +436,25 @@ namespace rmv // need to be taken into account and used as an offset for the current bucket. out_y_pos = 0.0; qreal histogram_value = 0.0; - for (int i = 0; i <= bucket_group_index; i++) + + if (timeline->timeline_type == kRmtDataTimelineTypeResourceUsageVirtualSize) + { + // For the Resource Usage Size timeline view, reverse the order of the items in the stacked graph. + for (int i = bucket_count; i >= bucket_group_index; i--) + { + histogram_value = (double)RmtDataTimelineHistogramGetValue(&histogram_, bucket_index, i); + histogram_value /= timeline->maximum_value_in_all_series; + out_y_pos += histogram_value; + } + } + else { - histogram_value = (double)RmtDataTimelineHistogramGetValue(&histogram_, bucket_index, i); - histogram_value /= timeline->maximum_value_in_all_series; - out_y_pos += histogram_value; + for (int i = 0; i <= bucket_group_index; i++) + { + histogram_value = (double)RmtDataTimelineHistogramGetValue(&histogram_, bucket_index, i); + histogram_value /= timeline->maximum_value_in_all_series; + out_y_pos += histogram_value; + } } // Height is just the data for this particular sub-bucket. diff --git a/source/frontend/models/timeline/timeline_model.h b/source/frontend/models/timeline/timeline_model.h index 76df6ed..3bcd768 100644 --- a/source/frontend/models/timeline/timeline_model.h +++ b/source/frontend/models/timeline/timeline_model.h @@ -116,10 +116,12 @@ namespace rmv /// /// @param [in] bucket_group_index The sub-bucket index. /// @param [in] bucket_index The bucket index. + /// @param [in] bucket_count The total number of buckets. /// @param [out] out_y_pos The y position offset for this bucket and sub-bucket. /// @param [out] out_height The height for this bucket and sub-bucket. + /// /// @return true if bucket/sub-bucket is valid, false if not. - bool GetHistogramData(int bucket_group_index, int bucket_index, qreal& out_y_pos, qreal& out_height); + bool GetHistogramData(int bucket_group_index, int bucket_index, const int bucket_count, qreal& out_y_pos, qreal& out_height); /// @brief Get the number of buckets. /// @@ -169,6 +171,14 @@ namespace rmv /// @return A pointer to the worker thread object. BackgroundTask* CreateWorkerThread(RmtDataTimelineType timeline_type); + /// @brief Flag that indicates the background task should be cancelled. + void CancelBackgroundTask(); + + /// @brief Check to see if the flag to cancel background task has been set. + /// + /// @return true if the background task should be cancelled, otherwise false. + bool IsBackgroundTaskCancelled() const; + private slots: /// @brief Handle what happens when the model data changes. /// diff --git a/source/frontend/stylesheet.qss b/source/frontend/stylesheet.qss index 7bfeb03..c9b1d59 100644 --- a/source/frontend/stylesheet.qss +++ b/source/frontend/stylesheet.qss @@ -32,6 +32,11 @@ FileLoadingWidget color: gray; } +QPushButton#cancel_button +{ + font: bold 14pt; +} + RecentTraceMiniWidget { font-size: 9.75pt; diff --git a/source/frontend/util/constants.h b/source/frontend/util/constants.h index 5c2c5bd..09be02b 100644 --- a/source/frontend/util/constants.h +++ b/source/frontend/util/constants.h @@ -118,8 +118,9 @@ namespace rmv static const QString kRmvLicenseFile = "/LICENSE.txt"; static const QString kSampleTraceLocation = "/samples/sample_trace" + QString(kRMVTraceFileExtension); - static const QString kFileOpenFileTypes = - "RMV trace files (*" + QString(kRMVTraceFileExtension) + ") ;; RGD crash dump files (*" + kRGDTraceFileExtension + ")"; + static const QString kFileOpenFileTypes = "All supported files (*" + QString(kRMVTraceFileExtension) + " *" + QString(kRGDTraceFileExtension) + + ") ;; RMV trace files (*" + QString(kRMVTraceFileExtension) + ") ;; RGD crash dump files (*" + + kRGDTraceFileExtension + ")"; static const QString kMissingRmvTrace = "Missing RMV sample trace: "; static const QString kMissingRmvHelpFile = "Missing RMV help file: "; diff --git a/source/frontend/util/thread_controller.cpp b/source/frontend/util/thread_controller.cpp index fe601ba..acb35f0 100644 --- a/source/frontend/util/thread_controller.cpp +++ b/source/frontend/util/thread_controller.cpp @@ -13,7 +13,8 @@ namespace rmv { - BackgroundTask::BackgroundTask() + BackgroundTask::BackgroundTask(const bool can_cancel) + : can_cancel_(can_cancel) { } @@ -27,6 +28,15 @@ namespace rmv emit WorkerFinished(); } + void BackgroundTask::Cancel() + { + } + + bool BackgroundTask::CanCancel() const + { + return can_cancel_; + } + ThreadController::ThreadController(QWidget* parent, BackgroundTask* background_task) : background_task_(background_task) , finished_(false) @@ -38,7 +48,7 @@ namespace rmv } else { - rmv::LoadAnimationManager::Get().StartAnimation(parent, 0); + rmv::LoadAnimationManager::Get().StartAnimation(parent, 0, background_task->CanCancel()); } // Create the thread. It will be setup to be deleted below when the thread has @@ -53,7 +63,7 @@ namespace rmv // set up signal connections to automatically delete thread_ and background_task_ when work is done: connect(background_task_, &BackgroundTask::WorkerFinished, background_task_, &BackgroundTask::deleteLater); connect(thread_, &QThread::finished, thread_, &QThread::deleteLater); - + connect(&rmv::LoadAnimationManager::Get(), &LoadAnimationManager::AnimationCancelled, this, &ThreadController::Cancelled); thread_->start(); } @@ -68,6 +78,12 @@ namespace rmv emit ThreadFinished(); } + void ThreadController::Cancelled() + { + // Set cancel flag for the job. + emit ThreadCancelled(); + } + bool ThreadController::Finished() const { return finished_; diff --git a/source/frontend/util/thread_controller.h b/source/frontend/util/thread_controller.h index 3d140df..30e2f2c 100644 --- a/source/frontend/util/thread_controller.h +++ b/source/frontend/util/thread_controller.h @@ -29,7 +29,7 @@ namespace rmv public: /// @brief Constructor. - BackgroundTask(); + BackgroundTask(const bool can_cancel); /// @brief Destructor. virtual ~BackgroundTask(); @@ -42,9 +42,20 @@ namespace rmv /// Calls the derived ThreadFunc() and cleans up afterwards. void Start(); + /// @brief Indicates whether or no the background task can be cancelled. + /// + /// @return true if the background task can be cancelled, otherwise returns false. + bool CanCancel() const; + + /// @brief Request the background thread to be cancelled. + virtual void Cancel(); + signals: /// @brief Indicate that initial processing of the pane has completed. void WorkerFinished(); + + private: + bool can_cancel_; ///< A flag that indicates, if true, that the background task can be cancelled. }; class ThreadController : public QObject @@ -75,6 +86,13 @@ namespace rmv /// @brief Indicate that the worker thread has finished. void ThreadFinished(); + /// @brief Indicates that the worker thread has been cancelled. + void ThreadCancelled(); + + private slots: + /// @brief A slot that handles cancelling of the background task. + void Cancelled(); + private: QThread* thread_; ///< The worker thread. BackgroundTask* background_task_; ///< The worker object that does the work. diff --git a/source/frontend/views/custom_widgets/rmv_cancellable_loading_widget.cpp b/source/frontend/views/custom_widgets/rmv_cancellable_loading_widget.cpp new file mode 100644 index 0000000..4ea3d09 --- /dev/null +++ b/source/frontend/views/custom_widgets/rmv_cancellable_loading_widget.cpp @@ -0,0 +1,78 @@ +//============================================================================= +/// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. +/// \author AMD Developer Tools Team +/// \file +/// \brief Implementation of a loading widget with a cancel button. +//============================================================================= + +#include "views/custom_widgets/rmv_cancellable_loading_widget.h" + +#include +#include + +#include "qt_common/utils/scaling_manager.h" + +// The space between the bottom of the animation and the top of the cancel button. +static const int kCancelButtonVerticalSpace = 4; + +RmvCancellableLoadingWidget::RmvCancellableLoadingWidget(QWidget* parent, const bool can_cancel) + : FileLoadingWidget(parent) + , cancel_button_(nullptr) +{ + if (can_cancel) + { + // Find the main window and make this the parent of the cancel button. + // The rest of the UI will be disabled but the cancel button needs to be left enabled + // so that it can be clicked by the user. + for (QWidget* widget : QApplication::topLevelWidgets()) + { + if (widget->inherits("QMainWindow")) + { + QMainWindow* main_window = qobject_cast(widget); + + if (main_window != nullptr) + { + cancel_button_ = new ScaledPushButton("Cancel", main_window); + cancel_button_->setObjectName("cancel_button"); + connect(cancel_button_, &ScaledPushButton::clicked, this, &RmvCancellableLoadingWidget::HandleCancelClicked); + cancel_button_->show(); + + break; + } + } + } + } +} + +RmvCancellableLoadingWidget::~RmvCancellableLoadingWidget() +{ + delete cancel_button_; +} + +void RmvCancellableLoadingWidget::HandleCancelClicked(bool checked) +{ + Q_UNUSED(checked); + emit CancelClicked(); +} + +void RmvCancellableLoadingWidget::resizeEvent(QResizeEvent* event) +{ + FileLoadingWidget::resizeEvent(event); + + if (cancel_button_ != nullptr) + { + cancel_button_->resize(cancel_button_->sizeHint().width(), cancel_button_->sizeHint().height()); + + // The load animation is positioned by creating margins around the widget to squeeze it into the center. + // Determine the position of the animation so that the cancel button can be positioned centered underneath. + QMargins margins = contentsMargins(); + int animation_width = geometry().width() - (margins.left() + margins.right()); + int animation_height = geometry().height() - (margins.top() + margins.bottom()); + int x_position = geometry().x() + margins.left() + ((animation_width / 2) - (cancel_button_->geometry().width() / 2)); + int y_position = geometry().y() + margins.top() + animation_height + ScalingManager::Get().Scaled(kCancelButtonVerticalSpace); + QPoint button_position(x_position, y_position); + button_position = mapToGlobal(button_position); + button_position = cancel_button_->parentWidget()->mapFromGlobal(button_position); + cancel_button_->setGeometry(button_position.x(), button_position.y(), cancel_button_->geometry().width(), cancel_button_->geometry().height()); + } +} diff --git a/source/frontend/views/custom_widgets/rmv_cancellable_loading_widget.h b/source/frontend/views/custom_widgets/rmv_cancellable_loading_widget.h new file mode 100644 index 0000000..a23b534 --- /dev/null +++ b/source/frontend/views/custom_widgets/rmv_cancellable_loading_widget.h @@ -0,0 +1,51 @@ +//============================================================================= +/// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. +/// \author AMD Developer Tools Team +/// \file +/// \brief Header for a loading widget that has a cancel button +//============================================================================= + +#ifndef RMV_VIEWS_CUSTOM_WIDGETS_RMV_CANCELLABLE_LOADING_WIDGET_H_ +#define RMV_VIEWS_CUSTOM_WIDGETS_RMV_CANCELLABLE_LOADING_WIDGET_H_ + +#include + +#include "qt_common/custom_widgets/file_loading_widget.h" +#include "qt_common/custom_widgets/scaled_push_button.h" + +/// Class to handle the loading animation with cancel button. +class RmvCancellableLoadingWidget : public FileLoadingWidget +{ + Q_OBJECT + +public: + /// @brief Constructor. + /// + /// @param [in] parent The animation widget's parent. + /// @param [in] can_cancel If true, indicates that the user can cancel the operation by clicking a cancel button. + RmvCancellableLoadingWidget(QWidget* parent = nullptr, const bool can_cancel = true); + + /// @brief Virtual destructor. + virtual ~RmvCancellableLoadingWidget(); + +signals: + /// @brief Notifies when the user clicks the cancel button. + void CancelClicked(); + +private slots: + /// @brief Handle when the user clicks the cancel button. + /// + /// @param [in] checked Not used. + void HandleCancelClicked(bool checked = false); + +protected: + /// @brief Overridden resizeEvent handler. Adjusts position of cancel button. + /// + /// @param [in] event The resize event. + virtual void resizeEvent(QResizeEvent* event) Q_DECL_OVERRIDE; + +private: + ScaledPushButton* cancel_button_; ///< The cancel button. +}; + +#endif // RMV_VIEWS_CUSTOM_WIDGETS_RMV_CANCELLABLE_LOADING_WIDGET_H_ diff --git a/source/frontend/views/custom_widgets/rmv_resource_details.cpp b/source/frontend/views/custom_widgets/rmv_resource_details.cpp index 0cbefc0..60d378a 100644 --- a/source/frontend/views/custom_widgets/rmv_resource_details.cpp +++ b/source/frontend/views/custom_widgets/rmv_resource_details.cpp @@ -102,7 +102,7 @@ void RMVResourceDetails::paint(QPainter* painter, const QStyleOptionGraphicsItem if (RmtResourceGetAliasCount(&config_.resource) > 0) { x_pos += ScalingManager::Get().Scaled(100); - painter->drawText(x_pos, header_height, "Aliased size"); + painter->drawText(x_pos, header_height, "Size after aliasing"); painter->drawText(x_pos, value_height, rmv::string_util::LocalizedValueMemory(config_.resource.adjusted_size_in_bytes, false, false)); } diff --git a/source/frontend/views/custom_widgets/rmv_timeline_graph.cpp b/source/frontend/views/custom_widgets/rmv_timeline_graph.cpp index 0204dde..8ec4c35 100644 --- a/source/frontend/views/custom_widgets/rmv_timeline_graph.cpp +++ b/source/frontend/views/custom_widgets/rmv_timeline_graph.cpp @@ -48,7 +48,7 @@ void RMVTimelineGraph::paint(QPainter* painter, const QStyleOptionGraphicsItem* QColor color = config_.colorizer->GetColor(bucket_group_index); qreal y_pos = 0.0f; qreal height = 0.0f; - success = config_.model_data->GetHistogramData(bucket_group_index, bucket_index, y_pos, height); + success = config_.model_data->GetHistogramData(bucket_group_index, bucket_index, config_.model_data->GetNumBucketGroups(), y_pos, height); if (success) { QRectF rect; diff --git a/source/frontend/views/main_window.cpp b/source/frontend/views/main_window.cpp index 896dedb..6070c87 100644 --- a/source/frontend/views/main_window.cpp +++ b/source/frontend/views/main_window.cpp @@ -161,6 +161,8 @@ MainWindow::MainWindow(QWidget* parent) connect(themes_and_colors_pane, &ThemesAndColorsPane::RefreshedColors, this, &MainWindow::BroadcastChangeColoring); + connect(&rmv::MessageManager::Get(), &rmv::MessageManager::ChangeActionsRequested, this, &MainWindow::EnableActions); + // Connect to ScalingManager for notifications. connect(&ScalingManager::Get(), &ScalingManager::ScaleFactorChanged, this, &MainWindow::OnScaleFactorChanged); } @@ -355,6 +357,14 @@ void MainWindow::CreateActions() connect(about_action_, &QAction::triggered, this, &MainWindow::OpenAboutPane); } +void MainWindow::EnableActions(const bool enable) +{ + for (auto* action : actions()) + { + action->setEnabled(enable); + } +} + void MainWindow::CycleTimeUnits() { rmv::RMVSettings::Get().CycleTimeUnits(); diff --git a/source/frontend/views/main_window.h b/source/frontend/views/main_window.h index 0935b1b..8f4abbf 100644 --- a/source/frontend/views/main_window.h +++ b/source/frontend/views/main_window.h @@ -63,6 +63,11 @@ class MainWindow : public QMainWindow /// This include references to models or event indices that rely on backend data. void ResetUI(); + /// @brief Enable or disable shortcut keys. + /// + /// @param [in] enable If true, enables actions, otherwise disables actions. + void EnableActions(const bool enable); + public slots: /// @brief Called when trace file finished loading. void OpenTrace(); diff --git a/source/frontend/views/timeline/timeline_pane.cpp b/source/frontend/views/timeline/timeline_pane.cpp index b1f14e9..31d6f9f 100644 --- a/source/frontend/views/timeline/timeline_pane.cpp +++ b/source/frontend/views/timeline/timeline_pane.cpp @@ -34,6 +34,9 @@ const static QString kRenameAction = "Rename snapshot"; const static QString kDeleteAction = "Delete snapshot"; const static QString kCompareAction = "Compare snapshots"; +// The timeline type to revert to if calculating the resource usage size timeline type is cancelled. +static int saved_timeline_type_index_ = 0; + TimelinePane::TimelinePane(QWidget* parent) : BasePane(parent) , ui_(new Ui::TimelinePane) @@ -92,10 +95,10 @@ TimelinePane::TimelinePane(QWidget* parent) // Set up a list of required timeline modes, in order. // The list is terminated with -1. - static const RmtDataTimelineType type_list[] = {kRmtDataTimelineTypeResourceUsageVirtualSize, + static const RmtDataTimelineType type_list[] = {kRmtDataTimelineTypeCommitted, kRmtDataTimelineTypeResourceUsageCount, kRmtDataTimelineTypeVirtualMemory, - kRmtDataTimelineTypeCommitted, + kRmtDataTimelineTypeResourceUsageVirtualSize, // kRmtDataTimelineTypeProcess, RmtDataTimelineType(-1)}; @@ -234,6 +237,7 @@ void TimelinePane::OnTraceLoad() colorizer_->UpdateLegends(); UpdateTableDisplay(); + saved_timeline_type_index_ = 0; } void TimelinePane::UpdateSnapshotMarkers() @@ -677,19 +681,35 @@ void TimelinePane::TimelineTypeChanged() // When the worker thread has finished, a signal will be emitted. Wait for the signal here and update // the UI with the newly acquired data from the worker thread. connect(thread_controller_, &rmv::ThreadController::ThreadFinished, this, &TimelinePane::TimelineWorkerThreadFinished); + connect(thread_controller_, &rmv::ThreadController::ThreadCancelled, this, &TimelinePane::TimelineWorkerThreadCancelled); } } } +void TimelinePane::TimelineWorkerThreadCancelled() +{ + model_->CancelBackgroundTask(); +} + void TimelinePane::TimelineWorkerThreadFinished() { disconnect(thread_controller_, &rmv::ThreadController::ThreadFinished, this, &TimelinePane::TimelineWorkerThreadFinished); + disconnect(thread_controller_, &rmv::ThreadController::ThreadCancelled, this, &TimelinePane::TimelineWorkerThreadCancelled); thread_controller_->deleteLater(); thread_controller_ = nullptr; - model_->UpdateMemoryGraph(ui_->timeline_view_->ViewableStartClk(), ui_->timeline_view_->ViewableEndClk()); - ui_->timeline_view_->viewport()->update(); - colorizer_->UpdateLegends(); + if (model_->IsBackgroundTaskCancelled()) + { + // If the background task was cancelled, revert to the previously selected timeline type. + ui_->timeline_type_combo_box_->SetSelectedRow(saved_timeline_type_index_); + } + else + { + model_->UpdateMemoryGraph(ui_->timeline_view_->ViewableStartClk(), ui_->timeline_view_->ViewableEndClk()); + ui_->timeline_view_->viewport()->update(); + colorizer_->UpdateLegends(); + saved_timeline_type_index_ = ui_->timeline_type_combo_box_->CurrentRow(); + } } void TimelinePane::ScrollToSelectedSnapshot() diff --git a/source/frontend/views/timeline/timeline_pane.h b/source/frontend/views/timeline/timeline_pane.h index c978aa4..9f292cc 100644 --- a/source/frontend/views/timeline/timeline_pane.h +++ b/source/frontend/views/timeline/timeline_pane.h @@ -176,6 +176,9 @@ private slots: /// @brief Slot to handle what happens when the timeline worker thread has finished. void TimelineWorkerThreadFinished(); + /// @brief Slot to handle cancelling the worker thread. + void TimelineWorkerThreadCancelled(); + /// @brief Slot to handle what happens after the resource list table is sorted. /// /// Make sure the selected item (if there is one) is visible. diff --git a/source/parser/CMakeLists.txt b/source/parser/CMakeLists.txt index b0ecfa8..93fd5fa 100644 --- a/source/parser/CMakeLists.txt +++ b/source/parser/CMakeLists.txt @@ -6,17 +6,8 @@ add_definitions(-DRDF_CXX_BINDINGS) set(CMAKE_INCLUDE_CURRENT_DIR ON) include_directories(AFTER ../parser) -IF(WIN32) - # Warnings as errors for Windows - add_compile_options(/W4 /WX) -ELSEIF(UNIX) - # Remove warnings for Linux in backend - add_compile_options(-D_LINUX -Wall -Wextra -Werror) -ENDIF(WIN32) - # List of all source files. It may be possible to have the build process call cmake to update the makefiles # only when this file has changed (ie source files have been added or removed) - set( SOURCES "rmt_address_helper.cpp" "rmt_address_helper.h" @@ -77,3 +68,4 @@ foreach(source IN LISTS SOURCES) endforeach() ENDIF(WIN32) +devtools_target_options(${PROJECT_NAME})