Validate OpenGL command plan dependencies
This commit is contained in:
@@ -279,7 +279,9 @@ Known local toolchain state:
|
||||
generation, texture transitions, texture copies, texture readbacks, frame
|
||||
captures, passthrough commands, trace commands, unsupported commands, and
|
||||
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,
|
||||
depth clear masks, active texture units, and fallback 2D texture unbind
|
||||
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
|
||||
status, render-pass/draw/shader-bind/uniform/texture-upload/mipmap/
|
||||
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
|
||||
interrupted-frame recorder clear/reuse behavior and reports the result in
|
||||
JSON, and is covered by `pano_cli_record_render_smoke`,
|
||||
|
||||
@@ -538,7 +538,9 @@ passes, draws, shader binds, shader uniforms, texture uploads, mipmap
|
||||
generation, texture transitions, texture copies, texture readbacks, frame
|
||||
captures, passthrough commands, trace commands, unsupported commands, and
|
||||
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
|
||||
behind the renderer interfaces.
|
||||
|
||||
@@ -853,7 +855,8 @@ Results:
|
||||
upload/mipmap/transition/copy/readback/capture metadata, blit filters and
|
||||
byte totals, planned command names, unsupported enum/state rejection, whole
|
||||
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
|
||||
validation target and renderer-boundary guard.
|
||||
- `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
|
||||
planned command count, support status, render-pass/draw/shader-bind/uniform/
|
||||
texture-upload/mipmap/transition/copy/readback/capture/passthrough/trace
|
||||
counts, unsupported command count, render-pass order error count, and
|
||||
unclosed-pass state. The
|
||||
counts, unsupported command count, render-pass order error count, dependency
|
||||
error count, and unclosed-pass state. The
|
||||
`--exercise-clear` mode deliberately clears an interrupted trace/render pass,
|
||||
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
|
||||
|
||||
@@ -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
|
||||
@@ -286,6 +294,8 @@ OpenGlCommandPlan plan_recorded_render_commands(
|
||||
plan.commands.reserve(commands.size());
|
||||
|
||||
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) {
|
||||
OpenGlPlannedCommand planned = plan_recorded_render_command(commands[index]);
|
||||
|
||||
@@ -300,30 +310,47 @@ OpenGlCommandPlan plan_recorded_render_commands(
|
||||
record_render_pass_order_error(plan, index);
|
||||
}
|
||||
in_render_pass = true;
|
||||
shader_bound_in_pass = false;
|
||||
mesh_bound_in_pass = false;
|
||||
break;
|
||||
case OpenGlPlannedCommandKind::end_render_pass:
|
||||
if (!in_render_pass) {
|
||||
record_render_pass_order_error(plan, index);
|
||||
}
|
||||
in_render_pass = false;
|
||||
shader_bound_in_pass = false;
|
||||
mesh_bound_in_pass = false;
|
||||
break;
|
||||
case OpenGlPlannedCommandKind::draw:
|
||||
++plan.draw_command_count;
|
||||
if (!in_render_pass) {
|
||||
record_render_pass_order_error(plan, index);
|
||||
}
|
||||
if (!shader_bound_in_pass || !mesh_bound_in_pass) {
|
||||
record_dependency_error(plan, index);
|
||||
}
|
||||
break;
|
||||
case OpenGlPlannedCommandKind::bind_shader:
|
||||
++plan.shader_bind_command_count;
|
||||
if (!in_render_pass) {
|
||||
record_render_pass_order_error(plan, index);
|
||||
}
|
||||
shader_bound_in_pass = true;
|
||||
break;
|
||||
case OpenGlPlannedCommandKind::set_shader_uniform:
|
||||
++plan.uniform_command_count;
|
||||
if (!in_render_pass) {
|
||||
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;
|
||||
case OpenGlPlannedCommandKind::upload_texture:
|
||||
++plan.upload_command_count;
|
||||
@@ -368,7 +395,8 @@ OpenGlCommandPlan plan_recorded_render_commands(
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -89,8 +89,10 @@ struct OpenGlCommandPlan {
|
||||
std::uint32_t trace_command_count = 0;
|
||||
std::uint32_t unsupported_command_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_render_pass_order_error = npos;
|
||||
std::size_t first_dependency_error = npos;
|
||||
bool ended_in_render_pass = false;
|
||||
bool supported = true;
|
||||
};
|
||||
|
||||
@@ -384,7 +384,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\".*\"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
|
||||
COMMAND pano_cli record-render --width 32 --height 16 --exercise-clear)
|
||||
|
||||
@@ -59,6 +59,16 @@ pp::renderer::RecordedRenderCommand shader_uniform_command(const char* name, std
|
||||
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 command;
|
||||
@@ -380,6 +390,7 @@ void plans_valid_recorded_command_streams(pp::tests::Harness& h)
|
||||
viewport_command(),
|
||||
bind_shader_command("stream-shader"),
|
||||
shader_uniform_command("mvp", 64U),
|
||||
bind_mesh_command(),
|
||||
draw_command(),
|
||||
command_with_kind(pp::renderer::RecordedRenderCommandKind::end_render_pass),
|
||||
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.unsupported_command_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_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.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[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[7].kind == pp::renderer::gl::OpenGlPlannedCommandKind::blit_render_target);
|
||||
PP_EXPECT(h, plan.commands[5].kind == pp::renderer::gl::OpenGlPlannedCommandKind::bind_mesh);
|
||||
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)
|
||||
@@ -439,10 +453,65 @@ void flags_broken_render_pass_command_order(pp::tests::Harness& h)
|
||||
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)
|
||||
{
|
||||
std::vector<pp::renderer::RecordedRenderCommand> commands {
|
||||
begin_render_pass_command(),
|
||||
bind_shader_command("shader"),
|
||||
bind_mesh_command(),
|
||||
draw_command(),
|
||||
command_with_kind(pp::renderer::RecordedRenderCommandKind::end_render_pass),
|
||||
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.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.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)
|
||||
@@ -525,6 +595,7 @@ int main()
|
||||
harness.run("names_planned_command_kinds", names_planned_command_kinds);
|
||||
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_missing_render_dependencies", flags_missing_render_dependencies);
|
||||
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);
|
||||
return harness.finish();
|
||||
|
||||
@@ -2797,6 +2797,7 @@ int record_render(int argc, char** argv)
|
||||
<< ",\"traceCommands\":" << open_gl_plan.trace_command_count
|
||||
<< ",\"unsupportedCommands\":" << open_gl_plan.unsupported_command_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)
|
||||
<< "}"
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user