diff --git a/tests/paint_renderer/compositor_tests.cpp b/tests/paint_renderer/compositor_tests.cpp index 488ce54..f7cce09 100644 --- a/tests/paint_renderer/compositor_tests.cpp +++ b/tests/paint_renderer/compositor_tests.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -1926,6 +1927,137 @@ void retained_stroke_commit_dilate_copy_uses_layer_scratch_slot(pp::tests::Harne PP_EXPECT(h, copy_regions[0].height == 128); } +void retained_stroke_commit_runner_preserves_per_face_step_order(pp::tests::Harness& h) +{ + const auto sequence = plan_canvas_stroke_commit_sequence( + CanvasStrokeCommitRequest { + .erase_mode = false, + .alpha_locked = false, + .selection_mask_active = true, + .dual_stroke_enabled = true, + .pattern_enabled = true, + }); + + std::vector events; + const auto record = [&](std::string_view event, int face_index = -1) { + if (face_index >= 0) { + events.push_back(std::string(event) + ":" + std::to_string(face_index)); + } else { + events.push_back(std::string(event)); + } + }; + + const auto result = pp::panopainter::execute_legacy_canvas_stroke_commit_sequence( + pp::panopainter::LegacyCanvasStrokeCommitRequest { + .context = "test", + .faces = { + pp::panopainter::LegacyCanvasStrokeCommitFace { .index = 0, .dirty = true }, + pp::panopainter::LegacyCanvasStrokeCommitFace { .index = 1, .dirty = false }, + pp::panopainter::LegacyCanvasStrokeCommitFace { .index = 2, .dirty = true }, + }, + .sequence = sequence, + .callbacks = { + .mark_commit_started = [&]() { record("start"); }, + .capture_render_state = [&]() { record("capture"); }, + .prepare_render_state = [&]() { record("prepare"); }, + .restore_render_state = [&]() { record("restore"); }, + .publish_history = [&]() { record("publish"); }, + .capture_timelapse_frame = [&]() { record("timelapse"); }, + .bind_layer_framebuffer = [&](int face_index) { record("bind-fbo", face_index); }, + .capture_history_region = [&](int face_index) { record("history", face_index); }, + .apply_layer_dirty_region = [&](int face_index) { record("dirty", face_index); }, + .copy_layer_to_commit_destination = [&](int face_index) { record("copy-layer", face_index); }, + .bind_commit_inputs = [&](int face_index) { record("bind-inputs", face_index); }, + .execute_erase_composite = [&](int face_index) { record("erase", face_index); }, + .execute_paint_composite = [&](int face_index) { record("paint", face_index); }, + .copy_committed_to_dilate_source = [&](int face_index) { record("copy-committed", face_index); }, + .execute_commit_dilate = [&](int face_index) { record("dilate", face_index); }, + .unbind_layer_framebuffer = [&](int face_index) { record("unbind-fbo", face_index); }, + }, + }); + + const std::vector expected { + "start", + "capture", + "prepare", + "bind-fbo:0", + "history:0", + "dirty:0", + "copy-layer:0", + "bind-inputs:0", + "paint:0", + "copy-committed:0", + "dilate:0", + "unbind-fbo:0", + "bind-fbo:2", + "history:2", + "dirty:2", + "copy-layer:2", + "bind-inputs:2", + "paint:2", + "copy-committed:2", + "dilate:2", + "unbind-fbo:2", + "restore", + "publish", + "timelapse", + }; + + PP_EXPECT(h, result.ok); + PP_EXPECT(h, result.committed_faces == 2); + PP_EXPECT(h, events == expected); +} + +void retained_stroke_commit_copy_skips_missing_layer_scratch_or_invalid_extent(pp::tests::Harness& h) +{ + pp::paint_renderer::CanvasStrokeCommitSequencePlan missing_scratch; + missing_scratch.texture_binding_count = 1U; + missing_scratch.texture_bindings[0] = pp::paint_renderer::CanvasStrokeCommitTextureBindingPlan { + .role = CanvasStrokeCommitTextureRole::stroke, + .slot = 4, + }; + + int setup_calls = 0; + int active_texture_calls = 0; + int bind_layer_scratch_calls = 0; + int copy_calls = 0; + + pp::panopainter::copy_legacy_canvas_stroke_commit_to_dilate_source( + missing_scratch, + [&]() { ++setup_calls; }, + [&](int) { ++active_texture_calls; }, + [&]() { ++bind_layer_scratch_calls; }, + [&](int, int, int, int, int, int) { ++copy_calls; }, + pp::panopainter::LegacyCanvasStrokeCommitCopyExtent { + .width = 256, + .height = 128, + }); + + const auto valid_sequence = plan_canvas_stroke_commit_sequence( + CanvasStrokeCommitRequest { + .erase_mode = true, + .alpha_locked = true, + .selection_mask_active = false, + .dual_stroke_enabled = false, + .pattern_enabled = false, + }); + pp::panopainter::copy_legacy_canvas_stroke_commit_to_dilate_source( + valid_sequence, + [&]() { ++setup_calls; }, + [&](int) { ++active_texture_calls; }, + [&]() { ++bind_layer_scratch_calls; }, + [&](int, int, int, int, int, int) { ++copy_calls; }, + pp::panopainter::LegacyCanvasStrokeCommitCopyExtent { + .width = 0, + .height = 128, + }); + + PP_EXPECT(h, setup_calls == 0); + PP_EXPECT(h, active_texture_calls == 0); + PP_EXPECT(h, bind_layer_scratch_calls == 0); + PP_EXPECT(h, copy_calls == 0); +} + void plans_stroke_preview_composite_for_simple_brush(pp::tests::Harness& h) { const auto plan = plan_stroke_preview_composite(StrokePreviewCompositeRequest {}); @@ -2486,6 +2618,12 @@ int main() harness.run( "retained_stroke_commit_dilate_copy_uses_layer_scratch_slot", retained_stroke_commit_dilate_copy_uses_layer_scratch_slot); + harness.run( + "retained_stroke_commit_runner_preserves_per_face_step_order", + retained_stroke_commit_runner_preserves_per_face_step_order); + harness.run( + "retained_stroke_commit_copy_skips_missing_layer_scratch_or_invalid_extent", + retained_stroke_commit_copy_skips_missing_layer_scratch_or_invalid_extent); harness.run("plans_stroke_preview_composite_for_simple_brush", plans_stroke_preview_composite_for_simple_brush); harness.run("plans_stroke_preview_composite_with_mixer_input", plans_stroke_preview_composite_with_mixer_input); harness.run("plans_stroke_preview_composite_with_dual_input", plans_stroke_preview_composite_with_dual_input);