Validate OpenGL command pass barriers

This commit is contained in:
2026-06-02 21:16:58 +02:00
parent 55b725e876
commit 6a3cd867f0
4 changed files with 115 additions and 10 deletions

View File

@@ -279,12 +279,13 @@ Known local toolchain state:
uploads, mipmap generation, texture transitions, texture copies, texture uploads, mipmap generation, texture transitions, texture copies, texture
readbacks, frame captures, passthrough commands, trace commands, unsupported readbacks, frame captures, passthrough commands, trace commands, unsupported
commands, and render-pass ordering errors such as state changes outside a commands, and render-pass ordering errors such as state changes outside a
pass, nested passes, and unclosed passes. It also validates executable pass, nested passes, texture I/O or blits inside a pass, and unclosed passes.
command dependencies, including shader-before-uniform and shader-plus-mesh It also validates executable command dependencies, including
before draw within each render pass, and rejects invalid texture/sampler bind shader-before-uniform and shader-plus-mesh before draw within each render
slots in malformed recorded streams. `pano_cli record-render` emits the pass, and rejects invalid texture/sampler bind slots in malformed recorded
OpenGL plan texture/sampler bind counts so automation can assert backend streams. `pano_cli record-render` emits the OpenGL plan texture/sampler bind
interpretation without an OpenGL context. counts so automation can assert backend interpretation without an OpenGL
context.
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.

View File

@@ -538,10 +538,10 @@ passes, draws, shader binds, shader uniforms, texture/sampler binds, texture
uploads, mipmap generation, texture transitions, texture copies, texture uploads, mipmap generation, texture transitions, texture copies, texture
readbacks, frame captures, passthrough commands, trace commands, unsupported readbacks, frame captures, passthrough commands, trace commands, unsupported
commands, and render-pass ordering errors such as state changes outside a pass, commands, and render-pass ordering errors such as state changes outside a pass,
nested passes, and unclosed passes. It also validates executable command nested passes, texture I/O or blits inside a pass, and unclosed passes. It
dependencies, including shader-before-uniform and shader-plus-mesh before draw also validates executable command dependencies, including shader-before-uniform
within each render pass, and rejects invalid texture/sampler bind slots in and shader-plus-mesh before draw within each render pass, and rejects invalid
malformed recorded streams. texture/sampler bind slots in malformed recorded streams.
The existing renderer classes are not yet fully The existing renderer classes are not yet fully
behind the renderer interfaces. behind the renderer interfaces.

View File

@@ -370,21 +370,39 @@ OpenGlCommandPlan plan_recorded_render_commands(
break; break;
case OpenGlPlannedCommandKind::upload_texture: case OpenGlPlannedCommandKind::upload_texture:
++plan.upload_command_count; ++plan.upload_command_count;
if (in_render_pass) {
record_render_pass_order_error(plan, index);
}
break; break;
case OpenGlPlannedCommandKind::generate_mipmaps: case OpenGlPlannedCommandKind::generate_mipmaps:
++plan.mipmap_command_count; ++plan.mipmap_command_count;
if (in_render_pass) {
record_render_pass_order_error(plan, index);
}
break; break;
case OpenGlPlannedCommandKind::transition_texture: case OpenGlPlannedCommandKind::transition_texture:
++plan.transition_command_count; ++plan.transition_command_count;
if (in_render_pass) {
record_render_pass_order_error(plan, index);
}
break; break;
case OpenGlPlannedCommandKind::copy_texture: case OpenGlPlannedCommandKind::copy_texture:
++plan.copy_command_count; ++plan.copy_command_count;
if (in_render_pass) {
record_render_pass_order_error(plan, index);
}
break; break;
case OpenGlPlannedCommandKind::read_texture: case OpenGlPlannedCommandKind::read_texture:
++plan.readback_command_count; ++plan.readback_command_count;
if (in_render_pass) {
record_render_pass_order_error(plan, index);
}
break; break;
case OpenGlPlannedCommandKind::capture_frame: case OpenGlPlannedCommandKind::capture_frame:
++plan.capture_command_count; ++plan.capture_command_count;
if (in_render_pass) {
record_render_pass_order_error(plan, index);
}
break; break;
case OpenGlPlannedCommandKind::passthrough: case OpenGlPlannedCommandKind::passthrough:
++plan.passthrough_command_count; ++plan.passthrough_command_count;
@@ -395,6 +413,11 @@ OpenGlCommandPlan plan_recorded_render_commands(
case OpenGlPlannedCommandKind::trace: case OpenGlPlannedCommandKind::trace:
++plan.trace_command_count; ++plan.trace_command_count;
break; break;
case OpenGlPlannedCommandKind::blit_render_target:
if (in_render_pass) {
record_render_pass_order_error(plan, index);
}
break;
default: default:
if (planned.requires_render_pass && !in_render_pass) { if (planned.requires_render_pass && !in_render_pass) {
record_render_pass_order_error(plan, index); record_render_pass_order_error(plan, index);

View File

@@ -476,6 +476,86 @@ void counts_texture_and_sampler_bindings_in_streams(pp::tests::Harness& h)
PP_EXPECT(h, plan.commands[3].sampler_slot == 2U); PP_EXPECT(h, plan.commands[3].sampler_slot == 2U);
} }
void expect_inside_pass_order_error(
pp::tests::Harness& h,
pp::renderer::RecordedRenderCommand command,
pp::renderer::gl::OpenGlPlannedCommandKind expected_kind)
{
const std::vector<pp::renderer::RecordedRenderCommand> commands {
begin_render_pass_command(),
command,
command_with_kind(pp::renderer::RecordedRenderCommandKind::end_render_pass),
};
const auto plan = pp::renderer::gl::plan_recorded_render_commands(commands);
PP_EXPECT(h, !plan.supported);
PP_EXPECT(h, plan.unsupported_command_count == 0U);
PP_EXPECT(h, plan.render_pass_order_error_count == 1U);
PP_EXPECT(h, plan.first_render_pass_order_error == 1U);
PP_EXPECT(h, plan.dependency_error_count == 0U);
PP_EXPECT(h, plan.commands[1].kind == expected_kind);
}
void flags_texture_io_inside_render_pass(pp::tests::Harness& h)
{
pp::renderer::RecordedRenderCommand upload_command;
upload_command.kind = pp::renderer::RecordedRenderCommandKind::upload_texture;
upload_command.texture_desc.format = pp::renderer::TextureFormat::rgba8;
pp::renderer::RecordedRenderCommand mipmap_command;
mipmap_command.kind = pp::renderer::RecordedRenderCommandKind::generate_mipmaps;
mipmap_command.texture_desc.format = pp::renderer::TextureFormat::rgba8;
pp::renderer::RecordedRenderCommand transition_command;
transition_command.kind = pp::renderer::RecordedRenderCommandKind::transition_texture;
transition_command.texture_desc.format = pp::renderer::TextureFormat::rgba8;
transition_command.before_state = pp::renderer::TextureState::upload_destination;
transition_command.after_state = pp::renderer::TextureState::shader_read;
pp::renderer::RecordedRenderCommand copy_command;
copy_command.kind = pp::renderer::RecordedRenderCommandKind::copy_texture;
copy_command.source_desc.format = pp::renderer::TextureFormat::rgba8;
copy_command.destination_desc.format = pp::renderer::TextureFormat::rgba8;
pp::renderer::RecordedRenderCommand read_command;
read_command.kind = pp::renderer::RecordedRenderCommandKind::read_texture;
read_command.texture_desc.format = pp::renderer::TextureFormat::rgba8;
pp::renderer::RecordedRenderCommand capture_command;
capture_command.kind = pp::renderer::RecordedRenderCommandKind::capture_frame;
capture_command.target_desc.format = pp::renderer::TextureFormat::rgba8;
expect_inside_pass_order_error(
h,
upload_command,
pp::renderer::gl::OpenGlPlannedCommandKind::upload_texture);
expect_inside_pass_order_error(
h,
mipmap_command,
pp::renderer::gl::OpenGlPlannedCommandKind::generate_mipmaps);
expect_inside_pass_order_error(
h,
transition_command,
pp::renderer::gl::OpenGlPlannedCommandKind::transition_texture);
expect_inside_pass_order_error(
h,
copy_command,
pp::renderer::gl::OpenGlPlannedCommandKind::copy_texture);
expect_inside_pass_order_error(
h,
read_command,
pp::renderer::gl::OpenGlPlannedCommandKind::read_texture);
expect_inside_pass_order_error(
h,
capture_command,
pp::renderer::gl::OpenGlPlannedCommandKind::capture_frame);
expect_inside_pass_order_error(
h,
blit_command(pp::renderer::BlitFilter::nearest),
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)
{ {
const std::vector<pp::renderer::RecordedRenderCommand> outside_pass { const std::vector<pp::renderer::RecordedRenderCommand> outside_pass {
@@ -650,6 +730,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("counts_texture_and_sampler_bindings_in_streams", counts_texture_and_sampler_bindings_in_streams); harness.run("counts_texture_and_sampler_bindings_in_streams", counts_texture_and_sampler_bindings_in_streams);
harness.run("flags_texture_io_inside_render_pass", flags_texture_io_inside_render_pass);
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("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);