Extract stroke mix request wiring

This commit is contained in:
2026-06-13 19:15:42 +02:00
parent 5b76f3d1f9
commit c62a60a4af
5 changed files with 174 additions and 55 deletions

View File

@@ -18,6 +18,10 @@ agent or engineer to remove them without reconstructing context from chat.
## Recent Reductions ## Recent Reductions
- 2026-06-13: `LATER-003` was narrowed again. `Canvas::stroke_draw_mix()` now
routes retained mix-pass request wiring through
`make_legacy_canvas_stroke_mix_pass_request(...)`; the legacy path still
owns the concrete framebuffer setup and per-plane GL callbacks.
- 2026-06-13: `PLT-004` was narrowed again. `LegacyPlatformServices::display_file()` - 2026-06-13: `PLT-004` was narrowed again. `LegacyPlatformServices::display_file()`
and `share_file()` now delegate Apple file actions through and `share_file()` now delegate Apple file actions through
`AppleDocumentPlatformServices`, leaving the legacy adapter with only the `AppleDocumentPlatformServices`, leaving the legacy adapter with only the

View File

@@ -588,6 +588,9 @@ Progress Notes:
execute, and unbind sequence through execute, and unbind sequence through
`execute_legacy_canvas_stroke_main_pass(...)`; the retained adapter still `execute_legacy_canvas_stroke_main_pass(...)`; the retained adapter still
owns the concrete sampler and texture callbacks. owns the concrete sampler and texture callbacks.
- 2026-06-13: `Canvas::stroke_draw_mix()` now routes retained mix-pass request
wiring through `make_legacy_canvas_stroke_mix_pass_request(...)`; the legacy
path still owns the concrete framebuffer setup and per-plane GL callbacks.
- 2026-06-13: `Canvas::stroke_draw()` live-pass sampler wiring now reuses a - 2026-06-13: `Canvas::stroke_draw()` live-pass sampler wiring now reuses a
retained helper builder, and the stroke execution tests cover it. `STR-004` retained helper builder, and the stroke execution tests cover it. `STR-004`
is now done after the final pad-destination helper extraction and tracker is now done after the final pad-destination helper extraction and tracker

View File

@@ -420,19 +420,19 @@ void Canvas::stroke_draw_mix(const glm::vec2& bb_min, const glm::vec2& bb_sz)
[&] { [&] {
m_mixer.unbindFramebuffer(); m_mixer.unbindFramebuffer();
}, },
pp::panopainter::LegacyCanvasStrokeMixPassRequest { pp::panopainter::make_legacy_canvas_stroke_mix_pass_request(
.context = "Canvas::stroke_draw_mix", "Canvas::stroke_draw_mix",
.resolution = m_size, m_size,
.planes = mix_planes, mix_planes,
.bind_mix_samplers = [&] { [&] {
m_sampler.bind(0); m_sampler.bind(0);
m_sampler.bind(1); m_sampler.bind(1);
m_sampler.bind(2); m_sampler.bind(2);
}, },
.unbind_mix_samplers = [&] { [&] {
m_sampler.unbind(); m_sampler.unbind();
}, },
.setup_plane_shader = [&](int plane_index, const glm::mat4& plane_mvp_z) { [&](int plane_index, const glm::mat4& plane_mvp_z) {
(void)plane_index; (void)plane_index;
pp::panopainter::setup_legacy_stroke_composite_shader( pp::panopainter::setup_legacy_stroke_composite_shader(
pp::panopainter::LegacyStrokeCompositeUniforms { pp::panopainter::LegacyStrokeCompositeUniforms {
@@ -448,33 +448,32 @@ void Canvas::stroke_draw_mix(const glm::vec2& bb_min, const glm::vec2& bb_sz)
.use_pattern = false, .use_pattern = false,
}); });
}, },
.bind_layer_texture = [&](int plane_index) { [&](int plane_index) {
set_active_texture_unit(0); set_active_texture_unit(0);
current_layer.rtt(plane_index).bindTexture(); current_layer.rtt(plane_index).bindTexture();
}, },
.bind_stroke_texture = [&](int plane_index) { [&](int plane_index) {
set_active_texture_unit(1); set_active_texture_unit(1);
m_tmp[plane_index].bindTexture(); m_tmp[plane_index].bindTexture();
}, },
.bind_mask_texture = [&](int plane_index) { [&](int plane_index) {
set_active_texture_unit(2); set_active_texture_unit(2);
m_smask.rtt(plane_index).bindTexture(); m_smask.rtt(plane_index).bindTexture();
}, },
.draw_plane = [&] { [&] {
m_node->m_face_plane.draw_fill(); m_node->m_face_plane.draw_fill();
}, },
.unbind_mask_texture = [&](int plane_index) { [&](int plane_index) {
set_active_texture_unit(2); set_active_texture_unit(2);
m_smask.rtt(plane_index).unbindTexture(); m_smask.rtt(plane_index).unbindTexture();
}, },
.unbind_stroke_texture = [&](int plane_index) { [&](int plane_index) {
set_active_texture_unit(1); set_active_texture_unit(1);
m_tmp[plane_index].unbindTexture(); m_tmp[plane_index].unbindTexture();
}, },
.unbind_layer_texture = [&](int plane_index) { [&](int plane_index) {
set_active_texture_unit(0); set_active_texture_unit(0);
current_layer.rtt(plane_index).unbindTexture(); current_layer.rtt(plane_index).unbindTexture();
},
}); });
gl.restore(); gl.restore();

View File

@@ -340,6 +340,38 @@ struct LegacyCanvasStrokeMixPassResult {
std::size_t composed_planes = 0; std::size_t composed_planes = 0;
}; };
[[nodiscard]] inline LegacyCanvasStrokeMixPassRequest make_legacy_canvas_stroke_mix_pass_request(
std::string_view context,
glm::vec2 resolution,
std::span<const LegacyCanvasStrokeMixPassPlane> planes,
std::function<void()> bind_mix_samplers,
std::function<void()> unbind_mix_samplers,
std::function<void(int, const glm::mat4&)> setup_plane_shader,
std::function<void(int)> bind_layer_texture,
std::function<void(int)> bind_stroke_texture,
std::function<void(int)> bind_mask_texture,
std::function<void()> draw_plane,
std::function<void(int)> unbind_mask_texture,
std::function<void(int)> unbind_stroke_texture,
std::function<void(int)> unbind_layer_texture)
{
return LegacyCanvasStrokeMixPassRequest {
.context = context,
.resolution = resolution,
.planes = planes,
.bind_mix_samplers = std::move(bind_mix_samplers),
.unbind_mix_samplers = std::move(unbind_mix_samplers),
.setup_plane_shader = std::move(setup_plane_shader),
.bind_layer_texture = std::move(bind_layer_texture),
.bind_stroke_texture = std::move(bind_stroke_texture),
.bind_mask_texture = std::move(bind_mask_texture),
.draw_plane = std::move(draw_plane),
.unbind_mask_texture = std::move(unbind_mask_texture),
.unbind_stroke_texture = std::move(unbind_stroke_texture),
.unbind_layer_texture = std::move(unbind_layer_texture),
};
}
struct LegacyCanvasStrokeDualPassRequest { struct LegacyCanvasStrokeDualPassRequest {
std::string_view context; std::string_view context;
std::function<void()> bind_brush_tip; std::function<void()> bind_brush_tip;

View File

@@ -1807,6 +1807,84 @@ void retained_stroke_mix_pass_rejects_incomplete_requests(pp::tests::Harness& h)
PP_EXPECT(h, events.empty()); 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);
}
} // namespace } // namespace
int main() int main()
@@ -1911,5 +1989,8 @@ int main()
harness.run( harness.run(
"retained_stroke_mix_pass_rejects_incomplete_requests", "retained_stroke_mix_pass_rejects_incomplete_requests",
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);
return harness.finish(); return harness.finish();
} }