diff --git a/src/canvas.cpp b/src/canvas.cpp index ddaf313..08b664d 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_live_pass_face_framebuffers( 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 773160c..f432361 100644 --- a/src/legacy_canvas_stroke_execution_services.h +++ b/src/legacy_canvas_stroke_execution_services.h @@ -293,6 +293,38 @@ struct LegacyCanvasStrokeDualPassResult { [[nodiscard]] inline LegacyCanvasStrokeMixPassResult execute_legacy_canvas_stroke_mix_pass( const LegacyCanvasStrokeMixPassRequest& request); +template +std::size_t execute_legacy_canvas_stroke_live_pass_face_framebuffers( + 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, + BeginFace&& begin_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), + [&](auto& frame, int face_index, auto& vertices) { + begin_face(frame, face_index, vertices); + }, + 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 96c1f51..b97cb36 100644 --- a/tests/paint_renderer/stroke_execution_tests.cpp +++ b/tests/paint_renderer/stroke_execution_tests.cpp @@ -383,6 +383,101 @@ void retained_stroke_destination_texture_dispatch_helper_builds_expected_callbac PP_EXPECT(h, events == expected_events); } +void retained_stroke_live_pass_face_framebuffers_helper_preserves_callback_order(pp::tests::Harness& h) +{ + struct Frame { + int id = -1; + std::array, 6> shapes {}; + }; + struct Framebuffer { + std::vector* events = nullptr; + int face_index = -1; + + void bindFramebuffer() + { + events->push_back("bind:" + std::to_string(face_index)); + } + + void unbindFramebuffer() + { + events->push_back("unbind:" + std::to_string(face_index)); + } + }; + auto make_frame = [](int id, int face_index) { + Frame frame { .id = id }; + frame.shapes[face_index] = { + vertex_t(glm::vec2(0.0F, 0.0F)), + vertex_t(glm::vec2(1.0F, 0.0F)), + vertex_t(glm::vec2(0.5F, 1.0F)), + }; + return frame; + }; + const std::array frames { + make_frame(0, 0), + make_frame(1, 1), + }; + std::array framebuffers { + Framebuffer { .events = nullptr, .face_index = 0 }, + Framebuffer { .events = nullptr, .face_index = 1 }, + }; + + std::vector events; + framebuffers[0].events = &events; + framebuffers[1].events = &events; + + std::array accumulated_dirty_boxes { + glm::vec4(0.0F), + glm::vec4(0.0F), + }; + std::array pass_dirty_boxes { + glm::vec4(0.0F), + glm::vec4(0.0F), + }; + const std::array include_dirty_faces { true, true }; + std::array committed_dirty_faces { false, false }; + std::array pass_dirty_faces { false, false }; + + const auto executed_faces = pp::panopainter::execute_legacy_canvas_stroke_live_pass_face_framebuffers( + frames, + pp::renderer::Extent2D { 64U, 32U }, + accumulated_dirty_boxes, + pass_dirty_boxes, + include_dirty_faces, + [&](auto& frame) { + events.emplace_back("begin-frame:" + std::to_string(frame.id)); + }, + [&](auto& frame, int face_index, auto&) { + events.emplace_back("begin-face:" + std::to_string(frame.id) + ":" + std::to_string(face_index)); + }, + [&](auto& frame, int face_index, auto&) { + events.emplace_back("sample:" + std::to_string(frame.id) + ":" + std::to_string(face_index)); + return glm::vec4(0.0F); + }, + framebuffers, + true, + committed_dirty_faces, + pass_dirty_faces); + + const std::vector expected_events { + "begin-frame:0", + "begin-face:0:0", + "bind:0", + "sample:0:0", + "unbind:0", + "begin-frame:1", + "begin-face:1:1", + "bind:1", + "sample:1:1", + "unbind:1", + }; + PP_EXPECT(h, executed_faces == 2U); + PP_EXPECT(h, events == expected_events); + PP_EXPECT(h, committed_dirty_faces[0]); + PP_EXPECT(h, committed_dirty_faces[1]); + PP_EXPECT(h, pass_dirty_faces[0]); + PP_EXPECT(h, pass_dirty_faces[1]); +} + void retained_stroke_sample_executor_copies_destination_and_expands_quads(pp::tests::Harness& h) { const auto vertices = make_quad_vertices(); @@ -1475,6 +1570,9 @@ int main() harness.run( "retained_stroke_destination_texture_dispatch_helper_builds_expected_callback_wiring", retained_stroke_destination_texture_dispatch_helper_builds_expected_callback_wiring); + harness.run( + "retained_stroke_live_pass_face_framebuffers_helper_preserves_callback_order", + retained_stroke_live_pass_face_framebuffers_helper_preserves_callback_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);