diff --git a/docs/modernization/build-inventory.md b/docs/modernization/build-inventory.md index e00fd1e..14ef7ba 100644 --- a/docs/modernization/build-inventory.md +++ b/docs/modernization/build-inventory.md @@ -316,8 +316,10 @@ Known local toolchain state: shader-uniform/texture-bind/sampler-bind/upload/mipmap-generation/texture-transition/texture-copy/readback/ frame-capture/blit command and byte totals, trace marker/scope counts, labeled descriptor counts, backend resource creation counts, plus draw - descriptor vertex/index totals, and is covered by - `pano_cli_record_render_smoke` plus + descriptor vertex/index totals. 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`, + `pano_cli_record_render_exercises_clear_reset`, plus `pano_cli_record_render_rejects_oversized_target`. - `pano_cli simulate-document-history` exposes `pp_document::DocumentHistory` apply/undo/redo state through JSON automation and is covered by diff --git a/docs/modernization/roadmap.md b/docs/modernization/roadmap.md index ff7ec1b..8d874c6 100644 --- a/docs/modernization/roadmap.md +++ b/docs/modernization/roadmap.md @@ -860,7 +860,10 @@ Results: render-pass/depth-clear counts, and draw descriptor vertex/index totals, scissor/depth/blend-state plus shader-uniform/texture/sampler-bind/upload/mipmap-generation/texture-transition/texture-copy/readback/ - frame-capture/blit command/byte totals for agent automation, with an + frame-capture/blit command/byte totals for agent automation. 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 expected-failure smoke for oversized render/readback targets. - `pano_cli simulate-document-history` exercises pure document history apply/undo/redo behavior and emits JSON layer/frame/history state for agent diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index f4bd9d4..fce49f0 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -374,7 +374,13 @@ 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.*\"labeledCommandDescriptors\":16.*\"commands\":25.*\"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.*\"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) + set_tests_properties(pano_cli_record_render_exercises_clear_reset PROPERTIES + LABELS "renderer;integration;desktop-fast" + PASS_REGULAR_EXPRESSION "\"command\":\"record-render\".*\"exercisedClearReset\":true.*\"clearRejectedOrphanedTraceEnd\":true.*\"clearReusedRenderPass\":true.*\"commands\":25.*\"renderPasses\":1.*\"traceBeginScopes\":1.*\"traceEndScopes\":1") add_test(NAME pano_cli_record_render_rejects_oversized_target COMMAND "${CMAKE_COMMAND}" diff --git a/tools/pano_cli/main.cpp b/tools/pano_cli/main.cpp index e9878a1..c3f0f6f 100644 --- a/tools/pano_cli/main.cpp +++ b/tools/pano_cli/main.cpp @@ -130,6 +130,7 @@ struct SimulateDocumentHistoryArgs { struct RecordRenderArgs { std::uint32_t width = 64; std::uint32_t height = 32; + bool exercise_clear = false; }; void print_error(std::string_view command, std::string_view message) @@ -240,7 +241,7 @@ void print_help() << " inspect-project --path FILE\n" << " load-project --path FILE\n" << " parse-layout --path FILE\n" - << " record-render [--width N] [--height N]\n" + << " record-render [--width N] [--height N] [--exercise-clear]\n" << " save-document-project --path FILE [--width N] [--height N]\n" << " save-project --path FILE --width N --height N [--layer-name NAME] [--layer-opacity N] [--blend-mode N] [--alpha-locked] [--hidden] [--layers N] [--frames N] [--frame-duration-ms N] [--include-test-face-payload] [--payload-layer N] [--payload-frame N]\n" << " simulate-document-edits [--width N] [--height N]\n" @@ -2244,7 +2245,9 @@ pp::foundation::Status parse_record_render_args(int argc, char** argv, RecordRen { for (int i = 2; i < argc; ++i) { const std::string_view key(argv[i]); - if (key == "--width" || key == "--height") { + if (key == "--exercise-clear") { + args.exercise_clear = true; + } else if (key == "--width" || key == "--height") { if (i + 1 >= argc) { return pp::foundation::Status::invalid_argument("missing value for option"); } @@ -2365,6 +2368,40 @@ int record_render(int argc, char** argv) constexpr std::size_t created_resources = 7; auto* trace = device.trace(); + auto& context = device.immediate_context(); + bool clear_rejected_orphaned_trace_end = false; + bool clear_reused_render_pass = false; + if (args.exercise_clear) { + const auto interrupted_trace_status = trace->begin_scope("renderer", "interrupted-frame"); + if (!interrupted_trace_status.ok()) { + print_error("record-render", interrupted_trace_status.message); + return 2; + } + + const auto interrupted_begin_status = context.begin_render_pass(*target.value(), pp::renderer::RenderPassDesc {}); + if (!interrupted_begin_status.ok()) { + print_error("record-render", interrupted_begin_status.message); + return 2; + } + + device.clear(); + const auto stale_trace_end_status = trace->end_scope(); + if (stale_trace_end_status.ok()) { + print_error("record-render", "recording clear did not reset trace scope state"); + return 2; + } + clear_rejected_orphaned_trace_end = true; + + const auto reuse_begin_status = context.begin_render_pass(*target.value(), pp::renderer::RenderPassDesc {}); + if (!reuse_begin_status.ok()) { + print_error("record-render", reuse_begin_status.message); + return 2; + } + context.end_render_pass(); + clear_reused_render_pass = true; + device.clear(); + } + const auto trace_begin_status = trace->begin_scope("renderer", "pano_cli_record_render"); if (!trace_begin_status.ok()) { print_error("record-render", trace_begin_status.message); @@ -2381,7 +2418,6 @@ int record_render(int argc, char** argv) return 2; } - auto& context = device.immediate_context(); const auto transition_upload_status = context.transition_texture( *texture.value(), pp::renderer::TextureState::undefined, @@ -2732,6 +2768,9 @@ int record_render(int argc, char** argv) << ",\"height\":" << args.height << ",\"format\":\"rgba8\"}" << ",\"createdResources\":" << created_resources + << ",\"exercisedClearReset\":" << json_bool(args.exercise_clear) + << ",\"clearRejectedOrphanedTraceEnd\":" << json_bool(clear_rejected_orphaned_trace_end) + << ",\"clearReusedRenderPass\":" << json_bool(clear_reused_render_pass) << ",\"labeledCommandDescriptors\":" << labeled_command_descriptors << ",\"commands\":" << commands.size() << ",\"renderPasses\":" << render_passes