Share retained stroke preview sample helper

This commit is contained in:
2026-06-13 10:36:07 +02:00
parent 323abdea57
commit 24c0452229
4 changed files with 109 additions and 42 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. `NodeStrokePreview::stroke_draw_samples`
now routes destination bind/unbind, framebuffer copy callback wrapping,
sample-point assembly, and brush-vertex upload/draw through one local helper;
mixer-pass state execution and higher-level pass orchestration remain retained
in the preview node.
- 2026-06-13: DEBT-0036 was narrowed again. `Canvas::stroke_draw` main, pad, - 2026-06-13: DEBT-0036 was narrowed again. `Canvas::stroke_draw` main, pad,
and dual live-pass texture-input binding/unbinding intent now routes through and dual live-pass texture-input binding/unbinding intent now routes through
shared retained stroke execution helpers; sampler binding, concrete GL object shared retained stroke execution helpers; sampler binding, concrete GL object

View File

@@ -3115,6 +3115,10 @@ Results:
binding/unbinding intent now shares retained stroke execution helpers, while binding/unbinding intent now shares retained stroke execution helpers, while
sampler binding, concrete GL object mapping, framebuffer ownership, and final sampler binding, concrete GL object mapping, framebuffer ownership, and final
draw execution remain in the legacy Canvas path. draw execution remain in the legacy Canvas path.
- `NodeStrokePreview::stroke_draw_samples` now shares one local helper for
destination bind/unbind, framebuffer copy callback wrapping, sample-point
assembly, and brush-vertex upload/draw, while mixer-pass state execution and
higher-level pass orchestration remain in the preview node.
- `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: `NodeStrokePreview::stroke_draw_samples()` now routes
destination bind/unbind, framebuffer copy callback wrapping, sample-point
assembly, and brush-vertex upload/draw through one local helper; mixer-pass
state execution and higher-level pass orchestration remain local to the
preview node. Next slice should target the remaining mixer-pass state/copy
ordering without reopening the landed preview live-pass, binding, or final
composite helpers.
- 2026-06-13: `Canvas::stroke_draw` main, pad, and dual live-pass - 2026-06-13: `Canvas::stroke_draw` main, pad, and dual live-pass
texture-input binding/unbinding intent now routes through retained stroke texture-input binding/unbinding intent now routes through retained stroke
execution helpers; sampler binding, concrete GL object mapping, framebuffer execution helpers; sampler binding, concrete GL object mapping, framebuffer

View File

@@ -258,6 +258,93 @@ void bind_stroke_preview_main_pass_textures(
uses_mixer ? mixer_rtt.bindTexture() : unbind_texture_2d(); uses_mixer ? mixer_rtt.bindTexture() : unbind_texture_2d();
} }
void bind_stroke_preview_destination_texture(Texture2D& texture)
{
set_active_texture_unit(stroke_preview_live_slots::kDestination);
texture.bind();
}
void unbind_stroke_preview_destination_texture(Texture2D& texture)
{
set_active_texture_unit(stroke_preview_live_slots::kDestination);
texture.unbind();
}
void copy_stroke_preview_destination_texture_region(
int src_x,
int src_y,
int dst_x,
int dst_y,
int width,
int height)
{
copy_framebuffer_to_texture_2d(src_x, src_y, dst_x, dst_y, width, height);
}
std::array<pp::paint_renderer::CanvasStrokePoint, 4> make_stroke_preview_sample_points(
const std::array<vertex_t, 4>& vertices)
{
return {
pp::paint_renderer::CanvasStrokePoint { .x = vertices[0].pos.x, .y = vertices[0].pos.y },
pp::paint_renderer::CanvasStrokePoint { .x = vertices[1].pos.x, .y = vertices[1].pos.y },
pp::paint_renderer::CanvasStrokePoint { .x = vertices[2].pos.x, .y = vertices[2].pos.y },
pp::paint_renderer::CanvasStrokePoint { .x = vertices[3].pos.x, .y = vertices[3].pos.y },
};
}
void upload_stroke_preview_brush_vertices(DynamicShape& brush_shape, std::span<const vertex_t> vertices)
{
brush_shape.update_vertices(
const_cast<vertex_t*>(vertices.data()),
static_cast<int>(vertices.size()));
}
glm::vec4 execute_stroke_preview_sample_pass(
std::array<vertex_t, 4>& vertices,
glm::vec2 target_size,
Texture2D& blend_texture,
DynamicShape& brush_shape,
bool copy_stroke_destination)
{
const auto sample_points = make_stroke_preview_sample_points(vertices);
const auto result = pp::panopainter::execute_legacy_canvas_stroke_sample(
pp::panopainter::LegacyStrokeSampleExecutionRequest {
.context = "NodeStrokePreview::stroke_draw_samples",
.target_size = target_size,
.vertices = vertices,
.sample_points = sample_points,
.copy_stroke_destination = copy_stroke_destination,
.bind_destination_texture = [&] {
bind_stroke_preview_destination_texture(blend_texture);
},
.copy_framebuffer_to_destination_texture = [](
int src_x,
int src_y,
int dst_x,
int dst_y,
int width,
int height) {
copy_stroke_preview_destination_texture_region(
src_x,
src_y,
dst_x,
dst_y,
width,
height);
},
.unbind_destination_texture = [&] {
unbind_stroke_preview_destination_texture(blend_texture);
},
.upload_brush_vertices = [&](std::span<const vertex_t> brush_vertices) {
upload_stroke_preview_brush_vertices(brush_shape, brush_vertices);
},
.draw_brush_shape = [&] {
brush_shape.draw_fill();
},
});
return result.dirty_bounds;
}
void execute_stroke_preview_background_capture_pass( void execute_stroke_preview_background_capture_pass(
glm::vec2 size, glm::vec2 size,
bool colorize, bool colorize,
@@ -486,48 +573,12 @@ glm::vec4 NodeStrokePreview::stroke_draw_samples(
bool copy_stroke_destination) bool copy_stroke_destination)
{ {
const glm::vec2 size = { m_rtt.getWidth(), m_rtt.getHeight() }; const glm::vec2 size = { m_rtt.getWidth(), m_rtt.getHeight() };
const std::array<pp::paint_renderer::CanvasStrokePoint, 4> sample_points { return execute_stroke_preview_sample_pass(
pp::paint_renderer::CanvasStrokePoint { .x = P[0].pos.x, .y = P[0].pos.y }, P,
pp::paint_renderer::CanvasStrokePoint { .x = P[1].pos.x, .y = P[1].pos.y }, size,
pp::paint_renderer::CanvasStrokePoint { .x = P[2].pos.x, .y = P[2].pos.y }, blend_tex,
pp::paint_renderer::CanvasStrokePoint { .x = P[3].pos.x, .y = P[3].pos.y }, m_brush_shape,
}; copy_stroke_destination);
const auto result = pp::panopainter::execute_legacy_canvas_stroke_sample(
pp::panopainter::LegacyStrokeSampleExecutionRequest {
.context = "NodeStrokePreview::stroke_draw_samples",
.target_size = size,
.vertices = P,
.sample_points = sample_points,
.copy_stroke_destination = copy_stroke_destination,
.bind_destination_texture = [&] {
set_active_texture_unit(1U);
blend_tex.bind(); // bg, copy of framebuffer (copied before drawing)
},
.copy_framebuffer_to_destination_texture = [](
int src_x,
int src_y,
int dst_x,
int dst_y,
int width,
int height) {
// this is also used by the mixer
copy_framebuffer_to_texture_2d(src_x, src_y, dst_x, dst_y, width, height);
},
.unbind_destination_texture = [&] {
set_active_texture_unit(1U);
blend_tex.unbind();
},
.upload_brush_vertices = [&](std::span<const vertex_t> vertices) {
m_brush_shape.update_vertices(
const_cast<vertex_t*>(vertices.data()),
static_cast<int>(vertices.size()));
},
.draw_brush_shape = [&] {
m_brush_shape.draw_fill();
},
});
return result.dirty_bounds;
} }
std::vector<NodeStrokePreview::StrokeFrame> NodeStrokePreview::stroke_draw_compute(Stroke& stroke, float zoom) const std::vector<NodeStrokePreview::StrokeFrame> NodeStrokePreview::stroke_draw_compute(Stroke& stroke, float zoom) const