Share retained stroke face framebuffer helper

This commit is contained in:
2026-06-13 10:22:56 +02:00
parent bc624ceb8d
commit 084f58573f
5 changed files with 72 additions and 20 deletions

View File

@@ -18,6 +18,11 @@ agent or engineer to remove them without reconstructing context from chat.
## Recent Reductions ## Recent Reductions
- 2026-06-13: DEBT-0036 was narrowed again. `Canvas::stroke_draw` main and
dual live-pass per-face framebuffer begin/end execution plus pad-face array
assembly now route through `legacy_canvas_stroke_execution_services.h`;
shader activation timing, texture/sampler binding, framebuffer ownership, pad
execution, and final OpenGL draw remain retained in `Canvas`.
- 2026-06-13: DEBT-0036 was narrowed again. `Canvas::stroke_draw` main and - 2026-06-13: DEBT-0036 was narrowed again. `Canvas::stroke_draw` main and
dual live-pass dirty semantics now route through dual live-pass dirty semantics now route through
`execute_legacy_canvas_stroke_live_pass_with_dirty_tracking(...)` in `execute_legacy_canvas_stroke_live_pass_with_dirty_tracking(...)` in

View File

@@ -3098,6 +3098,11 @@ Results:
dual-pass dirty preservation, while shader timing, sampler/texture binding, dual-pass dirty preservation, while shader timing, sampler/texture binding,
framebuffer ownership, pad execution, and final OpenGL draw ordering remain framebuffer ownership, pad execution, and final OpenGL draw ordering remain
in the legacy Canvas path. in the legacy Canvas path.
- `Canvas::stroke_draw` main and dual live-pass per-face framebuffer
bind/unbind execution now shares one retained helper, and pad-face array
assembly now shares one retained utility, while shader activation timing,
texture/sampler binding, framebuffer ownership, pad execution, and final
OpenGL draw ordering remain in the legacy Canvas path.
- `Canvas::stroke_draw` pad-pass destination bind/copy/unbind ordering now - `Canvas::stroke_draw` pad-pass destination bind/copy/unbind ordering now
shares the retained stroke execution helper callback surface, while shader shares the retained stroke execution helper callback surface, while shader
setup, pad color selection, framebuffer ownership, and final OpenGL draw setup, pad color selection, framebuffer ownership, and final OpenGL draw

View File

@@ -509,6 +509,13 @@ Done Checks:
Progress Notes: Progress Notes:
- 2026-06-13: `Canvas::stroke_draw` main and dual live-pass per-face
framebuffer begin/end execution plus pad-face array assembly now route
through `legacy_canvas_stroke_execution_services.h`; shader activation
timing, texture/sampler binding, framebuffer ownership, and final draw
execution remain local to `Canvas`. Next slice should target the remaining
pass-level texture binding or final composite setup without reopening the
newer pad, dirty, or commit helpers.
- 2026-06-13: `Canvas::stroke_draw` current and dual stroke per-face - 2026-06-13: `Canvas::stroke_draw` current and dual stroke per-face
framebuffer/sample callback ordering now routes through framebuffer/sample callback ordering now routes through
`legacy_canvas_stroke_execution_services.h`; framebuffer ownership, shader `legacy_canvas_stroke_execution_services.h`; framebuffer ownership, shader

View File

@@ -689,7 +689,7 @@ void Canvas::stroke_draw()
std::array<bool, 6> box_dirty = SIXPLETTE(false); std::array<bool, 6> box_dirty = SIXPLETTE(false);
const std::array<bool, 6> include_main_dirty = SIXPLETTE(true); const std::array<bool, 6> include_main_dirty = SIXPLETTE(true);
glm::vec4 pad_color; glm::vec4 pad_color;
pp::panopainter::execute_legacy_canvas_stroke_live_pass_with_dirty_tracking( pp::panopainter::execute_legacy_canvas_stroke_live_pass_with_face_framebuffers(
frames, frames,
stroke_extent, stroke_extent,
std::span<glm::vec4>(m_dirty_box), std::span<glm::vec4>(m_dirty_box),
@@ -705,7 +705,6 @@ void Canvas::stroke_draw()
[&](auto&, int i, auto&) { [&](auto&, int i, auto&) {
m_dirty_face[i] = true; m_dirty_face[i] = true;
box_dirty[i] = true; box_dirty[i] = true;
m_tmp[i].bindFramebuffer();
}, },
[&](auto& f, int i, auto& P) { [&](auto& f, int i, auto& P) {
pp::panopainter::use_legacy_stroke_shader(); pp::panopainter::use_legacy_stroke_shader();
@@ -717,9 +716,7 @@ void Canvas::stroke_draw()
}); });
return stroke_draw_samples(i, P, copy_stroke_destination); return stroke_draw_samples(i, P, copy_stroke_destination);
}, },
[&](auto&, int i, auto&, glm::vec4) { m_tmp);
m_tmp[i].unbindFramebuffer();
});
set_active_texture_unit(3); set_active_texture_unit(3);
m_mixer.unbindTexture(); m_mixer.unbindTexture();
@@ -740,14 +737,7 @@ void Canvas::stroke_draw()
.color = pad_color, .color = pad_color,
.uses_destination_feedback = copy_stroke_destination, .uses_destination_feedback = copy_stroke_destination,
}); });
const std::array<pp::panopainter::LegacyCanvasStrokePadFace, 6> pad_faces = { const auto pad_faces = pp::panopainter::make_legacy_canvas_stroke_pad_faces(box_dirty, box_face);
pp::panopainter::LegacyCanvasStrokePadFace { .index = 0, .dirty = box_dirty[0], .pass_dirty_box = box_face[0] },
pp::panopainter::LegacyCanvasStrokePadFace { .index = 1, .dirty = box_dirty[1], .pass_dirty_box = box_face[1] },
pp::panopainter::LegacyCanvasStrokePadFace { .index = 2, .dirty = box_dirty[2], .pass_dirty_box = box_face[2] },
pp::panopainter::LegacyCanvasStrokePadFace { .index = 3, .dirty = box_dirty[3], .pass_dirty_box = box_face[3] },
pp::panopainter::LegacyCanvasStrokePadFace { .index = 4, .dirty = box_dirty[4], .pass_dirty_box = box_face[4] },
pp::panopainter::LegacyCanvasStrokePadFace { .index = 5, .dirty = box_dirty[5], .pass_dirty_box = box_face[5] },
};
[[maybe_unused]] const auto pad_result = pp::panopainter::execute_legacy_canvas_stroke_pad_faces( [[maybe_unused]] const auto pad_result = pp::panopainter::execute_legacy_canvas_stroke_pad_faces(
pp::panopainter::LegacyCanvasStrokePadExecutionRequest { pp::panopainter::LegacyCanvasStrokePadExecutionRequest {
.context = "Canvas::stroke_draw", .context = "Canvas::stroke_draw",
@@ -802,7 +792,7 @@ void Canvas::stroke_draw()
auto frames_dual = stroke_draw_compute(*m_dual_stroke); auto frames_dual = stroke_draw_compute(*m_dual_stroke);
const std::array<bool, 6> include_dual_dirty = const std::array<bool, 6> include_dual_dirty =
SIXPLETTE(stroke_material.composite_pass.dual_blend_mode == 0); SIXPLETTE(stroke_material.composite_pass.dual_blend_mode == 0);
pp::panopainter::execute_legacy_canvas_stroke_live_pass_with_dirty_tracking( pp::panopainter::execute_legacy_canvas_stroke_live_pass_with_face_framebuffers(
frames_dual, frames_dual,
stroke_extent, stroke_extent,
std::span<glm::vec4>(m_dirty_box), std::span<glm::vec4>(m_dirty_box),
@@ -816,15 +806,11 @@ void Canvas::stroke_draw()
.opacity = f.opacity, .opacity = f.opacity,
}); });
}, },
[&](auto&, int i, auto&) { [](auto&, int, auto&) {},
m_tmp_dual[i].bindFramebuffer();
},
[&](auto&, int i, auto& P) { [&](auto&, int i, auto& P) {
return stroke_draw_samples(i, P, copy_stroke_destination); return stroke_draw_samples(i, P, copy_stroke_destination);
}, },
[&](auto&, int i, auto&, glm::vec4) { m_tmp_dual,
m_tmp_dual[i].unbindFramebuffer();
},
true); true);
set_active_texture_unit(0); set_active_texture_unit(0);

View File

@@ -351,6 +351,41 @@ std::size_t execute_legacy_canvas_stroke_live_pass_with_dirty_tracking(
pass_dirty_faces); pass_dirty_faces);
} }
template <typename Frames, typename Framebuffers, typename BeginFrame, typename PrepareFace, typename ExecuteSample>
std::size_t execute_legacy_canvas_stroke_live_pass_with_face_framebuffers(
Frames&& frames,
pp::renderer::Extent2D extent,
std::span<glm::vec4> accumulated_dirty_boxes,
std::span<glm::vec4> pass_dirty_boxes,
std::span<const bool> include_in_committed_dirty_box,
BeginFrame&& begin_frame,
PrepareFace&& prepare_face,
ExecuteSample&& execute_sample,
Framebuffers& face_framebuffers,
bool preserve_sample_dirty_as_pass_dirty = false,
std::span<bool> committed_dirty_faces = {},
std::span<bool> pass_dirty_faces = {})
{
return execute_legacy_canvas_stroke_live_pass_with_dirty_tracking(
std::forward<Frames>(frames),
extent,
accumulated_dirty_boxes,
pass_dirty_boxes,
include_in_committed_dirty_box,
std::forward<BeginFrame>(begin_frame),
[&](auto& frame, int face_index, auto& vertices) {
prepare_face(frame, face_index, vertices);
face_framebuffers[face_index].bindFramebuffer();
},
std::forward<ExecuteSample>(execute_sample),
[&](auto&, int face_index, auto&, glm::vec4) {
face_framebuffers[face_index].unbindFramebuffer();
},
preserve_sample_dirty_as_pass_dirty,
committed_dirty_faces,
pass_dirty_faces);
}
[[nodiscard]] inline pp::paint_renderer::CanvasStrokeBox legacy_canvas_stroke_box(glm::vec4 box) noexcept [[nodiscard]] inline pp::paint_renderer::CanvasStrokeBox legacy_canvas_stroke_box(glm::vec4 box) noexcept
{ {
return pp::paint_renderer::CanvasStrokeBox { return pp::paint_renderer::CanvasStrokeBox {
@@ -405,6 +440,20 @@ std::size_t execute_legacy_canvas_stroke_live_pass_with_dirty_tracking(
}; };
} }
template <std::size_t FaceCount>
[[nodiscard]] inline std::array<LegacyCanvasStrokePadFace, FaceCount> make_legacy_canvas_stroke_pad_faces(
const std::array<bool, FaceCount>& dirty_faces,
const std::array<glm::vec4, FaceCount>& pass_dirty_boxes) noexcept
{
std::array<LegacyCanvasStrokePadFace, FaceCount> faces {};
for (std::size_t face_index = 0; face_index < FaceCount; ++face_index) {
faces[face_index].index = static_cast<int>(face_index);
faces[face_index].dirty = dirty_faces[face_index];
faces[face_index].pass_dirty_box = pass_dirty_boxes[face_index];
}
return faces;
}
[[nodiscard]] inline LegacyCanvasStrokeFaceDirtyResult apply_legacy_canvas_stroke_face_dirty_update( [[nodiscard]] inline LegacyCanvasStrokeFaceDirtyResult apply_legacy_canvas_stroke_face_dirty_update(
const LegacyCanvasStrokeFaceDirtyRequest& request, const LegacyCanvasStrokeFaceDirtyRequest& request,
glm::vec4& accumulated_dirty_box, glm::vec4& accumulated_dirty_box,