diff --git a/docs/modernization/build-inventory.md b/docs/modernization/build-inventory.md index b1e2b0d..9887ea8 100644 --- a/docs/modernization/build-inventory.md +++ b/docs/modernization/build-inventory.md @@ -275,13 +275,16 @@ Known local toolchain state: blit filters into GL-facing planned command data while rejecting unsupported enum tokens before a real GL context is needed. It also plans whole recorded command streams, preserving per-command planned data while counting render - 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. It also validates executable command - dependencies, including shader-before-uniform and shader-plus-mesh before - draw within each render pass. + passes, draws, shader binds, shader uniforms, texture/sampler binds, 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. It also validates executable + command dependencies, including shader-before-uniform and shader-plus-mesh + before draw within each render pass, and rejects invalid texture/sampler bind + slots in malformed recorded streams. `pano_cli record-render` emits the + OpenGL plan texture/sampler bind counts so automation can assert backend + interpretation without an OpenGL context. 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. diff --git a/docs/modernization/roadmap.md b/docs/modernization/roadmap.md index ce41ac0..0ee7609 100644 --- a/docs/modernization/roadmap.md +++ b/docs/modernization/roadmap.md @@ -534,13 +534,14 @@ blend/depth/sampler state, texture formats, primitive modes, draw counts, and blit filters into GL-facing planned command data with explicit unsupported-token rejection before a runtime GL context is needed. It also plans whole recorded command streams, preserving per-command planned data while counting render -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. It also validates executable command dependencies, -including shader-before-uniform and shader-plus-mesh before draw within each -render pass. +passes, draws, shader binds, shader uniforms, texture/sampler binds, 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. It also validates executable command +dependencies, including shader-before-uniform and shader-plus-mesh before draw +within each render pass, and rejects invalid texture/sampler bind slots in +malformed recorded streams. The existing renderer classes are not yet fully behind the renderer interfaces. diff --git a/src/renderer_gl/command_plan.cpp b/src/renderer_gl/command_plan.cpp index da25bcd..4e97fa2 100644 --- a/src/renderer_gl/command_plan.cpp +++ b/src/renderer_gl/command_plan.cpp @@ -191,12 +191,16 @@ OpenGlPlannedCommand plan_recorded_render_command(pp::renderer::RecordedRenderCo case pp::renderer::RecordedRenderCommandKind::bind_texture: planned.kind = OpenGlPlannedCommandKind::bind_texture; planned.texture_format = texture_format_for_renderer_format(command.texture_desc.format); - planned.supported = texture_format_supported(planned.texture_format); + planned.texture_slot = command.texture_slot; + planned.supported = texture_format_supported(planned.texture_format) + && planned.texture_slot < pp::renderer::max_texture_slots; break; case pp::renderer::RecordedRenderCommandKind::bind_sampler: planned.kind = OpenGlPlannedCommandKind::bind_sampler; planned.sampler = sampler_state_for_renderer_sampler_desc(command.sampler_desc); - planned.supported = planned.sampler.supported; + planned.sampler_slot = command.sampler_slot; + planned.supported = planned.sampler.supported + && planned.sampler_slot < pp::renderer::max_texture_slots; break; case pp::renderer::RecordedRenderCommandKind::bind_mesh: planned.kind = OpenGlPlannedCommandKind::bind_mesh; @@ -346,6 +350,18 @@ OpenGlCommandPlan plan_recorded_render_commands( record_dependency_error(plan, index); } break; + case OpenGlPlannedCommandKind::bind_texture: + ++plan.texture_bind_command_count; + if (!in_render_pass) { + record_render_pass_order_error(plan, index); + } + break; + case OpenGlPlannedCommandKind::bind_sampler: + ++plan.sampler_bind_command_count; + if (!in_render_pass) { + record_render_pass_order_error(plan, index); + } + break; case OpenGlPlannedCommandKind::bind_mesh: if (!in_render_pass) { record_render_pass_order_error(plan, index); diff --git a/src/renderer_gl/command_plan.h b/src/renderer_gl/command_plan.h index a523f5a..d7d3c19 100644 --- a/src/renderer_gl/command_plan.h +++ b/src/renderer_gl/command_plan.h @@ -53,6 +53,8 @@ struct OpenGlPlannedCommand { pp::renderer::ReadbackRegion readback_region; pp::renderer::ReadbackRegion source_region; pp::renderer::ReadbackRegion destination_region; + std::uint32_t texture_slot = 0; + std::uint32_t sampler_slot = 0; std::uint64_t upload_bytes = 0; std::uint32_t generated_mip_levels = 0; std::uint64_t generated_mip_bytes = 0; @@ -79,6 +81,8 @@ struct OpenGlCommandPlan { std::uint32_t draw_command_count = 0; std::uint32_t shader_bind_command_count = 0; std::uint32_t uniform_command_count = 0; + std::uint32_t texture_bind_command_count = 0; + std::uint32_t sampler_bind_command_count = 0; std::uint32_t upload_command_count = 0; std::uint32_t mipmap_command_count = 0; std::uint32_t transition_command_count = 0; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 57d3c3c..3b9fb6c 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -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.*\"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") + 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.*\"textureBindCommands\":1.*\"samplerBindCommands\":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) diff --git a/tests/renderer_gl/command_plan_tests.cpp b/tests/renderer_gl/command_plan_tests.cpp index 3010161..997f6e0 100644 --- a/tests/renderer_gl/command_plan_tests.cpp +++ b/tests/renderer_gl/command_plan_tests.cpp @@ -164,6 +164,7 @@ void maps_binding_draw_and_blit_commands(pp::tests::Harness& h) .extent = pp::renderer::Extent2D { .width = 16U, .height = 16U }, .format = pp::renderer::TextureFormat::depth24_stencil8, }, + .texture_slot = 3U, }); const auto sampler = pp::renderer::gl::plan_recorded_render_command(pp::renderer::RecordedRenderCommand { .kind = pp::renderer::RecordedRenderCommandKind::bind_sampler, @@ -175,6 +176,7 @@ void maps_binding_draw_and_blit_commands(pp::tests::Harness& h) .address_v = pp::renderer::SamplerAddressMode::mirrored_repeat, .address_w = pp::renderer::SamplerAddressMode::clamp_to_border, }, + .sampler_slot = 3U, }); const auto mesh = pp::renderer::gl::plan_recorded_render_command(pp::renderer::RecordedRenderCommand { .kind = pp::renderer::RecordedRenderCommandKind::bind_mesh, @@ -191,9 +193,11 @@ void maps_binding_draw_and_blit_commands(pp::tests::Harness& h) PP_EXPECT(h, texture.supported); PP_EXPECT(h, texture.requires_render_pass); PP_EXPECT(h, texture.texture_format.internal_format == 0x88F0U); + PP_EXPECT(h, texture.texture_slot == 3U); PP_EXPECT(h, sampler.supported); PP_EXPECT(h, sampler.sampler.min_filter == 0x2702U); PP_EXPECT(h, sampler.sampler.wrap_t == 0x8370U); + PP_EXPECT(h, sampler.sampler_slot == 3U); PP_EXPECT(h, mesh.supported); PP_EXPECT(h, mesh.primitive_mode == 0x0005U); PP_EXPECT(h, draw.supported); @@ -321,6 +325,18 @@ void rejects_unsupported_command_tokens(pp::tests::Harness& h) .address_u = static_cast(255U), }, }); + const auto bad_texture_slot = pp::renderer::gl::plan_recorded_render_command(pp::renderer::RecordedRenderCommand { + .kind = pp::renderer::RecordedRenderCommandKind::bind_texture, + .texture_desc = pp::renderer::TextureDesc { + .extent = pp::renderer::Extent2D { .width = 1U, .height = 1U }, + .format = pp::renderer::TextureFormat::rgba8, + }, + .texture_slot = pp::renderer::max_texture_slots, + }); + const auto bad_sampler_slot = pp::renderer::gl::plan_recorded_render_command(pp::renderer::RecordedRenderCommand { + .kind = pp::renderer::RecordedRenderCommandKind::bind_sampler, + .sampler_slot = pp::renderer::max_texture_slots, + }); const auto bad_mesh = pp::renderer::gl::plan_recorded_render_command(pp::renderer::RecordedRenderCommand { .kind = pp::renderer::RecordedRenderCommandKind::draw, .mesh_desc = pp::renderer::MeshDesc { @@ -349,6 +365,10 @@ void rejects_unsupported_command_tokens(pp::tests::Harness& h) PP_EXPECT(h, bad_depth.depth.compare_function == 0U); PP_EXPECT(h, !bad_sampler.supported); PP_EXPECT(h, bad_sampler.sampler.wrap_s == 0U); + PP_EXPECT(h, !bad_texture_slot.supported); + PP_EXPECT(h, bad_texture_slot.texture_slot == pp::renderer::max_texture_slots); + PP_EXPECT(h, !bad_sampler_slot.supported); + PP_EXPECT(h, bad_sampler_slot.sampler_slot == pp::renderer::max_texture_slots); PP_EXPECT(h, !bad_mesh.supported); PP_EXPECT(h, bad_mesh.primitive_mode == 0U); PP_EXPECT(h, !bad_blit.supported); @@ -404,6 +424,8 @@ void plans_valid_recorded_command_streams(pp::tests::Harness& h) PP_EXPECT(h, plan.draw_command_count == 1U); PP_EXPECT(h, plan.shader_bind_command_count == 1U); PP_EXPECT(h, plan.uniform_command_count == 1U); + PP_EXPECT(h, plan.texture_bind_command_count == 0U); + PP_EXPECT(h, plan.sampler_bind_command_count == 0U); PP_EXPECT(h, plan.passthrough_command_count == 0U); PP_EXPECT(h, plan.trace_command_count == 1U); PP_EXPECT(h, plan.unsupported_command_count == 0U); @@ -421,6 +443,39 @@ void plans_valid_recorded_command_streams(pp::tests::Harness& h) PP_EXPECT(h, plan.commands[8].kind == pp::renderer::gl::OpenGlPlannedCommandKind::blit_render_target); } +void counts_texture_and_sampler_bindings_in_streams(pp::tests::Harness& h) +{ + pp::renderer::RecordedRenderCommand texture_command; + texture_command.kind = pp::renderer::RecordedRenderCommandKind::bind_texture; + texture_command.texture_desc.format = pp::renderer::TextureFormat::rgba8; + texture_command.texture_slot = 2U; + + pp::renderer::RecordedRenderCommand sampler_command; + sampler_command.kind = pp::renderer::RecordedRenderCommandKind::bind_sampler; + sampler_command.sampler_slot = 2U; + + const std::vector commands { + begin_render_pass_command(), + bind_shader_command("shader"), + texture_command, + sampler_command, + bind_mesh_command(), + draw_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.texture_bind_command_count == 1U); + PP_EXPECT(h, plan.sampler_bind_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.commands[2].texture_slot == 2U); + PP_EXPECT(h, plan.commands[3].sampler_slot == 2U); +} + void flags_broken_render_pass_command_order(pp::tests::Harness& h) { const std::vector outside_pass { @@ -594,6 +649,7 @@ int main() harness.run("rejects_unsupported_command_tokens", rejects_unsupported_command_tokens); harness.run("names_planned_command_kinds", names_planned_command_kinds); 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("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); diff --git a/tools/pano_cli/main.cpp b/tools/pano_cli/main.cpp index 9e3f7c3..2c335a4 100644 --- a/tools/pano_cli/main.cpp +++ b/tools/pano_cli/main.cpp @@ -2787,6 +2787,8 @@ int record_render(int argc, char** argv) << ",\"drawCommands\":" << open_gl_plan.draw_command_count << ",\"shaderBindCommands\":" << open_gl_plan.shader_bind_command_count << ",\"uniformCommands\":" << open_gl_plan.uniform_command_count + << ",\"textureBindCommands\":" << open_gl_plan.texture_bind_command_count + << ",\"samplerBindCommands\":" << open_gl_plan.sampler_bind_command_count << ",\"uploadCommands\":" << open_gl_plan.upload_command_count << ",\"mipmapCommands\":" << open_gl_plan.mipmap_command_count << ",\"transitionCommands\":" << open_gl_plan.transition_command_count