From 87c4bee1123af56fd0903dd89f50b01509b89b99 Mon Sep 17 00:00:00 2001 From: omigamedev Date: Sat, 13 Jun 2026 18:36:43 +0200 Subject: [PATCH] Extract preview final composite orchestration --- docs/modernization/debt.md | 4 + ...y_node_stroke_preview_execution_services.h | 64 ++++++++ src/node_stroke_preview.cpp | 137 +++++++++--------- tests/paint_renderer/compositor_tests.cpp | 55 +++++++ 4 files changed, 195 insertions(+), 65 deletions(-) diff --git a/docs/modernization/debt.md b/docs/modernization/debt.md index a9dd91f..9dd3cf8 100644 --- a/docs/modernization/debt.md +++ b/docs/modernization/debt.md @@ -18,6 +18,10 @@ agent or engineer to remove them without reconstructing context from chat. ## Recent Reductions +- 2026-06-13: DEBT-0036 was narrowed again. `NodeStrokePreview::draw_stroke_immediate()` + now routes final-composite setup and preview copy-back through retained + helpers in `legacy_node_stroke_preview_execution_services.h`; the retained + path still owns the concrete texture objects and pass-order wiring. - 2026-06-13: DEBT-0036 was narrowed again. `NodeStrokePreview::draw_stroke_immediate()` now routes main-pass texture binding through `bind_legacy_node_stroke_preview_main_pass_textures(...)`; the retained path diff --git a/src/legacy_node_stroke_preview_execution_services.h b/src/legacy_node_stroke_preview_execution_services.h index ab2a52d..1575b61 100644 --- a/src/legacy_node_stroke_preview_execution_services.h +++ b/src/legacy_node_stroke_preview_execution_services.h @@ -6,6 +6,7 @@ #include "legacy_canvas_stroke_shader_services.h" #include "legacy_canvas_stroke_services.h" #include "paint_renderer/compositor.h" +#include "texture.h" #include #include @@ -242,6 +243,69 @@ struct LegacyNodeStrokePreviewMainPassTextureDispatch { std::function bind_mixer; }; +struct LegacyNodeStrokePreviewFinalCompositeRequest { + glm::vec2 resolution {}; + glm::vec2 pattern_scale {}; + const Brush* brush = nullptr; + const pp::paint_renderer::CanvasStrokeCompositePassPlan* composite_pass = nullptr; + std::function setup_composite_shader; + std::function bind_composite_samplers; + std::function bind_composite_inputs; + std::function draw_composite; +}; + +[[nodiscard]] inline bool execute_legacy_node_stroke_preview_final_composite( + const LegacyNodeStrokePreviewFinalCompositeRequest& request) +{ + if (!request.brush || + !request.composite_pass || + !request.setup_composite_shader || + !request.bind_composite_samplers || + !request.bind_composite_inputs || + !request.draw_composite) { + return false; + } + + pp::panopainter::execute_legacy_stroke_preview_final_composite( + [&] { + request.setup_composite_shader(); + }, + [&] { + request.bind_composite_samplers(); + }, + [&] { + request.bind_composite_inputs(); + }, + [&] { + request.draw_composite(); + }); + return true; +} + +struct LegacyNodeStrokePreviewCopyResultRequest { + Texture2D* preview_texture = nullptr; + glm::vec2 size {}; + std::function copy_framebuffer_to_texture; +}; + +[[nodiscard]] inline bool copy_legacy_node_stroke_preview_result( + const LegacyNodeStrokePreviewCopyResultRequest& request) +{ + if (!request.preview_texture || !request.copy_framebuffer_to_texture) { + return false; + } + pp::panopainter::copy_legacy_stroke_preview_texture( + [&] { + request.preview_texture->bind(); + }, + request.copy_framebuffer_to_texture, + pp::panopainter::LegacyStrokePreviewCopySize { + .width = static_cast(request.size.x), + .height = static_cast(request.size.y), + }); + return true; +} + [[nodiscard]] inline LegacyNodeStrokePreviewMainPassTextureDispatch make_legacy_node_stroke_preview_main_pass_texture_dispatch( std::function activate_texture_unit, std::function bind_brush_tip, diff --git a/src/node_stroke_preview.cpp b/src/node_stroke_preview.cpp index 0232284..8c26333 100644 --- a/src/node_stroke_preview.cpp +++ b/src/node_stroke_preview.cpp @@ -151,54 +151,62 @@ pp::panopainter::LegacyStrokeCompositeUniforms make_stroke_preview_mix_composite void execute_stroke_preview_final_composite_pass(const StrokePreviewCompositePassInputs& inputs) { - pp::panopainter::execute_legacy_stroke_preview_final_composite( - [&] { - pp::panopainter::setup_legacy_stroke_composite_shader( - pp::panopainter::LegacyStrokeCompositeUniforms { - .resolution = inputs.resolution, - .pattern = { - .scale = inputs.pattern_scale, - .invert = static_cast(inputs.brush.m_pattern_invert), - .brightness = inputs.brush.m_pattern_brightness, - .contrast = inputs.brush.m_pattern_contrast, - .depth = inputs.brush.m_pattern_depth, - .blend_mode = inputs.composite_pass.pattern_blend_mode, - .offset = glm::vec2(inputs.brush.m_pattern_rand_offset ? 0.5f : 0.0f), - }, - .mvp = glm::ortho(-.5f, .5f, -.5f, .5f, -1.f, 1.f), - .layer_alpha = 1.0f, - .alpha_lock = false, - .mask_enabled = false, - .use_fragcoord = false, - .blend_mode = inputs.brush.m_blend_mode, - .use_dual = inputs.composite_pass.use_dual, - .dual_blend_mode = inputs.composite_pass.dual_blend_mode, - .dual_alpha = inputs.composite_pass.dual_alpha, - .use_pattern = inputs.composite_pass.use_pattern, - }); - }, - [&] { - inputs.linear_sampler.bind(stroke_preview_composite_slots::kBackground); - inputs.linear_sampler.bind(stroke_preview_composite_slots::kStroke); - inputs.linear_sampler.bind(2U); - inputs.linear_sampler.bind(stroke_preview_composite_slots::kDual); - inputs.repeat_sampler.bind(stroke_preview_composite_slots::kPattern); - }, - [&] { - set_active_texture_unit(stroke_preview_composite_slots::kBackground); - inputs.background_texture.bind(); - set_active_texture_unit(stroke_preview_composite_slots::kStroke); - inputs.stroke_texture.bind(); - set_active_texture_unit(stroke_preview_composite_slots::kDual); - inputs.dual_texture.bind(); - set_active_texture_unit(stroke_preview_composite_slots::kPattern); - inputs.brush.m_pattern_texture ? - inputs.brush.m_pattern_texture->bind() : - unbind_texture_2d(); - }, - [&] { - inputs.draw_composite(); - }); + [[maybe_unused]] const bool composite_ok = + pp::panopainter::execute_legacy_node_stroke_preview_final_composite( + pp::panopainter::LegacyNodeStrokePreviewFinalCompositeRequest { + .resolution = inputs.resolution, + .pattern_scale = inputs.pattern_scale, + .brush = &inputs.brush, + .composite_pass = &inputs.composite_pass, + .setup_composite_shader = [&] { + pp::panopainter::setup_legacy_stroke_composite_shader( + pp::panopainter::LegacyStrokeCompositeUniforms { + .resolution = inputs.resolution, + .pattern = { + .scale = inputs.pattern_scale, + .invert = static_cast(inputs.brush.m_pattern_invert), + .brightness = inputs.brush.m_pattern_brightness, + .contrast = inputs.brush.m_pattern_contrast, + .depth = inputs.brush.m_pattern_depth, + .blend_mode = inputs.composite_pass.pattern_blend_mode, + .offset = glm::vec2(inputs.brush.m_pattern_rand_offset ? 0.5f : 0.0f), + }, + .mvp = glm::ortho(-.5f, .5f, -.5f, .5f, -1.f, 1.f), + .layer_alpha = 1.0f, + .alpha_lock = false, + .mask_enabled = false, + .use_fragcoord = false, + .blend_mode = inputs.brush.m_blend_mode, + .use_dual = inputs.composite_pass.use_dual, + .dual_blend_mode = inputs.composite_pass.dual_blend_mode, + .dual_alpha = inputs.composite_pass.dual_alpha, + .use_pattern = inputs.composite_pass.use_pattern, + }); + }, + .bind_composite_samplers = [&] { + inputs.linear_sampler.bind(stroke_preview_composite_slots::kBackground); + inputs.linear_sampler.bind(stroke_preview_composite_slots::kStroke); + inputs.linear_sampler.bind(2U); + inputs.linear_sampler.bind(stroke_preview_composite_slots::kDual); + inputs.repeat_sampler.bind(stroke_preview_composite_slots::kPattern); + }, + .bind_composite_inputs = [&] { + set_active_texture_unit(stroke_preview_composite_slots::kBackground); + inputs.background_texture.bind(); + set_active_texture_unit(stroke_preview_composite_slots::kStroke); + inputs.stroke_texture.bind(); + set_active_texture_unit(stroke_preview_composite_slots::kDual); + inputs.dual_texture.bind(); + set_active_texture_unit(stroke_preview_composite_slots::kPattern); + inputs.brush.m_pattern_texture ? + inputs.brush.m_pattern_texture->bind() : + unbind_texture_2d(); + }, + .draw_composite = [&] { + inputs.draw_composite(); + }, + }); + assert(composite_ok); } void copy_stroke_preview_framebuffer_to_texture( @@ -394,23 +402,22 @@ void execute_stroke_preview_background_capture_pass( void copy_stroke_preview_result_to_texture(Texture2D& preview_texture, glm::vec2 size) { - pp::panopainter::copy_legacy_stroke_preview_texture( - [&] { - preview_texture.bind(); - }, - []( - 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); - }, - pp::panopainter::LegacyStrokePreviewCopySize { - .width = static_cast(size.x), - .height = static_cast(size.y), - }); + [[maybe_unused]] const bool copy_ok = + pp::panopainter::copy_legacy_node_stroke_preview_result( + pp::panopainter::LegacyNodeStrokePreviewCopyResultRequest { + .preview_texture = &preview_texture, + .size = size, + .copy_framebuffer_to_texture = []( + 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); + }, + }); + assert(copy_ok); } } diff --git a/tests/paint_renderer/compositor_tests.cpp b/tests/paint_renderer/compositor_tests.cpp index 40c2d3b..c6e9bc1 100644 --- a/tests/paint_renderer/compositor_tests.cpp +++ b/tests/paint_renderer/compositor_tests.cpp @@ -2659,6 +2659,58 @@ void legacy_node_stroke_preview_main_pass_texture_dispatch_preserves_order(pp::t }); } +void legacy_node_stroke_preview_final_composite_and_copy_helpers_preserve_order(pp::tests::Harness& h) +{ + std::vector steps; + const bool composite_ok = pp::panopainter::execute_legacy_node_stroke_preview_final_composite( + pp::panopainter::LegacyNodeStrokePreviewFinalCompositeRequest { + .resolution = glm::vec2(64.0F, 32.0F), + .pattern_scale = glm::vec2(0.25F, -0.5F), + .brush = reinterpret_cast(1), + .composite_pass = reinterpret_cast(1), + .setup_composite_shader = [&] { + steps.emplace_back("setup"); + }, + .bind_composite_samplers = [&] { + steps.emplace_back("bind_samplers"); + }, + .bind_composite_inputs = [&] { + steps.emplace_back("bind_inputs"); + }, + .draw_composite = [&] { + steps.emplace_back("draw"); + }, + }); + PP_EXPECT(h, composite_ok); + PP_EXPECT(h, steps == std::vector { + "setup", + "bind_samplers", + "bind_inputs", + "draw", + }); + + steps.clear(); + const bool copy_ok = pp::panopainter::copy_legacy_node_stroke_preview_result( + pp::panopainter::LegacyNodeStrokePreviewCopyResultRequest { + .preview_texture = reinterpret_cast(1), + .size = glm::vec2(32.0F, 16.0F), + .copy_framebuffer_to_texture = [&](int src_x, int src_y, int dst_x, int dst_y, int width, int height) { + steps.emplace_back( + "copy:" + + std::to_string(src_x) + "," + + std::to_string(src_y) + "," + + std::to_string(dst_x) + "," + + std::to_string(dst_y) + "," + + std::to_string(width) + "," + + std::to_string(height)); + }, + }); + PP_EXPECT(h, copy_ok); + PP_EXPECT(h, steps == std::vector { + "copy:0,0,0,0,32,16", + }); +} + void legacy_node_stroke_preview_stroke_setup_plan_preserves_curve_and_dual_inputs(pp::tests::Harness& h) { const auto plan = pp::panopainter::plan_legacy_node_stroke_preview_stroke_setup( @@ -3255,6 +3307,9 @@ int main() harness.run( "legacy_node_stroke_preview_main_pass_texture_dispatch_preserves_order", legacy_node_stroke_preview_main_pass_texture_dispatch_preserves_order); + harness.run( + "legacy_node_stroke_preview_final_composite_and_copy_helpers_preserve_order", + legacy_node_stroke_preview_final_composite_and_copy_helpers_preserve_order); harness.run( "legacy_node_stroke_preview_stroke_setup_plan_preserves_curve_and_dual_inputs", legacy_node_stroke_preview_stroke_setup_plan_preserves_curve_and_dual_inputs);