Extract preview final composite orchestration

This commit is contained in:
2026-06-13 18:36:43 +02:00
parent 3d3a99a536
commit 87c4bee112
4 changed files with 195 additions and 65 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: 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()` - 2026-06-13: DEBT-0036 was narrowed again. `NodeStrokePreview::draw_stroke_immediate()`
now routes main-pass texture binding through now routes main-pass texture binding through
`bind_legacy_node_stroke_preview_main_pass_textures(...)`; the retained path `bind_legacy_node_stroke_preview_main_pass_textures(...)`; the retained path

View File

@@ -6,6 +6,7 @@
#include "legacy_canvas_stroke_shader_services.h" #include "legacy_canvas_stroke_shader_services.h"
#include "legacy_canvas_stroke_services.h" #include "legacy_canvas_stroke_services.h"
#include "paint_renderer/compositor.h" #include "paint_renderer/compositor.h"
#include "texture.h"
#include <algorithm> #include <algorithm>
#include <cmath> #include <cmath>
@@ -242,6 +243,69 @@ struct LegacyNodeStrokePreviewMainPassTextureDispatch {
std::function<void()> bind_mixer; std::function<void()> 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<void()> setup_composite_shader;
std::function<void()> bind_composite_samplers;
std::function<void()> bind_composite_inputs;
std::function<void()> 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<void(int, int, int, int, int, int)> 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<int>(request.size.x),
.height = static_cast<int>(request.size.y),
});
return true;
}
[[nodiscard]] inline LegacyNodeStrokePreviewMainPassTextureDispatch make_legacy_node_stroke_preview_main_pass_texture_dispatch( [[nodiscard]] inline LegacyNodeStrokePreviewMainPassTextureDispatch make_legacy_node_stroke_preview_main_pass_texture_dispatch(
std::function<void(int)> activate_texture_unit, std::function<void(int)> activate_texture_unit,
std::function<void()> bind_brush_tip, std::function<void()> bind_brush_tip,

View File

@@ -151,54 +151,62 @@ pp::panopainter::LegacyStrokeCompositeUniforms make_stroke_preview_mix_composite
void execute_stroke_preview_final_composite_pass(const StrokePreviewCompositePassInputs& inputs) void execute_stroke_preview_final_composite_pass(const StrokePreviewCompositePassInputs& inputs)
{ {
pp::panopainter::execute_legacy_stroke_preview_final_composite( [[maybe_unused]] const bool composite_ok =
[&] { pp::panopainter::execute_legacy_node_stroke_preview_final_composite(
pp::panopainter::setup_legacy_stroke_composite_shader( pp::panopainter::LegacyNodeStrokePreviewFinalCompositeRequest {
pp::panopainter::LegacyStrokeCompositeUniforms { .resolution = inputs.resolution,
.resolution = inputs.resolution, .pattern_scale = inputs.pattern_scale,
.pattern = { .brush = &inputs.brush,
.scale = inputs.pattern_scale, .composite_pass = &inputs.composite_pass,
.invert = static_cast<float>(inputs.brush.m_pattern_invert), .setup_composite_shader = [&] {
.brightness = inputs.brush.m_pattern_brightness, pp::panopainter::setup_legacy_stroke_composite_shader(
.contrast = inputs.brush.m_pattern_contrast, pp::panopainter::LegacyStrokeCompositeUniforms {
.depth = inputs.brush.m_pattern_depth, .resolution = inputs.resolution,
.blend_mode = inputs.composite_pass.pattern_blend_mode, .pattern = {
.offset = glm::vec2(inputs.brush.m_pattern_rand_offset ? 0.5f : 0.0f), .scale = inputs.pattern_scale,
}, .invert = static_cast<float>(inputs.brush.m_pattern_invert),
.mvp = glm::ortho(-.5f, .5f, -.5f, .5f, -1.f, 1.f), .brightness = inputs.brush.m_pattern_brightness,
.layer_alpha = 1.0f, .contrast = inputs.brush.m_pattern_contrast,
.alpha_lock = false, .depth = inputs.brush.m_pattern_depth,
.mask_enabled = false, .blend_mode = inputs.composite_pass.pattern_blend_mode,
.use_fragcoord = false, .offset = glm::vec2(inputs.brush.m_pattern_rand_offset ? 0.5f : 0.0f),
.blend_mode = inputs.brush.m_blend_mode, },
.use_dual = inputs.composite_pass.use_dual, .mvp = glm::ortho(-.5f, .5f, -.5f, .5f, -1.f, 1.f),
.dual_blend_mode = inputs.composite_pass.dual_blend_mode, .layer_alpha = 1.0f,
.dual_alpha = inputs.composite_pass.dual_alpha, .alpha_lock = false,
.use_pattern = inputs.composite_pass.use_pattern, .mask_enabled = false,
}); .use_fragcoord = false,
}, .blend_mode = inputs.brush.m_blend_mode,
[&] { .use_dual = inputs.composite_pass.use_dual,
inputs.linear_sampler.bind(stroke_preview_composite_slots::kBackground); .dual_blend_mode = inputs.composite_pass.dual_blend_mode,
inputs.linear_sampler.bind(stroke_preview_composite_slots::kStroke); .dual_alpha = inputs.composite_pass.dual_alpha,
inputs.linear_sampler.bind(2U); .use_pattern = inputs.composite_pass.use_pattern,
inputs.linear_sampler.bind(stroke_preview_composite_slots::kDual); });
inputs.repeat_sampler.bind(stroke_preview_composite_slots::kPattern); },
}, .bind_composite_samplers = [&] {
[&] { inputs.linear_sampler.bind(stroke_preview_composite_slots::kBackground);
set_active_texture_unit(stroke_preview_composite_slots::kBackground); inputs.linear_sampler.bind(stroke_preview_composite_slots::kStroke);
inputs.background_texture.bind(); inputs.linear_sampler.bind(2U);
set_active_texture_unit(stroke_preview_composite_slots::kStroke); inputs.linear_sampler.bind(stroke_preview_composite_slots::kDual);
inputs.stroke_texture.bind(); inputs.repeat_sampler.bind(stroke_preview_composite_slots::kPattern);
set_active_texture_unit(stroke_preview_composite_slots::kDual); },
inputs.dual_texture.bind(); .bind_composite_inputs = [&] {
set_active_texture_unit(stroke_preview_composite_slots::kPattern); set_active_texture_unit(stroke_preview_composite_slots::kBackground);
inputs.brush.m_pattern_texture ? inputs.background_texture.bind();
inputs.brush.m_pattern_texture->bind() : set_active_texture_unit(stroke_preview_composite_slots::kStroke);
unbind_texture_2d(); inputs.stroke_texture.bind();
}, set_active_texture_unit(stroke_preview_composite_slots::kDual);
[&] { inputs.dual_texture.bind();
inputs.draw_composite(); 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( 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) void copy_stroke_preview_result_to_texture(Texture2D& preview_texture, glm::vec2 size)
{ {
pp::panopainter::copy_legacy_stroke_preview_texture( [[maybe_unused]] const bool copy_ok =
[&] { pp::panopainter::copy_legacy_node_stroke_preview_result(
preview_texture.bind(); pp::panopainter::LegacyNodeStrokePreviewCopyResultRequest {
}, .preview_texture = &preview_texture,
[]( .size = size,
int src_x, .copy_framebuffer_to_texture = [](
int src_y, int src_x,
int dst_x, int src_y,
int dst_y, int dst_x,
int width, int dst_y,
int height) { int width,
copy_framebuffer_to_texture_2d(src_x, src_y, dst_x, dst_y, width, height); int height) {
}, copy_framebuffer_to_texture_2d(src_x, src_y, dst_x, dst_y, width, height);
pp::panopainter::LegacyStrokePreviewCopySize { },
.width = static_cast<int>(size.x), });
.height = static_cast<int>(size.y), assert(copy_ok);
});
} }
} }

View File

@@ -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<std::string> 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<const Brush*>(1),
.composite_pass = reinterpret_cast<const pp::paint_renderer::CanvasStrokeCompositePassPlan*>(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<std::string> {
"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<Texture2D*>(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<std::string> {
"copy:0,0,0,0,32,16",
});
}
void legacy_node_stroke_preview_stroke_setup_plan_preserves_curve_and_dual_inputs(pp::tests::Harness& h) 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( const auto plan = pp::panopainter::plan_legacy_node_stroke_preview_stroke_setup(
@@ -3255,6 +3307,9 @@ int main()
harness.run( harness.run(
"legacy_node_stroke_preview_main_pass_texture_dispatch_preserves_order", "legacy_node_stroke_preview_main_pass_texture_dispatch_preserves_order",
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( 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",
legacy_node_stroke_preview_stroke_setup_plan_preserves_curve_and_dual_inputs); legacy_node_stroke_preview_stroke_setup_plan_preserves_curve_and_dual_inputs);