From 6c58b6bb5dd9173e4d20617be570f61e777af6fc Mon Sep 17 00:00:00 2001 From: omigamedev Date: Fri, 12 Jun 2026 22:35:07 +0200 Subject: [PATCH] Isolate legacy stroke shader setup --- docs/modernization/debt.md | 13 +- docs/modernization/roadmap.md | 8 +- src/canvas.cpp | 69 +++++---- src/legacy_canvas_stroke_shader_services.h | 168 +++++++++++++++++++++ src/node_stroke_preview.cpp | 79 +++++----- 5 files changed, 262 insertions(+), 75 deletions(-) create mode 100644 src/legacy_canvas_stroke_shader_services.h diff --git a/docs/modernization/debt.md b/docs/modernization/debt.md index 4220ef3..c087233 100644 --- a/docs/modernization/debt.md +++ b/docs/modernization/debt.md @@ -18,12 +18,19 @@ agent or engineer to remove them without reconstructing context from chat. ## Recent Reductions +- 2026-06-12: DEBT-0036 was narrowed again. Canvas and `NodeStrokePreview` + live stroke passes now route retained `kShader::Stroke` binding plus setup, + blend, pattern, and per-sample uniform writes through + `legacy_canvas_stroke_shader_services.h`. RTT/texture ownership, + dirty-box policy, composite/pad/checkerboard shaders, and retained callback + execution remain in the legacy callers until the stroke backend is owned by + renderer services. - 2026-06-12: DEBT-0036 was narrowed again. Canvas and `NodeStrokePreview` stroke sample execution now delegate optional destination copy, brush vertex upload, brush-shape draw, and destination unbind through - `execute_legacy_canvas_stroke_sample`. Shader setup, uniforms, RTT/texture - ownership, dirty-box policy, and retained callback execution remain in the - legacy callers until a renderer-owned stroke backend replaces the adapter. + `execute_legacy_canvas_stroke_sample`. Shader setup, RTT/texture ownership, + dirty-box policy, and retained callback execution remain in the legacy callers + until a renderer-owned stroke backend replaces the adapter. - 2026-06-12: DEBT-0036 was narrowed again. `NodeStrokePreview` now consumes the same `CanvasStrokeMaterialPlan` boundary for preview dual-brush, each-sample pattern, and composite material decisions. Preview GL draw calls, diff --git a/docs/modernization/roadmap.md b/docs/modernization/roadmap.md index 56c4f78..6a8e0e3 100644 --- a/docs/modernization/roadmap.md +++ b/docs/modernization/roadmap.md @@ -1346,9 +1346,11 @@ each-sample pattern, dual-brush, and final composite decisions, and Canvas and `NodeStrokePreview` stroke sample execution also delegate optional destination copy, brush vertex upload, brush-shape draw, and destination unbind through `execute_legacy_canvas_stroke_sample`, creating the first retained -OpenGL stroke execution service seam. Shader setup, uniforms, RTT/texture -ownership, dirty-box policy, and retained callback execution remain under -`DEBT-0036`. +OpenGL stroke execution service seam. Live stroke `kShader::Stroke` binding, +setup, blend, pattern, and per-sample uniform writes now pass through +`legacy_canvas_stroke_shader_services.h`, leaving RTT/texture ownership, +dirty-box policy, composite/pad/checkerboard shaders, and retained callback +execution under `DEBT-0036`. It also owns renderer API texture-format to OpenGL internal/pixel/component token mapping, including depth-stencil formats, for future backend texture objects. `Texture2D` 2D texture binding, upload, diff --git a/src/canvas.cpp b/src/canvas.cpp index 5dc4e02..c09391c 100644 --- a/src/canvas.cpp +++ b/src/canvas.cpp @@ -4,6 +4,7 @@ #include "app.h" #include "legacy_gl_renderbuffer_dispatch.h" #include "legacy_canvas_stroke_execution_services.h" +#include "legacy_canvas_stroke_shader_services.h" #include "legacy_canvas_stroke_services.h" #include "legacy_ui_gl_dispatch.h" #include "legacy_ui_overlay_services.h" @@ -675,27 +676,27 @@ void Canvas::stroke_draw() const auto stroke_material = canvas_stroke_material_plan(*brush, copy_stroke_destination); apply_canvas_capability(blend_state(), false); - ShaderManager::use(kShader::Stroke); - ShaderManager::u_int(kShaderUniform::Tex, 0); // brush - if (stroke_material.stroke_pass.uses_destination_feedback) - ShaderManager::u_int(kShaderUniform::TexBG, 1); // bg - ShaderManager::u_int(kShaderUniform::TexPattern, 2); // pattern - ShaderManager::u_int(kShaderUniform::TexMix, 3); // mixer - //ShaderManager::u_int(kShaderUniform::TexMixA, 4); // mixer - ShaderManager::u_float(kShaderUniform::Opacity, brush->m_tip_opacity); - ShaderManager::u_vec2(kShaderUniform::Resolution, { m_width, m_height }); - ShaderManager::u_vec2(kShaderUniform::PatternScale, patt_scale); - ShaderManager::u_float(kShaderUniform::PatternInvert, brush->m_pattern_invert); - ShaderManager::u_float(kShaderUniform::PatternBright, brush->m_pattern_brightness); - ShaderManager::u_float(kShaderUniform::PatternContrast, brush->m_pattern_contrast); - ShaderManager::u_float(kShaderUniform::PatternDepth, brush->m_pattern_depth); - ShaderManager::u_int(kShaderUniform::PatternBlendMode, brush->m_pattern_blend_mode); - ShaderManager::u_vec2(kShaderUniform::PatternOffset, m_pattern_offset); - ShaderManager::u_int(kShaderUniform::UsePattern, stroke_material.stroke_pass.uses_pattern); - ShaderManager::u_float(kShaderUniform::MixAlpha, brush->m_tip_mix); - ShaderManager::u_float(kShaderUniform::Wet, brush->m_tip_wet); - ShaderManager::u_float(kShaderUniform::Noise, brush->m_tip_noise); - ShaderManager::u_mat4(kShaderUniform::MVP, ortho_proj); + pp::panopainter::setup_legacy_stroke_shader( + pp::panopainter::LegacyStrokeShaderSetupUniforms { + .resolution = glm::vec2(static_cast(m_width), static_cast(m_height)), + .pattern = { + .scale = patt_scale, + .invert = static_cast(brush->m_pattern_invert), + .brightness = brush->m_pattern_brightness, + .contrast = brush->m_pattern_contrast, + .depth = brush->m_pattern_depth, + .blend_mode = brush->m_pattern_blend_mode, + .offset = m_pattern_offset, + }, + .mvp = ortho_proj, + .uses_destination_feedback = stroke_material.stroke_pass.uses_destination_feedback, + .uses_pattern = stroke_material.stroke_pass.uses_pattern, + .mix_alpha = brush->m_tip_mix, + .wet = brush->m_tip_wet, + .noise = brush->m_tip_noise, + .set_opacity = true, + .opacity = brush->m_tip_opacity, + }); // DRAW MAIN BRUSH @@ -731,10 +732,13 @@ void Canvas::stroke_draw() m_tmp[i].bindFramebuffer(); - ShaderManager::use(kShader::Stroke); - ShaderManager::u_vec4(kShaderUniform::Col, f.col); - ShaderManager::u_float(kShaderUniform::Alpha, f.flow); - ShaderManager::u_float(kShaderUniform::Opacity, f.opacity); + pp::panopainter::use_legacy_stroke_shader(); + pp::panopainter::apply_legacy_stroke_sample_uniforms( + pp::panopainter::LegacyStrokeSampleUniforms { + .color = f.col, + .alpha = f.flow, + .opacity = f.opacity, + }); auto box_sample = stroke_draw_samples(i, P, copy_stroke_destination); m_tmp[i].unbindFramebuffer(); @@ -812,11 +816,7 @@ void Canvas::stroke_draw() if (stroke_material.dual_pass.enabled) { - ShaderManager::use(kShader::Stroke); - ShaderManager::u_int(kShaderUniform::UsePattern, stroke_material.dual_pass.uses_pattern); - ShaderManager::u_float(kShaderUniform::MixAlpha, 0); - ShaderManager::u_float(kShaderUniform::Wet, 0); - ShaderManager::u_float(kShaderUniform::Noise, 0); + pp::panopainter::setup_legacy_stroke_dual_shader(stroke_material.dual_pass.uses_pattern); set_active_texture_unit(0); dual_brush->m_tip_texture ? @@ -825,9 +825,12 @@ void Canvas::stroke_draw() auto frames_dual = stroke_draw_compute(*m_dual_stroke); for (auto& f : frames_dual) { - ShaderManager::u_vec4(kShaderUniform::Col, f.col); - ShaderManager::u_float(kShaderUniform::Alpha, f.flow); - ShaderManager::u_float(kShaderUniform::Opacity, f.opacity); + pp::panopainter::apply_legacy_stroke_sample_uniforms( + pp::panopainter::LegacyStrokeSampleUniforms { + .color = f.col, + .alpha = f.flow, + .opacity = f.opacity, + }); for (int i = 0; i < 6; i++) { auto& P = f.shapes[i]; diff --git a/src/legacy_canvas_stroke_shader_services.h b/src/legacy_canvas_stroke_shader_services.h new file mode 100644 index 0000000..c2d8902 --- /dev/null +++ b/src/legacy_canvas_stroke_shader_services.h @@ -0,0 +1,168 @@ +#pragma once + +#include "shader.h" +#include "util.h" + +#include + +namespace pp::panopainter { + +struct LegacyStrokePatternUniforms { + glm::vec2 scale {}; + float invert = 0.0f; + float brightness = 0.0f; + float contrast = 0.0f; + float depth = 0.0f; + int blend_mode = 0; + glm::vec2 offset {}; +}; + +struct LegacyStrokeShaderSetupUniforms { + glm::vec2 resolution {}; + LegacyStrokePatternUniforms pattern; + glm::mat4 mvp { 1.0f }; + bool uses_destination_feedback = false; + bool uses_pattern = false; + float mix_alpha = 0.0f; + float wet = 0.0f; + float noise = 0.0f; + bool set_opacity = false; + float opacity = 1.0f; +}; + +struct LegacyStrokeSampleUniforms { + glm::vec4 color {}; + float alpha = 0.0f; + float opacity = 1.0f; +}; + +struct LegacyStrokeShaderExecution { + std::function use_shader; + std::function set_int; + std::function set_float; + std::function set_vec2; + std::function set_vec4; + std::function set_mat4; +}; + +[[nodiscard]] inline LegacyStrokeShaderExecution legacy_shader_manager_stroke_execution() noexcept +{ + return { + .use_shader = [](kShader shader) { ShaderManager::use(shader); }, + .set_int = [](kShaderUniform uniform, int value) { ShaderManager::u_int(uniform, value); }, + .set_float = [](kShaderUniform uniform, float value) { ShaderManager::u_float(uniform, value); }, + .set_vec2 = [](kShaderUniform uniform, const glm::vec2& value) { ShaderManager::u_vec2(uniform, value); }, + .set_vec4 = [](kShaderUniform uniform, const glm::vec4& value) { ShaderManager::u_vec4(uniform, value); }, + .set_mat4 = [](kShaderUniform uniform, const glm::mat4& value) { ShaderManager::u_mat4(uniform, value); }, + }; +} + +inline void apply_legacy_stroke_pattern_uniforms( + const LegacyStrokePatternUniforms& uniforms, + const LegacyStrokeShaderExecution& execution) noexcept +{ + execution.set_vec2(kShaderUniform::PatternScale, uniforms.scale); + execution.set_float(kShaderUniform::PatternInvert, uniforms.invert); + execution.set_float(kShaderUniform::PatternBright, uniforms.brightness); + execution.set_float(kShaderUniform::PatternContrast, uniforms.contrast); + execution.set_float(kShaderUniform::PatternDepth, uniforms.depth); + execution.set_int(kShaderUniform::PatternBlendMode, uniforms.blend_mode); + execution.set_vec2(kShaderUniform::PatternOffset, uniforms.offset); +} + +inline void use_legacy_stroke_shader(const LegacyStrokeShaderExecution& execution) noexcept +{ + execution.use_shader(kShader::Stroke); +} + +inline void use_legacy_stroke_shader() +{ + use_legacy_stroke_shader(legacy_shader_manager_stroke_execution()); +} + +inline void apply_legacy_stroke_blend_uniforms( + bool uses_pattern, + float mix_alpha, + float wet, + float noise, + const LegacyStrokeShaderExecution& execution) noexcept +{ + execution.set_int(kShaderUniform::UsePattern, uses_pattern); + execution.set_float(kShaderUniform::MixAlpha, mix_alpha); + execution.set_float(kShaderUniform::Wet, wet); + execution.set_float(kShaderUniform::Noise, noise); +} + +inline void apply_legacy_stroke_blend_uniforms( + bool uses_pattern, + float mix_alpha, + float wet, + float noise) +{ + apply_legacy_stroke_blend_uniforms( + uses_pattern, + mix_alpha, + wet, + noise, + legacy_shader_manager_stroke_execution()); +} + +inline void setup_legacy_stroke_shader( + const LegacyStrokeShaderSetupUniforms& uniforms, + const LegacyStrokeShaderExecution& execution) noexcept +{ + use_legacy_stroke_shader(execution); + execution.set_int(kShaderUniform::Tex, 0); // brush + if (uniforms.uses_destination_feedback) { + execution.set_int(kShaderUniform::TexBG, 1); // bg + } + execution.set_int(kShaderUniform::TexPattern, 2); // pattern + execution.set_int(kShaderUniform::TexMix, 3); // mixer + // ShaderManager::u_int(kShaderUniform::TexMixA, 4); // mixer + if (uniforms.set_opacity) { + execution.set_float(kShaderUniform::Opacity, uniforms.opacity); + } + execution.set_vec2(kShaderUniform::Resolution, uniforms.resolution); + apply_legacy_stroke_pattern_uniforms(uniforms.pattern, execution); + apply_legacy_stroke_blend_uniforms( + uniforms.uses_pattern, + uniforms.mix_alpha, + uniforms.wet, + uniforms.noise, + execution); + execution.set_mat4(kShaderUniform::MVP, uniforms.mvp); +} + +inline void setup_legacy_stroke_shader(const LegacyStrokeShaderSetupUniforms& uniforms) +{ + setup_legacy_stroke_shader(uniforms, legacy_shader_manager_stroke_execution()); +} + +inline void setup_legacy_stroke_dual_shader( + bool uses_pattern, + const LegacyStrokeShaderExecution& execution) noexcept +{ + use_legacy_stroke_shader(execution); + apply_legacy_stroke_blend_uniforms(uses_pattern, 0.0f, 0.0f, 0.0f, execution); +} + +inline void setup_legacy_stroke_dual_shader(bool uses_pattern) +{ + setup_legacy_stroke_dual_shader(uses_pattern, legacy_shader_manager_stroke_execution()); +} + +inline void apply_legacy_stroke_sample_uniforms( + const LegacyStrokeSampleUniforms& uniforms, + const LegacyStrokeShaderExecution& execution) noexcept +{ + execution.set_vec4(kShaderUniform::Col, uniforms.color); + execution.set_float(kShaderUniform::Alpha, uniforms.alpha); + execution.set_float(kShaderUniform::Opacity, uniforms.opacity); +} + +inline void apply_legacy_stroke_sample_uniforms(const LegacyStrokeSampleUniforms& uniforms) +{ + apply_legacy_stroke_sample_uniforms(uniforms, legacy_shader_manager_stroke_execution()); +} + +} // namespace pp::panopainter diff --git a/src/node_stroke_preview.cpp b/src/node_stroke_preview.cpp index e784688..b8bdbee 100644 --- a/src/node_stroke_preview.cpp +++ b/src/node_stroke_preview.cpp @@ -7,6 +7,7 @@ #include "canvas.h" #include "app.h" #include "legacy_canvas_stroke_execution_services.h" +#include "legacy_canvas_stroke_shader_services.h" #include "legacy_canvas_stroke_services.h" #include "legacy_ui_gl_dispatch.h" #include "paint_renderer/compositor.h" @@ -439,36 +440,36 @@ void NodeStrokePreview::draw_stroke_immediate() if (b->m_pattern_flipy) patt_scale.y *= -1.f; apply_stroke_preview_capability(pp::renderer::gl::blend_state(), false); - ShaderManager::use(kShader::Stroke); - ShaderManager::u_int(kShaderUniform::Tex, 0); // brush const auto stroke_feedback = stroke_preview_destination_feedback_plan(m_rtt.getWidth(), m_rtt.getHeight()); const bool copy_stroke_destination = !stroke_feedback.reads_destination_color; const auto material = stroke_preview_material_plan(*b, copy_stroke_destination); - if (copy_stroke_destination) - ShaderManager::u_int(kShaderUniform::TexBG, 1); // bg - ShaderManager::u_int(kShaderUniform::TexPattern, 2); // pattern - ShaderManager::u_int(kShaderUniform::TexMix, 3); // mixer - //ShaderManager::u_int(kShaderUniform::TexMixA, 4); // mixer - ShaderManager::u_vec2(kShaderUniform::Resolution, size); - ShaderManager::u_vec2(kShaderUniform::PatternScale, patt_scale); - ShaderManager::u_float(kShaderUniform::PatternInvert, b->m_pattern_invert); - ShaderManager::u_float(kShaderUniform::PatternBright, b->m_pattern_brightness); - ShaderManager::u_float(kShaderUniform::PatternContrast, b->m_pattern_contrast); - ShaderManager::u_float(kShaderUniform::PatternDepth, b->m_pattern_depth); - ShaderManager::u_int(kShaderUniform::PatternBlendMode, b->m_pattern_blend_mode); - ShaderManager::u_vec2(kShaderUniform::PatternOffset, glm::vec2(b->m_pattern_rand_offset ? 0.5f : 0.0f)); - ShaderManager::u_mat4(kShaderUniform::MVP, ortho_proj); + pp::panopainter::setup_legacy_stroke_shader( + pp::panopainter::LegacyStrokeShaderSetupUniforms { + .resolution = size, + .pattern = { + .scale = patt_scale, + .invert = static_cast(b->m_pattern_invert), + .brightness = b->m_pattern_brightness, + .contrast = b->m_pattern_contrast, + .depth = b->m_pattern_depth, + .blend_mode = b->m_pattern_blend_mode, + .offset = glm::vec2(b->m_pattern_rand_offset ? 0.5f : 0.0f), + }, + .mvp = ortho_proj, + .uses_destination_feedback = copy_stroke_destination, + .uses_pattern = false, + .mix_alpha = 0.0f, + .wet = 0.0f, + .noise = 0.0f, + .set_opacity = false, + }); // DRAW DUAL BRUSH if (material.dual_pass.enabled) { m_rtt.clear(); - ShaderManager::use(kShader::Stroke); - ShaderManager::u_int(kShaderUniform::UsePattern, material.dual_pass.uses_pattern); - ShaderManager::u_float(kShaderUniform::MixAlpha, 0); - ShaderManager::u_float(kShaderUniform::Wet, 0); - ShaderManager::u_float(kShaderUniform::Noise, 0); + pp::panopainter::setup_legacy_stroke_dual_shader(material.dual_pass.uses_pattern); set_active_texture_unit(0U); dual_brush->m_tip_texture ? dual_brush->m_tip_texture->bind() : @@ -476,9 +477,12 @@ void NodeStrokePreview::draw_stroke_immediate() auto frames_dual = stroke_draw_compute(m_dual_stroke, zoom); for (auto& f : frames_dual) { - ShaderManager::u_vec4(kShaderUniform::Col, { 0, 0, 0, 1 }); - ShaderManager::u_float(kShaderUniform::Alpha, f.flow); - ShaderManager::u_float(kShaderUniform::Opacity, f.opacity); + pp::panopainter::apply_legacy_stroke_sample_uniforms( + pp::panopainter::LegacyStrokeSampleUniforms { + .color = { 0, 0, 0, 1 }, + .alpha = f.flow, + .opacity = f.opacity, + }); /*auto rect =*/ stroke_draw_samples(f.shapes, m_tex_dual, copy_stroke_destination); } @@ -514,11 +518,12 @@ void NodeStrokePreview::draw_stroke_immediate() // DRAW MAIN BRUSH - ShaderManager::use(kShader::Stroke); - ShaderManager::u_int(kShaderUniform::UsePattern, material.stroke_pass.uses_pattern); - ShaderManager::u_float(kShaderUniform::MixAlpha, b->m_tip_mix); - ShaderManager::u_float(kShaderUniform::Wet, b->m_tip_wet); - ShaderManager::u_float(kShaderUniform::Noise, b->m_tip_noise); + pp::panopainter::use_legacy_stroke_shader(); + pp::panopainter::apply_legacy_stroke_blend_uniforms( + material.stroke_pass.uses_pattern, + b->m_tip_mix, + b->m_tip_wet, + b->m_tip_noise); set_active_texture_unit(0U); b->m_tip_texture->bind(); @@ -542,13 +547,15 @@ void NodeStrokePreview::draw_stroke_immediate() stroke_draw_mix(xy(f.m_mixer_rect), zw(f.m_mixer_rect)); } - ShaderManager::use(kShader::Stroke); - if (b->m_blend_mode != 0 || b->m_tip_mix > 0.f) - ShaderManager::u_vec4(kShaderUniform::Col, { .7, .4, .1, 1 } /*f.col*/); - else - ShaderManager::u_vec4(kShaderUniform::Col, { 0, 0, 0, 1 } /*f.col*/); - ShaderManager::u_float(kShaderUniform::Alpha, glm::max(f.flow, m_min_flow)); - ShaderManager::u_float(kShaderUniform::Opacity, f.opacity); + pp::panopainter::use_legacy_stroke_shader(); + pp::panopainter::apply_legacy_stroke_sample_uniforms( + pp::panopainter::LegacyStrokeSampleUniforms { + .color = b->m_blend_mode != 0 || b->m_tip_mix > 0.f ? + glm::vec4 { .7, .4, .1, 1 } /*f.col*/ : + glm::vec4 { 0, 0, 0, 1 } /*f.col*/, + .alpha = glm::max(f.flow, m_min_flow), + .opacity = f.opacity, + }); /*auto rect =*/ stroke_draw_samples(f.shapes, m_tex, copy_stroke_destination); } set_active_texture_unit(3U);