diff --git a/docs/modernization/debt.md b/docs/modernization/debt.md index 689f16c..2407656 100644 --- a/docs/modernization/debt.md +++ b/docs/modernization/debt.md @@ -18,6 +18,11 @@ agent or engineer to remove them without reconstructing context from chat. ## 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 dual live-pass per-face framebuffer begin/end execution plus pad-face array assembly now route through `legacy_canvas_stroke_execution_services.h`; diff --git a/docs/modernization/roadmap.md b/docs/modernization/roadmap.md index c16c9cc..7c66641 100644 --- a/docs/modernization/roadmap.md +++ b/docs/modernization/roadmap.md @@ -3103,6 +3103,10 @@ Results: assembly now shares one retained utility, while shader activation timing, texture/sampler binding, framebuffer ownership, pad execution, and final 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 shares the retained stroke execution helper callback surface, while shader setup, pad color selection, framebuffer ownership, and final OpenGL draw diff --git a/docs/modernization/tasks.md b/docs/modernization/tasks.md index 9ee623b..0ff33de 100644 --- a/docs/modernization/tasks.md +++ b/docs/modernization/tasks.md @@ -509,6 +509,13 @@ Done Checks: 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 framebuffer begin/end execution plus pad-face array assembly now route through `legacy_canvas_stroke_execution_services.h`; shader activation diff --git a/src/node_stroke_preview.cpp b/src/node_stroke_preview.cpp index 3996014..3b92536 100644 --- a/src/node_stroke_preview.cpp +++ b/src/node_stroke_preview.cpp @@ -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(size.x), + static_cast(size.y)); +} + +template +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(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 }; @@ -540,32 +580,27 @@ void NodeStrokePreview::draw_stroke_immediate() if (material.dual_pass.enabled) { - m_rtt.clear(); pp::panopainter::setup_legacy_stroke_dual_shader(material.dual_pass.uses_pattern); set_active_texture_unit(0U); dual_brush->m_tip_texture ? dual_brush->m_tip_texture->bind() : unbind_texture_2d(); - auto frames_dual = stroke_draw_compute(m_dual_stroke, zoom); - execute_stroke_preview_frames( - frames_dual, + execute_stroke_preview_live_pass( + m_tex_dual, + size, + copy_stroke_destination, + [&] { + m_rtt.clear(); + }, + [&] { + return stroke_draw_compute(m_dual_stroke, zoom); + }, [](auto& frame) { frame.col = { 0, 0, 0, 1 }; }, - [&](auto& frame) { - /*auto rect =*/ stroke_draw_samples(frame.shapes, m_tex_dual, copy_stroke_destination); + [&](auto& frame, Texture2D& blend_texture, bool copy_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(size.x), - static_cast(size.y)); } // CHEKCERBOARD @@ -623,10 +658,16 @@ void NodeStrokePreview::draw_stroke_immediate() unbind_texture_2d(); set_active_texture_unit(3U); preview_composite_plan.uses_mixer ? m_rtt_mixer.bindTexture() : unbind_texture_2d(); - auto frames = stroke_draw_compute(m_stroke, zoom); - m_rtt.clear(); - execute_stroke_preview_frames( - frames, + execute_stroke_preview_live_pass( + m_tex, + size, + copy_stroke_destination, + [&] { + m_rtt.clear(); + }, + [&] { + return stroke_draw_compute(m_stroke, zoom); + }, [&](auto& frame) { if (b->m_tip_mix > 0.f) { @@ -638,23 +679,12 @@ void NodeStrokePreview::draw_stroke_immediate() glm::vec4 { 0, 0, 0, 1 }; frame.flow = glm::max(frame.flow, m_min_flow); }, - [&](auto& frame) { - /*auto rect =*/ stroke_draw_samples(frame.shapes, m_tex, copy_stroke_destination); + [&](auto& frame, Texture2D& blend_texture, bool copy_destination) { + /*auto rect =*/ stroke_draw_samples(frame.shapes, blend_texture, copy_destination); }); set_active_texture_unit(3U); 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(size.x), - static_cast(size.y)); - // COMPOSITE execute_stroke_preview_final_composite_pass(