diff --git a/docs/modernization/debt.md b/docs/modernization/debt.md index 2a13329..a9dd91f 100644 --- a/docs/modernization/debt.md +++ b/docs/modernization/debt.md @@ -18,6 +18,10 @@ agent or engineer to remove them without reconstructing context from chat. ## Recent Reductions +- 2026-06-13: DEBT-0036 was narrowed again. `NodeStrokePreview::draw_stroke_immediate()` + now routes main-pass texture binding through + `bind_legacy_node_stroke_preview_main_pass_textures(...)`; the retained path + still owns the concrete texture objects and mixer binding callbacks. - 2026-06-13: DEBT-0036 was narrowed again. `NodeStrokePreview::draw_stroke_immediate()` no longer duplicates main-pass setup in the outer `prepare_main_pass` block; the retained main-live-pass helper now owns blend-uniform and texture binding diff --git a/src/legacy_node_stroke_preview_execution_services.h b/src/legacy_node_stroke_preview_execution_services.h index 3fdc307..ab2a52d 100644 --- a/src/legacy_node_stroke_preview_execution_services.h +++ b/src/legacy_node_stroke_preview_execution_services.h @@ -234,6 +234,49 @@ struct LegacyNodeStrokePreviewPassOrchestrationPlan { bool background_colorize = false; }; +struct LegacyNodeStrokePreviewMainPassTextureDispatch { + std::function activate_texture_unit; + std::function bind_brush_tip; + std::function bind_stroke_destination; + std::function bind_pattern; + std::function bind_mixer; +}; + +[[nodiscard]] inline LegacyNodeStrokePreviewMainPassTextureDispatch make_legacy_node_stroke_preview_main_pass_texture_dispatch( + std::function activate_texture_unit, + std::function bind_brush_tip, + std::function bind_stroke_destination, + std::function bind_pattern, + std::function bind_mixer) +{ + return LegacyNodeStrokePreviewMainPassTextureDispatch { + .activate_texture_unit = std::move(activate_texture_unit), + .bind_brush_tip = std::move(bind_brush_tip), + .bind_stroke_destination = std::move(bind_stroke_destination), + .bind_pattern = std::move(bind_pattern), + .bind_mixer = std::move(bind_mixer), + }; +} + +inline void bind_legacy_node_stroke_preview_main_pass_textures( + const LegacyNodeStrokePreviewMainPassTextureDispatch& dispatch, + bool copy_stroke_destination, + bool uses_mixer) +{ + dispatch.activate_texture_unit(0); + dispatch.bind_brush_tip(); + if (copy_stroke_destination) { + dispatch.activate_texture_unit(1); + dispatch.bind_stroke_destination(); + } + dispatch.activate_texture_unit(2); + dispatch.bind_pattern(); + dispatch.activate_texture_unit(3); + if (uses_mixer) { + dispatch.bind_mixer(); + } +} + template struct LegacyNodeStrokePreviewMainLivePassRequestT { std::function setup_blend_uniforms; diff --git a/src/node_stroke_preview.cpp b/src/node_stroke_preview.cpp index 8d50ac0..0232284 100644 --- a/src/node_stroke_preview.cpp +++ b/src/node_stroke_preview.cpp @@ -244,24 +244,29 @@ void bind_stroke_preview_main_pass_textures( bool copy_stroke_destination, bool uses_mixer) { - set_active_texture_unit(stroke_preview_live_slots::kTip); - brush.m_tip_texture ? - brush.m_tip_texture->bind() : - unbind_texture_2d(); - - if (copy_stroke_destination) - { - set_active_texture_unit(stroke_preview_live_slots::kDestination); - stroke_destination_texture.bind(); - } - - set_active_texture_unit(stroke_preview_live_slots::kPattern); - brush.m_pattern_texture ? - brush.m_pattern_texture->bind() : - unbind_texture_2d(); - - set_active_texture_unit(stroke_preview_live_slots::kMixer); - uses_mixer ? mixer_rtt.bindTexture() : unbind_texture_2d(); + pp::panopainter::bind_legacy_node_stroke_preview_main_pass_textures( + pp::panopainter::make_legacy_node_stroke_preview_main_pass_texture_dispatch( + [&](int texture_slot) { + set_active_texture_unit(texture_slot); + }, + [&] { + brush.m_tip_texture ? + brush.m_tip_texture->bind() : + unbind_texture_2d(); + }, + [&] { + stroke_destination_texture.bind(); + }, + [&] { + brush.m_pattern_texture ? + brush.m_pattern_texture->bind() : + unbind_texture_2d(); + }, + [&] { + mixer_rtt.bindTexture(); + }), + copy_stroke_destination, + uses_mixer); } void bind_stroke_preview_destination_texture(Texture2D& texture) diff --git a/tests/paint_renderer/compositor_tests.cpp b/tests/paint_renderer/compositor_tests.cpp index f39cff3..40c2d3b 100644 --- a/tests/paint_renderer/compositor_tests.cpp +++ b/tests/paint_renderer/compositor_tests.cpp @@ -2611,6 +2611,54 @@ void legacy_node_stroke_preview_main_live_pass_preserves_order(pp::tests::Harnes PP_EXPECT(h, !invalid); } +void legacy_node_stroke_preview_main_pass_texture_dispatch_preserves_order(pp::tests::Harness& h) +{ + std::vector steps; + const auto run = [&](bool copy_stroke_destination, bool uses_mixer) { + steps.clear(); + pp::panopainter::bind_legacy_node_stroke_preview_main_pass_textures( + pp::panopainter::make_legacy_node_stroke_preview_main_pass_texture_dispatch( + [&](int texture_slot) { + steps.emplace_back("unit:" + std::to_string(texture_slot)); + }, + [&] { + steps.emplace_back("bind_tip"); + }, + [&] { + steps.emplace_back("bind_destination"); + }, + [&] { + steps.emplace_back("bind_pattern"); + }, + [&] { + steps.emplace_back("bind_mixer"); + }), + copy_stroke_destination, + uses_mixer); + }; + + run(false, false); + PP_EXPECT(h, steps == std::vector { + "unit:0", + "bind_tip", + "unit:2", + "bind_pattern", + "unit:3", + }); + + run(true, true); + PP_EXPECT(h, steps == std::vector { + "unit:0", + "bind_tip", + "unit:1", + "bind_destination", + "unit:2", + "bind_pattern", + "unit:3", + "bind_mixer", + }); +} + void legacy_node_stroke_preview_stroke_setup_plan_preserves_curve_and_dual_inputs(pp::tests::Harness& h) { const auto plan = pp::panopainter::plan_legacy_node_stroke_preview_stroke_setup( @@ -3204,6 +3252,9 @@ int main() harness.run( "legacy_node_stroke_preview_main_live_pass_preserves_order", legacy_node_stroke_preview_main_live_pass_preserves_order); + harness.run( + "legacy_node_stroke_preview_main_pass_texture_dispatch_preserves_order", + legacy_node_stroke_preview_main_pass_texture_dispatch_preserves_order); harness.run( "legacy_node_stroke_preview_stroke_setup_plan_preserves_curve_and_dual_inputs", legacy_node_stroke_preview_stroke_setup_plan_preserves_curve_and_dual_inputs);