Add renderer resource label contract

This commit is contained in:
2026-06-02 17:01:10 +02:00
parent bbe3db1747
commit a5dbf05ab5
7 changed files with 132 additions and 14 deletions

View File

@@ -288,7 +288,7 @@ Known local toolchain state:
backend-owned resource creation, explicit texture usage flags, command order,
render-pass color/depth/stencil clear intent, scissor state, depth state,
blend state, texture-slot binding, sampler-state binding, texture-upload byte
counts, texture mip-level counts, mipmap-generation commands,
counts, texture mip-level counts, resource debug labels, mipmap-generation commands,
shader-uniform writes, explicit draw descriptor ranges, texture-copy regions,
readback bounds, frame-capture sources, destination buffer sizes, and
render-target blit regions, records
@@ -300,8 +300,8 @@ Known local toolchain state:
automation, including render-pass/depth-clear counts, scissor/depth/blend/
shader-uniform/texture-bind/sampler-bind/upload/texture-copy/readback/
frame-capture/blit command and byte totals, trace marker/scope counts,
backend resource creation counts, plus draw descriptor vertex/index totals,
and is covered by
labeled descriptor counts, backend resource creation counts, plus draw
descriptor vertex/index totals, and is covered by
`pano_cli_record_render_smoke` plus
`pano_cli_record_render_rejects_oversized_target`.
- `pano_cli simulate-document-history` exposes `pp_document::DocumentHistory`

View File

@@ -417,7 +417,8 @@ Status: started. `pp_renderer_api` exists as a headless renderer-neutral target
with explicit texture usage flags, texture descriptor, byte-size, viewport,
mesh, readback bounds, command context, render device, shader program
descriptor, mesh, render target, readback byte-size helpers,
texture mip-level validation, texture-upload/readback command validation,
texture mip-level validation, resource debug-label validation,
texture-upload/readback command validation,
mipmap-generation command validation, frame-capture byte-size helpers,
frame-capture command validation, render-target blit validation, texture-slot
binding validation, blend-state validation, scissor-state validation,
@@ -723,10 +724,10 @@ Results:
plus malformed payload rejection at the export boundary.
- `pp_renderer_api_tests` passed, including shader descriptor validation,
PanoPainter shader catalog validation, explicit texture usage validation,
texture mip-level validation, readback byte-size and command-order
validation, texture-upload byte-count validation, mipmap-generation command
validation, trace marker/scope validation, frame-capture byte-size and
command-order validation,
texture mip-level validation, resource debug-label validation, readback
byte-size and command-order validation, texture-upload byte-count validation,
mipmap-generation command validation, trace marker/scope validation,
frame-capture byte-size and command-order validation,
render-target blit validation, texture-slot binding validation, blend-state
validation, scissor-state validation, render-pass color/depth/stencil clear
validation, shader-uniform write validation, draw descriptor/range
@@ -834,8 +835,8 @@ Results:
texture-usage/texture-bind/sampler-bind/shader-uniform/texture-upload/
mipmap-generation/readback/frame-capture/blit validation plus explicit draw
descriptor and texture-copy validation; it creates validated textures,
render targets, shaders, meshes, and readback buffers, then records commands,
trace markers/scopes, render-pass
render targets, shaders, meshes, and readback buffers with validated debug
labels, then records commands, trace markers/scopes, render-pass
color/depth/stencil clear intent, scissor state, depth state, blend state,
shader uniform writes, texture/sampler binds, draw mesh inputs, explicit draw
ranges, texture uploads/mipmap generations/copies/readbacks, frame captures,
@@ -843,7 +844,8 @@ Results:
does not require a window or GL context.
- `pano_cli record-render` exercises that headless recording renderer and emits
JSON command counts, resource creation counts, target dimensions, backend
name, trace marker/scope and draw summary, render-pass/depth-clear counts, and draw
name, trace marker/scope and draw summary, labeled descriptor counts,
render-pass/depth-clear counts, and draw
descriptor vertex/index totals, scissor/depth/blend-state plus
shader-uniform/texture/sampler-bind/upload/texture-copy/readback/
frame-capture/blit command/byte totals for agent automation, with an

View File

@@ -131,6 +131,19 @@ pp::foundation::Status validate_texture_usage(TextureUsage usage) noexcept
return pp::foundation::Status::success();
}
pp::foundation::Status validate_resource_label(const char* label) noexcept
{
if (label == nullptr) {
return pp::foundation::Status::invalid_argument("resource label must not be null");
}
if (bounded_c_string_length(label, max_resource_label_bytes) > max_resource_label_bytes) {
return pp::foundation::Status::out_of_range("resource label exceeds the configured limit");
}
return pp::foundation::Status::success();
}
pp::foundation::Status validate_texture_desc(TextureDesc desc) noexcept
{
const auto extent_status = validate_extent(desc.extent);
@@ -150,7 +163,12 @@ pp::foundation::Status validate_texture_desc(TextureDesc desc) noexcept
return pp::foundation::Status::out_of_range("texture mip level count exceeds the texture extent");
}
return validate_texture_usage(desc.usage);
const auto usage_status = validate_texture_usage(desc.usage);
if (!usage_status.ok()) {
return usage_status;
}
return validate_resource_label(desc.debug_name);
}
pp::foundation::Result<std::uint64_t> texture_byte_size(TextureDesc desc) noexcept
@@ -417,6 +435,11 @@ pp::foundation::Status validate_sampler_desc(SamplerDesc desc) noexcept
pp::foundation::Status validate_mesh_desc(MeshDesc desc) noexcept
{
const auto label_status = validate_resource_label(desc.debug_name);
if (!label_status.ok()) {
return label_status;
}
if (desc.vertex_count == 0) {
return pp::foundation::Status::invalid_argument("mesh must contain at least one vertex");
}

View File

@@ -17,6 +17,7 @@ constexpr std::uint64_t max_texture_bytes = 1024ULL * 1024ULL * 1024ULL;
constexpr std::size_t max_shader_source_bytes = 4ULL * 1024ULL * 1024ULL;
constexpr std::size_t max_shader_uniform_bytes = 64ULL * 1024ULL;
constexpr std::size_t max_trace_label_bytes = 256;
constexpr std::size_t max_resource_label_bytes = 256;
enum class TextureFormat : std::uint8_t {
rgba8,
@@ -66,6 +67,7 @@ struct TextureDesc {
| TextureUsage::readback_source
| TextureUsage::copy_source
| TextureUsage::copy_destination;
const char* debug_name = "";
};
struct ReadbackRegion {
@@ -190,6 +192,7 @@ struct MeshDesc {
std::uint32_t vertex_count = 0;
std::uint32_t index_count = 0;
PrimitiveTopology topology = PrimitiveTopology::triangles;
const char* debug_name = "";
};
struct DrawDesc {
@@ -322,6 +325,7 @@ public:
[[nodiscard]] bool has_texture_usage(TextureUsage usage, TextureUsage required) noexcept;
[[nodiscard]] pp::foundation::Status validate_extent(Extent2D extent) noexcept;
[[nodiscard]] pp::foundation::Status validate_texture_usage(TextureUsage usage) noexcept;
[[nodiscard]] pp::foundation::Status validate_resource_label(const char* label) noexcept;
[[nodiscard]] pp::foundation::Status validate_texture_desc(TextureDesc desc) noexcept;
[[nodiscard]] pp::foundation::Status validate_viewport(Viewport viewport, Extent2D target_extent) noexcept;
[[nodiscard]] pp::foundation::Status validate_scissor(ScissorRect scissor, Extent2D target_extent) noexcept;

View File

@@ -365,7 +365,7 @@ if(TARGET pano_cli)
COMMAND pano_cli record-render --width 32 --height 16)
set_tests_properties(pano_cli_record_render_smoke PROPERTIES
LABELS "renderer;integration;desktop-fast"
PASS_REGULAR_EXPRESSION "\"backend\":\"recording\".*\"width\":32.*\"height\":16.*\"createdResources\":6.*\"commands\":20.*\"renderPasses\":1.*\"depthClears\":1.*\"stencilClears\":0.*\"drawCommands\":1.*\"drawVertices\":3.*\"drawIndices\":3.*\"scissorCommands\":1.*\"blendCommands\":1.*\"depthCommands\":1.*\"uniformCommands\":1.*\"uniformBytes\":64.*\"bindTextureCommands\":1.*\"bindSamplerCommands\":1.*\"boundTextureBytes\":2048.*\"uploadCommands\":1.*\"uploadBytes\":4.*\"copyCommands\":1.*\"copySourceBytes\":2048.*\"copyDestinationBytes\":2048.*\"readbackCommands\":1.*\"readbackBytes\":2048.*\"captureCommands\":1.*\"captureBytes\":2048.*\"blitCommands\":1.*\"blitSourceBytes\":2048.*\"blitDestinationBytes\":2048.*\"traceMarkers\":1.*\"traceBeginScopes\":1.*\"traceEndScopes\":1")
PASS_REGULAR_EXPRESSION "\"backend\":\"recording\".*\"width\":32.*\"height\":16.*\"createdResources\":6.*\"labeledCommandDescriptors\":10.*\"commands\":20.*\"renderPasses\":1.*\"depthClears\":1.*\"stencilClears\":0.*\"drawCommands\":1.*\"drawVertices\":3.*\"drawIndices\":3.*\"scissorCommands\":1.*\"blendCommands\":1.*\"depthCommands\":1.*\"uniformCommands\":1.*\"uniformBytes\":64.*\"bindTextureCommands\":1.*\"bindSamplerCommands\":1.*\"boundTextureBytes\":2048.*\"uploadCommands\":1.*\"uploadBytes\":4.*\"copyCommands\":1.*\"copySourceBytes\":2048.*\"copyDestinationBytes\":2048.*\"readbackCommands\":1.*\"readbackBytes\":2048.*\"captureCommands\":1.*\"captureBytes\":2048.*\"blitCommands\":1.*\"blitSourceBytes\":2048.*\"blitDestinationBytes\":2048.*\"traceMarkers\":1.*\"traceBeginScopes\":1.*\"traceEndScopes\":1")
add_test(NAME pano_cli_record_render_rejects_oversized_target
COMMAND "${CMAKE_COMMAND}"

View File

@@ -59,6 +59,7 @@ using pp::renderer::Viewport;
using pp::renderer::max_shader_source_bytes;
using pp::renderer::max_shader_uniform_bytes;
using pp::renderer::max_mip_levels_for_extent;
using pp::renderer::max_resource_label_bytes;
using pp::renderer::max_texture_dimension;
using pp::renderer::max_texture_slots;
using pp::renderer::max_trace_label_bytes;
@@ -82,6 +83,7 @@ using pp::renderer::validate_mesh_desc;
using pp::renderer::validate_mipmap_generation_desc;
using pp::renderer::validate_readback_region;
using pp::renderer::validate_render_pass_desc;
using pp::renderer::validate_resource_label;
using pp::renderer::validate_sampler_address_mode;
using pp::renderer::validate_sampler_desc;
using pp::renderer::validate_sampler_filter;
@@ -744,6 +746,50 @@ void validates_texture_usage_contract(pp::tests::Harness& h)
PP_EXPECT(h, unknown_format.code == StatusCode::invalid_argument);
}
void validates_resource_labels(pp::tests::Harness& h)
{
std::array<char, max_resource_label_bytes + 2U> oversized_label {};
oversized_label.fill('r');
oversized_label[max_resource_label_bytes + 1U] = '\0';
PP_EXPECT(h, validate_resource_label("").ok());
PP_EXPECT(h, validate_resource_label("stroke-target").ok());
PP_EXPECT(h, validate_texture_desc(TextureDesc {
.extent = Extent2D { .width = 4, .height = 4 },
.format = TextureFormat::rgba8,
.debug_name = "paint-texture",
})
.ok());
PP_EXPECT(h, validate_mesh_desc(MeshDesc {
.vertex_count = 3,
.topology = PrimitiveTopology::triangles,
.debug_name = "brush-quad",
})
.ok());
const auto null_label = validate_resource_label(nullptr);
const auto oversized = validate_resource_label(oversized_label.data());
const auto null_texture_label = validate_texture_desc(TextureDesc {
.extent = Extent2D { .width = 4, .height = 4 },
.format = TextureFormat::rgba8,
.debug_name = nullptr,
});
const auto oversized_mesh_label = validate_mesh_desc(MeshDesc {
.vertex_count = 3,
.topology = PrimitiveTopology::triangles,
.debug_name = oversized_label.data(),
});
PP_EXPECT(h, !null_label.ok());
PP_EXPECT(h, null_label.code == StatusCode::invalid_argument);
PP_EXPECT(h, !oversized.ok());
PP_EXPECT(h, oversized.code == StatusCode::out_of_range);
PP_EXPECT(h, !null_texture_label.ok());
PP_EXPECT(h, null_texture_label.code == StatusCode::invalid_argument);
PP_EXPECT(h, !oversized_mesh_label.ok());
PP_EXPECT(h, oversized_mesh_label.code == StatusCode::out_of_range);
}
void validates_mipmap_generation_contract(pp::tests::Harness& h)
{
const TextureDesc mipmapped_desc {
@@ -1469,11 +1515,13 @@ void render_devices_create_validated_resources(pp::tests::Harness& h)
.extent = Extent2D { .width = 8, .height = 4 },
.format = TextureFormat::rgba8,
.usage = TextureUsage::sampled | TextureUsage::upload_destination | TextureUsage::readback_source | TextureUsage::copy_source | TextureUsage::copy_destination,
.debug_name = "factory-texture",
});
const auto target = device.create_render_target(TextureDesc {
.extent = Extent2D { .width = 8, .height = 4 },
.format = TextureFormat::rgba8,
.usage = TextureUsage::render_target | TextureUsage::sampled | TextureUsage::upload_destination | TextureUsage::readback_source | TextureUsage::copy_source | TextureUsage::copy_destination,
.debug_name = "factory-target",
});
const auto shader = device.create_shader_program(ShaderProgramDesc {
.debug_name = "factory-shader",
@@ -1484,18 +1532,22 @@ void render_devices_create_validated_resources(pp::tests::Harness& h)
.vertex_count = 6,
.index_count = 6,
.topology = PrimitiveTopology::triangles,
.debug_name = "factory-mesh",
});
const auto readback = device.create_readback_buffer(8U * 4U * 4U);
PP_EXPECT(h, texture.ok());
PP_EXPECT(h, texture.value()->desc().extent.width == 8U);
PP_EXPECT(h, !has_texture_usage(texture.value()->desc().usage, TextureUsage::render_target));
PP_EXPECT(h, texture.value()->desc().debug_name == std::string_view("factory-texture"));
PP_EXPECT(h, target.ok());
PP_EXPECT(h, has_texture_usage(target.value()->color_desc().usage, TextureUsage::render_target));
PP_EXPECT(h, target.value()->color_desc().debug_name == std::string_view("factory-target"));
PP_EXPECT(h, shader.ok());
PP_EXPECT(h, shader.value()->debug_name() == std::string_view("factory-shader"));
PP_EXPECT(h, mesh.ok());
PP_EXPECT(h, mesh.value()->desc().index_count == 6U);
PP_EXPECT(h, mesh.value()->desc().debug_name == std::string_view("factory-mesh"));
PP_EXPECT(h, readback.ok());
PP_EXPECT(h, readback.value()->size_bytes() == 128U);
@@ -1620,6 +1672,7 @@ void recording_renderer_records_valid_command_sequences(pp::tests::Harness& h)
.extent = Extent2D { .width = 64, .height = 32 },
.format = TextureFormat::rgba8,
.usage = TextureUsage::render_target | TextureUsage::sampled | TextureUsage::upload_destination | TextureUsage::readback_source | TextureUsage::copy_source | TextureUsage::copy_destination,
.debug_name = "recorded-texture",
});
RecordingReadbackBuffer readback_buffer(64U * 32U * 4U);
const std::array<std::byte, 96> upload_bytes {};
@@ -1627,14 +1680,21 @@ void recording_renderer_records_valid_command_sequences(pp::tests::Harness& h)
.extent = Extent2D { .width = 64, .height = 32 },
.format = TextureFormat::rgba8,
.usage = TextureUsage::render_target | TextureUsage::sampled | TextureUsage::upload_destination | TextureUsage::readback_source | TextureUsage::copy_source | TextureUsage::copy_destination,
.debug_name = "recorded-target",
});
RecordingRenderTarget blit_target(TextureDesc {
.extent = Extent2D { .width = 64, .height = 32 },
.format = TextureFormat::rgba8,
.usage = TextureUsage::render_target | TextureUsage::sampled | TextureUsage::upload_destination | TextureUsage::readback_source | TextureUsage::copy_source | TextureUsage::copy_destination,
.debug_name = "recorded-blit-target",
});
RecordingShaderProgram shader("recorded-shader");
RecordingMesh mesh(MeshDesc { .vertex_count = 3, .index_count = 3, .topology = PrimitiveTopology::triangles });
RecordingMesh mesh(MeshDesc {
.vertex_count = 3,
.index_count = 3,
.topology = PrimitiveTopology::triangles,
.debug_name = "recorded-mesh",
});
PP_EXPECT(h, device.backend_name() == std::string_view("recording"));
PP_EXPECT(h, device.trace()->begin_scope("renderer", "recorded-frame").ok());
@@ -1693,6 +1753,7 @@ void recording_renderer_records_valid_command_sequences(pp::tests::Harness& h)
PP_EXPECT(h, recorded_render_command_kind_name(commands[2].kind) == std::string_view("trace_end_scope"));
PP_EXPECT(h, commands[3].kind == RecordedRenderCommandKind::begin_render_pass);
PP_EXPECT(h, commands[3].target_desc.extent.width == 64U);
PP_EXPECT(h, commands[3].target_desc.debug_name == std::string_view("recorded-target"));
PP_EXPECT(h, commands[3].clear_color_enabled);
PP_EXPECT(h, commands[3].clear_color.a == 1.0F);
PP_EXPECT(h, commands[3].clear_depth_enabled);
@@ -1720,6 +1781,7 @@ void recording_renderer_records_valid_command_sequences(pp::tests::Harness& h)
PP_EXPECT(h, commands[9].kind == RecordedRenderCommandKind::bind_texture);
PP_EXPECT(h, commands[9].texture_slot == 1U);
PP_EXPECT(h, commands[9].texture_desc.extent.height == 32U);
PP_EXPECT(h, commands[9].texture_desc.debug_name == std::string_view("recorded-texture"));
PP_EXPECT(h, recorded_render_command_kind_name(commands[9].kind) == std::string_view("bind_texture"));
PP_EXPECT(h, commands[10].kind == RecordedRenderCommandKind::bind_sampler);
PP_EXPECT(h, commands[10].sampler_slot == 1U);
@@ -1729,6 +1791,7 @@ void recording_renderer_records_valid_command_sequences(pp::tests::Harness& h)
PP_EXPECT(h, commands[11].kind == RecordedRenderCommandKind::bind_mesh);
PP_EXPECT(h, commands[11].mesh_desc.vertex_count == 3U);
PP_EXPECT(h, commands[11].mesh_desc.index_count == 3U);
PP_EXPECT(h, commands[11].mesh_desc.debug_name == std::string_view("recorded-mesh"));
PP_EXPECT(h, commands[12].kind == RecordedRenderCommandKind::draw);
PP_EXPECT(h, commands[12].mesh_desc.vertex_count == 3U);
PP_EXPECT(h, commands[12].mesh_desc.index_count == 3U);
@@ -2225,6 +2288,7 @@ int main()
pp::tests::Harness harness;
harness.run("computes_texture_sizes", computes_texture_sizes);
harness.run("validates_texture_usage_contract", validates_texture_usage_contract);
harness.run("validates_resource_labels", validates_resource_labels);
harness.run("validates_mipmap_generation_contract", validates_mipmap_generation_contract);
harness.run("rejects_invalid_or_excessive_extents", rejects_invalid_or_excessive_extents);
harness.run("validates_readback_bounds", validates_readback_bounds);

View File

@@ -1643,6 +1643,7 @@ pp::foundation::Status parse_simulate_document_edits_args(
.extent = pp::renderer::Extent2D { .width = args.width, .height = args.height },
.format = pp::renderer::TextureFormat::rgba8,
.usage = pp::renderer::TextureUsage::render_target | pp::renderer::TextureUsage::sampled | pp::renderer::TextureUsage::upload_destination | pp::renderer::TextureUsage::readback_source | pp::renderer::TextureUsage::copy_source | pp::renderer::TextureUsage::copy_destination,
.debug_name = "record-render-texture",
});
if (!render_target_size) {
return render_target_size.status();
@@ -2215,11 +2216,13 @@ int record_render(int argc, char** argv)
.extent = pp::renderer::Extent2D { .width = args.width, .height = args.height },
.format = pp::renderer::TextureFormat::rgba8,
.usage = pp::renderer::TextureUsage::render_target | pp::renderer::TextureUsage::sampled | pp::renderer::TextureUsage::upload_destination | pp::renderer::TextureUsage::readback_source | pp::renderer::TextureUsage::copy_source | pp::renderer::TextureUsage::copy_destination,
.debug_name = "record-render-target",
});
const auto target = device.create_render_target(pp::renderer::TextureDesc {
.extent = pp::renderer::Extent2D { .width = args.width, .height = args.height },
.format = pp::renderer::TextureFormat::rgba8,
.usage = pp::renderer::TextureUsage::render_target | pp::renderer::TextureUsage::sampled | pp::renderer::TextureUsage::upload_destination | pp::renderer::TextureUsage::readback_source | pp::renderer::TextureUsage::copy_source | pp::renderer::TextureUsage::copy_destination,
.debug_name = "record-render-blit-target",
});
const auto blit_target = device.create_render_target(pp::renderer::TextureDesc {
.extent = pp::renderer::Extent2D { .width = args.width, .height = args.height },
@@ -2251,6 +2254,7 @@ int record_render(int argc, char** argv)
.vertex_count = 3,
.index_count = 3,
.topology = pp::renderer::PrimitiveTopology::triangles,
.debug_name = "record-render-mesh",
});
if (!texture.ok()) {
@@ -2495,10 +2499,17 @@ int record_render(int argc, char** argv)
std::uint64_t capture_bytes = 0;
std::uint64_t blit_source_bytes = 0;
std::uint64_t blit_destination_bytes = 0;
std::size_t labeled_command_descriptors = 0;
const auto count_label = [&labeled_command_descriptors](const char* label) {
if (label != nullptr && label[0] != '\0') {
++labeled_command_descriptors;
}
};
const auto commands = device.commands();
for (const auto& command : commands) {
if (command.kind == pp::renderer::RecordedRenderCommandKind::begin_render_pass) {
++render_passes;
count_label(command.target_desc.debug_name);
if (command.clear_depth_enabled) {
++depth_clears;
}
@@ -2507,6 +2518,7 @@ int record_render(int argc, char** argv)
}
} else if (command.kind == pp::renderer::RecordedRenderCommandKind::draw) {
++draw_commands;
count_label(command.mesh_desc.debug_name);
draw_vertices += command.draw_desc.vertex_count;
draw_indices += command.draw_desc.index_count;
} else if (command.kind == pp::renderer::RecordedRenderCommandKind::set_scissor) {
@@ -2520,27 +2532,39 @@ int record_render(int argc, char** argv)
uniform_bytes += command.uniform_bytes;
} else if (command.kind == pp::renderer::RecordedRenderCommandKind::bind_texture) {
++bind_texture_commands;
count_label(command.texture_desc.debug_name);
const auto bound_bytes = pp::renderer::texture_byte_size(command.texture_desc);
if (bound_bytes.ok()) {
bound_texture_bytes += bound_bytes.value();
}
} else if (command.kind == pp::renderer::RecordedRenderCommandKind::bind_sampler) {
++bind_sampler_commands;
} else if (command.kind == pp::renderer::RecordedRenderCommandKind::bind_mesh) {
count_label(command.mesh_desc.debug_name);
} else if (command.kind == pp::renderer::RecordedRenderCommandKind::generate_mipmaps) {
count_label(command.texture_desc.debug_name);
} else if (command.kind == pp::renderer::RecordedRenderCommandKind::upload_texture) {
++upload_commands;
count_label(command.texture_desc.debug_name);
upload_bytes += command.upload_bytes;
} else if (command.kind == pp::renderer::RecordedRenderCommandKind::copy_texture) {
++copy_commands;
count_label(command.source_desc.debug_name);
count_label(command.destination_desc.debug_name);
copy_source_bytes += command.copy_source_bytes;
copy_destination_bytes += command.copy_destination_bytes;
} else if (command.kind == pp::renderer::RecordedRenderCommandKind::read_texture) {
++readback_commands;
count_label(command.texture_desc.debug_name);
readback_bytes += command.readback_bytes;
} else if (command.kind == pp::renderer::RecordedRenderCommandKind::capture_frame) {
++capture_commands;
count_label(command.target_desc.debug_name);
capture_bytes += command.capture_bytes;
} else if (command.kind == pp::renderer::RecordedRenderCommandKind::blit_render_target) {
++blit_commands;
count_label(command.source_desc.debug_name);
count_label(command.destination_desc.debug_name);
blit_source_bytes += command.blit_source_bytes;
blit_destination_bytes += command.blit_destination_bytes;
} else if (command.kind == pp::renderer::RecordedRenderCommandKind::trace_marker) {
@@ -2558,6 +2582,7 @@ int record_render(int argc, char** argv)
<< ",\"height\":" << args.height
<< ",\"format\":\"rgba8\"}"
<< ",\"createdResources\":" << created_resources
<< ",\"labeledCommandDescriptors\":" << labeled_command_descriptors
<< ",\"commands\":" << commands.size()
<< ",\"renderPasses\":" << render_passes
<< ",\"depthClears\":" << depth_clears