diff --git a/docs/modernization/debt.md b/docs/modernization/debt.md index a387978..c4f8862 100644 --- a/docs/modernization/debt.md +++ b/docs/modernization/debt.md @@ -533,6 +533,10 @@ agent or engineer to remove them without reconstructing context from chat. texture dispatch now has regression coverage through `make_legacy_canvas_stroke_main_pass_texture_dispatch(...)`; the live path still owns the concrete texture objects and sampler state. +- 2026-06-13: DEBT-0036 was narrowed again. `Canvas::stroke_draw()` live-pass + sampler dispatch now has regression coverage through + `make_legacy_canvas_stroke_live_pass_sampler_dispatch(...)`; the live path + still owns the concrete sampler objects and binding state. - 2026-06-13: DEBT-0036 was narrowed again. `Canvas::draw_merge` checkerboard background shader setup and final merged-texture redraw setup now route through `legacy_canvas_draw_merge_services.h`. The retained Canvas path still diff --git a/docs/modernization/tasks.md b/docs/modernization/tasks.md index ee6bf80..a746010 100644 --- a/docs/modernization/tasks.md +++ b/docs/modernization/tasks.md @@ -609,7 +609,7 @@ ctest --preset desktop-fast --build-config Debug -R "pp_paint_renderer_composito ### STR-021 - Extract Stroke Draw Live Pass Sampler Dispatch -Status: Ready +Status: Done Score: +1 renderer boundary and OpenGL parity Debt: `DEBT-0036` Scope: `src/canvas.cpp`, `src/legacy_canvas_stroke_execution_services.h`, `tests/paint_renderer/compositor_tests.cpp` @@ -634,6 +634,12 @@ Validation: ctest --preset desktop-fast --build-config Debug -R "pp_paint_renderer_compositor|pp_paint_renderer_stroke_execution" --output-on-failure ``` +### Completed Task Log + +| Date | Task | Score | Validation | Commit | +| --- | --- | --- | --- | --- | +| 2026-06-13 | STR-021 | +1 renderer boundary and OpenGL parity | `ctest --preset desktop-fast --build-config Debug -R "pp_paint_renderer_compositor|pp_paint_renderer_stroke_execution" --output-on-failure` | `pending` | + Progress Notes: - 2026-06-13: `NodeStrokePreview::draw_stroke_immediate()` now routes final diff --git a/tests/paint_renderer/compositor_tests.cpp b/tests/paint_renderer/compositor_tests.cpp index 3c59eb4..6ba2383 100644 --- a/tests/paint_renderer/compositor_tests.cpp +++ b/tests/paint_renderer/compositor_tests.cpp @@ -1797,6 +1797,101 @@ void legacy_canvas_stroke_main_pass_texture_dispatch_preserves_order(pp::tests:: PP_EXPECT(h, steps == expected); } +void legacy_canvas_stroke_live_pass_sampler_dispatch_preserves_order(pp::tests::Harness& h) +{ + std::vector steps; + const auto dispatch = pp::panopainter::make_legacy_canvas_stroke_live_pass_sampler_dispatch( + [&](int slot) { + steps.emplace_back("activate:" + std::to_string(slot)); + }, + [&] { + steps.emplace_back("bind-brush"); + }, + [&] { + steps.emplace_back("unbind-brush"); + }, + [&] { + steps.emplace_back("bind-pattern"); + }, + [&] { + steps.emplace_back("unbind-pattern"); + }, + [&] { + steps.emplace_back("bind-mixer"); + }, + [&] { + steps.emplace_back("unbind-mixer"); + }, + [&] { + steps.emplace_back("bind-stencil"); + }, + [&] { + steps.emplace_back("unbind-stencil"); + }); + + pp::panopainter::bind_legacy_canvas_stroke_sampler_inputs( + std::array { + pp::panopainter::LegacyCanvasStrokeTextureBinding { + .input = pp::panopainter::LegacyCanvasStrokeTextureInput::brush_tip, + .slot = 0, + }, + pp::panopainter::LegacyCanvasStrokeTextureBinding { + .input = pp::panopainter::LegacyCanvasStrokeTextureInput::stroke_destination, + .slot = 1, + }, + pp::panopainter::LegacyCanvasStrokeTextureBinding { + .input = pp::panopainter::LegacyCanvasStrokeTextureInput::pattern, + .slot = 2, + }, + pp::panopainter::LegacyCanvasStrokeTextureBinding { + .input = pp::panopainter::LegacyCanvasStrokeTextureInput::mixer, + .slot = 3, + }, + }, + dispatch); + + pp::panopainter::unbind_legacy_canvas_stroke_sampler_inputs( + std::array { + pp::panopainter::LegacyCanvasStrokeTextureBinding { + .input = pp::panopainter::LegacyCanvasStrokeTextureInput::mixer, + .slot = 3, + }, + pp::panopainter::LegacyCanvasStrokeTextureBinding { + .input = pp::panopainter::LegacyCanvasStrokeTextureInput::pattern, + .slot = 2, + }, + pp::panopainter::LegacyCanvasStrokeTextureBinding { + .input = pp::panopainter::LegacyCanvasStrokeTextureInput::stroke_destination, + .slot = 1, + }, + pp::panopainter::LegacyCanvasStrokeTextureBinding { + .input = pp::panopainter::LegacyCanvasStrokeTextureInput::brush_tip, + .slot = 0, + }, + }, + dispatch); + + const std::vector expected { + "activate:0", + "bind-brush", + "activate:1", + "bind-stencil", + "activate:2", + "bind-pattern", + "activate:3", + "bind-mixer", + "activate:3", + "unbind-mixer", + "activate:2", + "unbind-pattern", + "activate:1", + "unbind-stencil", + "activate:0", + "unbind-brush", + }; + PP_EXPECT(h, steps == expected); +} + void plans_canvas_stroke_commit_erase_sequence(pp::tests::Harness& h) { const auto plan = plan_canvas_stroke_commit_sequence( @@ -3669,6 +3764,9 @@ int main() harness.run( "legacy_canvas_stroke_main_pass_texture_dispatch_preserves_order", legacy_canvas_stroke_main_pass_texture_dispatch_preserves_order); + harness.run( + "legacy_canvas_stroke_live_pass_sampler_dispatch_preserves_order", + legacy_canvas_stroke_live_pass_sampler_dispatch_preserves_order); harness.run("plans_canvas_stroke_commit_erase_sequence", plans_canvas_stroke_commit_erase_sequence); harness.run("plans_canvas_stroke_commit_composite_sequence", plans_canvas_stroke_commit_composite_sequence); harness.run(