From 24c0452229ea4b3768904ce10f43679ce636e842 Mon Sep 17 00:00:00 2001 From: omigamedev Date: Sat, 13 Jun 2026 10:36:07 +0200 Subject: [PATCH] Share retained stroke preview sample helper --- docs/modernization/debt.md | 5 ++ docs/modernization/roadmap.md | 4 + docs/modernization/tasks.md | 7 ++ src/node_stroke_preview.cpp | 135 +++++++++++++++++++++++----------- 4 files changed, 109 insertions(+), 42 deletions(-) diff --git a/docs/modernization/debt.md b/docs/modernization/debt.md index 3d15bc8..8e2f348 100644 --- a/docs/modernization/debt.md +++ b/docs/modernization/debt.md @@ -18,6 +18,11 @@ agent or engineer to remove them without reconstructing context from chat. ## 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, and dual live-pass texture-input binding/unbinding intent now routes through shared retained stroke execution helpers; sampler binding, concrete GL object diff --git a/docs/modernization/roadmap.md b/docs/modernization/roadmap.md index 6f8772b..7e535cc 100644 --- a/docs/modernization/roadmap.md +++ b/docs/modernization/roadmap.md @@ -3115,6 +3115,10 @@ Results: binding/unbinding intent now shares retained stroke execution helpers, while sampler binding, concrete GL object mapping, framebuffer ownership, and final 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 shares the retained stroke execution helper callback surface, while shader setup, pad color selection, framebuffer ownership, and final OpenGL draw diff --git a/docs/modernization/tasks.md b/docs/modernization/tasks.md index 1736211..c18d7f9 100644 --- a/docs/modernization/tasks.md +++ b/docs/modernization/tasks.md @@ -509,6 +509,13 @@ Done Checks: 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 texture-input binding/unbinding intent now routes through retained stroke execution helpers; sampler binding, concrete GL object mapping, framebuffer diff --git a/src/node_stroke_preview.cpp b/src/node_stroke_preview.cpp index e22cf4e..cd8a108 100644 --- a/src/node_stroke_preview.cpp +++ b/src/node_stroke_preview.cpp @@ -258,6 +258,93 @@ void bind_stroke_preview_main_pass_textures( 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 make_stroke_preview_sample_points( + const std::array& 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 vertices) +{ + brush_shape.update_vertices( + const_cast(vertices.data()), + static_cast(vertices.size())); +} + +glm::vec4 execute_stroke_preview_sample_pass( + std::array& 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 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( glm::vec2 size, bool colorize, @@ -486,48 +573,12 @@ glm::vec4 NodeStrokePreview::stroke_draw_samples( bool copy_stroke_destination) { const glm::vec2 size = { m_rtt.getWidth(), m_rtt.getHeight() }; - const std::array sample_points { - pp::paint_renderer::CanvasStrokePoint { .x = P[0].pos.x, .y = P[0].pos.y }, - pp::paint_renderer::CanvasStrokePoint { .x = P[1].pos.x, .y = P[1].pos.y }, - pp::paint_renderer::CanvasStrokePoint { .x = P[2].pos.x, .y = P[2].pos.y }, - pp::paint_renderer::CanvasStrokePoint { .x = P[3].pos.x, .y = P[3].pos.y }, - }; - 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 vertices) { - m_brush_shape.update_vertices( - const_cast(vertices.data()), - static_cast(vertices.size())); - }, - .draw_brush_shape = [&] { - m_brush_shape.draw_fill(); - }, - }); - - return result.dirty_bounds; + return execute_stroke_preview_sample_pass( + P, + size, + blend_tex, + m_brush_shape, + copy_stroke_destination); } std::vector NodeStrokePreview::stroke_draw_compute(Stroke& stroke, float zoom) const