#include "pch.h"

#include <algorithm>
#include <array>
#include <cfloat>
#include <condition_variable>
#include <cstdio>
#include <cmath>
#include <cstdint>
#include <deque>
#include <functional>
#include <future>
#include <memory>
#include <mutex>
#include <random>
#include <span>
#include <string>
#include <string_view>
#include <utility>
#include <vector>

#include <poly2tri.h>

#include "legacy_canvas_stroke_execution_services.h"
#include "legacy_canvas_stroke_preview_services.h"
#include "test_harness.h"

using pp::panopainter::LegacyCanvasStrokePadExecutionRequest;
using pp::panopainter::LegacyCanvasStrokePadFace;
using pp::panopainter::LegacyCanvasStrokeMixPassPlane;
using pp::panopainter::LegacyCanvasStrokeMixPassRequest;
using pp::panopainter::LegacyCanvasStrokeSamplerDispatch;
using pp::panopainter::LegacyCanvasStrokeTextureBinding;
using pp::panopainter::LegacyCanvasStrokeTextureInputDispatch;
using pp::panopainter::LegacyCanvasStrokeTextureInput;
using pp::panopainter::LegacyStrokeSampleExecutionRequest;

std::vector<vertex_t> triangulate_simple(const std::vector<vertex_t>& vertices)
{
    std::vector<vertex_t> ret;
    std::vector<p2t::Point> points(vertices.size());
    std::vector<p2t::Point*> points_ptr(vertices.size());
    for (std::size_t i = 0; i < vertices.size(); ++i)
    {
        points[i] = { vertices[i].pos.x, vertices[i].pos.y };
        points_ptr[i] = &points[i];
    }

    auto cdt = std::make_unique<p2t::CDT>(points_ptr);
    cdt->Triangulate();
    const auto triangles = cdt->GetTriangles();
    for (auto* triangle : triangles)
    {
        for (int i = 0; i < 3; ++i)
        {
            const auto index = std::distance(points.data(), triangle->GetPoint(i));
            ret.push_back(vertices[static_cast<std::size_t>(index)]);
        }
    }

    return ret;
}

namespace {

bool nearly_equal(float a, float b)
{
    return std::fabs(a - b) < 0.0001F;
}

std::array<vertex_t, 4> make_quad_vertices()
{
    return {
        vertex_t(glm::vec2(10.0F, 20.0F)),
        vertex_t(glm::vec2(10.0F, 30.0F)),
        vertex_t(glm::vec2(30.0F, 30.0F)),
        vertex_t(glm::vec2(30.0F, 20.0F)),
    };
}

std::array<pp::paint_renderer::CanvasStrokePoint, 4> make_sample_points()
{
    return {
        pp::paint_renderer::CanvasStrokePoint { .x = 10.0F, .y = 20.0F },
        pp::paint_renderer::CanvasStrokePoint { .x = 10.0F, .y = 30.0F },
        pp::paint_renderer::CanvasStrokePoint { .x = 30.0F, .y = 30.0F },
        pp::paint_renderer::CanvasStrokePoint { .x = 30.0F, .y = 20.0F },
    };
}

std::vector<vertex_t> make_polygon_vertices(std::initializer_list<glm::vec2> points)
{
    std::vector<vertex_t> vertices;
    vertices.reserve(points.size());
    for (const auto& point : points) {
        vertices.emplace_back(point);
    }
    return vertices;
}

void retained_stroke_texture_inputs_bind_and_unbind_in_declared_order(pp::tests::Harness& h)
{
    const std::array<LegacyCanvasStrokeTextureBinding, 4> bindings {
        LegacyCanvasStrokeTextureBinding { .input = LegacyCanvasStrokeTextureInput::brush_tip, .slot = 1 },
        LegacyCanvasStrokeTextureBinding { .input = LegacyCanvasStrokeTextureInput::stroke_destination, .slot = 0 },
        LegacyCanvasStrokeTextureBinding { .input = LegacyCanvasStrokeTextureInput::mixer, .slot = 3 },
        LegacyCanvasStrokeTextureBinding { .input = LegacyCanvasStrokeTextureInput::pattern, .slot = 4 },
    };

    std::vector<std::pair<LegacyCanvasStrokeTextureInput, int>> bound;
    std::vector<std::pair<LegacyCanvasStrokeTextureInput, int>> unbound;

    pp::panopainter::bind_legacy_canvas_stroke_texture_inputs(
        bindings,
        [&](LegacyCanvasStrokeTextureInput input, int slot) {
            bound.emplace_back(input, slot);
        });
    pp::panopainter::unbind_legacy_canvas_stroke_texture_inputs(
        bindings,
        [&](LegacyCanvasStrokeTextureInput input, int slot) {
            unbound.emplace_back(input, slot);
        });

    PP_EXPECT(h, bound.size() == bindings.size());
    PP_EXPECT(h, unbound.size() == bindings.size());
    for (std::size_t index = 0; index < bindings.size(); ++index) {
        PP_EXPECT(h, bound[index].first == bindings[index].input);
        PP_EXPECT(h, bound[index].second == bindings[index].slot);
        PP_EXPECT(h, unbound[index] == bound[index]);
    }
}

void retained_stroke_texture_dispatch_activates_units_and_routes_per_input(pp::tests::Harness& h)
{
    const std::array<LegacyCanvasStrokeTextureBinding, 4> bindings {
        LegacyCanvasStrokeTextureBinding { .input = LegacyCanvasStrokeTextureInput::brush_tip, .slot = 2 },
        LegacyCanvasStrokeTextureBinding { .input = LegacyCanvasStrokeTextureInput::stroke_destination, .slot = 0 },
        LegacyCanvasStrokeTextureBinding { .input = LegacyCanvasStrokeTextureInput::pattern, .slot = 4 },
        LegacyCanvasStrokeTextureBinding { .input = LegacyCanvasStrokeTextureInput::mixer, .slot = 1 },
    };

    std::vector<std::string> events;
    const LegacyCanvasStrokeTextureInputDispatch dispatch {
        .activate_texture_unit = [&](int slot) { events.emplace_back("activate:" + std::to_string(slot)); },
        .bind_brush_tip = [&]() { events.emplace_back("bind:brush_tip"); },
        .unbind_brush_tip = [&]() { events.emplace_back("unbind:brush_tip"); },
        .bind_stroke_destination = [&]() { events.emplace_back("bind:stroke_destination"); },
        .unbind_stroke_destination = [&]() { events.emplace_back("unbind:stroke_destination"); },
        .bind_pattern = [&]() { events.emplace_back("bind:pattern"); },
        .unbind_pattern = [&]() { events.emplace_back("unbind:pattern"); },
        .bind_mixer = [&]() { events.emplace_back("bind:mixer"); },
        .unbind_mixer = [&]() { events.emplace_back("unbind:mixer"); },
    };

    pp::panopainter::bind_legacy_canvas_stroke_texture_input(
        LegacyCanvasStrokeTextureInput::brush_tip,
        dispatch);
    pp::panopainter::unbind_legacy_canvas_stroke_texture_input(
        LegacyCanvasStrokeTextureInput::brush_tip,
        dispatch);
    pp::panopainter::bind_legacy_canvas_stroke_texture_inputs(bindings, dispatch);
    pp::panopainter::unbind_legacy_canvas_stroke_texture_inputs(bindings, dispatch);

    const std::vector<std::string> expected_events {
        "bind:brush_tip",
        "unbind:brush_tip",
        "activate:2",
        "bind:brush_tip",
        "activate:0",
        "bind:stroke_destination",
        "activate:4",
        "bind:pattern",
        "activate:1",
        "bind:mixer",
        "activate:2",
        "unbind:brush_tip",
        "activate:0",
        "unbind:stroke_destination",
        "activate:4",
        "unbind:pattern",
        "activate:1",
        "unbind:mixer",
    };
    PP_EXPECT(h, events == expected_events);
}

void retained_stroke_sampler_dispatch_routes_bind_and_unbind_per_input(pp::tests::Harness& h)
{
    const std::array<LegacyCanvasStrokeTextureBinding, 4> bindings {
        LegacyCanvasStrokeTextureBinding { .input = LegacyCanvasStrokeTextureInput::brush_tip, .slot = 3 },
        LegacyCanvasStrokeTextureBinding { .input = LegacyCanvasStrokeTextureInput::stroke_destination, .slot = 1 },
        LegacyCanvasStrokeTextureBinding { .input = LegacyCanvasStrokeTextureInput::pattern, .slot = 7 },
        LegacyCanvasStrokeTextureBinding { .input = LegacyCanvasStrokeTextureInput::mixer, .slot = 5 },
    };

    std::vector<std::string> events;
    const LegacyCanvasStrokeSamplerDispatch dispatch {
        .bind_brush_tip_sampler = [&](int slot) { events.emplace_back("bind:brush_tip:" + std::to_string(slot)); },
        .unbind_brush_tip_sampler = [&]() { events.emplace_back("unbind:brush_tip"); },
        .bind_stroke_destination_sampler =
            [&](int slot) { events.emplace_back("bind:stroke_destination:" + std::to_string(slot)); },
        .unbind_stroke_destination_sampler = [&]() { events.emplace_back("unbind:stroke_destination"); },
        .bind_pattern_sampler = [&](int slot) { events.emplace_back("bind:pattern:" + std::to_string(slot)); },
        .unbind_pattern_sampler = [&]() { events.emplace_back("unbind:pattern"); },
        .bind_mixer_sampler = [&](int slot) { events.emplace_back("bind:mixer:" + std::to_string(slot)); },
        .unbind_mixer_sampler = [&]() { events.emplace_back("unbind:mixer"); },
    };

    pp::panopainter::bind_legacy_canvas_stroke_sampler_input(
        LegacyCanvasStrokeTextureInput::mixer,
        9,
        dispatch);
    pp::panopainter::unbind_legacy_canvas_stroke_sampler_input(
        LegacyCanvasStrokeTextureInput::mixer,
        dispatch);
    pp::panopainter::bind_legacy_canvas_stroke_sampler_inputs(bindings, dispatch);
    pp::panopainter::unbind_legacy_canvas_stroke_sampler_inputs(bindings, dispatch);

    const std::vector<std::string> expected_events {
        "bind:mixer:9",
        "unbind:mixer",
        "bind:brush_tip:3",
        "bind:stroke_destination:1",
        "bind:pattern:7",
        "bind:mixer:5",
        "unbind:brush_tip",
        "unbind:stroke_destination",
        "unbind:pattern",
        "unbind:mixer",
    };
    PP_EXPECT(h, events == expected_events);
}

void retained_stroke_sampler_dispatch_helper_builds_expected_callback_wiring(pp::tests::Harness& h)
{
    std::vector<std::string> events;
    const auto dispatch = pp::panopainter::make_legacy_canvas_stroke_sampler_dispatch(
        [&](int slot) { events.emplace_back("bind:brush_tip:" + std::to_string(slot)); },
        [&] { events.emplace_back("unbind:brush_tip"); },
        [&](int slot) { events.emplace_back("bind:destination:" + std::to_string(slot)); },
        [&] { events.emplace_back("unbind:destination"); },
        [&](int slot) { events.emplace_back("bind:pattern:" + std::to_string(slot)); },
        [&] { events.emplace_back("unbind:pattern"); },
        [&](int slot) { events.emplace_back("bind:mixer:" + std::to_string(slot)); },
        [&] { events.emplace_back("unbind:mixer"); });

    pp::panopainter::bind_legacy_canvas_stroke_sampler_input(
        LegacyCanvasStrokeTextureInput::pattern,
        6,
        dispatch);
    pp::panopainter::unbind_legacy_canvas_stroke_sampler_input(
        LegacyCanvasStrokeTextureInput::pattern,
        dispatch);

    const std::vector<std::string> expected_events {
        "bind:pattern:6",
        "unbind:pattern",
    };
    PP_EXPECT(h, events == expected_events);
}

void retained_stroke_main_pass_texture_dispatch_helper_builds_expected_callback_wiring(pp::tests::Harness& h)
{
    std::vector<std::string> events;
    const auto dispatch = pp::panopainter::make_legacy_canvas_stroke_main_pass_texture_dispatch(
        [&](int slot) { events.emplace_back("activate:" + std::to_string(slot)); },
        [&] { events.emplace_back("bind:brush_tip"); },
        [&] { events.emplace_back("unbind:brush_tip"); },
        [&] { events.emplace_back("bind:pattern"); },
        [&] { events.emplace_back("bind:mixer"); },
        [&] { events.emplace_back("unbind:mixer"); });

    dispatch.activate_texture_unit(2);
    dispatch.bind_pattern();
    dispatch.activate_texture_unit(3);
    dispatch.unbind_mixer();

    const std::vector<std::string> expected_events {
        "activate:2",
        "bind:pattern",
        "activate:3",
        "unbind:mixer",
    };
    PP_EXPECT(h, events == expected_events);
}

void retained_stroke_brush_tip_texture_dispatch_helper_builds_expected_callback_wiring(pp::tests::Harness& h)
{
    const std::array<LegacyCanvasStrokeTextureBinding, 1> bindings {
        LegacyCanvasStrokeTextureBinding { .input = LegacyCanvasStrokeTextureInput::brush_tip, .slot = 0 },
    };

    std::vector<std::string> events;
    const auto dispatch = pp::panopainter::make_legacy_canvas_stroke_brush_tip_texture_dispatch(
        [&](int slot) { events.emplace_back("activate:" + std::to_string(slot)); },
        [&] { events.emplace_back("bind:brush_tip"); },
        [&] { events.emplace_back("unbind:brush_tip"); });

    pp::panopainter::bind_legacy_canvas_stroke_texture_inputs(bindings, dispatch);
    pp::panopainter::unbind_legacy_canvas_stroke_texture_inputs(bindings, dispatch);

    const std::vector<std::string> expected_events {
        "activate:0",
        "bind:brush_tip",
        "activate:0",
        "unbind:brush_tip",
    };
    PP_EXPECT(h, events == expected_events);
}

void retained_stroke_brush_tip_texture_dispatch_helper_builds_expected_face_wiring(pp::tests::Harness& h)
{
    std::vector<std::string> events;
    const auto dispatch = pp::panopainter::make_legacy_canvas_stroke_brush_tip_texture_dispatch(
        [&](int slot) { events.emplace_back("activate:" + std::to_string(slot)); },
        [&](int face_index) { events.emplace_back("bind:" + std::to_string(face_index)); },
        [&](int face_index) { events.emplace_back("unbind:" + std::to_string(face_index)); },
        3);

    dispatch.activate_texture_unit(0);
    dispatch.bind_brush_tip();
    dispatch.unbind_brush_tip();

    const std::vector<std::string> expected_events {
        "activate:0",
        "bind:3",
        "unbind:3",
    };
    PP_EXPECT(h, events == expected_events);
}

void retained_stroke_live_pass_sampler_dispatch_helper_builds_expected_callback_wiring(pp::tests::Harness& h)
{
    std::vector<std::string> events;
    const auto dispatch = pp::panopainter::make_legacy_canvas_stroke_live_pass_sampler_dispatch(
        [&](int slot) { events.emplace_back("bind:brush_tip:" + std::to_string(slot)); },
        [&] { events.emplace_back("unbind:brush_tip"); },
        [&](int slot) { events.emplace_back("bind:destination:" + std::to_string(slot)); },
        [&] { events.emplace_back("unbind:destination"); },
        [&](int slot) { events.emplace_back("bind:pattern:" + std::to_string(slot)); },
        [&] { events.emplace_back("unbind:pattern"); },
        [&](int slot) { events.emplace_back("bind:mixer:" + std::to_string(slot)); },
        [&] { events.emplace_back("unbind:mixer"); });

    pp::panopainter::bind_legacy_canvas_stroke_sampler_input(
        LegacyCanvasStrokeTextureInput::brush_tip,
        4,
        dispatch);
    pp::panopainter::unbind_legacy_canvas_stroke_sampler_input(
        LegacyCanvasStrokeTextureInput::brush_tip,
        dispatch);

    const std::vector<std::string> expected_events {
        "bind:brush_tip:4",
        "unbind:brush_tip",
    };
    PP_EXPECT(h, events == expected_events);
}

void retained_stroke_pad_destination_texture_dispatch_helper_builds_expected_callback_wiring(pp::tests::Harness& h)
{
    const std::array<LegacyCanvasStrokeTextureBinding, 1> bindings {
        LegacyCanvasStrokeTextureBinding { .input = LegacyCanvasStrokeTextureInput::stroke_destination, .slot = 1 },
    };

    std::vector<std::string> events;
    const auto dispatch = pp::panopainter::make_legacy_canvas_stroke_pad_destination_texture_dispatch(
        [&](int slot) { events.emplace_back("activate:" + std::to_string(slot)); },
        [&] { events.emplace_back("bind:destination"); },
        [&] { events.emplace_back("unbind:destination"); });

    pp::panopainter::bind_legacy_canvas_stroke_texture_inputs(bindings, dispatch);
    pp::panopainter::unbind_legacy_canvas_stroke_texture_inputs(bindings, dispatch);

    const std::vector<std::string> expected_events {
        "activate:1",
        "bind:destination",
        "activate:1",
        "unbind:destination",
    };
    PP_EXPECT(h, events == expected_events);
}

void retained_stroke_pad_destination_texture_dispatch_helper_builds_expected_face_wiring(pp::tests::Harness& h)
{
    std::vector<std::string> events;
    const auto dispatch = pp::panopainter::make_legacy_canvas_stroke_pad_destination_texture_dispatch(
        [&](int slot) { events.emplace_back("activate:" + std::to_string(slot)); },
        [&](int face_index) { events.emplace_back("bind:" + std::to_string(face_index)); },
        [&](int face_index) { events.emplace_back("unbind:" + std::to_string(face_index)); },
        4);

    dispatch.activate_texture_unit(1);
    dispatch.bind_stroke_destination();
    dispatch.unbind_stroke_destination();

    const std::vector<std::string> expected_events {
        "activate:1",
        "bind:4",
        "unbind:4",
    };
    PP_EXPECT(h, events == expected_events);
}

void retained_stroke_destination_texture_dispatch_helper_builds_expected_callback_wiring(pp::tests::Harness& h)
{
    std::vector<std::string> events;
    const auto dispatch = pp::panopainter::make_legacy_canvas_stroke_destination_texture_dispatch(
        [&](int slot) { events.emplace_back("activate:" + std::to_string(slot)); },
        [&] { events.emplace_back("bind:destination"); },
        [&] { events.emplace_back("unbind:destination"); });

    pp::panopainter::bind_legacy_canvas_stroke_texture_input(
        LegacyCanvasStrokeTextureInput::stroke_destination,
        dispatch);
    pp::panopainter::unbind_legacy_canvas_stroke_texture_input(
        LegacyCanvasStrokeTextureInput::stroke_destination,
        dispatch);

    const std::vector<std::string> expected_events {
        "bind:destination",
        "unbind:destination",
    };
    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<std::vector<vertex_t>, 6> shapes {};
    };
    struct Framebuffer {
        std::vector<std::string>* 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<Frame, 2> frames {
        make_frame(0, 0),
        make_frame(1, 1),
    };
    std::array<Framebuffer, 2> framebuffers {
        Framebuffer { .events = nullptr, .face_index = 0 },
        Framebuffer { .events = nullptr, .face_index = 1 },
    };

    std::vector<std::string> events;
    framebuffers[0].events = &events;
    framebuffers[1].events = &events;

    std::array<glm::vec4, 2> accumulated_dirty_boxes {
        glm::vec4(0.0F),
        glm::vec4(0.0F),
    };
    std::array<glm::vec4, 2> pass_dirty_boxes {
        glm::vec4(0.0F),
        glm::vec4(0.0F),
    };
    const std::array<bool, 2> include_dirty_faces { true, true };
    std::array<bool, 2> committed_dirty_faces { false, false };
    std::array<bool, 2> pass_dirty_faces { false, false };

    const auto executed_faces = pp::panopainter::execute_legacy_canvas_stroke_live_pass_with_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(
                static_cast<float>(face_index + 1),
                static_cast<float>(face_index + 2),
                static_cast<float>(face_index + 3),
                static_cast<float>(face_index + 4));
        },
        framebuffers,
        true,
        committed_dirty_faces,
        pass_dirty_faces);

    const std::vector<std::string> 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);
}

void retained_stroke_sample_executor_copies_destination_and_expands_quads(pp::tests::Harness& h)
{
    const auto vertices = make_quad_vertices();
    const auto sample_points = make_sample_points();

    std::vector<std::string> events;
    std::vector<vertex_t> uploaded_vertices;
    std::array<int, 6> copy_args {};

    const auto result = pp::panopainter::execute_legacy_canvas_stroke_sample(
        LegacyStrokeSampleExecutionRequest {
            .context = "test",
            .target_size = glm::vec2(64.0F, 64.0F),
            .vertices = vertices,
            .sample_points = sample_points,
            .copy_stroke_destination = true,
            .bind_destination_texture = [&]() { events.emplace_back("bind"); },
            .copy_framebuffer_to_destination_texture =
                [&](int dst_x, int dst_y, int src_x, int src_y, int width, int height) {
                    events.emplace_back("copy");
                    copy_args = { dst_x, dst_y, src_x, src_y, width, height };
                },
            .unbind_destination_texture = [&]() { events.emplace_back("unbind"); },
            .upload_brush_vertices = [&](std::span<const vertex_t> uploaded) {
                events.emplace_back("upload");
                uploaded_vertices.assign(uploaded.begin(), uploaded.end());
            },
            .draw_brush_shape = [&]() { events.emplace_back("draw"); },
        });

    PP_EXPECT(h, result.ok);
    PP_EXPECT(h, result.copy_position == glm::ivec2(9, 19));
    PP_EXPECT(h, result.copy_size == glm::ivec2(22, 12));
    const std::vector<std::string> expected_events { "bind", "copy", "upload", "draw", "unbind" };
    PP_EXPECT(h, nearly_equal(result.dirty_bounds.x, 9.0F));
    PP_EXPECT(h, nearly_equal(result.dirty_bounds.y, 19.0F));
    PP_EXPECT(h, nearly_equal(result.dirty_bounds.z, 31.0F));
    PP_EXPECT(h, nearly_equal(result.dirty_bounds.w, 31.0F));
    PP_EXPECT(h, events == expected_events);
    PP_EXPECT(h, copy_args[0] == 9);
    PP_EXPECT(h, copy_args[1] == 19);
    PP_EXPECT(h, copy_args[2] == 9);
    PP_EXPECT(h, copy_args[3] == 19);
    PP_EXPECT(h, copy_args[4] == 22);
    PP_EXPECT(h, copy_args[5] == 12);
    PP_EXPECT(h, uploaded_vertices.size() == 6U);
    PP_EXPECT(h, nearly_equal(uploaded_vertices[0].pos.x, vertices[0].pos.x));
    PP_EXPECT(h, nearly_equal(uploaded_vertices[1].pos.y, vertices[1].pos.y));
    PP_EXPECT(h, nearly_equal(uploaded_vertices[2].pos.x, vertices[2].pos.x));
    PP_EXPECT(h, nearly_equal(uploaded_vertices[3].pos.x, vertices[0].pos.x));
    PP_EXPECT(h, nearly_equal(uploaded_vertices[4].pos.x, vertices[2].pos.x));
    PP_EXPECT(h, nearly_equal(uploaded_vertices[5].pos.y, vertices[3].pos.y));
}

void retained_stroke_sample_executor_unbinds_and_skips_draw_when_bounds_are_empty(pp::tests::Harness& h)
{
    const auto vertices = make_quad_vertices();

    int bind_calls = 0;
    int copy_calls = 0;
    int unbind_calls = 0;
    int upload_calls = 0;
    int draw_calls = 0;

    const auto result = pp::panopainter::execute_legacy_canvas_stroke_sample(
        LegacyStrokeSampleExecutionRequest {
            .context = "test",
            .target_size = glm::vec2(64.0F, 64.0F),
            .vertices = vertices,
            .sample_points = {},
            .copy_stroke_destination = true,
            .bind_destination_texture = [&]() { ++bind_calls; },
            .copy_framebuffer_to_destination_texture =
                [&](int, int, int, int, int, int) { ++copy_calls; },
            .unbind_destination_texture = [&]() { ++unbind_calls; },
            .upload_brush_vertices = [&](std::span<const vertex_t>) { ++upload_calls; },
            .draw_brush_shape = [&]() { ++draw_calls; },
        });

    PP_EXPECT(h, !result.ok);
    PP_EXPECT(h, bind_calls == 1);
    PP_EXPECT(h, copy_calls == 0);
    PP_EXPECT(h, unbind_calls == 1);
    PP_EXPECT(h, upload_calls == 0);
    PP_EXPECT(h, draw_calls == 0);
}

void retained_stroke_face_sample_polygon_triangulates_and_forwards_face_callbacks(pp::tests::Harness& h)
{
    const auto polygon_vertices = make_polygon_vertices({
        glm::vec2(10.0F, 10.0F),
        glm::vec2(30.0F, 10.0F),
        glm::vec2(35.0F, 20.0F),
        glm::vec2(20.0F, 35.0F),
        glm::vec2(5.0F, 20.0F),
    });

    std::vector<std::string> events;
    std::vector<vertex_t> uploaded_vertices;
    std::array<int, 7> copy_args {};
    int upload_face_index = -1;
    int draw_face_index = -1;

    const auto result = pp::panopainter::execute_legacy_canvas_stroke_face_sample_polygon(
        pp::panopainter::LegacyStrokeFaceSamplePolygonExecutionRequest {
            .context = "test",
            .target_size = glm::vec2(64.0F, 64.0F),
            .polygon_vertices = polygon_vertices,
            .face_index = 4,
            .copy_stroke_destination = true,
            .bind_destination_texture = [&](int face_index) {
                events.emplace_back("bind:" + std::to_string(face_index));
            },
            .copy_framebuffer_to_destination_texture =
                [&](int face_index, int src_x, int src_y, int dst_x, int dst_y, int width, int height) {
                    events.emplace_back("copy:" + std::to_string(face_index));
                    copy_args = { face_index, src_x, src_y, dst_x, dst_y, width, height };
                },
            .unbind_destination_texture = [&](int face_index) {
                events.emplace_back("unbind:" + std::to_string(face_index));
            },
            .upload_brush_vertices = [&](int face_index, std::span<const vertex_t> vertices) {
                events.emplace_back("upload:" + std::to_string(face_index) + ":" + std::to_string(vertices.size()));
                upload_face_index = face_index;
                uploaded_vertices.assign(vertices.begin(), vertices.end());
            },
            .draw_brush_shape = [&](int face_index) {
                events.emplace_back("draw:" + std::to_string(face_index));
                draw_face_index = face_index;
            },
        });

    PP_EXPECT(h, result.ok);
    PP_EXPECT(h, result.copy_position == glm::ivec2(4, 9));
    PP_EXPECT(h, result.copy_size == glm::ivec2(32, 27));
    PP_EXPECT(h, nearly_equal(result.dirty_bounds.x, 4.0F));
    PP_EXPECT(h, nearly_equal(result.dirty_bounds.y, 9.0F));
    PP_EXPECT(h, nearly_equal(result.dirty_bounds.z, 36.0F));
    PP_EXPECT(h, nearly_equal(result.dirty_bounds.w, 36.0F));
    PP_EXPECT(h, upload_face_index == 4);
    PP_EXPECT(h, draw_face_index == 4);
    const std::vector<std::string> expected_events {
        "bind:4",
        "copy:4",
        "upload:4:9",
        "draw:4",
        "unbind:4",
    };
    PP_EXPECT(h, events == expected_events);
    PP_EXPECT(h, copy_args[0] == 4);
    PP_EXPECT(h, copy_args[1] == 4);
    PP_EXPECT(h, copy_args[2] == 9);
    PP_EXPECT(h, copy_args[3] == 4);
    PP_EXPECT(h, copy_args[4] == 9);
    PP_EXPECT(h, copy_args[5] == 32);
    PP_EXPECT(h, copy_args[6] == 27);
    PP_EXPECT(h, uploaded_vertices.size() == 9U);

    const auto expected_triangulated_vertices = make_polygon_vertices({
        glm::vec2(5.0F, 20.0F),
        glm::vec2(10.0F, 10.0F),
        glm::vec2(20.0F, 35.0F),
        glm::vec2(20.0F, 35.0F),
        glm::vec2(10.0F, 10.0F),
        glm::vec2(30.0F, 10.0F),
        glm::vec2(20.0F, 35.0F),
        glm::vec2(30.0F, 10.0F),
        glm::vec2(35.0F, 20.0F),
    });
    PP_EXPECT(h, uploaded_vertices.size() == expected_triangulated_vertices.size());
    for (std::size_t index = 0; index < uploaded_vertices.size(); ++index) {
        PP_EXPECT(h, nearly_equal(uploaded_vertices[index].pos.x, expected_triangulated_vertices[index].pos.x));
        PP_EXPECT(h, nearly_equal(uploaded_vertices[index].pos.y, expected_triangulated_vertices[index].pos.y));
    }
}

void retained_stroke_face_sample_polygon_skips_destination_callbacks_when_copy_is_disabled(pp::tests::Harness& h)
{
    const auto polygon_vertices = make_polygon_vertices({
        glm::vec2(8.0F, 9.0F),
        glm::vec2(18.0F, 9.0F),
        glm::vec2(13.0F, 21.0F),
    });

    int bind_face_index = -1;
    int copy_face_index = -1;
    int unbind_face_index = -1;
    int upload_face_index = -1;
    int draw_face_index = -1;
    int bind_calls = 0;
    int copy_calls = 0;
    int unbind_calls = 0;
    std::size_t uploaded_vertex_count = 0;

    const auto result = pp::panopainter::execute_legacy_canvas_stroke_face_sample_polygon(
        pp::panopainter::LegacyStrokeFaceSamplePolygonExecutionRequest {
            .context = "test",
            .target_size = glm::vec2(64.0F, 64.0F),
            .polygon_vertices = polygon_vertices,
            .face_index = 3,
            .copy_stroke_destination = false,
            .bind_destination_texture = [&](int face_index) {
                ++bind_calls;
                bind_face_index = face_index;
            },
            .copy_framebuffer_to_destination_texture =
                [&](int face_index, int, int, int, int, int, int) {
                    ++copy_calls;
                    copy_face_index = face_index;
                },
            .unbind_destination_texture = [&](int face_index) {
                ++unbind_calls;
                unbind_face_index = face_index;
            },
            .upload_brush_vertices = [&](int face_index, std::span<const vertex_t> vertices) {
                upload_face_index = face_index;
                uploaded_vertex_count = vertices.size();
            },
            .draw_brush_shape = [&](int face_index) {
                draw_face_index = face_index;
            },
    });

    PP_EXPECT(h, result.ok);
    PP_EXPECT(h, bind_calls == 0);
    PP_EXPECT(h, copy_calls == 0);
    PP_EXPECT(h, unbind_calls == 0);
    PP_EXPECT(h, upload_face_index == 3);
    PP_EXPECT(h, draw_face_index == 3);
    PP_EXPECT(h, uploaded_vertex_count == 3U);
    PP_EXPECT(h, bind_face_index == -1);
    PP_EXPECT(h, copy_face_index == -1);
    PP_EXPECT(h, unbind_face_index == -1);
}

struct StrokeFrame {
    int id = -1;
    std::array<std::vector<vertex_t>, 6> shapes {};
};

struct DummyFramebuffer {
    std::vector<std::string>* 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));
    }
};

std::vector<vertex_t> make_face_triangle(float origin_x, float origin_y)
{
    return {
        vertex_t(glm::vec2(origin_x + 0.0F, origin_y + 0.0F)),
        vertex_t(glm::vec2(origin_x + 1.0F, origin_y + 0.0F)),
        vertex_t(glm::vec2(origin_x + 0.5F, origin_y + 1.0F)),
    };
}

void retained_stroke_frame_samples_preserve_frame_face_callback_order(pp::tests::Harness& h)
{
    std::array<StrokeFrame, 2> frames {};
    frames[0].id = 11;
    frames[0].shapes[1] = make_face_triangle(1.0F, 10.0F);
    frames[0].shapes[4] = make_face_triangle(4.0F, 40.0F);
    frames[1].id = 12;
    frames[1].shapes[0] = make_face_triangle(2.0F, 20.0F);

    std::vector<std::string> events;
    std::vector<glm::vec4> finish_boxes;

    const auto executed_faces = pp::panopainter::execute_legacy_canvas_stroke_frame_samples(
        frames,
        [&](StrokeFrame& frame) {
            events.emplace_back("begin-frame:" + std::to_string(frame.id));
        },
        [&](StrokeFrame& frame, int face_index, std::span<const vertex_t> vertices) {
            events.emplace_back(
                "begin-face:" + std::to_string(frame.id) + ":" + std::to_string(face_index) + ":" +
                std::to_string(vertices.size()));
        },
        [&](StrokeFrame& frame, int face_index, std::span<const vertex_t>) {
            events.emplace_back("execute:" + std::to_string(frame.id) + ":" + std::to_string(face_index));
            return glm::vec4(
                static_cast<float>(frame.id),
                static_cast<float>(face_index),
                static_cast<float>(frame.id + face_index),
                static_cast<float>(frame.id + face_index + 1));
        },
        [&](StrokeFrame& frame, int face_index, glm::vec4 sample_dirty_box) {
            events.emplace_back("finish:" + std::to_string(frame.id) + ":" + std::to_string(face_index));
            finish_boxes.push_back(sample_dirty_box);
        });

    PP_EXPECT(h, executed_faces == 3U);
    const std::vector<std::string> expected_events {
        "begin-frame:11",
        "begin-face:11:1:3",
        "execute:11:1",
        "finish:11:1",
        "begin-face:11:4:3",
        "execute:11:4",
        "finish:11:4",
        "begin-frame:12",
        "begin-face:12:0:3",
        "execute:12:0",
        "finish:12:0",
    };
    PP_EXPECT(h, events == expected_events);
    PP_EXPECT(h, finish_boxes.size() == 3U);
    PP_EXPECT(h, nearly_equal(finish_boxes[0].x, 11.0F));
    PP_EXPECT(h, nearly_equal(finish_boxes[0].y, 1.0F));
    PP_EXPECT(h, nearly_equal(finish_boxes[1].x, 11.0F));
    PP_EXPECT(h, nearly_equal(finish_boxes[1].y, 4.0F));
    PP_EXPECT(h, nearly_equal(finish_boxes[2].x, 12.0F));
    PP_EXPECT(h, nearly_equal(finish_boxes[2].y, 0.0F));
}

void retained_stroke_frame_samples_with_dirty_tracking_updates_after_finish(pp::tests::Harness& h)
{
    std::array<StrokeFrame, 2> frames {};
    frames[0].id = 21;
    frames[0].shapes[1] = make_face_triangle(2.0F, 6.0F);
    frames[1].id = 22;
    frames[1].shapes[4] = make_face_triangle(8.0F, 12.0F);

    std::array<glm::vec4, 6> accumulated_dirty_boxes;
    std::array<glm::vec4, 6> 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));
    accumulated_dirty_boxes[1] = glm::vec4(10.0F, 10.0F, 14.0F, 14.0F);
    pass_dirty_boxes[1] = glm::vec4(20.0F, 20.0F, 21.0F, 21.0F);
    accumulated_dirty_boxes[4] = glm::vec4(40.0F, 40.0F, 44.0F, 44.0F);
    pass_dirty_boxes[4] = glm::vec4(50.0F, 50.0F, 51.0F, 51.0F);
    std::array<bool, 6> include_in_committed_dirty_box { true, false, true, true, true, true };
    std::array<bool, 6> committed_dirty_faces {};
    std::array<bool, 6> pass_dirty_faces {};

    std::vector<std::string> events;
    std::vector<glm::vec4> finish_seen_accumulated;
    std::vector<glm::vec4> finish_seen_pass;

    const auto executed_faces = pp::panopainter::execute_legacy_canvas_stroke_frame_samples_with_dirty_tracking(
        frames,
        pp::renderer::Extent2D { .width = 64, .height = 64 },
        accumulated_dirty_boxes,
        pass_dirty_boxes,
        include_in_committed_dirty_box,
        [&](StrokeFrame& frame) {
            events.emplace_back("begin-frame:" + std::to_string(frame.id));
        },
        [&](StrokeFrame& frame, int face_index, std::span<const vertex_t>) {
            events.emplace_back("begin-face:" + std::to_string(frame.id) + ":" + std::to_string(face_index));
        },
        [&](StrokeFrame& frame, int face_index, std::span<const vertex_t>) {
            events.emplace_back("execute:" + std::to_string(frame.id) + ":" + std::to_string(face_index));
            if (face_index == 1) {
                return glm::vec4(1.0F, 2.0F, 5.0F, 6.0F);
            }
            return glm::vec4(7.0F, 8.0F, 9.0F, 10.0F);
        },
        [&](StrokeFrame& frame, int face_index, std::span<const vertex_t>, auto& request) {
            events.emplace_back("prepare:" + std::to_string(frame.id) + ":" + std::to_string(face_index));
            if (face_index == 4) {
                request.previous_pass_dirty_box = request.sample_dirty_box;
            }
        },
        [&](StrokeFrame& frame, int face_index, std::span<const vertex_t>, glm::vec4 sample_dirty_box) {
            events.emplace_back("finish:" + std::to_string(frame.id) + ":" + std::to_string(face_index));
            finish_seen_accumulated.push_back(accumulated_dirty_boxes[face_index]);
            finish_seen_pass.push_back(pass_dirty_boxes[face_index]);
            if (face_index == 1) {
                PP_EXPECT(h, nearly_equal(sample_dirty_box.x, 1.0F));
                PP_EXPECT(h, nearly_equal(sample_dirty_box.w, 6.0F));
            } else {
                PP_EXPECT(h, nearly_equal(sample_dirty_box.x, 7.0F));
                PP_EXPECT(h, nearly_equal(sample_dirty_box.w, 10.0F));
            }
        },
        committed_dirty_faces,
        pass_dirty_faces);

    PP_EXPECT(h, executed_faces == 2U);
    const std::vector<std::string> expected_events {
        "begin-frame:21",
        "begin-face:21:1",
        "execute:21:1",
        "prepare:21:1",
        "finish:21:1",
        "begin-frame:22",
        "begin-face:22:4",
        "execute:22:4",
        "prepare:22:4",
        "finish:22:4",
    };
    PP_EXPECT(h, events == expected_events);
    PP_EXPECT(h, finish_seen_accumulated.size() == 2U);
    PP_EXPECT(h, nearly_equal(finish_seen_accumulated[0].x, 10.0F));
    PP_EXPECT(h, nearly_equal(finish_seen_accumulated[0].w, 14.0F));
    PP_EXPECT(h, nearly_equal(finish_seen_pass[0].x, 20.0F));
    PP_EXPECT(h, nearly_equal(finish_seen_pass[0].w, 21.0F));
    PP_EXPECT(h, nearly_equal(finish_seen_accumulated[1].x, 40.0F));
    PP_EXPECT(h, nearly_equal(finish_seen_accumulated[1].w, 44.0F));
    PP_EXPECT(h, nearly_equal(finish_seen_pass[1].x, 50.0F));
    PP_EXPECT(h, nearly_equal(finish_seen_pass[1].w, 51.0F));
    PP_EXPECT(h, nearly_equal(accumulated_dirty_boxes[1].x, 10.0F));
    PP_EXPECT(h, nearly_equal(accumulated_dirty_boxes[1].y, 10.0F));
    PP_EXPECT(h, nearly_equal(accumulated_dirty_boxes[1].z, 14.0F));
    PP_EXPECT(h, nearly_equal(accumulated_dirty_boxes[1].w, 14.0F));
    PP_EXPECT(h, !committed_dirty_faces[1]);
    PP_EXPECT(h, pass_dirty_faces[1]);
    PP_EXPECT(h, nearly_equal(pass_dirty_boxes[1].x, 1.0F));
    PP_EXPECT(h, nearly_equal(pass_dirty_boxes[1].y, 2.0F));
    PP_EXPECT(h, nearly_equal(pass_dirty_boxes[1].z, 21.0F));
    PP_EXPECT(h, nearly_equal(pass_dirty_boxes[1].w, 21.0F));
    PP_EXPECT(h, nearly_equal(accumulated_dirty_boxes[4].x, 7.0F));
    PP_EXPECT(h, nearly_equal(accumulated_dirty_boxes[4].y, 8.0F));
    PP_EXPECT(h, nearly_equal(accumulated_dirty_boxes[4].z, 44.0F));
    PP_EXPECT(h, nearly_equal(accumulated_dirty_boxes[4].w, 44.0F));
    PP_EXPECT(h, committed_dirty_faces[4]);
    PP_EXPECT(h, pass_dirty_faces[4]);
    PP_EXPECT(h, nearly_equal(pass_dirty_boxes[4].x, 7.0F));
    PP_EXPECT(h, nearly_equal(pass_dirty_boxes[4].y, 8.0F));
    PP_EXPECT(h, nearly_equal(pass_dirty_boxes[4].z, 9.0F));
    PP_EXPECT(h, nearly_equal(pass_dirty_boxes[4].w, 10.0F));
}

void retained_stroke_frame_planner_uses_previous_sample_and_projection_mode(pp::tests::Harness& h)
{
    struct FramePlan {
        glm::vec4 col;
        float flow;
        float opacity;
        std::array<vertex_t, 4> shapes;
        glm::vec4 m_mixer_rect;
    };

    const auto frames = pp::panopainter::plan_legacy_canvas_stroke_frames(
        pp::panopainter::LegacyCanvasStrokeComputeRequest {
            .previous_sample = StrokeSample {
                .col = { 0.25F, 0.5F, 0.75F },
                .pos = { 10.0F, 20.0F, 0.0F },
                .origin = { 0.0F, 0.0F, 0.0F },
                .scale = { 1.0F, 1.0F },
                .size = 4.0F,
                .flow = 0.5F,
                .opacity = 0.75F,
                .angle = 0.0F,
            },
            .samples = std::array<StrokeSample, 2> {
                StrokeSample {
                    .col = { 1.0F, 0.0F, 0.0F },
                    .pos = { 14.0F, 26.0F, 0.0F },
                    .origin = { 0.0F, 0.0F, 0.0F },
                    .scale = { 1.0F, 1.0F },
                    .size = 2.0F,
                    .flow = 0.6F,
                    .opacity = 0.7F,
                    .angle = 0.0F,
                },
                StrokeSample {
                    .col = { 0.0F, 1.0F, 0.0F },
                    .pos = { 18.0F, 30.0F, 2.0F },
                    .origin = { 0.0F, 0.0F, 0.0F },
                    .scale = { 1.0F, 1.0F },
                    .size = 6.0F,
                    .flow = 0.8F,
                    .opacity = 0.9F,
                    .angle = 0.0F,
                },
            },
            .zoom = 1.0F,
            .mixer_size = glm::vec2(64.0F, 64.0F),
            .model_view = glm::mat4(1.0F),
        },
        [&](std::array<vertex_t, 4>& brush_quad, bool project_3d, glm::mat4 model_view) {
            PP_EXPECT(h, !glm::any(glm::isnan(model_view[0])));
            for (const auto& vertex : brush_quad) {
                PP_EXPECT(h, !glm::any(glm::isnan(vertex.pos)));
            }
            return std::array<vertex_t, 4> { brush_quad };
        },
        [](glm::vec4 mixer_rect, glm::vec4 color, float flow, float opacity, auto&& shapes) {
            return FramePlan {
                .col = color,
                .flow = flow,
                .opacity = opacity,
                .shapes = std::move(shapes),
                .m_mixer_rect = mixer_rect,
            };
        });

    PP_EXPECT(h, frames.size() == 2U);
    PP_EXPECT(h, nearly_equal(frames[0].col.r, 1.0F));
    PP_EXPECT(h, nearly_equal(frames[0].flow, 0.6F));
    PP_EXPECT(h, nearly_equal(frames[0].opacity, 0.7F));
    PP_EXPECT(h, nearly_equal(frames[1].col.g, 1.0F));
    PP_EXPECT(h, nearly_equal(frames[1].flow, 0.8F));
    PP_EXPECT(h, nearly_equal(frames[1].opacity, 0.9F));
    PP_EXPECT(h, frames[0].m_mixer_rect.z > 0.0F);
    PP_EXPECT(h, frames[0].m_mixer_rect.w > 0.0F);
    PP_EXPECT(h, frames[1].m_mixer_rect.z > 0.0F);
    PP_EXPECT(h, frames[1].m_mixer_rect.w > 0.0F);
}

void retained_stroke_frame_planner_scales_mixer_bounds_with_zoom(pp::tests::Harness& h)
{
    struct FramePlan {
        glm::vec4 col;
        float flow;
        float opacity;
        std::array<vertex_t, 4> shapes;
        glm::vec4 m_mixer_rect;
    };

    const auto frames = pp::panopainter::plan_legacy_canvas_stroke_frames(
        pp::panopainter::LegacyCanvasStrokeComputeRequest {
            .previous_sample = StrokeSample {
                .col = { 0.0F, 0.0F, 1.0F },
                .pos = { 5.0F, 6.0F, 0.0F },
                .origin = { 0.0F, 0.0F, 0.0F },
                .scale = { 1.0F, 1.0F },
                .size = 3.0F,
                .flow = 1.0F,
                .opacity = 1.0F,
                .angle = 0.0F,
            },
            .samples = std::array<StrokeSample, 1> {
                StrokeSample {
                    .col = { 1.0F, 1.0F, 0.0F },
                    .pos = { 9.0F, 10.0F, 0.0F },
                    .origin = { 0.0F, 0.0F, 0.0F },
                    .scale = { 1.0F, 1.0F },
                    .size = 4.0F,
                    .flow = 0.25F,
                    .opacity = 0.5F,
                    .angle = 0.0F,
                },
            },
            .zoom = 2.0F,
            .mixer_size = glm::vec2(64.0F, 64.0F),
            .model_view = glm::mat4(1.0F),
        },
        [&](std::array<vertex_t, 4>& brush_quad, bool, glm::mat4) {
            return std::array<vertex_t, 4> { brush_quad };
        },
        [](glm::vec4 mixer_rect, glm::vec4 color, float flow, float opacity, auto&& shapes) {
            return FramePlan {
                .col = color,
                .flow = flow,
                .opacity = opacity,
                .shapes = std::move(shapes),
                .m_mixer_rect = mixer_rect,
            };
        });

    PP_EXPECT(h, frames.size() == 1U);
    PP_EXPECT(h, nearly_equal(frames[0].col.b, 0.0F));
    PP_EXPECT(h, nearly_equal(frames[0].flow, 0.25F));
    PP_EXPECT(h, nearly_equal(frames[0].opacity, 0.5F));
    PP_EXPECT(h, frames[0].m_mixer_rect.z > 0.0F);
    PP_EXPECT(h, frames[0].m_mixer_rect.w > 0.0F);
}

void retained_stroke_live_pass_with_face_framebuffers_preserves_order_and_dirty_tracking(pp::tests::Harness& h)
{
    StrokeFrame frame;
    frame.id = 7;
    frame.shapes[0] = {
        vertex_t(glm::vec2(0.0F, 0.0F)),
        vertex_t(glm::vec2(1.0F, 0.0F)),
        vertex_t(glm::vec2(1.0F, 1.0F)),
    };
    frame.shapes[2] = {
        vertex_t(glm::vec2(2.0F, 2.0F)),
        vertex_t(glm::vec2(3.0F, 2.0F)),
        vertex_t(glm::vec2(3.0F, 3.0F)),
    };

    std::array<StrokeFrame, 1> frames { frame };
    std::array<glm::vec4, 6> accumulated_dirty_boxes;
    std::array<glm::vec4, 6> 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));
    accumulated_dirty_boxes[2] = glm::vec4(7.0F, 8.0F, 9.0F, 10.0F);
    pass_dirty_boxes[2] = glm::vec4(20.0F, 21.0F, 22.0F, 23.0F);
    std::array<bool, 6> include_in_committed_dirty_box { true, true, false, true, true, true };
    std::array<bool, 6> committed_dirty_faces {};
    std::array<bool, 6> pass_dirty_faces {};

    std::vector<std::string> events;
    std::array<DummyFramebuffer, 6> 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_live_pass_with_face_framebuffers(
        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<const vertex_t>) {
            events.push_back("prepare:" + std::to_string(face_index));
        },
        [&](StrokeFrame&, int face_index, std::span<const vertex_t>) {
            events.push_back("execute:" + std::to_string(face_index));
            if (face_index == 0) {
                return glm::vec4(1.0F, 2.0F, 3.0F, 4.0F);
            }
            return glm::vec4(10.0F, 11.0F, 12.0F, 13.0F);
        },
        face_framebuffers,
        true,
        committed_dirty_faces,
        pass_dirty_faces);

    PP_EXPECT(h, executed_faces == 2U);
    const std::vector<std::string> expected_events {
        "begin-frame:7",
        "prepare:0",
        "bind:0",
        "execute:0",
        "unbind:0",
        "prepare:2",
        "bind:2",
        "execute:2",
        "unbind:2",
    };
    PP_EXPECT(h, events == expected_events);
    PP_EXPECT(h, accumulated_dirty_boxes[0].x <= 1.0F);
    PP_EXPECT(h, accumulated_dirty_boxes[0].y <= 2.0F);
    PP_EXPECT(h, nearly_equal(accumulated_dirty_boxes[0].z, 3.0F));
    PP_EXPECT(h, nearly_equal(accumulated_dirty_boxes[0].w, 4.0F));
    PP_EXPECT(h, nearly_equal(pass_dirty_boxes[0].x, 1.0F));
    PP_EXPECT(h, nearly_equal(pass_dirty_boxes[0].w, 4.0F));
    PP_EXPECT(h, committed_dirty_faces[0]);
    PP_EXPECT(h, pass_dirty_faces[0]);
    PP_EXPECT(h, nearly_equal(accumulated_dirty_boxes[2].x, 7.0F));
    PP_EXPECT(h, nearly_equal(accumulated_dirty_boxes[2].y, 8.0F));
    PP_EXPECT(h, nearly_equal(accumulated_dirty_boxes[2].z, 9.0F));
    PP_EXPECT(h, nearly_equal(accumulated_dirty_boxes[2].w, 10.0F));
    PP_EXPECT(h, nearly_equal(pass_dirty_boxes[2].x, 10.0F));
    PP_EXPECT(h, nearly_equal(pass_dirty_boxes[2].y, 11.0F));
    PP_EXPECT(h, nearly_equal(pass_dirty_boxes[2].z, 12.0F));
    PP_EXPECT(h, nearly_equal(pass_dirty_boxes[2].w, 13.0F));
    PP_EXPECT(h, !committed_dirty_faces[2]);
    PP_EXPECT(h, pass_dirty_faces[2]);
}

void retained_stroke_live_pass_clears_before_traversal_and_copies_afterwards(pp::tests::Harness& h)
{
    StrokeFrame frame;
    frame.id = 9;
    frame.shapes[0] = {
        vertex_t(glm::vec2(0.0F, 0.0F)),
        vertex_t(glm::vec2(1.0F, 0.0F)),
        vertex_t(glm::vec2(1.0F, 1.0F)),
    };
    frame.shapes[2] = {
        vertex_t(glm::vec2(2.0F, 2.0F)),
        vertex_t(glm::vec2(3.0F, 2.0F)),
        vertex_t(glm::vec2(3.0F, 3.0F)),
    };

    std::array<StrokeFrame, 1> frames { frame };
    std::array<glm::vec4, 6> accumulated_dirty_boxes;
    std::array<glm::vec4, 6> 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<bool, 6> include_in_committed_dirty_box { true, true, true, true, true, true };
    std::array<bool, 6> committed_dirty_faces {};
    std::array<bool, 6> pass_dirty_faces {};

    std::vector<std::string> events;
    std::array<DummyFramebuffer, 6> 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;
    }

    std::size_t executed_faces = 0U;
    bool copy_ok = false;
    pp::panopainter::execute_legacy_stroke_preview_live_pass(
        [&] { events.push_back("clear"); },
        [&]() {
            return std::vector<int> { 0 };
        },
        [&](int&) {},
        [&](int&) {},
        [&](int&) {
            executed_faces = pp::panopainter::execute_legacy_canvas_stroke_live_pass_with_face_framebuffers(
                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<const vertex_t> vertices) {
                    events.push_back(
                        "prepare:" + std::to_string(face_index) + ":" + std::to_string(vertices.size()));
                },
                [&](StrokeFrame&, int face_index, std::span<const vertex_t>) {
                    events.push_back("execute:" + std::to_string(face_index));
                    return glm::vec4(
                        static_cast<float>(face_index + 1),
                        static_cast<float>(face_index + 2),
                        static_cast<float>(face_index + 3),
                        static_cast<float>(face_index + 4));
                },
                face_framebuffers,
                true,
                committed_dirty_faces,
                pass_dirty_faces);
        },
        [&]() {
            const auto copy_status = pp::paint_renderer::copy_stroke_preview_result_to_texture(
                [&]() { events.emplace_back("bind-preview"); },
                [&](int src_x, int src_y, int dst_x, int dst_y, int width, int height) {
                    events.emplace_back("copy-preview");
                    PP_EXPECT(h, src_x == 0);
                    PP_EXPECT(h, src_y == 0);
                    PP_EXPECT(h, dst_x == 0);
                    PP_EXPECT(h, dst_y == 0);
                    PP_EXPECT(h, width == 64);
                    PP_EXPECT(h, height == 64);
                },
                pp::paint_renderer::StrokePreviewCopySize { .width = 64, .height = 64 });
            PP_EXPECT(h, copy_status.ok());
            copy_ok = copy_status.ok();
        });

    PP_EXPECT(h, executed_faces == 2U);
    PP_EXPECT(h, copy_ok);
    const std::vector<std::string> expected_events {
        "clear",
        "begin-frame:9",
        "prepare:0:3",
        "bind:0",
        "execute:0",
        "unbind:0",
        "prepare:2:3",
        "bind:2",
        "execute:2",
        "unbind:2",
        "bind-preview",
        "copy-preview",
    };
    PP_EXPECT(h, events == expected_events);
    PP_EXPECT(h, committed_dirty_faces[0]);
    PP_EXPECT(h, pass_dirty_faces[0]);
    PP_EXPECT(h, committed_dirty_faces[2]);
    PP_EXPECT(h, pass_dirty_faces[2]);
}

void retained_stroke_live_pass_retains_previous_pass_dirty_union_without_override(pp::tests::Harness& h)
{
    StrokeFrame frame;
    frame.id = 11;
    frame.shapes[1] = {
        vertex_t(glm::vec2(4.0F, 5.0F)),
        vertex_t(glm::vec2(6.0F, 5.0F)),
        vertex_t(glm::vec2(6.0F, 7.0F)),
    };

    std::array<StrokeFrame, 1> frames { frame };
    std::array<glm::vec4, 6> accumulated_dirty_boxes;
    std::array<glm::vec4, 6> 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));
    accumulated_dirty_boxes[1] = glm::vec4(7.0F, 8.0F, 9.0F, 10.0F);
    pass_dirty_boxes[1] = glm::vec4(20.0F, 20.0F, 21.0F, 21.0F);
    std::array<bool, 6> include_in_committed_dirty_box { true, true, true, true, true, true };
    std::array<bool, 6> committed_dirty_faces {};
    std::array<bool, 6> pass_dirty_faces {};

    std::vector<std::string> events;
    std::array<DummyFramebuffer, 6> 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_live_pass_with_face_framebuffers(
        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<const vertex_t> vertices) {
            events.push_back(
                "prepare:" + std::to_string(face_index) + ":" + std::to_string(vertices.size()));
        },
        [&](StrokeFrame&, int face_index, std::span<const vertex_t>) {
            events.push_back("execute:" + std::to_string(face_index));
            return glm::vec4(1.0F, 2.0F, 3.0F, 4.0F);
        },
        face_framebuffers,
        false,
        committed_dirty_faces,
        pass_dirty_faces);

    PP_EXPECT(h, executed_faces == 1U);
    const std::vector<std::string> expected_events {
        "begin-frame:11",
        "prepare:1:3",
        "bind:1",
        "execute:1",
        "unbind:1",
    };
    PP_EXPECT(h, events == expected_events);
    PP_EXPECT(h, nearly_equal(accumulated_dirty_boxes[1].x, 1.0F));
    PP_EXPECT(h, nearly_equal(accumulated_dirty_boxes[1].y, 2.0F));
    PP_EXPECT(h, nearly_equal(accumulated_dirty_boxes[1].z, 9.0F));
    PP_EXPECT(h, nearly_equal(accumulated_dirty_boxes[1].w, 10.0F));
    PP_EXPECT(h, nearly_equal(pass_dirty_boxes[1].x, 1.0F));
    PP_EXPECT(h, nearly_equal(pass_dirty_boxes[1].y, 2.0F));
    PP_EXPECT(h, nearly_equal(pass_dirty_boxes[1].z, 21.0F));
    PP_EXPECT(h, nearly_equal(pass_dirty_boxes[1].w, 21.0F));
    PP_EXPECT(h, committed_dirty_faces[1]);
    PP_EXPECT(h, pass_dirty_faces[1]);
}

void retained_stroke_dual_pass_frame_callbacks_preserve_order(pp::tests::Harness& h)
{
    StrokeFrame frame;
    frame.id = 21;
    frame.shapes[0] = {
        vertex_t(glm::vec2(1.0F, 1.0F)),
        vertex_t(glm::vec2(2.0F, 1.0F)),
        vertex_t(glm::vec2(2.0F, 2.0F)),
    };
    frame.shapes[3] = {
        vertex_t(glm::vec2(3.0F, 3.0F)),
        vertex_t(glm::vec2(4.0F, 3.0F)),
        vertex_t(glm::vec2(4.0F, 4.0F)),
    };

    std::array<StrokeFrame, 1> frames { frame };
    std::array<glm::vec4, 6> accumulated_dirty_boxes;
    std::array<glm::vec4, 6> 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<bool, 6> include_in_committed_dirty_box { true, true, true, true, true, true };

    std::vector<std::string> events;
    std::array<DummyFramebuffer, 6> 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_dual_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<const vertex_t> vertices) {
            events.push_back(
                "prepare:" + std::to_string(face_index) + ":" + std::to_string(vertices.size()));
        },
        [&](StrokeFrame&, int face_index, std::span<const vertex_t>) {
            events.push_back("execute:" + std::to_string(face_index));
            return glm::vec4(
                static_cast<float>(face_index + 10),
                static_cast<float>(face_index + 11),
                static_cast<float>(face_index + 12),
                static_cast<float>(face_index + 13));
        },
        face_framebuffers,
        true);

    const std::vector<std::string> expected_events {
        "begin-frame:21",
        "prepare:0:3",
        "bind:0",
        "execute:0",
        "unbind:0",
        "prepare:3:3",
        "bind:3",
        "execute:3",
        "unbind:3",
    };
    PP_EXPECT(h, executed_faces == 2U);
    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<StrokeFrame, 1> frames { frame };
    std::array<glm::vec4, 6> accumulated_dirty_boxes;
    std::array<glm::vec4, 6> 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<bool, 6> include_in_committed_dirty_box { true, true, true, true, true, true };

    std::vector<std::string> events;
    std::array<DummyFramebuffer, 6> 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<const vertex_t> vertices) {
            events.push_back(
                "prepare:" + std::to_string(face_index) + ":" + std::to_string(vertices.size()));
        },
        [&](StrokeFrame&, int face_index, std::span<const vertex_t>) {
            events.push_back("execute:" + std::to_string(face_index));
            return glm::vec4(
                static_cast<float>(face_index + 1),
                static_cast<float>(face_index + 2),
                static_cast<float>(face_index + 3),
                static_cast<float>(face_index + 4));
        },
        face_framebuffers,
        true);

    const std::vector<std::string> 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_main_pass_execution_preserves_bind_and_unbind_order(pp::tests::Harness& h)
{
    std::vector<std::string> events;

    const auto ok = pp::panopainter::execute_legacy_canvas_stroke_main_pass(
        pp::panopainter::make_legacy_canvas_stroke_main_pass_execution_request(
            "test",
            [&] { events.emplace_back("bind-samplers"); },
            [&] { events.emplace_back("bind-textures"); },
            [&] { events.emplace_back("execute"); },
            [&] { events.emplace_back("unbind-textures"); },
            [&] { events.emplace_back("unbind-samplers"); }));

    const std::vector<std::string> expected_events {
        "bind-samplers",
        "bind-textures",
        "execute",
        "unbind-textures",
        "unbind-samplers",
    };
    PP_EXPECT(h, ok);
    PP_EXPECT(h, events == expected_events);
}

void retained_stroke_main_pass_execution_preserves_destination_binding_order_and_face_execution(pp::tests::Harness& h)
{
    std::vector<std::string> events;

    std::array<StrokeFrame, 1> frames {};
    frames[0].id = 31;
    frames[0].shapes[2] = {
        vertex_t(glm::vec2(2.0F, 3.0F)),
        vertex_t(glm::vec2(4.0F, 3.0F)),
        vertex_t(glm::vec2(3.0F, 5.0F)),
    };

    std::array<glm::vec4, 6> accumulated_dirty_boxes;
    std::array<glm::vec4, 6> 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<bool, 6> include_main_dirty = SIXPLETTE(true);
    std::array<DummyFramebuffer, 6> 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 std::array<pp::panopainter::LegacyCanvasStrokeTextureBinding, 4> main_pass_texture_bindings {
        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,
        },
    };
    const std::array<pp::panopainter::LegacyCanvasStrokeTextureBinding, 3> main_pass_texture_unbindings {
        pp::panopainter::LegacyCanvasStrokeTextureBinding {
            .input = pp::panopainter::LegacyCanvasStrokeTextureInput::mixer,
            .slot = 3,
        },
        pp::panopainter::LegacyCanvasStrokeTextureBinding {
            .input = pp::panopainter::LegacyCanvasStrokeTextureInput::stroke_destination,
            .slot = 1,
        },
        pp::panopainter::LegacyCanvasStrokeTextureBinding {
            .input = pp::panopainter::LegacyCanvasStrokeTextureInput::brush_tip,
            .slot = 0,
        },
    };

    const pp::panopainter::LegacyCanvasStrokeTextureInputDispatch main_pass_texture_dispatch {
        .activate_texture_unit = [&](int slot) {
            events.emplace_back("activate:" + std::to_string(slot));
        },
        .bind_brush_tip = [&]() {
            events.emplace_back("bind-brush");
        },
        .unbind_brush_tip = [&]() {
            events.emplace_back("unbind-brush");
        },
        .bind_stroke_destination = [&]() {
            events.emplace_back("bind-destination");
        },
        .unbind_stroke_destination = [&]() {
            events.emplace_back("unbind-destination");
        },
        .bind_pattern = [&]() {
            events.emplace_back("bind-pattern");
        },
        .unbind_pattern = [&]() {
            events.emplace_back("unbind-pattern");
        },
        .bind_mixer = [&]() {
            events.emplace_back("bind-mixer");
        },
        .unbind_mixer = [&]() {
            events.emplace_back("unbind-mixer");
        },
    };

    const auto ok = pp::panopainter::execute_legacy_canvas_stroke_main_pass(
        pp::panopainter::make_legacy_canvas_stroke_main_pass_execution_request(
            "Canvas::stroke_draw",
            [&] {
                events.emplace_back("bind-samplers");
            },
            [&] {
                events.emplace_back("bind-textures");
                pp::panopainter::bind_legacy_canvas_stroke_texture_inputs(
                    main_pass_texture_bindings,
                    main_pass_texture_dispatch);
            },
            [&] {
                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_main_dirty,
                    [&](StrokeFrame& frame) {
                        events.emplace_back("begin-frame:" + std::to_string(frame.id));
                    },
                    [&](StrokeFrame&, int face_index, std::span<const vertex_t> vertices) {
                        events.emplace_back(
                            "prepare:" + std::to_string(face_index) + ":" + std::to_string(vertices.size()));
                    },
                    [&](StrokeFrame&, int face_index, std::span<const vertex_t>) {
                        events.emplace_back("execute:" + std::to_string(face_index));
                        return glm::vec4(1.0F, 2.0F, 3.0F, 4.0F);
                    },
                    face_framebuffers);
            },
            [&] {
                events.emplace_back("unbind-textures");
                pp::panopainter::unbind_legacy_canvas_stroke_texture_inputs(
                    main_pass_texture_unbindings,
                    main_pass_texture_dispatch);
            },
            [&] {
                events.emplace_back("unbind-samplers");
            }));

    const std::vector<std::string> expected_events {
        "bind-samplers",
        "bind-textures",
        "activate:0",
        "bind-brush",
        "activate:1",
        "bind-destination",
        "activate:2",
        "bind-pattern",
        "activate:3",
        "bind-mixer",
        "begin-frame:31",
        "prepare:2:3",
        "bind:2",
        "execute:2",
        "unbind:2",
        "unbind-textures",
        "activate:3",
        "unbind-mixer",
        "activate:1",
        "unbind-destination",
        "activate:0",
        "unbind-brush",
        "unbind-samplers",
    };

    PP_EXPECT(h, ok);
    PP_EXPECT(h, events == expected_events);
}

void retained_stroke_pad_face_callbacks_preserve_order(pp::tests::Harness& h)
{
    const std::array<bool, 3> dirty_faces { true, false, true };
    const std::array<glm::vec4, 3> pass_dirty_boxes {
        glm::vec4(5.0F, 10.0F, 20.0F, 30.0F),
        glm::vec4(0.0F, 0.0F, 0.0F, 0.0F),
        glm::vec4(60.0F, 15.0F, 70.0F, 25.0F),
    };
    const auto faces = pp::panopainter::make_legacy_canvas_stroke_pad_faces(dirty_faces, pass_dirty_boxes);

    std::vector<std::string> events;
    std::vector<pp::paint_renderer::CanvasStrokeCopyRegion> copy_regions;
    std::array<vertex_t, 4> uploaded_quad {};

    const auto result = pp::panopainter::execute_legacy_canvas_stroke_pad_face_callbacks(
        std::span<const LegacyCanvasStrokePadFace>(faces),
        pp::renderer::Extent2D { .width = 100, .height = 80 },
        true,
        [&](std::span<const vertex_t> vertices) {
            events.emplace_back("upload:" + std::to_string(vertices.size()));
            std::copy(vertices.begin(), vertices.end(), uploaded_quad.begin());
        },
        [&](int face_index) {
            events.emplace_back("begin:" + std::to_string(face_index));
        },
        [&](int face_index) {
            events.emplace_back("bind:" + std::to_string(face_index));
        },
        [&](const pp::paint_renderer::CanvasStrokeCopyRegion& copy_region) {
            events.emplace_back("copy");
            copy_regions.push_back(copy_region);
        },
        [&](int face_index) {
            events.emplace_back("unbind:" + std::to_string(face_index));
        },
        [&] {
            events.emplace_back("draw");
        },
        [&](int face_index) {
            events.emplace_back("finish:" + std::to_string(face_index));
        });

    PP_EXPECT(h, result == 2U);
    const std::vector<std::string> expected_events {
        "upload:4",
        "begin:0",
        "bind:0",
        "draw",
        "unbind:0",
        "finish:0",
        "upload:4",
        "begin:2",
        "bind:2",
        "copy",
        "draw",
        "unbind:2",
        "finish:2",
    };
    PP_EXPECT(h, events == expected_events);
    PP_EXPECT(h, copy_regions.size() == 1U);
    PP_EXPECT(h, faces[0].dirty);
    PP_EXPECT(h, !faces[1].dirty);
    PP_EXPECT(h, faces[2].dirty);
    PP_EXPECT(h, uploaded_quad.size() == 4U);
}

void retained_stroke_pad_executor_copies_destination_for_dirty_faces_only(pp::tests::Harness& h)
{
    const std::array<bool, 3> dirty_faces { true, false, true };
    const std::array<glm::vec4, 3> pass_dirty_boxes {
        glm::vec4(5.0F, 10.0F, 20.0F, 30.0F),
        glm::vec4(0.0F, 0.0F, 0.0F, 0.0F),
        glm::vec4(60.0F, 15.0F, 70.0F, 25.0F),
    };
    const auto faces = pp::panopainter::make_legacy_canvas_stroke_pad_faces(dirty_faces, pass_dirty_boxes);

    std::vector<std::string> events;
    std::vector<pp::paint_renderer::CanvasStrokeCopyRegion> copy_regions;

    const auto result = pp::panopainter::execute_legacy_canvas_stroke_pad_faces(
        LegacyCanvasStrokePadExecutionRequest {
            .context = "test",
            .extent = pp::renderer::Extent2D { .width = 100, .height = 80 },
            .faces = std::span<const LegacyCanvasStrokePadFace>(faces),
            .copy_stroke_destination = true,
            .upload_pad_vertices = [&](std::span<const vertex_t> vertices) {
                events.emplace_back("upload:" + std::to_string(vertices.size()));
            },
            .begin_face = [&](int face_index) {
                events.emplace_back("begin:" + std::to_string(face_index));
            },
            .bind_destination_texture = [&](int face_index) {
                events.emplace_back("bind:" + std::to_string(face_index));
            },
            .copy_framebuffer_to_destination_texture =
                [&](const pp::paint_renderer::CanvasStrokeCopyRegion& copy_region) {
                    events.emplace_back("copy");
                    copy_regions.push_back(copy_region);
                },
            .unbind_destination_texture = [&](int face_index) {
                events.emplace_back("unbind:" + std::to_string(face_index));
            },
            .draw_pad = [&]() { events.emplace_back("draw"); },
            .finish_face = [&](int face_index) {
                events.emplace_back("finish:" + std::to_string(face_index));
            },
        });

    PP_EXPECT(h, result.ok);
    PP_EXPECT(h, result.padded_faces == 2U);
    PP_EXPECT(h, faces[0].index == 0);
    PP_EXPECT(h, faces[1].index == 1);
    PP_EXPECT(h, faces[2].index == 2);
    PP_EXPECT(h, faces[0].dirty);
    PP_EXPECT(h, !faces[1].dirty);
    PP_EXPECT(h, faces[2].dirty);
    const std::vector<std::string> expected_events {
        "upload:6",
        "begin:0",
        "bind:0",
        "copy",
        "draw",
        "unbind:0",
        "finish:0",
        "upload:6",
        "begin:2",
        "bind:2",
        "copy",
        "draw",
        "unbind:2",
        "finish:2",
    };
    PP_EXPECT(h, events == expected_events);
    PP_EXPECT(h, copy_regions.size() == 2U);
    PP_EXPECT(h, copy_regions[0].x == 0);
    PP_EXPECT(h, copy_regions[0].y == 0);
    PP_EXPECT(h, copy_regions[0].width == 40);
    PP_EXPECT(h, copy_regions[0].height == 50);
}

void retained_stroke_preview_background_capture_preserves_retained_call_order(pp::tests::Harness& h)
{
    std::vector<std::string> events;
    std::array<int, 6> copy_args {};
    bool copy_ok = false;

    pp::panopainter::execute_legacy_stroke_preview_live_pass(
        [&]() { events.emplace_back("setup"); },
        [&]() {
            return std::vector<int> { 0 };
        },
        [&](int&) {},
        [&](int&) { events.emplace_back("draw"); },
        [&](int&) {},
        [&]() {
            const auto copy_status = pp::paint_renderer::copy_stroke_preview_result_to_texture(
                [&]() { events.emplace_back("bind"); },
                [&](int src_x, int src_y, int dst_x, int dst_y, int width, int height) {
                    events.emplace_back("copy");
                    copy_args = { src_x, src_y, dst_x, dst_y, width, height };
                },
                pp::paint_renderer::StrokePreviewCopySize { .width = 48, .height = 32 });
            PP_EXPECT(h, copy_status.ok());
            copy_ok = copy_status.ok();
        });
    PP_EXPECT(h, copy_ok);

    const std::vector<std::string> expected_events { "setup", "draw", "bind", "copy" };
    PP_EXPECT(h, events == expected_events);
    PP_EXPECT(h, copy_args[0] == 0);
    PP_EXPECT(h, copy_args[1] == 0);
    PP_EXPECT(h, copy_args[2] == 0);
    PP_EXPECT(h, copy_args[3] == 0);
    PP_EXPECT(h, copy_args[4] == 48);
    PP_EXPECT(h, copy_args[5] == 32);
}

void retained_stroke_preview_final_composite_preserves_retained_call_order(pp::tests::Harness& h)
{
    std::vector<std::string> events;

    pp::panopainter::execute_legacy_stroke_preview_final_composite(
        [&]() { events.emplace_back("setup"); },
        [&]() { events.emplace_back("bind-samplers"); },
        [&]() { events.emplace_back("bind-inputs"); },
        [&]() { events.emplace_back("draw"); });

    const std::vector<std::string> expected_events {
        "setup",
        "bind-samplers",
        "bind-inputs",
        "draw",
    };
    PP_EXPECT(h, events == expected_events);
}

void retained_stroke_preview_texture_copy_binds_before_copy(pp::tests::Harness& h)
{
    std::vector<std::string> events;
    std::array<int, 6> copy_args {};

    const auto copy_status = pp::paint_renderer::copy_stroke_preview_result_to_texture(
        [&]() { events.emplace_back("bind"); },
        [&](int src_x, int src_y, int dst_x, int dst_y, int width, int height) {
            events.emplace_back("copy");
            copy_args = { src_x, src_y, dst_x, dst_y, width, height };
        },
        pp::paint_renderer::StrokePreviewCopySize { .width = 96, .height = 64 });
    PP_EXPECT(h, copy_status.ok());

    const std::vector<std::string> expected_events { "bind", "copy" };
    PP_EXPECT(h, events == expected_events);
    PP_EXPECT(h, copy_args[0] == 0);
    PP_EXPECT(h, copy_args[1] == 0);
    PP_EXPECT(h, copy_args[2] == 0);
    PP_EXPECT(h, copy_args[3] == 0);
    PP_EXPECT(h, copy_args[4] == 96);
    PP_EXPECT(h, copy_args[5] == 64);
}

void retained_stroke_temporary_composite_preserves_retained_call_order(pp::tests::Harness& h)
{
    std::vector<std::string> events;

    pp::panopainter::execute_legacy_canvas_stroke_temporary_composite(
        [&]() { events.emplace_back("setup"); },
        [&]() { events.emplace_back("bind-samplers"); },
        [&]() { events.emplace_back("bind-textures"); },
        [&]() { events.emplace_back("draw"); },
        [&]() { events.emplace_back("unbind-textures"); });

    const std::vector<std::string> expected_events {
        "setup",
        "bind-samplers",
        "bind-textures",
        "draw",
        "unbind-textures",
    };
    PP_EXPECT(h, events == expected_events);
}

void retained_stroke_mix_pass_skips_inactive_planes_and_preserves_texture_order(pp::tests::Harness& h)
{
    const std::array<LegacyCanvasStrokeMixPassPlane, 4> planes {
        LegacyCanvasStrokeMixPassPlane { .index = 0, .visible = true, .has_target = true, .opacity = 1.0F },
        LegacyCanvasStrokeMixPassPlane { .index = 1, .visible = false, .has_target = true, .opacity = 1.0F },
        LegacyCanvasStrokeMixPassPlane { .index = 2, .visible = true, .has_target = false, .opacity = 1.0F },
        LegacyCanvasStrokeMixPassPlane { .index = 3, .visible = true, .has_target = true, .opacity = 0.0F },
    };

    std::vector<std::string> events;
    const auto result = pp::panopainter::execute_legacy_canvas_stroke_mix_pass(
        LegacyCanvasStrokeMixPassRequest {
            .context = "test",
            .resolution = glm::vec2(128.0F, 64.0F),
            .planes = planes,
            .bind_mix_samplers = [&]() { events.emplace_back("bind-samplers"); },
            .unbind_mix_samplers = [&]() { events.emplace_back("unbind-samplers"); },
            .setup_plane_shader = [&](int plane_index, const glm::mat4&) {
                events.emplace_back("setup:" + std::to_string(plane_index));
            },
            .bind_layer_texture = [&](int plane_index) {
                events.emplace_back("bind-layer:" + std::to_string(plane_index));
            },
            .bind_stroke_texture = [&](int plane_index) {
                events.emplace_back("bind-stroke:" + std::to_string(plane_index));
            },
            .bind_mask_texture = [&](int plane_index) {
                events.emplace_back("bind-mask:" + std::to_string(plane_index));
            },
            .draw_plane = [&]() {
                events.emplace_back("draw");
            },
            .unbind_mask_texture = [&](int plane_index) {
                events.emplace_back("unbind-mask:" + std::to_string(plane_index));
            },
            .unbind_stroke_texture = [&](int plane_index) {
                events.emplace_back("unbind-stroke:" + std::to_string(plane_index));
            },
            .unbind_layer_texture = [&](int plane_index) {
                events.emplace_back("unbind-layer:" + std::to_string(plane_index));
            },
        });

    PP_EXPECT(h, result.ok);
    PP_EXPECT(h, result.composed_planes == 1U);
    const std::vector<std::string> expected_events {
        "bind-samplers",
        "setup:0",
        "bind-layer:0",
        "bind-stroke:0",
        "bind-mask:0",
        "draw",
        "unbind-mask:0",
        "unbind-stroke:0",
        "unbind-layer:0",
        "unbind-samplers",
    };
    PP_EXPECT(h, events == expected_events);
}

void retained_stroke_mix_pass_rejects_incomplete_requests(pp::tests::Harness& h)
{
    std::vector<std::string> events;
    const auto result = pp::panopainter::execute_legacy_canvas_stroke_mix_pass(
        LegacyCanvasStrokeMixPassRequest {
            .context = "test",
            .resolution = glm::vec2(128.0F, 64.0F),
            .bind_mix_samplers = [&]() { events.emplace_back("bind-samplers"); },
        });

    PP_EXPECT(h, !result.ok);
    PP_EXPECT(h, result.composed_planes == 0U);
    PP_EXPECT(h, events.empty());
}

void retained_stroke_mix_pass_request_builder_preserves_callback_wiring(pp::tests::Harness& h)
{
    const std::array<LegacyCanvasStrokeMixPassPlane, 1> planes {
        LegacyCanvasStrokeMixPassPlane { .index = 3, .visible = true, .has_target = true, .opacity = 0.75F },
    };

    std::vector<std::string> events;
    const auto request = pp::panopainter::make_legacy_canvas_stroke_mix_pass_request(
        "mix-test",
        glm::vec2(128.0F, 64.0F),
        planes,
        [&] { events.emplace_back("bind-samplers"); },
        [&] { events.emplace_back("unbind-samplers"); },
        [&](int plane_index, const glm::mat4&) {
            events.emplace_back("setup:" + std::to_string(plane_index));
        },
        [&](int plane_index) {
            events.emplace_back("bind-layer:" + std::to_string(plane_index));
        },
        [&](int plane_index) {
            events.emplace_back("bind-stroke:" + std::to_string(plane_index));
        },
        [&](int plane_index) {
            events.emplace_back("bind-mask:" + std::to_string(plane_index));
        },
        [&] { events.emplace_back("draw"); },
        [&](int plane_index) {
            events.emplace_back("unbind-mask:" + std::to_string(plane_index));
        },
        [&](int plane_index) {
            events.emplace_back("unbind-stroke:" + std::to_string(plane_index));
        },
        [&](int plane_index) {
            events.emplace_back("unbind-layer:" + std::to_string(plane_index));
        });

    PP_EXPECT(h, request.context == "mix-test");
    PP_EXPECT(h, request.resolution.x == 128.0F);
    PP_EXPECT(h, request.resolution.y == 64.0F);
    PP_EXPECT(h, request.planes.size() == 1U);
    PP_EXPECT(h, request.planes.front().index == 3);
    PP_EXPECT(h, request.bind_mix_samplers);
    PP_EXPECT(h, request.unbind_mix_samplers);
    PP_EXPECT(h, request.setup_plane_shader);
    PP_EXPECT(h, request.bind_layer_texture);
    PP_EXPECT(h, request.bind_stroke_texture);
    PP_EXPECT(h, request.bind_mask_texture);
    PP_EXPECT(h, request.draw_plane);
    PP_EXPECT(h, request.unbind_mask_texture);
    PP_EXPECT(h, request.unbind_stroke_texture);
    PP_EXPECT(h, request.unbind_layer_texture);

    request.bind_mix_samplers();
    request.setup_plane_shader(3, glm::mat4(1.0F));
    request.bind_layer_texture(3);
    request.bind_stroke_texture(3);
    request.bind_mask_texture(3);
    request.draw_plane();
    request.unbind_mask_texture(3);
    request.unbind_stroke_texture(3);
    request.unbind_layer_texture(3);
    request.unbind_mix_samplers();

    const std::vector<std::string> expected_events {
        "bind-samplers",
        "setup:3",
        "bind-layer:3",
        "bind-stroke:3",
        "bind-mask:3",
        "draw",
        "unbind-mask:3",
        "unbind-stroke:3",
        "unbind-layer:3",
        "unbind-samplers",
    };
    PP_EXPECT(h, events == expected_events);
}

void retained_stroke_mix_pass_setup_builder_preserves_boundaries(pp::tests::Harness& h)
{
    std::vector<std::string> events;
    const auto setup = pp::panopainter::make_legacy_canvas_stroke_mix_pass_setup(
        [&] { events.emplace_back("begin"); },
        [&] { events.emplace_back("end"); });

    PP_EXPECT(h, setup.begin);
    PP_EXPECT(h, setup.end);

    setup.begin();
    setup.end();

    PP_EXPECT(h, events.size() == 2U);
    PP_EXPECT(h, events[0] == "begin");
    PP_EXPECT(h, events[1] == "end");
}

void retained_stroke_mix_pass_shell_builder_preserves_combined_wiring(pp::tests::Harness& h)
{
    const std::array<LegacyCanvasStrokeMixPassPlane, 1> planes {
        LegacyCanvasStrokeMixPassPlane { .index = 2, .visible = true, .has_target = true, .opacity = 1.0F },
    };

    std::vector<std::string> events;
    const auto shell = pp::panopainter::make_legacy_canvas_stroke_mix_pass_shell(
        [&] { events.emplace_back("begin"); },
        [&] { events.emplace_back("end"); },
        "mix-shell",
        glm::vec2(64.0F, 32.0F),
        planes,
        [&] { events.emplace_back("bind-samplers"); },
        [&] { events.emplace_back("unbind-samplers"); },
        [&](int plane_index, const glm::mat4&) {
            events.emplace_back("setup:" + std::to_string(plane_index));
        },
        [&](int plane_index) {
            events.emplace_back("bind-layer:" + std::to_string(plane_index));
        },
        [&](int plane_index) {
            events.emplace_back("bind-stroke:" + std::to_string(plane_index));
        },
        [&](int plane_index) {
            events.emplace_back("bind-mask:" + std::to_string(plane_index));
        },
        [&] { events.emplace_back("draw"); },
        [&](int plane_index) {
            events.emplace_back("unbind-mask:" + std::to_string(plane_index));
        },
        [&](int plane_index) {
            events.emplace_back("unbind-stroke:" + std::to_string(plane_index));
        },
        [&](int plane_index) {
            events.emplace_back("unbind-layer:" + std::to_string(plane_index));
        });

    PP_EXPECT(h, shell.setup.begin);
    PP_EXPECT(h, shell.setup.end);
    PP_EXPECT(h, shell.request.context == "mix-shell");
    PP_EXPECT(h, shell.request.planes.size() == 1U);

    shell.setup.begin();
    shell.request.bind_mix_samplers();
    shell.request.setup_plane_shader(2, glm::mat4(1.0F));
    shell.request.bind_layer_texture(2);
    shell.request.bind_stroke_texture(2);
    shell.request.bind_mask_texture(2);
    shell.request.draw_plane();
    shell.request.unbind_mask_texture(2);
    shell.request.unbind_stroke_texture(2);
    shell.request.unbind_layer_texture(2);
    shell.request.unbind_mix_samplers();
    shell.setup.end();

    const std::vector<std::string> expected_events {
        "begin",
        "bind-samplers",
        "setup:2",
        "bind-layer:2",
        "bind-stroke:2",
        "bind-mask:2",
        "draw",
        "unbind-mask:2",
        "unbind-stroke:2",
        "unbind-layer:2",
        "unbind-samplers",
        "end",
    };
    PP_EXPECT(h, events == expected_events);
}

void retained_stroke_mix_pass_shell_executor_preserves_combined_wiring(pp::tests::Harness& h)
{
    const std::array<LegacyCanvasStrokeMixPassPlane, 1> planes {
        LegacyCanvasStrokeMixPassPlane { .index = 5, .visible = true, .has_target = true, .opacity = 0.5F },
    };

    std::vector<std::string> events;
    const auto shell = pp::panopainter::make_legacy_canvas_stroke_mix_pass_shell(
        [&] { events.emplace_back("begin"); },
        [&] { events.emplace_back("end"); },
        "mix-shell-executor",
        glm::vec2(32.0F, 16.0F),
        planes,
        [&] { events.emplace_back("bind-samplers"); },
        [&] { events.emplace_back("unbind-samplers"); },
        [&](int plane_index, const glm::mat4&) {
            events.emplace_back("setup:" + std::to_string(plane_index));
        },
        [&](int plane_index) {
            events.emplace_back("bind-layer:" + std::to_string(plane_index));
        },
        [&](int plane_index) {
            events.emplace_back("bind-stroke:" + std::to_string(plane_index));
        },
        [&](int plane_index) {
            events.emplace_back("bind-mask:" + std::to_string(plane_index));
        },
        [&] { events.emplace_back("draw"); },
        [&](int plane_index) {
            events.emplace_back("unbind-mask:" + std::to_string(plane_index));
        },
        [&](int plane_index) {
            events.emplace_back("unbind-stroke:" + std::to_string(plane_index));
        },
        [&](int plane_index) {
            events.emplace_back("unbind-layer:" + std::to_string(plane_index));
        });

    const auto shell_result = pp::panopainter::execute_legacy_canvas_stroke_mix_pass_shell(
        shell.setup.begin,
        shell.setup.end,
        shell.request);

    PP_EXPECT(h, shell_result.ok);
    PP_EXPECT(h, shell_result.composed_planes == 1U);
    const std::vector<std::string> expected_events {
        "begin",
        "bind-samplers",
        "setup:5",
        "bind-layer:5",
        "bind-stroke:5",
        "bind-mask:5",
        "draw",
        "unbind-mask:5",
        "unbind-stroke:5",
        "unbind-layer:5",
        "unbind-samplers",
        "end",
    };
    PP_EXPECT(h, events == expected_events);
}

} // namespace

int main()
{
    pp::tests::Harness harness;
    harness.run(
        "retained_stroke_texture_inputs_bind_and_unbind_in_declared_order",
        retained_stroke_texture_inputs_bind_and_unbind_in_declared_order);
    harness.run(
        "retained_stroke_sample_executor_copies_destination_and_expands_quads",
        retained_stroke_sample_executor_copies_destination_and_expands_quads);
    harness.run(
        "retained_stroke_texture_dispatch_activates_units_and_routes_per_input",
        retained_stroke_texture_dispatch_activates_units_and_routes_per_input);
    harness.run(
        "retained_stroke_sampler_dispatch_routes_bind_and_unbind_per_input",
        retained_stroke_sampler_dispatch_routes_bind_and_unbind_per_input);
    harness.run(
        "retained_stroke_sampler_dispatch_helper_builds_expected_callback_wiring",
        retained_stroke_sampler_dispatch_helper_builds_expected_callback_wiring);
    harness.run(
        "retained_stroke_main_pass_texture_dispatch_helper_builds_expected_callback_wiring",
        retained_stroke_main_pass_texture_dispatch_helper_builds_expected_callback_wiring);
    harness.run(
        "retained_stroke_pad_destination_texture_dispatch_helper_builds_expected_callback_wiring",
        retained_stroke_pad_destination_texture_dispatch_helper_builds_expected_callback_wiring);
    harness.run(
        "retained_stroke_pad_destination_texture_dispatch_helper_builds_expected_face_wiring",
        retained_stroke_pad_destination_texture_dispatch_helper_builds_expected_face_wiring);
    harness.run(
        "retained_stroke_brush_tip_texture_dispatch_helper_builds_expected_callback_wiring",
        retained_stroke_brush_tip_texture_dispatch_helper_builds_expected_callback_wiring);
    harness.run(
        "retained_stroke_brush_tip_texture_dispatch_helper_builds_expected_face_wiring",
        retained_stroke_brush_tip_texture_dispatch_helper_builds_expected_face_wiring);
    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_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_main_pass_execution_preserves_bind_and_unbind_order",
        retained_stroke_main_pass_execution_preserves_bind_and_unbind_order);
    harness.run(
        "retained_stroke_main_pass_execution_preserves_destination_binding_order_and_face_execution",
        retained_stroke_main_pass_execution_preserves_destination_binding_order_and_face_execution);
    harness.run(
        "retained_stroke_live_pass_sampler_dispatch_helper_builds_expected_callback_wiring",
        retained_stroke_live_pass_sampler_dispatch_helper_builds_expected_callback_wiring);
    harness.run(
        "retained_stroke_sample_executor_unbinds_and_skips_draw_when_bounds_are_empty",
        retained_stroke_sample_executor_unbinds_and_skips_draw_when_bounds_are_empty);
    harness.run(
        "retained_stroke_face_sample_polygon_triangulates_and_forwards_face_callbacks",
        retained_stroke_face_sample_polygon_triangulates_and_forwards_face_callbacks);
    harness.run(
        "retained_stroke_face_sample_polygon_skips_destination_callbacks_when_copy_is_disabled",
        retained_stroke_face_sample_polygon_skips_destination_callbacks_when_copy_is_disabled);
    harness.run(
        "retained_stroke_frame_samples_preserve_frame_face_callback_order",
        retained_stroke_frame_samples_preserve_frame_face_callback_order);
    harness.run(
        "retained_stroke_frame_samples_with_dirty_tracking_updates_after_finish",
        retained_stroke_frame_samples_with_dirty_tracking_updates_after_finish);
    harness.run(
        "retained_stroke_frame_planner_uses_previous_sample_and_projection_mode",
        retained_stroke_frame_planner_uses_previous_sample_and_projection_mode);
    harness.run(
        "retained_stroke_frame_planner_scales_mixer_bounds_with_zoom",
        retained_stroke_frame_planner_scales_mixer_bounds_with_zoom);
    harness.run(
        "retained_stroke_live_pass_with_face_framebuffers_preserves_order_and_dirty_tracking",
        retained_stroke_live_pass_with_face_framebuffers_preserves_order_and_dirty_tracking);
    harness.run(
        "retained_stroke_live_pass_clears_before_traversal_and_copies_afterwards",
        retained_stroke_live_pass_clears_before_traversal_and_copies_afterwards);
    harness.run(
        "retained_stroke_live_pass_retains_previous_pass_dirty_union_without_override",
        retained_stroke_live_pass_retains_previous_pass_dirty_union_without_override);
    harness.run(
        "retained_stroke_pad_executor_copies_destination_for_dirty_faces_only",
        retained_stroke_pad_executor_copies_destination_for_dirty_faces_only);
    harness.run(
        "retained_stroke_preview_background_capture_preserves_retained_call_order",
        retained_stroke_preview_background_capture_preserves_retained_call_order);
    harness.run(
        "retained_stroke_preview_final_composite_preserves_retained_call_order",
        retained_stroke_preview_final_composite_preserves_retained_call_order);
    harness.run(
        "retained_stroke_preview_texture_copy_binds_before_copy",
        retained_stroke_preview_texture_copy_binds_before_copy);
    harness.run(
        "retained_stroke_temporary_composite_preserves_retained_call_order",
        retained_stroke_temporary_composite_preserves_retained_call_order);
    harness.run(
        "retained_stroke_mix_pass_skips_inactive_planes_and_preserves_texture_order",
        retained_stroke_mix_pass_skips_inactive_planes_and_preserves_texture_order);
    harness.run(
        "retained_stroke_mix_pass_rejects_incomplete_requests",
        retained_stroke_mix_pass_rejects_incomplete_requests);
    harness.run(
        "retained_stroke_mix_pass_request_builder_preserves_callback_wiring",
        retained_stroke_mix_pass_request_builder_preserves_callback_wiring);
    harness.run(
        "retained_stroke_mix_pass_setup_builder_preserves_boundaries",
        retained_stroke_mix_pass_setup_builder_preserves_boundaries);
    harness.run(
        "retained_stroke_mix_pass_shell_builder_preserves_combined_wiring",
        retained_stroke_mix_pass_shell_builder_preserves_combined_wiring);
    harness.run(
        "retained_stroke_mix_pass_shell_executor_preserves_combined_wiring",
        retained_stroke_mix_pass_shell_executor_preserves_combined_wiring);
    return harness.finish();
}
