Plan canvas stroke feedback copies

This commit is contained in:
2026-06-03 18:52:37 +02:00
parent b576143afb
commit 2ac2c45b11
8 changed files with 174 additions and 14 deletions

View File

@@ -49,6 +49,27 @@ pp::renderer::RenderDeviceFeatures canvas_stroke_composite_features() noexcept
return ShaderManager::render_device_features();
}
pp::paint_renderer::CanvasStrokeFeedbackPlan canvas_stroke_feedback_plan(
int width,
int height) noexcept
{
const auto plan = pp::paint_renderer::plan_canvas_stroke_feedback(
canvas_stroke_composite_features(),
pp::renderer::Extent2D {
.width = static_cast<std::uint32_t>(std::max(width, 0)),
.height = static_cast<std::uint32_t>(std::max(height, 0)),
});
if (plan) {
return plan.value();
}
pp::paint_renderer::CanvasStrokeFeedbackPlan fallback;
fallback.compatibility_fallback = true;
fallback.path = pp::paint_renderer::StrokeCompositePath::ping_pong_textures;
fallback.requires_auxiliary_texture = true;
return fallback;
}
pp::paint_renderer::CanvasBlendGatePlan draw_merge_blend_gate_plan(
int width,
int height,
@@ -464,9 +485,12 @@ std::array<std::vector<vertex_t>, 6> Canvas::stroke_draw_project(std::array<vert
return ret;
}
glm::vec4 Canvas::stroke_draw_samples(int i, std::vector<vertex_t>& P)
glm::vec4 Canvas::stroke_draw_samples(
int i,
std::vector<vertex_t>& P,
bool copy_stroke_destination)
{
if (!ShaderManager::ext_framebuffer_fetch)
if (copy_stroke_destination)
{
set_active_texture_unit(1);
m_tex[i].bind(); // bg, copy of framebuffer (copied before drawing)
@@ -484,7 +508,7 @@ glm::vec4 Canvas::stroke_draw_samples(int i, std::vector<vertex_t>& P)
glm::vec2 pad(1);
glm::ivec2 tex_pos = glm::clamp(glm::floor(bb_min) - pad, { 0, 0 }, { m_width, m_height });
glm::ivec2 tex_sz = glm::clamp(glm::ceil(bb_sz) + pad * 2.f, { 0, 0 }, (glm::vec2)(glm::ivec2(m_width, m_height) - tex_pos));
if (!ShaderManager::ext_framebuffer_fetch)
if (copy_stroke_destination)
{
glCopyTexSubImage2D(texture_2d_target(), 0, tex_pos.x, tex_pos.y,
tex_pos.x, tex_pos.y, tex_sz.x, tex_sz.y);
@@ -512,7 +536,7 @@ glm::vec4 Canvas::stroke_draw_samples(int i, std::vector<vertex_t>& P)
}
m_brush_shape.draw_fill();
if (!ShaderManager::ext_framebuffer_fetch)
if (copy_stroke_destination)
{
set_active_texture_unit(1);
m_tex[i].unbind();
@@ -630,10 +654,13 @@ void Canvas::stroke_draw()
if (brush->m_pattern_flipx) patt_scale.x *= -1.f;
if (brush->m_pattern_flipy) patt_scale.y *= -1.f;
const auto stroke_feedback = canvas_stroke_feedback_plan(m_width, m_height);
const bool copy_stroke_destination = !stroke_feedback.reads_destination_color;
glDisable(blend_state());
ShaderManager::use(kShader::Stroke);
ShaderManager::u_int(kShaderUniform::Tex, 0); // brush
if (!ShaderManager::ext_framebuffer_fetch)
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
@@ -691,7 +718,7 @@ void Canvas::stroke_draw()
ShaderManager::u_vec4(kShaderUniform::Col, f.col);
ShaderManager::u_float(kShaderUniform::Alpha, f.flow);
ShaderManager::u_float(kShaderUniform::Opacity, f.opacity);
auto box_sample = stroke_draw_samples(i, P);
auto box_sample = stroke_draw_samples(i, P, copy_stroke_destination);
m_tmp[i].unbindFramebuffer();
@@ -718,7 +745,7 @@ void Canvas::stroke_draw()
// work on documents that doesn't have the padding, so on document loading.
ShaderManager::use(kShader::StrokePad);
ShaderManager::u_vec4(kShaderUniform::Col, pad_color);
if (!ShaderManager::ext_framebuffer_fetch)
if (copy_stroke_destination)
{
set_active_texture_unit(1);
ShaderManager::u_int(kShaderUniform::TexBG, 1);
@@ -748,7 +775,7 @@ void Canvas::stroke_draw()
m_brush_shape.update_vertices(pad_quad.data(), pad_quad.size());
m_tmp[i].bindFramebuffer();
if (!ShaderManager::ext_framebuffer_fetch)
if (copy_stroke_destination)
{
glm::vec2 o = glm::max({0, 0}, xy(b) - pad);
glm::vec2 sz = glm::min(m_size, zw(b) + pad) - o;
@@ -759,7 +786,7 @@ void Canvas::stroke_draw()
m_brush_shape.draw_fill();
m_tmp[i].unbindFramebuffer();
}
if (!ShaderManager::ext_framebuffer_fetch)
if (copy_stroke_destination)
{
unbind_texture_2d();
}
@@ -790,7 +817,7 @@ void Canvas::stroke_draw()
if (P.size() < 3)
continue;
m_tmp_dual[i].bindFramebuffer();
auto box_sample = stroke_draw_samples(i, P);
auto box_sample = stroke_draw_samples(i, P, copy_stroke_destination);
m_tmp_dual[i].unbindFramebuffer();
// this mode overflows the main brush boundries