Share retained stroke preview live pass helper

This commit is contained in:
2026-06-13 10:24:06 +02:00
parent 084f58573f
commit 65b24d9516
4 changed files with 80 additions and 34 deletions

View File

@@ -18,6 +18,11 @@ agent or engineer to remove them without reconstructing context from chat.
## Recent Reductions ## Recent Reductions
- 2026-06-13: DEBT-0036 was narrowed again. `NodeStrokePreview` dual-pass and
main-pass frame-loop execution plus full-frame copy-back now route through
shared local helpers; checkerboard/background capture ordering, texture-unit
binding, mixer ownership, and final composite semantics remain retained in
the preview node.
- 2026-06-13: DEBT-0036 was narrowed again. `Canvas::stroke_draw` main and - 2026-06-13: DEBT-0036 was narrowed again. `Canvas::stroke_draw` main and
dual live-pass per-face framebuffer begin/end execution plus pad-face array dual live-pass per-face framebuffer begin/end execution plus pad-face array
assembly now route through `legacy_canvas_stroke_execution_services.h`; assembly now route through `legacy_canvas_stroke_execution_services.h`;

View File

@@ -3103,6 +3103,10 @@ Results:
assembly now shares one retained utility, while shader activation timing, assembly now shares one retained utility, while shader activation timing,
texture/sampler binding, framebuffer ownership, pad execution, and final texture/sampler binding, framebuffer ownership, pad execution, and final
OpenGL draw ordering remain in the legacy Canvas path. OpenGL draw ordering remain in the legacy Canvas path.
- `NodeStrokePreview` dual-pass and main-pass frame-loop execution plus
full-frame copy-back now share one local retained helper surface, while
checkerboard/background capture ordering, texture-unit binding, mixer
ownership, and final composite semantics remain in the preview node.
- `Canvas::stroke_draw` pad-pass destination bind/copy/unbind ordering now - `Canvas::stroke_draw` pad-pass destination bind/copy/unbind ordering now
shares the retained stroke execution helper callback surface, while shader shares the retained stroke execution helper callback surface, while shader
setup, pad color selection, framebuffer ownership, and final OpenGL draw setup, pad color selection, framebuffer ownership, and final OpenGL draw

View File

@@ -509,6 +509,13 @@ Done Checks:
Progress Notes: Progress Notes:
- 2026-06-13: `NodeStrokePreview::draw_stroke_immediate()` dual-pass and
main-pass frame-loop execution plus full-frame copy-back now route through
shared local helpers; checkerboard/background capture ordering, texture-unit
binding, mixer ownership, and final composite semantics remain local to the
preview node. Next slice should target background/final-copy helpers or
pass-level texture binding without reopening the new preview composite
helper.
- 2026-06-13: `Canvas::stroke_draw` main and dual live-pass per-face - 2026-06-13: `Canvas::stroke_draw` main and dual live-pass per-face
framebuffer begin/end execution plus pad-face array assembly now route framebuffer begin/end execution plus pad-face array assembly now route
through `legacy_canvas_stroke_execution_services.h`; shader activation through `legacy_canvas_stroke_execution_services.h`; shader activation

View File

@@ -187,6 +187,46 @@ void execute_stroke_preview_frames(
} }
} }
void copy_stroke_preview_framebuffer_to_texture(
Texture2D& texture,
glm::vec2 size,
std::uint32_t texture_unit)
{
set_active_texture_unit(texture_unit);
texture.bind();
copy_framebuffer_to_texture_2d(
0,
0,
0,
0,
static_cast<int>(size.x),
static_cast<int>(size.y));
}
template <typename ClearTarget, typename ComputeFrames, typename BeforeFrame, typename DrawSample>
void execute_stroke_preview_live_pass(
Texture2D& output_texture,
glm::vec2 size,
bool copy_stroke_destination,
ClearTarget&& clear_target,
ComputeFrames&& compute_frames,
BeforeFrame&& before_frame,
DrawSample&& draw_sample)
{
auto frames = compute_frames();
clear_target();
execute_stroke_preview_frames(
frames,
std::forward<BeforeFrame>(before_frame),
[&](auto& frame) {
draw_sample(frame, output_texture, copy_stroke_destination);
});
copy_stroke_preview_framebuffer_to_texture(
output_texture,
size,
stroke_preview_composite_slots::kStroke);
}
} }
std::atomic_int NodeStrokePreview::s_instances{ 0 }; std::atomic_int NodeStrokePreview::s_instances{ 0 };
@@ -540,32 +580,27 @@ void NodeStrokePreview::draw_stroke_immediate()
if (material.dual_pass.enabled) if (material.dual_pass.enabled)
{ {
m_rtt.clear();
pp::panopainter::setup_legacy_stroke_dual_shader(material.dual_pass.uses_pattern); pp::panopainter::setup_legacy_stroke_dual_shader(material.dual_pass.uses_pattern);
set_active_texture_unit(0U); set_active_texture_unit(0U);
dual_brush->m_tip_texture ? dual_brush->m_tip_texture ?
dual_brush->m_tip_texture->bind() : dual_brush->m_tip_texture->bind() :
unbind_texture_2d(); unbind_texture_2d();
auto frames_dual = stroke_draw_compute(m_dual_stroke, zoom); execute_stroke_preview_live_pass(
execute_stroke_preview_frames( m_tex_dual,
frames_dual, size,
copy_stroke_destination,
[&] {
m_rtt.clear();
},
[&] {
return stroke_draw_compute(m_dual_stroke, zoom);
},
[](auto& frame) { [](auto& frame) {
frame.col = { 0, 0, 0, 1 }; frame.col = { 0, 0, 0, 1 };
}, },
[&](auto& frame) { [&](auto& frame, Texture2D& blend_texture, bool copy_destination) {
/*auto rect =*/ stroke_draw_samples(frame.shapes, m_tex_dual, copy_stroke_destination); /*auto rect =*/ stroke_draw_samples(frame.shapes, blend_texture, copy_destination);
}); });
// copy raw stroke to tex
set_active_texture_unit(1U);
m_tex_dual.bind();
copy_framebuffer_to_texture_2d(
0,
0,
0,
0,
static_cast<int>(size.x),
static_cast<int>(size.y));
} }
// CHEKCERBOARD // CHEKCERBOARD
@@ -623,10 +658,16 @@ void NodeStrokePreview::draw_stroke_immediate()
unbind_texture_2d(); unbind_texture_2d();
set_active_texture_unit(3U); set_active_texture_unit(3U);
preview_composite_plan.uses_mixer ? m_rtt_mixer.bindTexture() : unbind_texture_2d(); preview_composite_plan.uses_mixer ? m_rtt_mixer.bindTexture() : unbind_texture_2d();
auto frames = stroke_draw_compute(m_stroke, zoom); execute_stroke_preview_live_pass(
m_tex,
size,
copy_stroke_destination,
[&] {
m_rtt.clear(); m_rtt.clear();
execute_stroke_preview_frames( },
frames, [&] {
return stroke_draw_compute(m_stroke, zoom);
},
[&](auto& frame) { [&](auto& frame) {
if (b->m_tip_mix > 0.f) if (b->m_tip_mix > 0.f)
{ {
@@ -638,23 +679,12 @@ void NodeStrokePreview::draw_stroke_immediate()
glm::vec4 { 0, 0, 0, 1 }; glm::vec4 { 0, 0, 0, 1 };
frame.flow = glm::max(frame.flow, m_min_flow); frame.flow = glm::max(frame.flow, m_min_flow);
}, },
[&](auto& frame) { [&](auto& frame, Texture2D& blend_texture, bool copy_destination) {
/*auto rect =*/ stroke_draw_samples(frame.shapes, m_tex, copy_stroke_destination); /*auto rect =*/ stroke_draw_samples(frame.shapes, blend_texture, copy_destination);
}); });
set_active_texture_unit(3U); set_active_texture_unit(3U);
m_rtt_mixer.unbindTexture(); m_rtt_mixer.unbindTexture();
// copy raw stroke to tex
set_active_texture_unit(1U);
m_tex.bind();
copy_framebuffer_to_texture_2d(
0,
0,
0,
0,
static_cast<int>(size.x),
static_cast<int>(size.y));
// COMPOSITE // COMPOSITE
execute_stroke_preview_final_composite_pass( execute_stroke_preview_final_composite_pass(