Clean retained stroke extraction build

This commit is contained in:
2026-06-14 10:23:29 +02:00
parent 8f02e39058
commit be42224561
13 changed files with 658 additions and 287 deletions

View File

@@ -247,6 +247,11 @@ void delete_canvas_renderbuffer(GLuint renderbuffer)
}
pp::panopainter::LegacyCanvasStrokeMixPassShell make_canvas_stroke_mix_pass_shell(
Canvas& canvas,
const glm::vec2& bb_min,
const glm::vec2& bb_sz);
Canvas* Canvas::I;
std::vector<CanvasMode*> Canvas::modes[] = {
{ new CanvasModePen, new CanvasModeBasicCamera }, // brush
@@ -508,19 +513,44 @@ static void execute_canvas_draw_merge_final_plane_composite(
pp::panopainter::execute_legacy_canvas_draw_merge_final_plane_composite(uniforms, execution);
}
static void execute_canvas_draw_merge_temporary_erase_dispatch(
Canvas& canvas,
int plane_index,
int layer_index,
const std::shared_ptr<Layer>& layer,
const glm::mat4& ortho);
static pp::panopainter::LegacyCanvasDrawMergeTemporaryCompositeExecution
make_canvas_draw_merge_temporary_paint_request(
Canvas& canvas,
int layer_index,
int plane_index,
const std::shared_ptr<Layer>& layer,
const Brush& brush,
const glm::mat4& ortho);
static pp::panopainter::LegacyCanvasDrawMergeLayerTextureExecution
make_canvas_draw_merge_layer_texture_dispatch(
Canvas& canvas,
int plane_index,
int layer_index);
static pp::panopainter::LegacyCanvasDrawMergeLayerBlendExecution
make_canvas_draw_merge_layer_blend_dispatch(Canvas& canvas);
static pp::panopainter::LegacyCanvasDrawMergeFinalPlaneCompositeExecution
make_canvas_draw_merge_final_plane_composite_execution(Canvas& canvas)
{
return {
.bind_merged_texture_copy_target = [&] {
canvas.set_active_texture_unit(2);
set_active_texture_unit(2);
canvas.m_merge_tex.bind();
},
.copy_merged_framebuffer = [&] {
canvas.copy_framebuffer_to_texture_2d(0, 0, 0, 0, canvas.m_width, canvas.m_height);
copy_framebuffer_to_texture_2d(0, 0, 0, 0, canvas.m_width, canvas.m_height);
},
.enable_blend = [&] {
canvas.apply_canvas_capability(canvas.blend_state(), true);
apply_canvas_capability(blend_state(), true);
},
.draw = [&] {
canvas.m_plane.draw_fill();
@@ -529,7 +559,7 @@ make_canvas_draw_merge_final_plane_composite_execution(Canvas& canvas)
canvas.m_sampler.bind(0);
},
.bind_merged_texture = [&] {
canvas.set_active_texture_unit(0);
set_active_texture_unit(0);
canvas.m_merge_tex.bind();
},
.unbind_merged_texture = [&] {
@@ -616,14 +646,15 @@ static pp::panopainter::LegacyCanvasDrawMergeLayerCompositeExecution make_canvas
ortho);
},
.execute_temporary_paint = [&] {
canvas.draw_merge_temporary_paint_branch(
layer_index,
plane_index,
layer,
brush,
ortho,
copy_blend_destination,
draw_checkerboard);
(void)draw_checkerboard;
pp::panopainter::execute_legacy_canvas_draw_merge_temporary_composite(
make_canvas_draw_merge_temporary_paint_request(
canvas,
layer_index,
plane_index,
layer,
brush,
ortho));
},
.execute_layer_texture = [&] {
execute_canvas_draw_merge_layer_texture(
@@ -700,8 +731,24 @@ static auto make_canvas_draw_merge_temporary_erase_dispatch(
canvas.m_layers[layer_index]->rtt(plane_index).unbindTexture();
});
}
static void execute_canvas_draw_merge_temporary_erase_dispatch(
Canvas& canvas,
int plane_index,
int layer_index,
const std::shared_ptr<Layer>& layer,
const glm::mat4& ortho)
{
pp::panopainter::execute_legacy_canvas_draw_merge_temporary_composite(
make_canvas_draw_merge_temporary_erase_dispatch(
canvas,
plane_index,
layer_index,
layer,
ortho));
}
static auto make_canvas_draw_merge_layer_texture_dispatch(
static pp::panopainter::LegacyCanvasDrawMergeLayerTextureExecution
make_canvas_draw_merge_layer_texture_dispatch(
Canvas& canvas,
int plane_index,
int layer_index)
@@ -723,7 +770,8 @@ static auto make_canvas_draw_merge_layer_texture_dispatch(
};
}
static auto make_canvas_draw_merge_layer_blend_dispatch(Canvas& canvas)
static pp::panopainter::LegacyCanvasDrawMergeLayerBlendExecution
make_canvas_draw_merge_layer_blend_dispatch(Canvas& canvas)
{
return pp::panopainter::LegacyCanvasDrawMergeLayerBlendExecution {
.unbind_merge_framebuffer = [&] {
@@ -822,7 +870,8 @@ static void execute_canvas_draw_merge_plane_dispatch(
for (int layer_index = 0; layer_index < layers.size(); layer_index++)
{
canvas.draw_merge_branch_orchestration(
execute_canvas_draw_merge_branch_body(
canvas,
plane_index,
layer_index,
layers[layer_index],
@@ -845,23 +894,48 @@ static void execute_canvas_draw_merge_plane_final_composite(
{
if (use_blend)
{
canvas.draw_merge_final_plane_composite(ortho, draw_checkerboard);
execute_canvas_draw_merge_final_plane_composite(
pp::panopainter::LegacyCanvasDrawMergeFinalPlaneCompositeUniforms {
.checkerboard = {
.mvp = ortho,
.colorize = false,
},
.texture = {
.mvp = ortho,
.texture_slot = 0,
},
.draw_checkerboard = draw_checkerboard,
},
make_canvas_draw_merge_final_plane_composite_execution(canvas));
}
}
static auto make_canvas_stroke_mix_pass_shell(
pp::panopainter::LegacyCanvasStrokeMixPassShell make_canvas_stroke_mix_pass_shell(
Canvas& canvas,
const glm::vec2& bb_min,
const glm::vec2& bb_sz)
{
const auto layer_index = canvas.m_current_layer_idx;
auto& current_layer = *canvas.m_layers[layer_index];
std::array<glm::mat4, 6> plane_transform {};
std::copy(std::begin(Canvas::m_plane_transform), std::end(Canvas::m_plane_transform), plane_transform.begin());
const auto mix_planes = pp::panopainter::plan_legacy_canvas_stroke_mix_pass_planes(
current_layer.m_visible,
current_layer.m_opacity,
glm::scale(glm::vec3(1, -1, 1)) * canvas.m_proj * canvas.m_mv,
plane_transform,
[&](int plane_index) {
return current_layer.face(plane_index);
});
const auto& b = canvas.m_current_stroke->m_brush;
return pp::panopainter::make_legacy_canvas_stroke_mix_pass_shell(
[&] {
canvas.m_mixer.bindFramebuffer();
canvas.apply_canvas_viewport(0, 0, canvas.m_mixer.getWidth(), canvas.m_mixer.getHeight());
canvas.apply_canvas_capability(canvas.depth_test_state(), false);
canvas.apply_canvas_capability(canvas.scissor_test_state(), true);
canvas.apply_canvas_capability(canvas.blend_state(), false);
canvas.apply_canvas_scissor(
apply_canvas_viewport(0, 0, canvas.m_mixer.getWidth(), canvas.m_mixer.getHeight());
apply_canvas_capability(depth_test_state(), false);
apply_canvas_capability(scissor_test_state(), true);
apply_canvas_capability(blend_state(), false);
apply_canvas_scissor(
static_cast<std::int32_t>(bb_min.x),
static_cast<std::int32_t>(bb_min.y),
static_cast<std::int32_t>(bb_sz.x),
@@ -869,20 +943,91 @@ static auto make_canvas_stroke_mix_pass_shell(
},
[&] {
canvas.m_mixer.unbindFramebuffer();
},
"Canvas::stroke_draw_mix",
canvas.m_size,
mix_planes,
[&] {
canvas.m_sampler.bind(0);
canvas.m_sampler.bind(1);
canvas.m_sampler.bind(2);
},
[&] {
canvas.m_sampler.unbind();
},
[&](int plane_index, const glm::mat4& plane_mvp_z) {
(void)plane_index;
pp::panopainter::setup_legacy_stroke_composite_shader(
pp::panopainter::LegacyStrokeCompositeUniforms {
.resolution = canvas.m_size,
.mvp = plane_mvp_z,
.pattern_texture_slot = 3,
.layer_alpha = 1.0f,
.alpha_lock = false,
.mask_enabled = false,
.use_fragcoord = false,
.blend_mode = b->m_blend_mode,
.use_dual = false,
.use_pattern = false,
});
},
[&](int plane_index) {
set_active_texture_unit(0);
current_layer.rtt(plane_index).bindTexture();
},
[&](int plane_index) {
set_active_texture_unit(1);
canvas.m_tmp[plane_index].bindTexture();
},
[&](int plane_index) {
set_active_texture_unit(2);
canvas.m_smask.rtt(plane_index).bindTexture();
},
[&] {
canvas.m_node->m_face_plane.draw_fill();
},
[&](int plane_index) {
set_active_texture_unit(2);
canvas.m_smask.rtt(plane_index).unbindTexture();
},
[&](int plane_index) {
set_active_texture_unit(1);
canvas.m_tmp[plane_index].unbindTexture();
},
[&](int plane_index) {
set_active_texture_unit(0);
current_layer.rtt(plane_index).unbindTexture();
});
}
static void stamp_canvas_stroke_commit_action(
Canvas& canvas,
ActionStroke* action);
static void capture_canvas_stroke_commit_layer_state(
Canvas& canvas,
ActionStroke* action,
int i);
static void apply_canvas_stroke_commit_dirty_mutation(
Canvas& canvas,
int i);
static void copy_canvas_stroke_commit_layer_image(
Canvas& canvas,
int i);
template <typename SetActiveTextureUnit>
static auto make_canvas_stroke_commit_callbacks(
Canvas& canvas,
const glm::vec4& vp,
const glm::vec4& cc,
pp::renderer::gl::OpenGlViewportRect vp,
std::array<float, 4> cc,
bool blend,
SetActiveTextureUnit&& set_active_texture_unit,
ActionStroke* action,
const Stroke* current_stroke,
const pp::paint_renderer::CanvasStrokeCommitSequencePlan& sequence,
const pp::paint_renderer::CanvasStrokeCommitMaterialPlan& stroke_material)
const pp::paint_renderer::CanvasStrokeMaterialPlan& stroke_material)
{
const auto& b = current_stroke->m_brush;
auto bind_commit_inputs = [&](int i) {
@@ -938,19 +1083,20 @@ static auto make_canvas_stroke_commit_callbacks(
},
[]() {},
[&]() {
canvas.apply_canvas_viewport(0, 0, canvas.m_width, canvas.m_height);
canvas.apply_canvas_capability(canvas.blend_state(), false);
apply_canvas_viewport(0, 0, canvas.m_width, canvas.m_height);
apply_canvas_capability(blend_state(), false);
},
[&]() {
blend ? canvas.apply_canvas_capability(canvas.blend_state(), true) : canvas.apply_canvas_capability(canvas.blend_state(), false);
canvas.apply_canvas_viewport(vp.x, vp.y, vp.width, vp.height);
canvas.apply_canvas_clear_color(cc);
blend ? apply_canvas_capability(blend_state(), true) : apply_canvas_capability(blend_state(), false);
apply_canvas_viewport(vp.x, vp.y, vp.width, vp.height);
apply_canvas_clear_color(cc);
set_active_texture_unit(0);
},
[&]() { stamp_canvas_stroke_commit_action(canvas, action); },
[&]() {
canvas.stroke_commit_timelapse();
},
[](int) {},
[&](int i) { capture_canvas_stroke_commit_layer_state(canvas, action, i); },
[&](int i) { apply_canvas_stroke_commit_dirty_mutation(canvas, i); },
[&](int i) { copy_canvas_stroke_commit_layer_image(canvas, i); },
@@ -1082,7 +1228,7 @@ pp::panopainter::LegacyCanvasStrokeTextureInputDispatch Canvas::make_stroke_draw
pp::panopainter::LegacyStrokeFaceSamplePolygonExecutionRequest Canvas::make_stroke_draw_samples_request(
int face_index,
std::vector<vertex_t>& polygon_vertices,
bool copy_stroke_destination) const
bool copy_stroke_destination)
{
return pp::panopainter::LegacyStrokeFaceSamplePolygonExecutionRequest {
.context = "Canvas::stroke_draw_samples",
@@ -1122,14 +1268,14 @@ static auto execute_canvas_stroke_commit_sequence(
template <typename SetActiveTextureUnit>
static auto make_canvas_stroke_commit_request(
Canvas& canvas,
const glm::vec4& vp,
const glm::vec4& cc,
pp::renderer::gl::OpenGlViewportRect vp,
std::array<float, 4> cc,
bool blend,
SetActiveTextureUnit&& set_active_texture_unit,
ActionStroke* action,
const Stroke* current_stroke,
const pp::paint_renderer::CanvasStrokeCommitSequencePlan& sequence,
const pp::paint_renderer::CanvasStrokeCommitMaterialPlan& stroke_material)
const pp::paint_renderer::CanvasStrokeMaterialPlan& stroke_material)
{
const auto commit_callbacks = make_canvas_stroke_commit_callbacks(
canvas,
@@ -1157,14 +1303,14 @@ static auto make_canvas_stroke_commit_request(
template <typename SetActiveTextureUnit>
static auto execute_canvas_stroke_commit_request(
Canvas& canvas,
const glm::vec4& vp,
const glm::vec4& cc,
pp::renderer::gl::OpenGlViewportRect vp,
std::array<float, 4> cc,
bool blend,
SetActiveTextureUnit&& set_active_texture_unit,
ActionStroke* action,
const Stroke* current_stroke,
const pp::paint_renderer::CanvasStrokeCommitSequencePlan& sequence,
const pp::paint_renderer::CanvasStrokeCommitMaterialPlan& stroke_material)
const pp::paint_renderer::CanvasStrokeMaterialPlan& stroke_material)
{
return execute_canvas_stroke_commit_sequence([&]() {
return make_canvas_stroke_commit_request(
@@ -1183,14 +1329,14 @@ static auto execute_canvas_stroke_commit_request(
template <typename SetActiveTextureUnit>
static auto execute_canvas_stroke_commit_dispatch(
Canvas& canvas,
const glm::vec4& vp,
const glm::vec4& cc,
pp::renderer::gl::OpenGlViewportRect vp,
std::array<float, 4> cc,
bool blend,
SetActiveTextureUnit&& set_active_texture_unit,
ActionStroke* action,
const Stroke* current_stroke,
const pp::paint_renderer::CanvasStrokeCommitSequencePlan& sequence,
const pp::paint_renderer::CanvasStrokeCommitMaterialPlan& stroke_material)
const pp::paint_renderer::CanvasStrokeMaterialPlan& stroke_material)
{
return execute_canvas_stroke_commit_request(
canvas,
@@ -1204,13 +1350,32 @@ static auto execute_canvas_stroke_commit_dispatch(
stroke_material);
}
struct CanvasStrokeCommitPrelude {
pp::renderer::gl::OpenGlViewportRect viewport;
std::array<float, 4> clear_color;
bool blend;
ActionStroke* action;
};
static CanvasStrokeCommitPrelude make_canvas_stroke_commit_prelude(Canvas& canvas)
{
CanvasStrokeCommitPrelude prelude {
.viewport = query_canvas_viewport(),
.clear_color = query_canvas_clear_color(),
.blend = query_canvas_capability(blend_state()),
.action = new ActionStroke,
};
prelude.action->was_saved = !canvas.m_unsaved;
return prelude;
}
static pp::paint_renderer::CanvasStrokeCommitSequencePlan
make_canvas_stroke_commit_sequence_plan(
const Canvas& canvas,
kCanvasMode current_mode,
int current_layer_idx,
bool smask_active,
const pp::paint_renderer::CanvasStrokeCommitMaterialPlan& stroke_material)
const pp::paint_renderer::CanvasStrokeMaterialPlan& stroke_material)
{
return pp::paint_renderer::plan_canvas_stroke_commit_sequence(
pp::paint_renderer::CanvasStrokeCommitRequest {
@@ -1340,7 +1505,9 @@ void Canvas::stroke_draw_pad_pass(
[&](const pp::paint_renderer::CanvasStrokeCopyRegion& copy_region) {
pp::panopainter::execute_legacy_canvas_stroke_pad_copy_region(
copy_region,
copy_framebuffer_to_texture_2d);
[](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);
});
},
[&](int) {
pp::panopainter::unbind_legacy_canvas_stroke_texture_inputs(
@@ -1410,7 +1577,8 @@ void Canvas::stroke_draw_pad_face_callback_body(
pad_color);
}
static auto make_canvas_draw_merge_temporary_paint_request(
static pp::panopainter::LegacyCanvasDrawMergeTemporaryCompositeExecution
make_canvas_draw_merge_temporary_paint_request(
Canvas& canvas,
int layer_index,
int plane_index,
@@ -1418,10 +1586,10 @@ static auto make_canvas_draw_merge_temporary_paint_request(
const Brush& brush,
const glm::mat4& ortho)
{
const auto stroke_material = canvas_stroke_material_plan(*brush, false);
glm::vec2 patt_scale = glm::vec2(brush->m_pattern_scale);
if (brush->m_pattern_flipx) patt_scale.x *= -1.f;
if (brush->m_pattern_flipy) patt_scale.y *= -1.f;
const auto stroke_material = canvas_stroke_material_plan(brush, false);
glm::vec2 patt_scale = glm::vec2(brush.m_pattern_scale);
if (brush.m_pattern_flipx) patt_scale.x *= -1.f;
if (brush.m_pattern_flipy) patt_scale.y *= -1.f;
return pp::panopainter::make_legacy_canvas_draw_merge_temporary_paint_composite(
[&] {
@@ -1430,11 +1598,11 @@ static auto make_canvas_draw_merge_temporary_paint_request(
.resolution = Canvas::I->m_size,
.pattern = {
.scale = patt_scale,
.invert = static_cast<float>(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,
.invert = static_cast<float>(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 = Canvas::I->m_pattern_offset,
},
.mvp = ortho,
@@ -1442,7 +1610,7 @@ static auto make_canvas_draw_merge_temporary_paint_request(
.alpha_lock = layer->m_alpha_locked,
.mask_enabled = canvas.m_smask_active,
.use_fragcoord = false,
.blend_mode = brush->m_blend_mode,
.blend_mode = brush.m_blend_mode,
.use_dual = stroke_material.composite_pass.use_dual,
.dual_blend_mode = stroke_material.composite_pass.dual_blend_mode,
.dual_alpha = stroke_material.composite_pass.dual_alpha,
@@ -1467,7 +1635,7 @@ static auto make_canvas_draw_merge_temporary_paint_request(
if (stroke_material.composite_pass.use_dual)
canvas.m_tmp_dual[plane_index].bindTexture();
set_active_texture_unit(4);
brush->m_pattern_texture ? brush->m_pattern_texture->bind() : unbind_texture_2d();
brush.m_pattern_texture ? brush.m_pattern_texture->bind() : unbind_texture_2d();
},
[&] {
canvas.m_plane.draw_fill();
@@ -1572,6 +1740,22 @@ void Canvas::stroke_draw_dual_pass(
copy_stroke_destination));
}
pp::panopainter::LegacyCanvasStrokeMainPassExecutionRequest Canvas::make_stroke_draw_main_pass_request(
std::function<void()> bind_samplers,
std::function<void()> bind_textures,
std::function<void()> execute_frame_pass,
std::function<void()> unbind_textures,
std::function<void()> unbind_samplers)
{
return pp::panopainter::make_legacy_canvas_stroke_main_pass_execution_request(
"Canvas::stroke_draw",
std::move(bind_samplers),
std::move(bind_textures),
std::move(execute_frame_pass),
std::move(unbind_textures),
std::move(unbind_samplers));
}
pp::panopainter::LegacyCanvasStrokeDualPassRequest Canvas::make_stroke_draw_dual_pass_request(
const std::vector<StrokeFrame>& frames_dual,
const std::array<pp::panopainter::LegacyCanvasStrokeTextureBinding, 1>& dual_pass_texture_bindings,
@@ -1628,8 +1812,9 @@ void Canvas::stroke_draw_dual_pass_frame_pass(
});
},
[](auto&, int, auto&) {},
[&](auto&, int i, auto& P) {
return stroke_draw_samples(i, P, copy_stroke_destination);
[&](auto&, int i, auto&& P) {
auto polygon = std::move(P);
return stroke_draw_samples(i, polygon, copy_stroke_destination);
},
m_tmp_dual,
true);
@@ -1784,19 +1969,18 @@ void Canvas::stroke_draw()
glm::vec4 pad_color;
[[maybe_unused]] const auto main_pass_result =
pp::panopainter::execute_legacy_canvas_stroke_main_pass(
pp::panopainter::LegacyCanvasStrokeMainPassExecutionRequest {
.context = "Canvas::stroke_draw",
.bind_samplers = [&] {
make_stroke_draw_main_pass_request(
[&] {
pp::panopainter::bind_legacy_canvas_stroke_sampler_inputs(
live_pass_sampler_bindings,
live_pass_sampler_dispatch);
},
.bind_textures = [&] {
[&] {
pp::panopainter::bind_legacy_canvas_stroke_texture_inputs(
main_pass_texture_bindings,
main_pass_texture_dispatch);
},
.execute_frame_pass = [&] {
[&] {
pp::panopainter::execute_legacy_canvas_stroke_main_pass_frame_callbacks(
frames,
stroke_extent,
@@ -1821,22 +2005,22 @@ void Canvas::stroke_draw()
.color = f.col,
.alpha = f.flow,
.opacity = f.opacity,
});
});
return stroke_draw_samples(i, P, copy_stroke_destination);
},
m_tmp);
},
.unbind_textures = [&] {
[&] {
pp::panopainter::unbind_legacy_canvas_stroke_texture_inputs(
main_pass_texture_unbindings,
main_pass_texture_dispatch);
},
.unbind_samplers = [&] {
[&] {
pp::panopainter::unbind_legacy_canvas_stroke_sampler_inputs(
live_pass_sampler_bindings,
live_pass_sampler_dispatch);
},
});
}
));
// pad stroke
// In order to mitigate color bleeding at the edge of shapes in transparent layers
@@ -2003,15 +2187,7 @@ void Canvas::stroke_commit()
{
if (!m_dirty || m_layers.empty())
return;
// save viewport and clear color states
const auto vp = query_canvas_viewport();
const auto cc = query_canvas_clear_color();
auto blend = query_canvas_capability(blend_state());
// allocate action to add to history
auto action = new ActionStroke;
action->was_saved = !m_unsaved;
const auto prelude = make_canvas_stroke_commit_prelude(*this);
const auto& b = m_current_stroke->m_brush;
const auto stroke_material = canvas_stroke_material_plan(*b, false);
@@ -2023,14 +2199,14 @@ void Canvas::stroke_commit()
stroke_material);
[[maybe_unused]] const auto commit_result = execute_canvas_stroke_commit_dispatch(
*this,
vp,
cc,
blend,
prelude.viewport,
prelude.clear_color,
prelude.blend,
[&](int texture_slot) {
set_active_texture_unit(texture_slot);
},
action,
m_current_stroke,
prelude.action,
m_current_stroke.get(),
stroke_commit_sequence,
stroke_material);
}