Plan stroke preview composite sequence

This commit is contained in:
2026-06-13 04:40:54 +02:00
parent 36861cbf97
commit c810cc178b
7 changed files with 348 additions and 64 deletions

View File

@@ -0,0 +1,55 @@
#pragma once
namespace pp::panopainter {
struct LegacyStrokePreviewCopySize {
int width = 0;
int height = 0;
};
template <
typename SetupCheckerboard,
typename DrawPlane,
typename BindBackgroundTexture,
typename CopyFramebufferToTexture>
void execute_legacy_stroke_preview_background_capture(
SetupCheckerboard&& setup_checkerboard,
DrawPlane&& draw_plane,
BindBackgroundTexture&& bind_background_texture,
CopyFramebufferToTexture&& copy_framebuffer_to_texture,
LegacyStrokePreviewCopySize copy_size)
{
setup_checkerboard();
draw_plane();
bind_background_texture();
copy_framebuffer_to_texture(0, 0, 0, 0, copy_size.width, copy_size.height);
}
template <
typename SetupCompositeShader,
typename BindCompositeSamplers,
typename BindCompositeInputs,
typename DrawPlane>
void execute_legacy_stroke_preview_final_composite(
SetupCompositeShader&& setup_composite_shader,
BindCompositeSamplers&& bind_composite_samplers,
BindCompositeInputs&& bind_composite_inputs,
DrawPlane&& draw_plane)
{
setup_composite_shader();
bind_composite_samplers();
bind_composite_inputs();
draw_plane();
}
template <typename BindPreviewTexture, typename CopyFramebufferToTexture>
void copy_legacy_stroke_preview_texture(
BindPreviewTexture&& bind_preview_texture,
CopyFramebufferToTexture&& copy_framebuffer_to_texture,
LegacyStrokePreviewCopySize copy_size)
{
bind_preview_texture();
copy_framebuffer_to_texture(0, 0, 0, 0, copy_size.width, copy_size.height);
}
} // namespace pp::panopainter

View File

@@ -8,6 +8,7 @@
#include "app.h"
#include "legacy_canvas_stroke_composite_services.h"
#include "legacy_canvas_stroke_execution_services.h"
#include "legacy_canvas_stroke_preview_services.h"
#include "legacy_canvas_stroke_shader_services.h"
#include "legacy_canvas_stroke_services.h"
#include "legacy_ui_gl_dispatch.h"
@@ -451,6 +452,12 @@ void NodeStrokePreview::draw_stroke_immediate()
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);
const auto preview_composite_plan = pp::paint_renderer::plan_stroke_preview_composite(
pp::paint_renderer::StrokePreviewCompositeRequest {
.uses_mixer = b->m_tip_mix > 0.0f,
.uses_dual = material.composite_pass.use_dual,
.uses_pattern = material.composite_pass.use_pattern,
});
pp::panopainter::setup_legacy_stroke_shader(
pp::panopainter::LegacyStrokeShaderSetupUniforms {
.resolution = size,
@@ -508,21 +515,34 @@ void NodeStrokePreview::draw_stroke_immediate()
// CHEKCERBOARD
// copy background color to tex2
ShaderManager::use(kShader::Checkerboard);
ShaderManager::u_int(kShaderUniform::Colorize, b->m_tip_mix > 0.f || b->m_blend_mode != 0);
float aspect = size.x / size.y;
ShaderManager::u_mat4(kShaderUniform::MVP, glm::ortho(-.5f, .5f, -.5f / aspect, .5f / aspect, -1.f, 1.f));
m_plane.draw_fill();
//m_rtt.clear({ .3f, .3f, .3f, 1.f });
m_tex_background.bind();
copy_framebuffer_to_texture_2d(
0,
0,
0,
0,
static_cast<int>(size.x),
static_cast<int>(size.y));
pp::panopainter::execute_legacy_stroke_preview_background_capture(
[&] {
// copy background color to tex2
ShaderManager::use(kShader::Checkerboard);
ShaderManager::u_int(kShaderUniform::Colorize, b->m_tip_mix > 0.f || b->m_blend_mode != 0);
float aspect = size.x / size.y;
ShaderManager::u_mat4(kShaderUniform::MVP, glm::ortho(-.5f, .5f, -.5f / aspect, .5f / aspect, -1.f, 1.f));
},
[&] {
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
@@ -545,7 +565,7 @@ void NodeStrokePreview::draw_stroke_immediate()
b->m_pattern_texture->bind() :
unbind_texture_2d();
set_active_texture_unit(3U);
b->m_tip_mix > 0.f ? m_rtt_mixer.bindTexture() : unbind_texture_2d();
preview_composite_plan.uses_mixer ? m_rtt_mixer.bindTexture() : unbind_texture_2d();
auto frames = stroke_draw_compute(m_stroke, zoom);
m_rtt.clear();
for (auto& f : frames)
@@ -582,57 +602,73 @@ void NodeStrokePreview::draw_stroke_immediate()
// COMPOSITE
pp::panopainter::setup_legacy_stroke_composite_shader(
pp::panopainter::LegacyStrokeCompositeUniforms {
.resolution = size,
.pattern = {
.scale = patt_scale,
.invert = static_cast<float>(b->m_pattern_invert),
.brightness = b->m_pattern_brightness,
.contrast = b->m_pattern_contrast,
.depth = b->m_pattern_depth,
.blend_mode = material.composite_pass.pattern_blend_mode,
.offset = glm::vec2(b->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 = b->m_blend_mode,
.use_dual = material.composite_pass.use_dual,
.dual_blend_mode = material.composite_pass.dual_blend_mode,
.dual_alpha = material.composite_pass.dual_alpha,
.use_pattern = material.composite_pass.use_pattern,
pp::panopainter::execute_legacy_stroke_preview_final_composite(
[&] {
pp::panopainter::setup_legacy_stroke_composite_shader(
pp::panopainter::LegacyStrokeCompositeUniforms {
.resolution = size,
.pattern = {
.scale = patt_scale,
.invert = static_cast<float>(b->m_pattern_invert),
.brightness = b->m_pattern_brightness,
.contrast = b->m_pattern_contrast,
.depth = b->m_pattern_depth,
.blend_mode = material.composite_pass.pattern_blend_mode,
.offset = glm::vec2(b->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 = b->m_blend_mode,
.use_dual = material.composite_pass.use_dual,
.dual_blend_mode = material.composite_pass.dual_blend_mode,
.dual_alpha = material.composite_pass.dual_alpha,
.use_pattern = material.composite_pass.use_pattern,
});
},
[&] {
m_sampler_linear.bind(0);
m_sampler_linear.bind(1);
m_sampler_linear.bind(2);
m_sampler_linear.bind(3);
m_sampler_linear_repeat.bind(4);
},
[&] {
set_active_texture_unit(0U);
m_tex_background.bind();
set_active_texture_unit(1U);
m_tex.bind();
set_active_texture_unit(3U);
m_tex_dual.bind();
set_active_texture_unit(4U);
b->m_pattern_texture ?
b->m_pattern_texture->bind() :
unbind_texture_2d();
},
[&] {
m_plane.draw_fill();
});
m_sampler_linear.bind(0);
m_sampler_linear.bind(1);
m_sampler_linear.bind(2);
m_sampler_linear.bind(3);
m_sampler_linear_repeat.bind(4);
set_active_texture_unit(0U);
m_tex_background.bind();
set_active_texture_unit(1U);
m_tex.bind();
set_active_texture_unit(3U);
m_tex_dual.bind();
set_active_texture_unit(4U);
b->m_pattern_texture ?
b->m_pattern_texture->bind() :
unbind_texture_2d();
m_plane.draw_fill();
// copy the result to the actual preview
m_tex_preview.bind();
copy_framebuffer_to_texture_2d(
0,
0,
0,
0,
static_cast<int>(size.x),
static_cast<int>(size.y));
pp::panopainter::copy_legacy_stroke_preview_texture(
[&] {
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();

View File

@@ -1264,6 +1264,52 @@ CanvasStrokeMaterialPlan plan_canvas_stroke_material(CanvasStrokeMaterialRequest
return plan;
}
StrokePreviewCompositePlan plan_stroke_preview_composite(StrokePreviewCompositeRequest request) noexcept
{
StrokePreviewCompositePlan plan;
plan.uses_mixer = request.uses_mixer;
plan.uses_dual = request.uses_dual;
plan.uses_pattern = request.uses_pattern;
auto append_step = [&plan](StrokePreviewCompositeStep step) noexcept {
if (plan.step_count >= plan.steps.size()) {
return;
}
plan.steps[plan.step_count] = step;
++plan.step_count;
};
auto bind = [&plan](StrokePreviewTextureRole role, std::uint8_t slot) noexcept {
if (plan.texture_slot_count >= plan.texture_slots.size()) {
return;
}
plan.texture_slots[plan.texture_slot_count] = StrokePreviewTextureSlotPlan {
.role = role,
.slot = slot,
};
++plan.texture_slot_count;
};
append_step(StrokePreviewCompositeStep::checkerboard_background);
append_step(StrokePreviewCompositeStep::capture_background_texture);
append_step(StrokePreviewCompositeStep::bind_final_composite_inputs);
append_step(StrokePreviewCompositeStep::final_composite_draw);
append_step(StrokePreviewCompositeStep::copy_preview_texture);
bind(StrokePreviewTextureRole::background, 0);
bind(StrokePreviewTextureRole::stroke, 1);
if (request.uses_dual) {
bind(StrokePreviewTextureRole::dual, 3);
}
if (request.uses_pattern) {
bind(StrokePreviewTextureRole::pattern, 4);
}
if (request.uses_mixer) {
bind(StrokePreviewTextureRole::mixer, 3);
}
return plan;
}
pp::foundation::Result<CanvasBlendGatePlan> plan_canvas_blend_gate(
pp::renderer::RenderDeviceFeatures features,
CanvasBlendGateRequest request) noexcept

View File

@@ -109,6 +109,44 @@ struct CanvasStrokeMaterialPlan {
std::size_t texture_binding_count = 0;
};
enum class StrokePreviewCompositeStep : std::uint8_t {
checkerboard_background,
capture_background_texture,
bind_final_composite_inputs,
final_composite_draw,
copy_preview_texture,
};
enum class StrokePreviewTextureRole : std::uint8_t {
background,
stroke,
mask,
dual,
pattern,
mixer,
};
struct StrokePreviewTextureSlotPlan {
StrokePreviewTextureRole role = StrokePreviewTextureRole::background;
std::uint8_t slot = 0;
};
struct StrokePreviewCompositeRequest {
bool uses_mixer = false;
bool uses_dual = false;
bool uses_pattern = false;
};
struct StrokePreviewCompositePlan {
std::array<StrokePreviewCompositeStep, 5> steps {};
std::size_t step_count = 0;
std::array<StrokePreviewTextureSlotPlan, 5> texture_slots {};
std::size_t texture_slot_count = 0;
bool uses_mixer = false;
bool uses_dual = false;
bool uses_pattern = false;
};
struct CanvasBlendGateRequest {
pp::renderer::Extent2D extent {};
std::span<const int> layer_blend_modes;
@@ -440,6 +478,9 @@ export_document_animation_frames_equirectangular_pngs(
[[nodiscard]] CanvasStrokeMaterialPlan plan_canvas_stroke_material(
CanvasStrokeMaterialRequest request) noexcept;
[[nodiscard]] StrokePreviewCompositePlan plan_stroke_preview_composite(
StrokePreviewCompositeRequest request) noexcept;
[[nodiscard]] pp::foundation::Result<CanvasBlendGatePlan> plan_canvas_blend_gate(
pp::renderer::RenderDeviceFeatures features,
CanvasBlendGateRequest request) noexcept;