From fee09e534048a117612aedf7810e265081f45f6b Mon Sep 17 00:00:00 2001 From: omigamedev Date: Sat, 13 Jun 2026 17:58:45 +0200 Subject: [PATCH] Extract main stroke face loop --- docs/modernization/debt.md | 4 ++ docs/modernization/tasks.md | 8 ++- src/canvas.cpp | 2 +- src/legacy_canvas_stroke_execution_services.h | 30 ++++++++ .../paint_renderer/stroke_execution_tests.cpp | 71 +++++++++++++++++++ 5 files changed, 113 insertions(+), 2 deletions(-) diff --git a/docs/modernization/debt.md b/docs/modernization/debt.md index 282fc11..edbb58c 100644 --- a/docs/modernization/debt.md +++ b/docs/modernization/debt.md @@ -81,6 +81,10 @@ agent or engineer to remove them without reconstructing context from chat. `execute_legacy_canvas_stroke_live_pass_with_face_framebuffers(...)`; the retained path still owns the concrete shader, sampler, and framebuffer wiring. +- 2026-06-13: DEBT-0036 was narrowed again. `Canvas::stroke_draw()` main-pass + face loop orchestration now routes through + `execute_legacy_canvas_stroke_main_pass_frame_callbacks(...)`; the retained + path still owns the concrete shader, sampler, and framebuffer wiring. - 2026-06-13: DEBT-0036 was narrowed again. `Canvas::stroke_draw_samples()` now routes polygon triangulation, sample-point assembly, and retained destination-copy / upload / draw helper handoff through diff --git a/docs/modernization/tasks.md b/docs/modernization/tasks.md index 64f3ddc..eea7df4 100644 --- a/docs/modernization/tasks.md +++ b/docs/modernization/tasks.md @@ -919,7 +919,7 @@ ctest --preset desktop-fast --build-config Debug -R "pp_paint_renderer_stroke_ex ### STR-007 - Extract Main Stroke Face Loop Orchestration -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`, @@ -945,3 +945,9 @@ Validation: ctest --preset desktop-fast --build-config Debug -R "pp_paint_renderer_stroke_execution" --output-on-failure & 'C:\Program Files\Microsoft Visual Studio\18\Community\MSBuild\Current\Bin\MSBuild.exe' out\build\windows-msvc-default\tests\pp_paint_renderer_stroke_execution_tests.vcxproj /p:Configuration=Debug /p:Platform=x64 ``` + +### Completed Task Log + +| Date | Task | Score | Validation | Commit | +| --- | --- | ---: | --- | --- | +| 2026-06-13 | STR-007 | +1 renderer boundary and OpenGL parity | `ctest --preset desktop-fast --build-config Debug -R "pp_paint_renderer_stroke_execution" --output-on-failure`; `MSBuild.exe out\build\windows-msvc-default\tests\pp_paint_renderer_stroke_execution_tests.vcxproj /p:Configuration=Debug /p:Platform=x64` | pending | diff --git a/src/canvas.cpp b/src/canvas.cpp index 8f62790..79b2643 100644 --- a/src/canvas.cpp +++ b/src/canvas.cpp @@ -792,7 +792,7 @@ void Canvas::stroke_draw() std::array box_dirty = SIXPLETTE(false); const std::array include_main_dirty = SIXPLETTE(true); glm::vec4 pad_color; - pp::panopainter::execute_legacy_canvas_stroke_live_pass_with_face_framebuffers( + pp::panopainter::execute_legacy_canvas_stroke_main_pass_frame_callbacks( frames, stroke_extent, std::span(m_dirty_box), diff --git a/src/legacy_canvas_stroke_execution_services.h b/src/legacy_canvas_stroke_execution_services.h index 0b1c905..28a465a 100644 --- a/src/legacy_canvas_stroke_execution_services.h +++ b/src/legacy_canvas_stroke_execution_services.h @@ -338,6 +338,36 @@ std::size_t execute_legacy_canvas_stroke_dual_pass_frame_callbacks( pass_dirty_faces); } +template +std::size_t execute_legacy_canvas_stroke_main_pass_frame_callbacks( + Frames&& frames, + pp::renderer::Extent2D extent, + std::span accumulated_dirty_boxes, + std::span pass_dirty_boxes, + std::span include_in_committed_dirty_box, + BeginFrame&& begin_frame, + PrepareFace&& prepare_face, + ExecuteSample&& execute_sample, + Framebuffers& face_framebuffers, + bool preserve_sample_dirty_as_pass_dirty = false, + std::span committed_dirty_faces = {}, + std::span pass_dirty_faces = {}) +{ + return execute_legacy_canvas_stroke_live_pass_with_face_framebuffers( + std::forward(frames), + extent, + accumulated_dirty_boxes, + pass_dirty_boxes, + include_in_committed_dirty_box, + std::forward(begin_frame), + std::forward(prepare_face), + std::forward(execute_sample), + face_framebuffers, + preserve_sample_dirty_as_pass_dirty, + committed_dirty_faces, + pass_dirty_faces); +} + [[nodiscard]] inline LegacyCanvasStrokeDualPassResult execute_legacy_canvas_stroke_dual_pass( const LegacyCanvasStrokeDualPassRequest& request) { diff --git a/tests/paint_renderer/stroke_execution_tests.cpp b/tests/paint_renderer/stroke_execution_tests.cpp index b2b10c1..ae1069a 100644 --- a/tests/paint_renderer/stroke_execution_tests.cpp +++ b/tests/paint_renderer/stroke_execution_tests.cpp @@ -1369,6 +1369,74 @@ void retained_stroke_dual_pass_frame_callbacks_preserve_order(pp::tests::Harness PP_EXPECT(h, events == expected_events); } +void retained_stroke_main_pass_frame_callbacks_preserve_order(pp::tests::Harness& h) +{ + StrokeFrame frame; + frame.id = 31; + frame.shapes[0] = { + vertex_t(glm::vec2(5.0F, 5.0F)), + vertex_t(glm::vec2(6.0F, 5.0F)), + vertex_t(glm::vec2(6.0F, 6.0F)), + }; + frame.shapes[2] = { + vertex_t(glm::vec2(7.0F, 7.0F)), + vertex_t(glm::vec2(8.0F, 7.0F)), + vertex_t(glm::vec2(8.0F, 8.0F)), + }; + + std::array frames { frame }; + std::array accumulated_dirty_boxes; + std::array pass_dirty_boxes; + accumulated_dirty_boxes.fill(glm::vec4(64.0F, 64.0F, 0.0F, 0.0F)); + pass_dirty_boxes.fill(glm::vec4(64.0F, 64.0F, 0.0F, 0.0F)); + std::array include_in_committed_dirty_box { true, true, true, true, true, true }; + + std::vector events; + std::array face_framebuffers {}; + for (int face_index = 0; face_index < 6; ++face_index) { + face_framebuffers[face_index].events = &events; + face_framebuffers[face_index].face_index = face_index; + } + + const auto executed_faces = pp::panopainter::execute_legacy_canvas_stroke_main_pass_frame_callbacks( + frames, + pp::renderer::Extent2D { .width = 64, .height = 64 }, + accumulated_dirty_boxes, + pass_dirty_boxes, + include_in_committed_dirty_box, + [&](StrokeFrame& current_frame) { + events.push_back("begin-frame:" + std::to_string(current_frame.id)); + }, + [&](StrokeFrame&, int face_index, std::span vertices) { + events.push_back( + "prepare:" + std::to_string(face_index) + ":" + std::to_string(vertices.size())); + }, + [&](StrokeFrame&, int face_index, std::span) { + events.push_back("execute:" + std::to_string(face_index)); + return glm::vec4( + static_cast(face_index + 1), + static_cast(face_index + 2), + static_cast(face_index + 3), + static_cast(face_index + 4)); + }, + face_framebuffers, + true); + + const std::vector expected_events { + "begin-frame:31", + "prepare:0:3", + "bind:0", + "execute:0", + "unbind:0", + "prepare:2:3", + "bind:2", + "execute:2", + "unbind:2", + }; + PP_EXPECT(h, executed_faces == 2U); + PP_EXPECT(h, events == expected_events); +} + void retained_stroke_pad_executor_copies_destination_for_dirty_faces_only(pp::tests::Harness& h) { const std::array dirty_faces { true, false, true }; @@ -1644,6 +1712,9 @@ int main() harness.run( "retained_stroke_dual_pass_frame_callbacks_preserve_order", retained_stroke_dual_pass_frame_callbacks_preserve_order); + harness.run( + "retained_stroke_main_pass_frame_callbacks_preserve_order", + retained_stroke_main_pass_frame_callbacks_preserve_order); harness.run( "retained_stroke_live_pass_sampler_dispatch_helper_builds_expected_callback_wiring", retained_stroke_live_pass_sampler_dispatch_helper_builds_expected_callback_wiring);