Reset recording renderer state on clear

This commit is contained in:
2026-06-02 17:53:17 +02:00
parent 9b00acec6f
commit 860e5ad31e
5 changed files with 70 additions and 4 deletions

View File

@@ -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/

View File

@@ -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,

View File

@@ -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<RecordedRenderCommand>& 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<const RecordedRenderCommand> RecordingRenderDevice::commands() const n
void RecordingRenderDevice::clear() noexcept
{
commands_.clear();
context_.reset();
trace_.reset();
}
const char* recorded_render_command_kind_name(RecordedRenderCommandKind kind) noexcept

View File

@@ -172,6 +172,7 @@ public:
void end_render_pass() noexcept override;
[[nodiscard]] bool in_render_pass() const noexcept;
void reset() noexcept;
private:
std::vector<RecordedRenderCommand>* 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<RecordedRenderCommand>* commands_ = nullptr;

View File

@@ -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();
}