Share retained stroke preview binding helpers

This commit is contained in:
2026-06-13 10:27:50 +02:00
parent ddadaa0405
commit 3f071620dc
4 changed files with 145 additions and 67 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` live-pass
sampler binding, dual/main pass texture binding, checkerboard/background
capture wrapping, and final preview copy-back now route through shared local
helpers; mixer state execution and per-sample GL ordering remain retained in
the preview node.
- 2026-06-13: DEBT-0036 was narrowed again. `NodeStrokePreview` dual-pass and - 2026-06-13: DEBT-0036 was narrowed again. `NodeStrokePreview` dual-pass and
main-pass frame-loop execution plus full-frame copy-back now route through main-pass frame-loop execution plus full-frame copy-back now route through
shared local helpers; checkerboard/background capture ordering, texture-unit shared local helpers; checkerboard/background capture ordering, texture-unit

View File

@@ -3107,6 +3107,10 @@ Results:
full-frame copy-back now share one local retained helper surface, while full-frame copy-back now share one local retained helper surface, while
checkerboard/background capture ordering, texture-unit binding, mixer checkerboard/background capture ordering, texture-unit binding, mixer
ownership, and final composite semantics remain in the preview node. ownership, and final composite semantics remain in the preview node.
- `NodeStrokePreview` live-pass sampler binding, dual/main pass texture
binding, checkerboard/background capture wrapping, and final preview
copy-back now share named local helpers, while mixer state execution and
per-sample GL ordering 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::draw_stroke_immediate()` live-pass sampler
binding, dual/main pass texture binding, checkerboard/background capture
wrapping, and final preview copy-back now route through named local helpers;
mixer state execution and per-sample GL ordering remain local to the preview
node. Next slice should target the remaining mixer-pass state/copy ordering
or sample-pass destination callback wrapping without reopening the landed
preview live-pass or final-composite helpers.
- 2026-06-13: `NodeStrokePreview::draw_stroke_immediate()` dual-pass and - 2026-06-13: `NodeStrokePreview::draw_stroke_immediate()` dual-pass and
main-pass frame-loop execution plus full-frame copy-back now route through main-pass frame-loop execution plus full-frame copy-back now route through
shared local helpers; checkerboard/background capture ordering, texture-unit shared local helpers; checkerboard/background capture ordering, texture-unit

View File

@@ -103,6 +103,14 @@ constexpr std::uint32_t kDual = 3U;
constexpr std::uint32_t kPattern = 4U; constexpr std::uint32_t kPattern = 4U;
} }
namespace stroke_preview_live_slots {
constexpr std::uint32_t kTip = 0U;
constexpr std::uint32_t kDestination = 1U;
constexpr std::uint32_t kPattern = 2U;
constexpr std::uint32_t kMixer = 3U;
constexpr std::uint32_t kReservedLinear = 4U;
}
struct StrokePreviewCompositePassInputs { struct StrokePreviewCompositePassInputs {
glm::vec2 resolution; glm::vec2 resolution;
glm::vec2 pattern_scale; glm::vec2 pattern_scale;
@@ -203,6 +211,110 @@ void copy_stroke_preview_framebuffer_to_texture(
static_cast<int>(size.y)); static_cast<int>(size.y));
} }
void bind_stroke_preview_live_samplers(
Sampler& mipmap_sampler,
Sampler& linear_sampler,
Sampler& repeat_sampler)
{
mipmap_sampler.bind(stroke_preview_live_slots::kTip);
linear_sampler.bind(stroke_preview_live_slots::kDestination);
repeat_sampler.bind(stroke_preview_live_slots::kPattern);
linear_sampler.bind(stroke_preview_live_slots::kMixer);
linear_sampler.bind(stroke_preview_live_slots::kReservedLinear);
}
void bind_stroke_preview_dual_pass_textures(const Brush& dual_brush)
{
set_active_texture_unit(stroke_preview_live_slots::kTip);
dual_brush.m_tip_texture ?
dual_brush.m_tip_texture->bind() :
unbind_texture_2d();
}
void bind_stroke_preview_main_pass_textures(
const Brush& brush,
Texture2D& stroke_destination_texture,
RTT& mixer_rtt,
bool copy_stroke_destination,
bool uses_mixer)
{
set_active_texture_unit(stroke_preview_live_slots::kTip);
brush.m_tip_texture ?
brush.m_tip_texture->bind() :
unbind_texture_2d();
if (copy_stroke_destination)
{
set_active_texture_unit(stroke_preview_live_slots::kDestination);
stroke_destination_texture.bind();
}
set_active_texture_unit(stroke_preview_live_slots::kPattern);
brush.m_pattern_texture ?
brush.m_pattern_texture->bind() :
unbind_texture_2d();
set_active_texture_unit(stroke_preview_live_slots::kMixer);
uses_mixer ? mixer_rtt.bindTexture() : unbind_texture_2d();
}
void execute_stroke_preview_background_capture_pass(
glm::vec2 size,
bool colorize,
Texture2D& background_texture,
const std::function<void()>& draw_checkerboard)
{
pp::panopainter::execute_legacy_stroke_preview_background_capture(
[&] {
const float aspect = size.x / size.y;
pp::panopainter::setup_legacy_canvas_draw_merge_checkerboard_shader(
pp::panopainter::LegacyCanvasDrawMergeCheckerboardUniforms {
.mvp = glm::ortho(-.5f, .5f, -.5f / aspect, .5f / aspect, -1.f, 1.f),
.colorize = colorize,
});
},
[&] {
draw_checkerboard();
},
[&] {
background_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<int>(size.x),
.height = static_cast<int>(size.y),
});
}
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<int>(size.x),
.height = static_cast<int>(size.y),
});
}
template <typename ClearTarget, typename ComputeFrames, typename BeforeFrame, typename DrawSample> template <typename ClearTarget, typename ComputeFrames, typename BeforeFrame, typename DrawSample>
void execute_stroke_preview_live_pass( void execute_stroke_preview_live_pass(
Texture2D& output_texture, Texture2D& output_texture,
@@ -471,11 +583,10 @@ void NodeStrokePreview::draw_stroke_immediate()
apply_stroke_preview_viewport(0, 0, m_rtt.getWidth(), m_rtt.getHeight()); apply_stroke_preview_viewport(0, 0, m_rtt.getWidth(), m_rtt.getHeight());
m_rtt.bindFramebuffer(); m_rtt.bindFramebuffer();
m_rtt.clear(); m_rtt.clear();
m_sampler_mipmap.bind(0); bind_stroke_preview_live_samplers(
m_sampler_linear.bind(1); m_sampler_mipmap,
m_sampler_linear_repeat.bind(2); m_sampler_linear,
m_sampler_linear.bind(3); m_sampler_linear_repeat);
m_sampler_linear.bind(4);
const auto& b = m_brush; const auto& b = m_brush;
@@ -581,10 +692,7 @@ void NodeStrokePreview::draw_stroke_immediate()
if (material.dual_pass.enabled) if (material.dual_pass.enabled)
{ {
pp::panopainter::setup_legacy_stroke_dual_shader(material.dual_pass.uses_pattern); pp::panopainter::setup_legacy_stroke_dual_shader(material.dual_pass.uses_pattern);
set_active_texture_unit(0U); bind_stroke_preview_dual_pass_textures(*dual_brush);
dual_brush->m_tip_texture ?
dual_brush->m_tip_texture->bind() :
unbind_texture_2d();
execute_stroke_preview_live_pass( execute_stroke_preview_live_pass(
m_tex_dual, m_tex_dual,
size, size,
@@ -605,35 +713,12 @@ void NodeStrokePreview::draw_stroke_immediate()
// CHEKCERBOARD // CHEKCERBOARD
pp::panopainter::execute_legacy_stroke_preview_background_capture( execute_stroke_preview_background_capture_pass(
[&] { size,
// copy background color to tex2 b->m_tip_mix > 0.f || b->m_blend_mode != 0,
const float aspect = size.x / size.y; m_tex_background,
pp::panopainter::setup_legacy_canvas_draw_merge_checkerboard_shader(
pp::panopainter::LegacyCanvasDrawMergeCheckerboardUniforms {
.mvp = glm::ortho(-.5f, .5f, -.5f / aspect, .5f / aspect, -1.f, 1.f),
.colorize = b->m_tip_mix > 0.f || b->m_blend_mode != 0,
});
},
[&] { [&] {
m_plane.draw_fill(); m_plane.draw_fill();
},
[&] {
//m_rtt.clear({ .3f, .3f, .3f, 1.f });
m_tex_background.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<int>(size.x),
.height = static_cast<int>(size.y),
}); });
// DRAW MAIN BRUSH // DRAW MAIN BRUSH
@@ -645,19 +730,12 @@ void NodeStrokePreview::draw_stroke_immediate()
b->m_tip_wet, b->m_tip_wet,
b->m_tip_noise); b->m_tip_noise);
set_active_texture_unit(0U); bind_stroke_preview_main_pass_textures(
b->m_tip_texture->bind(); *b,
if (copy_stroke_destination) m_tex,
{ m_rtt_mixer,
set_active_texture_unit(1U); copy_stroke_destination,
m_tex.bind(); // tmp swap for blending preview_composite_plan.uses_mixer);
}
set_active_texture_unit(2U);
b->m_pattern_texture ?
b->m_pattern_texture->bind() :
unbind_texture_2d();
set_active_texture_unit(3U);
preview_composite_plan.uses_mixer ? m_rtt_mixer.bindTexture() : unbind_texture_2d();
execute_stroke_preview_live_pass( execute_stroke_preview_live_pass(
m_tex, m_tex,
size, size,
@@ -682,7 +760,7 @@ void NodeStrokePreview::draw_stroke_immediate()
[&](auto& frame, Texture2D& blend_texture, bool copy_destination) { [&](auto& frame, Texture2D& blend_texture, bool copy_destination) {
/*auto rect =*/ stroke_draw_samples(frame.shapes, blend_texture, copy_destination); /*auto rect =*/ stroke_draw_samples(frame.shapes, blend_texture, copy_destination);
}); });
set_active_texture_unit(3U); set_active_texture_unit(stroke_preview_live_slots::kMixer);
m_rtt_mixer.unbindTexture(); m_rtt_mixer.unbindTexture();
// COMPOSITE // COMPOSITE
@@ -704,23 +782,7 @@ void NodeStrokePreview::draw_stroke_immediate()
}); });
// copy the result to the actual preview // copy the result to the actual preview
pp::panopainter::copy_legacy_stroke_preview_texture( copy_stroke_preview_result_to_texture(m_tex_preview, size);
[&] {
m_tex_preview.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<int>(size.x),
.height = static_cast<int>(size.y),
});
m_rtt.unbindFramebuffer(); m_rtt.unbindFramebuffer();