Plan canvas stroke feedback copies
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -205,7 +205,7 @@ public:
|
||||
void stroke_draw_mix(const glm::vec2& bb_min, const glm::vec2& bb_sz);
|
||||
std::array<std::vector<vertex_t>, 6> stroke_draw_project(std::array<vertex_t, 4>& B, bool project_3d = false, glm::mat4 mv = glm::mat4(1)) const;
|
||||
// return rect {origin, size}
|
||||
glm::vec4 stroke_draw_samples(int i, std::vector<vertex_t>& P);
|
||||
glm::vec4 stroke_draw_samples(int i, std::vector<vertex_t>& P, bool copy_stroke_destination);
|
||||
std::vector<StrokeFrame> stroke_draw_compute(Stroke& stroke) const;
|
||||
void stroke_draw();
|
||||
void stroke_end();
|
||||
|
||||
@@ -110,12 +110,21 @@ namespace {
|
||||
void apply_stroke_plan(CanvasBlendGatePlan& gate, const StrokeCompositePlan& stroke) noexcept
|
||||
{
|
||||
gate.path = stroke.path;
|
||||
gate.reads_destination_color = stroke.reads_destination_color;
|
||||
gate.reads_destination_color = stroke.path == StrokeCompositePath::framebuffer_fetch;
|
||||
gate.requires_auxiliary_texture = stroke.requires_auxiliary_texture;
|
||||
gate.requires_texture_copy = stroke.requires_texture_copy;
|
||||
gate.requires_render_target_blit = stroke.requires_render_target_blit;
|
||||
}
|
||||
|
||||
void apply_feedback_plan(CanvasStrokeFeedbackPlan& plan, const pp::renderer::PaintFeedbackPlan& feedback) noexcept
|
||||
{
|
||||
plan.path = composite_path_from_feedback(feedback.path);
|
||||
plan.reads_destination_color = plan.path == StrokeCompositePath::framebuffer_fetch;
|
||||
plan.requires_auxiliary_texture = feedback.requires_auxiliary_texture;
|
||||
plan.requires_texture_copy = feedback.requires_texture_copy;
|
||||
plan.requires_render_target_blit = feedback.requires_render_target_blit;
|
||||
}
|
||||
|
||||
void mark_shader_blend_fallback(
|
||||
CanvasBlendGatePlan& gate,
|
||||
pp::renderer::RenderDeviceFeatures features) noexcept
|
||||
@@ -303,6 +312,45 @@ pp::foundation::Result<CanvasBlendGatePlan> plan_canvas_blend_gate(
|
||||
return pp::foundation::Result<CanvasBlendGatePlan>::success(gate);
|
||||
}
|
||||
|
||||
pp::foundation::Result<CanvasStrokeFeedbackPlan> plan_canvas_stroke_feedback(
|
||||
pp::renderer::RenderDeviceFeatures features,
|
||||
pp::renderer::Extent2D extent) noexcept
|
||||
{
|
||||
const auto extent_status = pp::renderer::validate_extent(extent);
|
||||
if (!extent_status.ok()) {
|
||||
return pp::foundation::Result<CanvasStrokeFeedbackPlan>::failure(extent_status);
|
||||
}
|
||||
|
||||
const pp::renderer::TextureDesc target_desc {
|
||||
.extent = extent,
|
||||
.format = pp::renderer::TextureFormat::rgba8,
|
||||
.usage = pp::renderer::TextureUsage::render_target
|
||||
| pp::renderer::TextureUsage::sampled
|
||||
| pp::renderer::TextureUsage::copy_source
|
||||
| pp::renderer::TextureUsage::copy_destination,
|
||||
.debug_name = "canvas-stroke-feedback-target",
|
||||
};
|
||||
const auto feedback = pp::renderer::plan_paint_feedback(features, target_desc, true);
|
||||
if (feedback) {
|
||||
CanvasStrokeFeedbackPlan plan;
|
||||
apply_feedback_plan(plan, feedback.value());
|
||||
return pp::foundation::Result<CanvasStrokeFeedbackPlan>::success(plan);
|
||||
}
|
||||
|
||||
CanvasStrokeFeedbackPlan fallback;
|
||||
fallback.compatibility_fallback = true;
|
||||
if (features.framebuffer_fetch) {
|
||||
fallback.path = StrokeCompositePath::framebuffer_fetch;
|
||||
fallback.reads_destination_color = true;
|
||||
} else {
|
||||
fallback.path = StrokeCompositePath::ping_pong_textures;
|
||||
fallback.requires_auxiliary_texture = true;
|
||||
fallback.requires_texture_copy = features.texture_copy;
|
||||
fallback.requires_render_target_blit = !features.texture_copy && features.render_target_blit;
|
||||
}
|
||||
return pp::foundation::Result<CanvasStrokeFeedbackPlan>::success(fallback);
|
||||
}
|
||||
|
||||
const char* stroke_composite_path_name(StrokeCompositePath path) noexcept
|
||||
{
|
||||
switch (path) {
|
||||
|
||||
@@ -74,6 +74,15 @@ struct CanvasBlendGatePlan {
|
||||
bool requires_render_target_blit = false;
|
||||
};
|
||||
|
||||
struct CanvasStrokeFeedbackPlan {
|
||||
StrokeCompositePath path = StrokeCompositePath::fixed_function_blend;
|
||||
bool reads_destination_color = false;
|
||||
bool requires_auxiliary_texture = false;
|
||||
bool requires_texture_copy = false;
|
||||
bool requires_render_target_blit = false;
|
||||
bool compatibility_fallback = false;
|
||||
};
|
||||
|
||||
[[nodiscard]] pp::foundation::Status composite_layer(
|
||||
std::span<pp::paint::Rgba> destination,
|
||||
pp::renderer::Extent2D extent,
|
||||
@@ -93,6 +102,10 @@ struct CanvasBlendGatePlan {
|
||||
pp::renderer::RenderDeviceFeatures features,
|
||||
CanvasBlendGateRequest request) noexcept;
|
||||
|
||||
[[nodiscard]] pp::foundation::Result<CanvasStrokeFeedbackPlan> plan_canvas_stroke_feedback(
|
||||
pp::renderer::RenderDeviceFeatures features,
|
||||
pp::renderer::Extent2D extent) noexcept;
|
||||
|
||||
[[nodiscard]] const char* stroke_composite_path_name(StrokeCompositePath path) noexcept;
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user