Repository: LunarG/gfxreconstruct
Pull request #2069: Handle ExecuteIndirect MaxCommandCount
Author: login: JerryAMD
Created: 2025-03-12T13:52:59Z | Merged: 2025-03-20T17:03:50Z

**Problem:**
If both of MaxCommandCount and CountBuffer are valid, the actual number of operations to be performed are defined by the minimum of them. But GFXR only takes the value from CountBuffer.

**Solution:**
Track the MaxCommandCount value, once resource value mapping GFXR should compare the two values and take the minimum one.


Diff:
diff --git a/framework/decode/dx12_object_info.h b/framework/decode/dx12_object_info.h
index e3f6756fca..f54e2dbd94 100644
--- a/framework/decode/dx12_object_info.h
+++ b/framework/decode/dx12_object_info.h
@@ -182,18 +182,21 @@ struct ResourceValueInfo
     uint64_t                size{ 0 };
     D3D12StateObjectInfo*   state_object{ nullptr }; ///< Used to map values in shader records.
     ArgumentBufferExtraInfo arg_buffer_extra_info;
+    uint32_t                max_command_count{ 0 };
 
     ResourceValueInfo(uint64_t                in_offset,
                       ResourceValueType       in_type,
                       uint64_t                in_size,
                       D3D12StateObjectInfo*   in_state_object,
-                      ArgumentBufferExtraInfo in_arg_buffer_extra_info)
+                      ArgumentBufferExtraInfo in_arg_buffer_extra_info,
+                      uint32_t                in_max_command_count)
     {
         offset                = in_offset;
         type                  = in_type;
         size                  = in_size;
         state_object          = in_state_object;
         arg_buffer_extra_info = in_arg_buffer_extra_info;
+        max_command_count     = in_max_command_count;
     }
 
     bool operator<(const ResourceValueInfo& other) const { return offset < other.offset; }
@@ -236,7 +239,7 @@ struct DxgiSwapchainInfo : DxObjectExtraInfo
 
     graphics::dx12::ID3D12CommandQueueComPtr command_queue{
         nullptr
-    };                           ///< The command queue that was used to create the swapchain.
+    }; ///< The command queue that was used to create the swapchain.
     bool is_fullscreen{ false }; ///< Swapchain full screen flag.
 };
 
diff --git a/framework/decode/dx12_resource_value_mapper.cpp b/framework/decode/dx12_resource_value_mapper.cpp
index 52964b97cb..3cad0f3f36 100644
--- a/framework/decode/dx12_resource_value_mapper.cpp
+++ b/framework/decode/dx12_resource_value_mapper.cpp
@@ -89,7 +89,8 @@ void GetRootSignatureResourceValueInfos(const T* root_signature_desc, std::set<R
                                      ResourceValueType::kGpuDescriptorHandle,
                                      sizeof(D3D12_GPU_DESCRIPTOR_HANDLE::ptr),
                                      nullptr,
-                                     { nullptr, nullptr, 0 } });
+                                     { nullptr, nullptr, 0 },
+                                     0 });
                 byte_offset = aligned_offset + sizeof(D3D12_GPU_DESCRIPTOR_HANDLE::ptr);
             }
             break;
@@ -102,7 +103,8 @@ void GetRootSignatureResourceValueInfos(const T* root_signature_desc, std::set<R
                                      ResourceValueType::kGpuVirtualAddress,
                                      sizeof(D3D12_GPU_VIRTUAL_ADDRESS),
                                      nullptr,
-                                     { nullptr, nullptr, 0 } });
+                                     { nullptr, nullptr, 0 },
+                                     0 });
                 byte_offset = aligned_offset + sizeof(D3D12_GPU_VIRTUAL_ADDRESS);
             }
             break;
@@ -128,7 +130,8 @@ void CopyResourceValuesFromDstToSrc(std::set<ResourceValueInfo>&          src,
                      (*dst_iter).type,
                      static_cast<uint64_t>((*dst_iter).size),
                      (*dst_iter).state_object,
-                     (*dst_iter).arg_buffer_extra_info });
+                     (*dst_iter).arg_buffer_extra_info,
+                     (*dst_iter).max_command_count });
     }
 }
 
@@ -178,8 +181,8 @@ Dx12ResourceValueMapper::Dx12ResourceValueMapper(std::function<DxObjectInfo*(for
                                                  const graphics::Dx12ShaderIdMap&                  shader_id_map,
                                                  const graphics::Dx12GpuVaMap&                     gpu_va_map,
                                                  const decode::Dx12DescriptorMap&                  descriptor_map) :
-    get_object_info_func_(get_object_info_func),
-    shader_id_map_(shader_id_map), gpu_va_map_(gpu_va_map), descriptor_map_(descriptor_map), do_value_mapping_(true)
+    get_object_info_func_(get_object_info_func), shader_id_map_(shader_id_map), gpu_va_map_(gpu_va_map),
+    descriptor_map_(descriptor_map), do_value_mapping_(true)
 {}
 
 void Dx12ResourceValueMapper::EnableResourceValueTracker(std::function<uint64_t(void)> get_current_block_index_func,
@@ -408,7 +411,8 @@ void Dx12ResourceValueMapper::PostProcessCreateCommandSignature(HandlePointerDec
                                               ResourceValueType::kGpuVirtualAddress,
                                               sizeof(D3D12_VERTEX_BUFFER_VIEW::BufferLocation),
                                               nullptr,
-                                              { nullptr, nullptr, 0 } });
+                                              { nullptr, nullptr, 0 },
+                                              0 });
                 byte_offset += sizeof(D3D12_VERTEX_BUFFER_VIEW);
                 break;
             case D3D12_INDIRECT_ARGUMENT_TYPE_INDEX_BUFFER_VIEW:
@@ -416,7 +420,8 @@ void Dx12ResourceValueMapper::PostProcessCreateCommandSignature(HandlePointerDec
                                               ResourceValueType::kGpuVirtualAddress,
                                               sizeof(D3D12_INDEX_BUFFER_VIEW::BufferLocation),
                                               nullptr,
-                                              { nullptr, nullptr, 0 } });
+                                              { nullptr, nullptr, 0 },
+                                              0 });
                 byte_offset += sizeof(D3D12_INDEX_BUFFER_VIEW);
                 break;
             case D3D12_INDIRECT_ARGUMENT_TYPE_CONSTANT:
@@ -429,7 +434,8 @@ void Dx12ResourceValueMapper::PostProcessCreateCommandSignature(HandlePointerDec
                                               ResourceValueType::kGpuVirtualAddress,
                                               sizeof(D3D12_GPU_VIRTUAL_ADDRESS),
                                               nullptr,
-                                              { nullptr, nullptr, 0 } });
+                                              { nullptr, nullptr, 0 },
+                                              0 });
                 byte_offset += sizeof(D3D12_GPU_VIRTUAL_ADDRESS);
                 break;
             case D3D12_INDIRECT_ARGUMENT_TYPE_DISPATCH_RAYS:
@@ -437,7 +443,8 @@ void Dx12ResourceValueMapper::PostProcessCreateCommandSignature(HandlePointerDec
                                               ResourceValueType::kIndirectArgumentDispatchRays,
                                               sizeof(D3D12_DISPATCH_RAYS_DESC),
                                               nullptr,
-                                              { nullptr, nullptr, 0 } });
+                                              { nullptr, nullptr, 0 },
+                                              0 });
                 byte_offset += sizeof(D3D12_DISPATCH_RAYS_DESC);
                 break;
             case D3D12_INDIRECT_ARGUMENT_TYPE_DISPATCH_MESH:
@@ -480,7 +487,8 @@ void Dx12ResourceValueMapper::PostProcessExecuteIndirect(DxObjectInfo* command_l
                   ResourceValueType::kExecuteIndirectCountBuffer,
                   sizeof(uint32_t),
                   state_object_extra_info,
-                  { command_signature_extra_info, argument_buffer_object_info, argument_buffer_offset } });
+                  { command_signature_extra_info, argument_buffer_object_info, argument_buffer_offset },
+                  max_command_count });
         }
         else
         {
@@ -549,7 +557,8 @@ void Dx12ResourceValueMapper::PostProcessBuildRaytracingAccelerationStructure(
                           ResourceValueType::kGpuVirtualAddress,
                           sizeof(D3D12_GPU_VIRTUAL_ADDRESS),
                           nullptr,
-                          { nullptr, nullptr, 0 } });
+                          { nullptr, nullptr, 0 },
+                          0 });
                 }
             }
             else if (build_desc->Inputs.DescsLayout == D3D12_ELEMENTS_LAYOUT_ARRAY_OF_POINTERS)
@@ -561,7 +570,8 @@ void Dx12ResourceValueMapper::PostProcessBuildRaytracingAccelerationStructure(
                                                   ResourceValueType::kRaytracingInstanceDescPointer,
                                                   sizeof(D3D12_GPU_VIRTUAL_ADDRESS),
                                                   nullptr,
-                                                  { nullptr, nullptr, 0 } });
+                                                  { nullptr, nullptr, 0 },
+                                                  0 });
                 }
             }
             else
@@ -918,12 +928,13 @@ void Dx12ResourceValueMapper::CopyResourceValues(const ResourceCopyInfo& copy_in
         if (copy_info.num_bytes != 0)
         {
             auto dst_values_begin = dst_values.lower_bound(
-                { copy_info.dst_offset, ResourceValueType::kUnknown, 0, nullptr, { nullptr, nullptr, 0 } });
+                { copy_info.dst_offset, ResourceValueType::kUnknown, 0, nullptr, { nullptr, nullptr, 0 }, 0 });
             auto dst_values_end = dst_values.upper_bound({ copy_info.dst_offset + copy_info.num_bytes,
                                                            ResourceValueType::kUnknown,
                                                            0,
                                                            nullptr,
-                                                           { nullptr, nullptr, 0 } });
+                                                           { nullptr, nullptr, 0 },
+                                                           0 });
             CopyResourceValuesFromDstToSrc(src_values, dst_values, dst_values_begin, dst_values_end, copy_info);
             dst_values.erase(dst_values_begin, dst_values_end);
         }
@@ -1284,7 +1295,7 @@ bool Dx12ResourceValueMapper::MapValue(const ResourceValueInfo& value_info,
         uint8_t* desc_data_ptr = result_data.data() + final_offset;
 
         // First map the StartAddress GPU VAs from D3D12_DISPATCH_RAYS_DESC in resource data.
-        ResourceValueInfo rvi{ 0, ResourceValueType::kUnknown, 0, nullptr, { nullptr, nullptr, 0 } };
+        ResourceValueInfo rvi{ 0, ResourceValueType::kUnknown, 0, nullptr, { nullptr, nullptr, 0 }, 0 };
         rvi.size         = sizeof(D3D12_GPU_VIRTUAL_ADDRESS);
         rvi.type         = ResourceValueType::kGpuVirtualAddress;
         rvi.state_object = value_info.state_object;
@@ -1329,6 +1340,10 @@ bool Dx12ResourceValueMapper::MapValue(const ResourceValueInfo& value_info,
         // Insert new ArgumentBuffer RV infos, which will queue them for translation.
         if (command_count != 0)
         {
+            if (value_info.max_command_count > 0)
+            {
+                command_count = std::min(value_info.max_command_count, command_count);
+            }
             GetExecuteIndirectResourceValues(
                 indirect_values_map[value_info.arg_buffer_extra_info.argument_buffer],
                 value_info.arg_buffer_extra_info.command_signature_info->resource_value_infos,
@@ -1393,7 +1408,8 @@ bool Dx12ResourceValueMapper::MapValue(const ResourceValueInfo& value_info,
                                               ResourceValueType::kGpuVirtualAddress,
                                               sizeof(D3D12_GPU_VIRTUAL_ADDRESS),
                                               nullptr,
-                                              { nullptr, nullptr, 0 } });
+                                              { nullptr, nullptr, 0 },
+                                              0 });
             }
             else
             {
@@ -1597,7 +1613,8 @@ void Dx12ResourceValueMapper::GetShaderTableResourceValues(ResourceValueInfoMap&
                                       ResourceValueType::kShaderIdentifier,
                                       shader_record_size,
                                       state_object_extra_info,
-                                      { nullptr, nullptr, 0 } });
+                                      { nullptr, nullptr, 0 },
+                                      0 });
         byte_offset += shader_record_size;
     }
 }
@@ -1652,7 +1669,8 @@ void Dx12ResourceValueMapper::GetExecuteIndirectResourceValues(
                                                  resource_value_info.type,
                                                  resource_value_info.size,
                                                  state_object,
-                                                 resource_value_info.arg_buffer_extra_info });
+                                                 resource_value_info.arg_buffer_extra_info,
+                                                 0 });
         }
         command_offset += stride;
     }
diff --git a/framework/graphics/dx12_resource_data_util.cpp b/framework/graphics/dx12_resource_data_util.cpp
index ceb9b70cd0..a136173df9 100644
--- a/framework/graphics/dx12_resource_data_util.cpp
+++ b/framework/graphics/dx12_resource_data_util.cpp
@@ -412,7 +412,7 @@ HRESULT Dx12ResourceDataUtil::ReadFromResource(ID3D12Resource*
     // If the resource can be mapped, map it, copy the data, and return success.
     if (try_map_and_copy && IsResourceCpuAccessible(target_resource, kCopyTypeRead))
     {
-        data.resize(static_cast<size_t>(required_data_size));
+        data.resize(static_cast<size_t>(required_data_size), 0);
         if (CopyMappableResource(
                 target_resource, kCopyTypeRead, &data, nullptr, subresource_offsets, subresource_sizes))
         {
@@ -448,7 +448,7 @@ HRESULT Dx12ResourceDataUtil::ReadFromResource(ID3D12Resource*
     // After the command list has completed, map the copy resource and read its data.
     if (!batching && SUCCEEDED(result))
     {
-        data.resize(static_cast<size_t>(required_data_size));
+        data.resize(static_cast<size_t>(required_data_size), 0);
         result = MapSubresourceAndReadData(staging_resource, 0, static_cast<size_t>(required_data_size), data.data());
     }
 


Reviews:
user: login: davidd-lunarg
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T16:15:41Z
user: login: JerryAMD
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-12T17:26:11Z
user: login: JerryAMD
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-12T17:40:38Z
user: login: davidd-lunarg
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-13T17:57:31Z
user: login: davidd-lunarg
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-20T17:01:54Z

Review comments:
diff_hunk: @@ -182,18 +182,21 @@ struct ResourceValueInfo
     uint64_t                size{ 0 };
     D3D12StateObjectInfo*   state_object{ nullptr }; ///< Used to map values in shader records.
     ArgumentBufferExtraInfo arg_buffer_extra_info;
+    uint32_t                max_command_count{ 0 };
 
     ResourceValueInfo(uint64_t                in_offset,
                       ResourceValueType       in_type,
                       uint64_t                in_size,
                       D3D12StateObjectInfo*   in_state_object,
-                      ArgumentBufferExtraInfo in_arg_buffer_extra_info)
+                      ArgumentBufferExtraInfo in_arg_buffer_extra_info,
+                      uint32_t                in_max_command_count = 0)
path: framework/decode/dx12_object_info.h
user: login: davidd-lunarg
body: The new argument should not have a default value. `ResourceValueInfo` structs get copied or recreated in several places (for example `CopyResourceValuesFromDstToSrc`) and providing default values can make that process prone to losing data when constructing new `ResourceValueInfo`s
created_at: 2025-03-12T15:53:19Z
author_association: CONTRIBUTOR
diff_hunk: @@ -412,7 +412,7 @@ HRESULT Dx12ResourceDataUtil::ReadFromResource(ID3D12Resource*
     // If the resource can be mapped, map it, copy the data, and return success.
     if (try_map_and_copy && IsResourceCpuAccessible(target_resource, kCopyTypeRead))
     {
-        data.resize(static_cast<size_t>(required_data_size));
+        data.resize(static_cast<size_t>(required_data_size), 0);
path: framework/graphics/dx12_resource_data_util.cpp
user: login: davidd-lunarg
body: Was there an issue found due to this not being initialized to 0?
created_at: 2025-03-12T16:13:09Z
author_association: CONTRIBUTOR
diff_hunk: @@ -182,18 +182,21 @@ struct ResourceValueInfo
     uint64_t                size{ 0 };
     D3D12StateObjectInfo*   state_object{ nullptr }; ///< Used to map values in shader records.
     ArgumentBufferExtraInfo arg_buffer_extra_info;
+    uint32_t                max_command_count{ 0 };
path: framework/decode/dx12_object_info.h
user: login: davidd-lunarg
body: I wonder if some of these members of `ResourceValueInfo` could be turned into a union to save space as there can be many instances of this struct created. It isn't something that needs to be done now, but possibly an improvement for the future.
created_at: 2025-03-12T16:14:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -412,7 +412,7 @@ HRESULT Dx12ResourceDataUtil::ReadFromResource(ID3D12Resource*
     // If the resource can be mapped, map it, copy the data, and return success.
     if (try_map_and_copy && IsResourceCpuAccessible(target_resource, kCopyTypeRead))
     {
-        data.resize(static_cast<size_t>(required_data_size));
+        data.resize(static_cast<size_t>(required_data_size), 0);
path: framework/graphics/dx12_resource_data_util.cpp
user: login: JerryAMD
body: No. It's safety reason. 
created_at: 2025-03-12T17:26:11Z
author_association: COLLABORATOR
diff_hunk: @@ -182,18 +182,21 @@ struct ResourceValueInfo
     uint64_t                size{ 0 };
     D3D12StateObjectInfo*   state_object{ nullptr }; ///< Used to map values in shader records.
     ArgumentBufferExtraInfo arg_buffer_extra_info;
+    uint32_t                max_command_count{ 0 };
path: framework/decode/dx12_object_info.h
user: login: JerryAMD
body: `max_command_count` have to keep stand alone. And ResourceValueInfo only could get determined when `ExecuteIndirect()` been issued, since at that time we could know what's value in the Countbuffer. 
created_at: 2025-03-12T17:40:38Z
author_association: COLLABORATOR
diff_hunk: @@ -128,7 +130,8 @@ void CopyResourceValuesFromDstToSrc(std::set<ResourceValueInfo>&          src,
                      (*dst_iter).type,
                      static_cast<uint64_t>((*dst_iter).size),
                      (*dst_iter).state_object,
-                     (*dst_iter).arg_buffer_extra_info });
+                     (*dst_iter).arg_buffer_extra_info,
+                     0 });
path: framework/decode/dx12_resource_value_mapper.cpp
user: login: davidd-lunarg
body: This should pass through the value of `(*dst_iter).max_command_count` instead of `0`
created_at: 2025-03-12T17:57:16Z
author_association: CONTRIBUTOR

PR comments:
user: login: ci-tester-lunarg
created_at: 2025-03-12T13:53:02Z
body: CI gfxreconstruct build queued with queue ID 393718.
author_association: COLLABORATOR
user: login: ci-tester-lunarg
created_at: 2025-03-12T14:37:02Z
body: CI gfxreconstruct build # 6312 running.
author_association: COLLABORATOR
user: login: ci-tester-lunarg
created_at: 2025-03-12T16:22:59Z
body: CI gfxreconstruct build # 6312 failed.
author_association: COLLABORATOR
user: login: ci-tester-lunarg
created_at: 2025-03-12T17:41:06Z
body: CI gfxreconstruct build queued with queue ID 393942.
author_association: COLLABORATOR
user: login: ci-tester-lunarg
created_at: 2025-03-12T18:09:21Z
body: CI gfxreconstruct build # 6314 running.
author_association: COLLABORATOR
user: login: ci-tester-lunarg
created_at: 2025-03-12T19:06:16Z
body: CI gfxreconstruct build # 6314 passed.
author_association: COLLABORATOR
user: login: ci-tester-lunarg
created_at: 2025-03-14T01:05:33Z
body: CI gfxreconstruct build queued with queue ID 395106.
author_association: COLLABORATOR
user: login: ci-tester-lunarg
created_at: 2025-03-14T01:10:02Z
body: CI gfxreconstruct build # 6330 running.
author_association: COLLABORATOR
user: login: ci-tester-lunarg
created_at: 2025-03-14T02:02:43Z
body: CI gfxreconstruct build # 6330 passed.
author_association: COLLABORATOR