Validate OpenGL command plan dependencies

This commit is contained in:
2026-06-02 21:06:44 +02:00
parent 1dcd96ab36
commit d664e9fc39
7 changed files with 120 additions and 12 deletions

View File

@@ -279,7 +279,9 @@ Known local toolchain state:
generation, texture transitions, texture copies, texture readbacks, frame generation, texture transitions, texture copies, texture readbacks, frame
captures, passthrough commands, trace commands, unsupported commands, and captures, passthrough commands, trace commands, unsupported commands, and
render-pass ordering errors such as state changes outside a pass, nested render-pass ordering errors such as state changes outside a pass, nested
passes, and unclosed passes. passes, and unclosed passes. It also validates executable command
dependencies, including shader-before-uniform and shader-plus-mesh before
draw within each render pass.
Desktop VR drawing also consumes backend-owned scissor/depth/blend state, Desktop VR drawing also consumes backend-owned scissor/depth/blend state,
depth clear masks, active texture units, and fallback 2D texture unbind depth clear masks, active texture units, and fallback 2D texture unbind
targets while retaining the existing VR SDK/platform bridge shape. targets while retaining the existing VR SDK/platform bridge shape.
@@ -346,7 +348,8 @@ Known local toolchain state:
emits an `openGlPlan` JSON object with the planned command count, support emits an `openGlPlan` JSON object with the planned command count, support
status, render-pass/draw/shader-bind/uniform/texture-upload/mipmap/ status, render-pass/draw/shader-bind/uniform/texture-upload/mipmap/
transition/copy/readback/capture/passthrough/trace counts, unsupported transition/copy/readback/capture/passthrough/trace counts, unsupported
command count, render-pass order error count, and unclosed-pass state. Its command count, render-pass order error count, dependency error count, and
unclosed-pass state. Its
`--exercise-clear` mode verifies `--exercise-clear` mode verifies
interrupted-frame recorder clear/reuse behavior and reports the result in interrupted-frame recorder clear/reuse behavior and reports the result in
JSON, and is covered by `pano_cli_record_render_smoke`, JSON, and is covered by `pano_cli_record_render_smoke`,

View File

@@ -538,7 +538,9 @@ passes, draws, shader binds, shader uniforms, texture uploads, mipmap
generation, texture transitions, texture copies, texture readbacks, frame generation, texture transitions, texture copies, texture readbacks, frame
captures, passthrough commands, trace commands, unsupported commands, and captures, passthrough commands, trace commands, unsupported commands, and
render-pass ordering errors such as state changes outside a pass, nested render-pass ordering errors such as state changes outside a pass, nested
passes, and unclosed passes. passes, and unclosed passes. It also validates executable command dependencies,
including shader-before-uniform and shader-plus-mesh before draw within each
render pass.
The existing renderer classes are not yet fully The existing renderer classes are not yet fully
behind the renderer interfaces. behind the renderer interfaces.
@@ -853,7 +855,8 @@ Results:
upload/mipmap/transition/copy/readback/capture metadata, blit filters and upload/mipmap/transition/copy/readback/capture metadata, blit filters and
byte totals, planned command names, unsupported enum/state rejection, whole byte totals, planned command names, unsupported enum/state rejection, whole
recorded stream planning, valid trace/render/shader/draw/blit ordering, typed recorded stream planning, valid trace/render/shader/draw/blit ordering, typed
texture-command counts, and broken render-pass order detection. texture-command counts, broken render-pass order detection, and executable
draw/uniform dependency failures.
- PowerShell analyze automation returns JSON summaries and includes the shader - PowerShell analyze automation returns JSON summaries and includes the shader
validation target and renderer-boundary guard. validation target and renderer-boundary guard.
- `windows-msvc-vcpkg-headless` configured through the Visual Studio bundled - `windows-msvc-vcpkg-headless` configured through the Visual Studio bundled
@@ -898,8 +901,8 @@ Results:
`pp_renderer_gl` is available, it also emits an `openGlPlan` JSON object with `pp_renderer_gl` is available, it also emits an `openGlPlan` JSON object with
planned command count, support status, render-pass/draw/shader-bind/uniform/ planned command count, support status, render-pass/draw/shader-bind/uniform/
texture-upload/mipmap/transition/copy/readback/capture/passthrough/trace texture-upload/mipmap/transition/copy/readback/capture/passthrough/trace
counts, unsupported command count, render-pass order error count, and counts, unsupported command count, render-pass order error count, dependency
unclosed-pass state. The error count, and unclosed-pass state. The
`--exercise-clear` mode deliberately clears an interrupted trace/render pass, `--exercise-clear` mode deliberately clears an interrupted trace/render pass,
verifies stale trace-scope state is rejected, verifies the render context can verifies stale trace-scope state is rejected, verifies the render context can
be reused, and then emits that reset status in JSON. It also has an be reused, and then emits that reset status in JSON. It also has an

View File

@@ -69,6 +69,14 @@ void record_render_pass_order_error(OpenGlCommandPlan& plan, std::size_t index)
} }
} }
void record_dependency_error(OpenGlCommandPlan& plan, std::size_t index) noexcept
{
++plan.dependency_error_count;
if (plan.first_dependency_error == OpenGlCommandPlan::npos) {
plan.first_dependency_error = index;
}
}
} }
const char* planned_command_kind_name(OpenGlPlannedCommandKind kind) noexcept const char* planned_command_kind_name(OpenGlPlannedCommandKind kind) noexcept
@@ -286,6 +294,8 @@ OpenGlCommandPlan plan_recorded_render_commands(
plan.commands.reserve(commands.size()); plan.commands.reserve(commands.size());
bool in_render_pass = false; bool in_render_pass = false;
bool shader_bound_in_pass = false;
bool mesh_bound_in_pass = false;
for (std::size_t index = 0; index < commands.size(); ++index) { for (std::size_t index = 0; index < commands.size(); ++index) {
OpenGlPlannedCommand planned = plan_recorded_render_command(commands[index]); OpenGlPlannedCommand planned = plan_recorded_render_command(commands[index]);
@@ -300,30 +310,47 @@ OpenGlCommandPlan plan_recorded_render_commands(
record_render_pass_order_error(plan, index); record_render_pass_order_error(plan, index);
} }
in_render_pass = true; in_render_pass = true;
shader_bound_in_pass = false;
mesh_bound_in_pass = false;
break; break;
case OpenGlPlannedCommandKind::end_render_pass: case OpenGlPlannedCommandKind::end_render_pass:
if (!in_render_pass) { if (!in_render_pass) {
record_render_pass_order_error(plan, index); record_render_pass_order_error(plan, index);
} }
in_render_pass = false; in_render_pass = false;
shader_bound_in_pass = false;
mesh_bound_in_pass = false;
break; break;
case OpenGlPlannedCommandKind::draw: case OpenGlPlannedCommandKind::draw:
++plan.draw_command_count; ++plan.draw_command_count;
if (!in_render_pass) { if (!in_render_pass) {
record_render_pass_order_error(plan, index); record_render_pass_order_error(plan, index);
} }
if (!shader_bound_in_pass || !mesh_bound_in_pass) {
record_dependency_error(plan, index);
}
break; break;
case OpenGlPlannedCommandKind::bind_shader: case OpenGlPlannedCommandKind::bind_shader:
++plan.shader_bind_command_count; ++plan.shader_bind_command_count;
if (!in_render_pass) { if (!in_render_pass) {
record_render_pass_order_error(plan, index); record_render_pass_order_error(plan, index);
} }
shader_bound_in_pass = true;
break; break;
case OpenGlPlannedCommandKind::set_shader_uniform: case OpenGlPlannedCommandKind::set_shader_uniform:
++plan.uniform_command_count; ++plan.uniform_command_count;
if (!in_render_pass) { if (!in_render_pass) {
record_render_pass_order_error(plan, index); record_render_pass_order_error(plan, index);
} }
if (!shader_bound_in_pass) {
record_dependency_error(plan, index);
}
break;
case OpenGlPlannedCommandKind::bind_mesh:
if (!in_render_pass) {
record_render_pass_order_error(plan, index);
}
mesh_bound_in_pass = true;
break; break;
case OpenGlPlannedCommandKind::upload_texture: case OpenGlPlannedCommandKind::upload_texture:
++plan.upload_command_count; ++plan.upload_command_count;
@@ -368,7 +395,8 @@ OpenGlCommandPlan plan_recorded_render_commands(
} }
plan.supported = plan.unsupported_command_count == 0U plan.supported = plan.unsupported_command_count == 0U
&& plan.render_pass_order_error_count == 0U; && plan.render_pass_order_error_count == 0U
&& plan.dependency_error_count == 0U;
return plan; return plan;
} }

View File

@@ -89,8 +89,10 @@ struct OpenGlCommandPlan {
std::uint32_t trace_command_count = 0; std::uint32_t trace_command_count = 0;
std::uint32_t unsupported_command_count = 0; std::uint32_t unsupported_command_count = 0;
std::uint32_t render_pass_order_error_count = 0; std::uint32_t render_pass_order_error_count = 0;
std::uint32_t dependency_error_count = 0;
std::size_t first_unsupported_command = npos; std::size_t first_unsupported_command = npos;
std::size_t first_render_pass_order_error = npos; std::size_t first_render_pass_order_error = npos;
std::size_t first_dependency_error = npos;
bool ended_in_render_pass = false; bool ended_in_render_pass = false;
bool supported = true; bool supported = true;
}; };

View File

@@ -384,7 +384,7 @@ if(TARGET pano_cli)
COMMAND pano_cli record-render --width 32 --height 16) COMMAND pano_cli record-render --width 32 --height 16)
set_tests_properties(pano_cli_record_render_smoke PROPERTIES set_tests_properties(pano_cli_record_render_smoke PROPERTIES
LABELS "renderer;integration;desktop-fast" LABELS "renderer;integration;desktop-fast"
PASS_REGULAR_EXPRESSION "\"backend\":\"recording\".*\"framebufferFetch\":false.*\"explicitTextureTransitions\":true.*\"textureCopy\":true.*\"renderTargetBlit\":true.*\"frameCapture\":true.*\"float16RenderTargets\":false.*\"float32RenderTargets\":false.*\"width\":32.*\"height\":16.*\"createdResources\":7.*\"exercisedClearReset\":false.*\"clearRejectedOrphanedTraceEnd\":false.*\"clearReusedRenderPass\":false.*\"labeledCommandDescriptors\":16.*\"commands\":25.*\"openGlPlan\":.*\"available\":true.*\"supported\":true.*\"commands\":25.*\"renderPasses\":1.*\"drawCommands\":1.*\"shaderBindCommands\":1.*\"uniformCommands\":1.*\"uploadCommands\":1.*\"mipmapCommands\":1.*\"transitionCommands\":4.*\"copyCommands\":1.*\"readbackCommands\":1.*\"captureCommands\":1.*\"passthroughCommands\":0.*\"traceCommands\":3.*\"unsupportedCommands\":0.*\"renderPassOrderErrors\":0.*\"endedInRenderPass\":false.*\"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.*\"mipmapCommands\":1.*\"mipmapLevels\":3.*\"mipmapBytes\":84.*\"transitionCommands\":4.*\"transitionToUpload\":1.*\"transitionToShaderRead\":2.*\"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\".*\"framebufferFetch\":false.*\"explicitTextureTransitions\":true.*\"textureCopy\":true.*\"renderTargetBlit\":true.*\"frameCapture\":true.*\"float16RenderTargets\":false.*\"float32RenderTargets\":false.*\"width\":32.*\"height\":16.*\"createdResources\":7.*\"exercisedClearReset\":false.*\"clearRejectedOrphanedTraceEnd\":false.*\"clearReusedRenderPass\":false.*\"labeledCommandDescriptors\":16.*\"commands\":25.*\"openGlPlan\":.*\"available\":true.*\"supported\":true.*\"commands\":25.*\"renderPasses\":1.*\"drawCommands\":1.*\"shaderBindCommands\":1.*\"uniformCommands\":1.*\"uploadCommands\":1.*\"mipmapCommands\":1.*\"transitionCommands\":4.*\"copyCommands\":1.*\"readbackCommands\":1.*\"captureCommands\":1.*\"passthroughCommands\":0.*\"traceCommands\":3.*\"unsupportedCommands\":0.*\"renderPassOrderErrors\":0.*\"dependencyErrors\":0.*\"endedInRenderPass\":false.*\"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.*\"mipmapCommands\":1.*\"mipmapLevels\":3.*\"mipmapBytes\":84.*\"transitionCommands\":4.*\"transitionToUpload\":1.*\"transitionToShaderRead\":2.*\"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_exercises_clear_reset add_test(NAME pano_cli_record_render_exercises_clear_reset
COMMAND pano_cli record-render --width 32 --height 16 --exercise-clear) COMMAND pano_cli record-render --width 32 --height 16 --exercise-clear)

View File

@@ -59,6 +59,16 @@ pp::renderer::RecordedRenderCommand shader_uniform_command(const char* name, std
return command; return command;
} }
pp::renderer::RecordedRenderCommand bind_mesh_command() noexcept
{
pp::renderer::RecordedRenderCommand command;
command.kind = pp::renderer::RecordedRenderCommandKind::bind_mesh;
command.mesh_desc.topology = pp::renderer::PrimitiveTopology::triangles;
command.mesh_desc.vertex_count = 3U;
command.mesh_desc.index_count = 3U;
return command;
}
pp::renderer::RecordedRenderCommand blit_command(pp::renderer::BlitFilter filter) noexcept pp::renderer::RecordedRenderCommand blit_command(pp::renderer::BlitFilter filter) noexcept
{ {
pp::renderer::RecordedRenderCommand command; pp::renderer::RecordedRenderCommand command;
@@ -380,6 +390,7 @@ void plans_valid_recorded_command_streams(pp::tests::Harness& h)
viewport_command(), viewport_command(),
bind_shader_command("stream-shader"), bind_shader_command("stream-shader"),
shader_uniform_command("mvp", 64U), shader_uniform_command("mvp", 64U),
bind_mesh_command(),
draw_command(), draw_command(),
command_with_kind(pp::renderer::RecordedRenderCommandKind::end_render_pass), command_with_kind(pp::renderer::RecordedRenderCommandKind::end_render_pass),
blit_command(pp::renderer::BlitFilter::nearest), blit_command(pp::renderer::BlitFilter::nearest),
@@ -397,14 +408,17 @@ void plans_valid_recorded_command_streams(pp::tests::Harness& h)
PP_EXPECT(h, plan.trace_command_count == 1U); PP_EXPECT(h, plan.trace_command_count == 1U);
PP_EXPECT(h, plan.unsupported_command_count == 0U); PP_EXPECT(h, plan.unsupported_command_count == 0U);
PP_EXPECT(h, plan.render_pass_order_error_count == 0U); PP_EXPECT(h, plan.render_pass_order_error_count == 0U);
PP_EXPECT(h, plan.dependency_error_count == 0U);
PP_EXPECT(h, plan.first_unsupported_command == pp::renderer::gl::OpenGlCommandPlan::npos); PP_EXPECT(h, plan.first_unsupported_command == pp::renderer::gl::OpenGlCommandPlan::npos);
PP_EXPECT(h, plan.first_render_pass_order_error == pp::renderer::gl::OpenGlCommandPlan::npos); PP_EXPECT(h, plan.first_render_pass_order_error == pp::renderer::gl::OpenGlCommandPlan::npos);
PP_EXPECT(h, plan.first_dependency_error == pp::renderer::gl::OpenGlCommandPlan::npos);
PP_EXPECT(h, !plan.ended_in_render_pass); PP_EXPECT(h, !plan.ended_in_render_pass);
PP_EXPECT(h, plan.commands[1].kind == pp::renderer::gl::OpenGlPlannedCommandKind::begin_render_pass); PP_EXPECT(h, plan.commands[1].kind == pp::renderer::gl::OpenGlPlannedCommandKind::begin_render_pass);
PP_EXPECT(h, plan.commands[3].kind == pp::renderer::gl::OpenGlPlannedCommandKind::bind_shader); PP_EXPECT(h, plan.commands[3].kind == pp::renderer::gl::OpenGlPlannedCommandKind::bind_shader);
PP_EXPECT(h, plan.commands[4].kind == pp::renderer::gl::OpenGlPlannedCommandKind::set_shader_uniform); PP_EXPECT(h, plan.commands[4].kind == pp::renderer::gl::OpenGlPlannedCommandKind::set_shader_uniform);
PP_EXPECT(h, plan.commands[5].kind == pp::renderer::gl::OpenGlPlannedCommandKind::draw); PP_EXPECT(h, plan.commands[5].kind == pp::renderer::gl::OpenGlPlannedCommandKind::bind_mesh);
PP_EXPECT(h, plan.commands[7].kind == pp::renderer::gl::OpenGlPlannedCommandKind::blit_render_target); PP_EXPECT(h, plan.commands[6].kind == pp::renderer::gl::OpenGlPlannedCommandKind::draw);
PP_EXPECT(h, plan.commands[8].kind == pp::renderer::gl::OpenGlPlannedCommandKind::blit_render_target);
} }
void flags_broken_render_pass_command_order(pp::tests::Harness& h) void flags_broken_render_pass_command_order(pp::tests::Harness& h)
@@ -439,10 +453,65 @@ void flags_broken_render_pass_command_order(pp::tests::Harness& h)
PP_EXPECT(h, unclosed.ended_in_render_pass); PP_EXPECT(h, unclosed.ended_in_render_pass);
} }
void flags_missing_render_dependencies(pp::tests::Harness& h)
{
const std::vector<pp::renderer::RecordedRenderCommand> uniform_before_shader {
begin_render_pass_command(),
shader_uniform_command("mvp", 64U),
bind_shader_command("shader"),
bind_mesh_command(),
draw_command(),
command_with_kind(pp::renderer::RecordedRenderCommandKind::end_render_pass),
};
const std::vector<pp::renderer::RecordedRenderCommand> draw_without_mesh {
begin_render_pass_command(),
bind_shader_command("shader"),
draw_command(),
command_with_kind(pp::renderer::RecordedRenderCommandKind::end_render_pass),
};
const std::vector<pp::renderer::RecordedRenderCommand> draw_without_shader {
begin_render_pass_command(),
bind_mesh_command(),
draw_command(),
command_with_kind(pp::renderer::RecordedRenderCommandKind::end_render_pass),
};
const std::vector<pp::renderer::RecordedRenderCommand> second_pass_missing_bindings {
begin_render_pass_command(),
bind_shader_command("shader"),
bind_mesh_command(),
draw_command(),
command_with_kind(pp::renderer::RecordedRenderCommandKind::end_render_pass),
begin_render_pass_command(),
draw_command(),
command_with_kind(pp::renderer::RecordedRenderCommandKind::end_render_pass),
};
const auto bad_uniform = pp::renderer::gl::plan_recorded_render_commands(uniform_before_shader);
const auto bad_mesh = pp::renderer::gl::plan_recorded_render_commands(draw_without_mesh);
const auto bad_shader = pp::renderer::gl::plan_recorded_render_commands(draw_without_shader);
const auto bad_second_pass = pp::renderer::gl::plan_recorded_render_commands(second_pass_missing_bindings);
PP_EXPECT(h, !bad_uniform.supported);
PP_EXPECT(h, bad_uniform.dependency_error_count == 1U);
PP_EXPECT(h, bad_uniform.first_dependency_error == 1U);
PP_EXPECT(h, !bad_mesh.supported);
PP_EXPECT(h, bad_mesh.dependency_error_count == 1U);
PP_EXPECT(h, bad_mesh.first_dependency_error == 2U);
PP_EXPECT(h, !bad_shader.supported);
PP_EXPECT(h, bad_shader.dependency_error_count == 1U);
PP_EXPECT(h, bad_shader.first_dependency_error == 2U);
PP_EXPECT(h, !bad_second_pass.supported);
PP_EXPECT(h, bad_second_pass.dependency_error_count == 1U);
PP_EXPECT(h, bad_second_pass.first_dependency_error == 6U);
PP_EXPECT(h, bad_second_pass.render_pass_order_error_count == 0U);
}
void tracks_unsupported_commands_in_streams(pp::tests::Harness& h) void tracks_unsupported_commands_in_streams(pp::tests::Harness& h)
{ {
std::vector<pp::renderer::RecordedRenderCommand> commands { std::vector<pp::renderer::RecordedRenderCommand> commands {
begin_render_pass_command(), begin_render_pass_command(),
bind_shader_command("shader"),
bind_mesh_command(),
draw_command(), draw_command(),
command_with_kind(pp::renderer::RecordedRenderCommandKind::end_render_pass), command_with_kind(pp::renderer::RecordedRenderCommandKind::end_render_pass),
command_with_kind(static_cast<pp::renderer::RecordedRenderCommandKind>(255U)), command_with_kind(static_cast<pp::renderer::RecordedRenderCommandKind>(255U)),
@@ -452,9 +521,10 @@ void tracks_unsupported_commands_in_streams(pp::tests::Harness& h)
PP_EXPECT(h, !plan.supported); PP_EXPECT(h, !plan.supported);
PP_EXPECT(h, plan.unsupported_command_count == 1U); PP_EXPECT(h, plan.unsupported_command_count == 1U);
PP_EXPECT(h, plan.first_unsupported_command == 3U); PP_EXPECT(h, plan.first_unsupported_command == 5U);
PP_EXPECT(h, plan.render_pass_order_error_count == 0U); PP_EXPECT(h, plan.render_pass_order_error_count == 0U);
PP_EXPECT(h, plan.commands[3].kind == pp::renderer::gl::OpenGlPlannedCommandKind::unknown); PP_EXPECT(h, plan.dependency_error_count == 0U);
PP_EXPECT(h, plan.commands[5].kind == pp::renderer::gl::OpenGlPlannedCommandKind::unknown);
} }
void counts_typed_texture_commands_in_streams(pp::tests::Harness& h) void counts_typed_texture_commands_in_streams(pp::tests::Harness& h)
@@ -525,6 +595,7 @@ int main()
harness.run("names_planned_command_kinds", names_planned_command_kinds); harness.run("names_planned_command_kinds", names_planned_command_kinds);
harness.run("plans_valid_recorded_command_streams", plans_valid_recorded_command_streams); harness.run("plans_valid_recorded_command_streams", plans_valid_recorded_command_streams);
harness.run("flags_broken_render_pass_command_order", flags_broken_render_pass_command_order); harness.run("flags_broken_render_pass_command_order", flags_broken_render_pass_command_order);
harness.run("flags_missing_render_dependencies", flags_missing_render_dependencies);
harness.run("tracks_unsupported_commands_in_streams", tracks_unsupported_commands_in_streams); harness.run("tracks_unsupported_commands_in_streams", tracks_unsupported_commands_in_streams);
harness.run("counts_typed_texture_commands_in_streams", counts_typed_texture_commands_in_streams); harness.run("counts_typed_texture_commands_in_streams", counts_typed_texture_commands_in_streams);
return harness.finish(); return harness.finish();

View File

@@ -2797,6 +2797,7 @@ int record_render(int argc, char** argv)
<< ",\"traceCommands\":" << open_gl_plan.trace_command_count << ",\"traceCommands\":" << open_gl_plan.trace_command_count
<< ",\"unsupportedCommands\":" << open_gl_plan.unsupported_command_count << ",\"unsupportedCommands\":" << open_gl_plan.unsupported_command_count
<< ",\"renderPassOrderErrors\":" << open_gl_plan.render_pass_order_error_count << ",\"renderPassOrderErrors\":" << open_gl_plan.render_pass_order_error_count
<< ",\"dependencyErrors\":" << open_gl_plan.dependency_error_count
<< ",\"endedInRenderPass\":" << json_bool(open_gl_plan.ended_in_render_pass) << ",\"endedInRenderPass\":" << json_bool(open_gl_plan.ended_in_render_pass)
<< "}" << "}"
#endif #endif