From 860e5ad31ee0ca065a6bca17b879c0858768470a Mon Sep 17 00:00:00 2001 From: omigamedev Date: Tue, 2 Jun 2026 17:53:17 +0200 Subject: [PATCH] Reset recording renderer state on clear --- docs/modernization/build-inventory.md | 4 ++- docs/modernization/roadmap.md | 8 +++-- src/renderer_api/recording_renderer.cpp | 16 +++++++++ src/renderer_api/recording_renderer.h | 2 ++ tests/renderer_api/renderer_api_tests.cpp | 44 +++++++++++++++++++++++ 5 files changed, 70 insertions(+), 4 deletions(-) diff --git a/docs/modernization/build-inventory.md b/docs/modernization/build-inventory.md index c7545df..e00fd1e 100644 --- a/docs/modernization/build-inventory.md +++ b/docs/modernization/build-inventory.md @@ -308,7 +308,9 @@ Known local toolchain state: render-pass-clear/scissor/depth/blend/shader-uniform/texture-bind/ sampler-bind/draw/upload/mipmap-generation/texture-transition/texture-copy/readback/ frame-capture/blit commands, draw mesh inputs, explicit draw ranges, and - records trace markers and scopes without a window or GL context. + records trace markers and scopes without a window or GL context. Recorder + `clear()` also resets active render-pass and trace-scope state so automation + can reuse the same recording device after an interrupted frame. - `pano_cli record-render` exposes the recording renderer through JSON automation, including backend feature flags, render-pass/depth-clear counts, scissor/depth/blend/ shader-uniform/texture-bind/sampler-bind/upload/mipmap-generation/texture-transition/texture-copy/readback/ diff --git a/docs/modernization/roadmap.md b/docs/modernization/roadmap.md index 32e7aa5..ff7ec1b 100644 --- a/docs/modernization/roadmap.md +++ b/docs/modernization/roadmap.md @@ -431,8 +431,8 @@ mipmap-generation command validation, texture-state transition validation, frame frame-capture command validation, render-target blit validation, texture-slot binding validation, blend-state validation, scissor-state validation, depth-state validation, trace marker/scope validation, sampler-state validation, -and the canonical PanoPainter shader catalog now consumed by the legacy OpenGL -app initialization path. +recording-device reuse/reset validation, and the canonical PanoPainter shader +catalog now consumed by the legacy OpenGL app initialization path. `pp_renderer_gl` now exists as the first OpenGL backend library and owns pure OpenGL capability detection for framebuffer fetch, map-buffer alignment, and float texture support. It also owns the OpenGL texture upload-type mapping used @@ -851,7 +851,9 @@ Results: shader uniform writes, texture/sampler binds, draw mesh inputs, explicit draw ranges, texture uploads/mipmap generations/state transitions/copies/readbacks, frame captures, and render-target blits, giving automation a backend-neutral render path that - does not require a window or GL context. + does not require a window or GL context. Clearing the recording device now + resets active render-pass and trace-scope state so interrupted automation can + reuse a recorder without carrying stale frame state forward. - `pano_cli record-render` exercises that headless recording renderer and emits JSON command counts, backend feature flags, resource creation counts, target dimensions, backend name, trace marker/scope and draw summary, labeled descriptor counts, diff --git a/src/renderer_api/recording_renderer.cpp b/src/renderer_api/recording_renderer.cpp index 28161d4..73bc315 100644 --- a/src/renderer_api/recording_renderer.cpp +++ b/src/renderer_api/recording_renderer.cpp @@ -594,6 +594,15 @@ bool RecordingCommandContext::in_render_pass() const noexcept return in_render_pass_; } +void RecordingCommandContext::reset() noexcept +{ + active_target_ = TextureDesc {}; + active_mesh_ = MeshDesc {}; + in_render_pass_ = false; + shader_bound_ = false; + mesh_bound_ = false; +} + RecordingRenderTrace::RecordingRenderTrace(std::vector& commands) noexcept : commands_(&commands) { @@ -643,6 +652,11 @@ pp::foundation::Status RecordingRenderTrace::end_scope() noexcept return pp::foundation::Status::success(); } +void RecordingRenderTrace::reset() noexcept +{ + scope_depth_ = 0U; +} + RecordingRenderDevice::RecordingRenderDevice() noexcept : context_(commands_) , trace_(commands_) @@ -757,6 +771,8 @@ std::span RecordingRenderDevice::commands() const n void RecordingRenderDevice::clear() noexcept { commands_.clear(); + context_.reset(); + trace_.reset(); } const char* recorded_render_command_kind_name(RecordedRenderCommandKind kind) noexcept diff --git a/src/renderer_api/recording_renderer.h b/src/renderer_api/recording_renderer.h index bb43332..476ead1 100644 --- a/src/renderer_api/recording_renderer.h +++ b/src/renderer_api/recording_renderer.h @@ -172,6 +172,7 @@ public: void end_render_pass() noexcept override; [[nodiscard]] bool in_render_pass() const noexcept; + void reset() noexcept; private: std::vector* commands_ = nullptr; @@ -188,6 +189,7 @@ public: [[nodiscard]] pp::foundation::Status marker(const char* component, const char* name) noexcept override; [[nodiscard]] pp::foundation::Status begin_scope(const char* component, const char* name) noexcept override; [[nodiscard]] pp::foundation::Status end_scope() noexcept override; + void reset() noexcept; private: std::vector* commands_ = nullptr; diff --git a/tests/renderer_api/renderer_api_tests.cpp b/tests/renderer_api/renderer_api_tests.cpp index 7914326..8afda9f 100644 --- a/tests/renderer_api/renderer_api_tests.cpp +++ b/tests/renderer_api/renderer_api_tests.cpp @@ -2127,6 +2127,49 @@ void recording_renderer_records_valid_command_sequences(pp::tests::Harness& h) PP_EXPECT(h, device.commands().empty()); } +void recording_renderer_clear_resets_context_and_trace_state(pp::tests::Harness& h) +{ + RecordingRenderDevice device; + RecordingRenderTarget target(TextureDesc { + .extent = Extent2D { .width = 16, .height = 8 }, + .format = TextureFormat::rgba8, + .usage = TextureUsage::render_target | TextureUsage::sampled | TextureUsage::readback_source, + .debug_name = "reset-target", + }); + + auto* trace = device.trace(); + PP_EXPECT(h, trace != nullptr); + if (trace == nullptr) { + return; + } + PP_EXPECT(h, trace->begin_scope("renderer", "interrupted-frame").ok()); + + auto& context = device.immediate_context(); + PP_EXPECT(h, context.begin_render_pass(target, RenderPassDesc {}).ok()); + PP_EXPECT(h, !device.commands().empty()); + + device.clear(); + PP_EXPECT(h, device.commands().empty()); + + const auto trace_end_after_clear = trace->end_scope(); + PP_EXPECT(h, !trace_end_after_clear.ok()); + PP_EXPECT(h, trace_end_after_clear.code == StatusCode::invalid_argument); + PP_EXPECT(h, device.commands().empty()); + + PP_EXPECT(h, trace->begin_scope("renderer", "next-frame").ok()); + PP_EXPECT(h, trace->end_scope().ok()); + + PP_EXPECT(h, context.begin_render_pass(target, RenderPassDesc {}).ok()); + context.end_render_pass(); + + const auto commands = device.commands(); + PP_EXPECT(h, commands.size() == 4U); + PP_EXPECT(h, commands[0].kind == RecordedRenderCommandKind::trace_begin_scope); + PP_EXPECT(h, commands[1].kind == RecordedRenderCommandKind::trace_end_scope); + PP_EXPECT(h, commands[2].kind == RecordedRenderCommandKind::begin_render_pass); + PP_EXPECT(h, commands[3].kind == RecordedRenderCommandKind::end_render_pass); +} + void recording_renderer_rejects_invalid_command_order_and_targets(pp::tests::Harness& h) { RecordingRenderDevice device; @@ -2565,6 +2608,7 @@ int main() harness.run("recording_renderer_records_mipmap_generation", recording_renderer_records_mipmap_generation); harness.run("recording_renderer_records_texture_transitions", recording_renderer_records_texture_transitions); harness.run("recording_renderer_records_valid_command_sequences", recording_renderer_records_valid_command_sequences); + harness.run("recording_renderer_clear_resets_context_and_trace_state", recording_renderer_clear_resets_context_and_trace_state); harness.run("recording_renderer_rejects_invalid_command_order_and_targets", recording_renderer_rejects_invalid_command_order_and_targets); return harness.finish(); }