Centralize canvas blend gate planning
This commit is contained in:
@@ -44,36 +44,6 @@ GLenum rgba_pixel_format()
|
||||
return static_cast<GLenum>(pp::renderer::gl::rgba_pixel_format());
|
||||
}
|
||||
|
||||
bool to_paint_blend_mode(int value, pp::paint::BlendMode& out) noexcept
|
||||
{
|
||||
switch (value) {
|
||||
case 0: out = pp::paint::BlendMode::normal; return true;
|
||||
case 1: out = pp::paint::BlendMode::multiply; return true;
|
||||
case 2: out = pp::paint::BlendMode::screen; return true;
|
||||
case 3: out = pp::paint::BlendMode::color_dodge; return true;
|
||||
case 4: out = pp::paint::BlendMode::overlay; return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool to_stroke_blend_mode(int value, pp::paint::StrokeBlendMode& out) noexcept
|
||||
{
|
||||
switch (value) {
|
||||
case 0: out = pp::paint::StrokeBlendMode::normal; return true;
|
||||
case 1: out = pp::paint::StrokeBlendMode::multiply; return true;
|
||||
case 2: out = pp::paint::StrokeBlendMode::subtract; return true;
|
||||
case 3: out = pp::paint::StrokeBlendMode::darken; return true;
|
||||
case 4: out = pp::paint::StrokeBlendMode::overlay; return true;
|
||||
case 5: out = pp::paint::StrokeBlendMode::color_dodge; return true;
|
||||
case 6: out = pp::paint::StrokeBlendMode::color_burn; return true;
|
||||
case 7: out = pp::paint::StrokeBlendMode::linear_burn; return true;
|
||||
case 8: out = pp::paint::StrokeBlendMode::hard_mix; return true;
|
||||
case 9: out = pp::paint::StrokeBlendMode::linear_height; return true;
|
||||
case 10: out = pp::paint::StrokeBlendMode::height; return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
pp::renderer::RenderDeviceFeatures canvas_stroke_composite_features() noexcept
|
||||
{
|
||||
return pp::renderer::RenderDeviceFeatures {
|
||||
@@ -82,64 +52,33 @@ pp::renderer::RenderDeviceFeatures canvas_stroke_composite_features() noexcept
|
||||
};
|
||||
}
|
||||
|
||||
bool stroke_composite_plan_needs_shader_blend(
|
||||
int width,
|
||||
int height,
|
||||
pp::paint::BlendMode layer_blend_mode,
|
||||
pp::paint::StrokeBlendMode stroke_blend_mode) noexcept
|
||||
{
|
||||
const auto plan = pp::paint_renderer::plan_stroke_composite(
|
||||
canvas_stroke_composite_features(),
|
||||
pp::paint_renderer::StrokeCompositeRequest {
|
||||
.extent = pp::renderer::Extent2D {
|
||||
.width = static_cast<std::uint32_t>(std::max(width, 0)),
|
||||
.height = static_cast<std::uint32_t>(std::max(height, 0)),
|
||||
},
|
||||
.layer_blend_mode = layer_blend_mode,
|
||||
.stroke_blend_mode = stroke_blend_mode,
|
||||
});
|
||||
return plan ? plan.value().complex_blend : true;
|
||||
}
|
||||
|
||||
bool draw_merge_needs_shader_blend(
|
||||
int width,
|
||||
int height,
|
||||
const std::vector<std::shared_ptr<Layer>>& layers,
|
||||
const Brush* brush) noexcept
|
||||
{
|
||||
std::vector<int> layer_blend_modes;
|
||||
layer_blend_modes.reserve(layers.size());
|
||||
for (const auto& layer : layers) {
|
||||
if (!layer) {
|
||||
continue;
|
||||
}
|
||||
pp::paint::BlendMode layer_blend = pp::paint::BlendMode::normal;
|
||||
if (!to_paint_blend_mode(layer->m_blend_mode, layer_blend)) {
|
||||
if (layer->m_blend_mode != 0) {
|
||||
return true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (stroke_composite_plan_needs_shader_blend(
|
||||
width,
|
||||
height,
|
||||
layer_blend,
|
||||
pp::paint::StrokeBlendMode::normal)) {
|
||||
return true;
|
||||
}
|
||||
layer_blend_modes.push_back(layer->m_blend_mode);
|
||||
}
|
||||
|
||||
if (brush) {
|
||||
pp::paint::StrokeBlendMode stroke_blend = pp::paint::StrokeBlendMode::normal;
|
||||
if (!to_stroke_blend_mode(brush->m_blend_mode, stroke_blend)) {
|
||||
return brush->m_blend_mode != 0;
|
||||
}
|
||||
return stroke_composite_plan_needs_shader_blend(
|
||||
width,
|
||||
height,
|
||||
pp::paint::BlendMode::normal,
|
||||
stroke_blend);
|
||||
}
|
||||
|
||||
return false;
|
||||
const auto plan = pp::paint_renderer::plan_canvas_blend_gate(
|
||||
canvas_stroke_composite_features(),
|
||||
pp::paint_renderer::CanvasBlendGateRequest {
|
||||
.extent = pp::renderer::Extent2D {
|
||||
.width = static_cast<std::uint32_t>(std::max(width, 0)),
|
||||
.height = static_cast<std::uint32_t>(std::max(height, 0)),
|
||||
},
|
||||
.layer_blend_modes = layer_blend_modes,
|
||||
.has_stroke_blend_mode = brush != nullptr,
|
||||
.stroke_blend_mode = brush ? brush->m_blend_mode : 0,
|
||||
});
|
||||
return plan ? plan.value().shader_blend : true;
|
||||
}
|
||||
|
||||
GLenum unsigned_byte_component_type()
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
#include "pch.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "app_core/canvas_tool_ui.h"
|
||||
#include "app_core/history_ui.h"
|
||||
@@ -8,6 +11,7 @@
|
||||
#include "log.h"
|
||||
#include "node_canvas.h"
|
||||
#include "node_image_texture.h"
|
||||
#include "paint_renderer/compositor.h"
|
||||
#include "settings.h"
|
||||
#include "renderer_gl/opengl_capabilities.h"
|
||||
|
||||
@@ -23,6 +27,43 @@ void unbind_texture_2d()
|
||||
glBindTexture(pp::renderer::gl::texture_2d_target(), 0);
|
||||
}
|
||||
|
||||
pp::renderer::RenderDeviceFeatures node_canvas_stroke_composite_features() noexcept
|
||||
{
|
||||
return pp::renderer::RenderDeviceFeatures {
|
||||
.framebuffer_fetch = ShaderManager::ext_framebuffer_fetch,
|
||||
.texture_copy = !ShaderManager::ext_framebuffer_fetch,
|
||||
};
|
||||
}
|
||||
|
||||
bool node_canvas_needs_shader_blend(
|
||||
int width,
|
||||
int height,
|
||||
const std::vector<std::shared_ptr<Layer>>& layers,
|
||||
const Brush* brush) noexcept
|
||||
{
|
||||
std::vector<int> layer_blend_modes;
|
||||
layer_blend_modes.reserve(layers.size());
|
||||
for (const auto& layer : layers) {
|
||||
if (!layer) {
|
||||
continue;
|
||||
}
|
||||
layer_blend_modes.push_back(layer->m_blend_mode);
|
||||
}
|
||||
|
||||
const auto plan = pp::paint_renderer::plan_canvas_blend_gate(
|
||||
node_canvas_stroke_composite_features(),
|
||||
pp::paint_renderer::CanvasBlendGateRequest {
|
||||
.extent = pp::renderer::Extent2D {
|
||||
.width = static_cast<std::uint32_t>(std::max(width, 0)),
|
||||
.height = static_cast<std::uint32_t>(std::max(height, 0)),
|
||||
},
|
||||
.layer_blend_modes = layer_blend_modes,
|
||||
.has_stroke_blend_mode = brush != nullptr,
|
||||
.stroke_blend_mode = brush ? brush->m_blend_mode : 0,
|
||||
});
|
||||
return plan ? plan.value().shader_blend : true;
|
||||
}
|
||||
|
||||
void run_history_undo_if_available()
|
||||
{
|
||||
const auto plan = pp::app::plan_history_undo(static_cast<int>(ActionManager::I.m_actions.size()));
|
||||
@@ -252,14 +293,11 @@ void NodeCanvas::draw()
|
||||
}
|
||||
else
|
||||
{
|
||||
// check if any layer use blend, otherwise draw directly on main framebuffer
|
||||
bool use_blend = false;
|
||||
for (size_t i = 0; i < m_canvas->m_layers.size(); i++)
|
||||
{
|
||||
use_blend |= m_canvas->m_layers[i]->m_blend_mode != 0;
|
||||
}
|
||||
if (Canvas::I->m_current_stroke)
|
||||
use_blend |= Canvas::I->m_current_stroke->m_brush->m_blend_mode != 0;
|
||||
const bool use_blend = node_canvas_needs_shader_blend(
|
||||
m_cache_rtt.getWidth(),
|
||||
m_cache_rtt.getHeight(),
|
||||
m_canvas->m_layers,
|
||||
m_canvas->m_current_stroke ? m_canvas->m_current_stroke->m_brush.get() : nullptr);
|
||||
|
||||
if (use_blend)
|
||||
{
|
||||
|
||||
@@ -40,6 +40,36 @@ namespace {
|
||||
return false;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool paint_blend_mode_from_persisted_index(int value, pp::paint::BlendMode& out) noexcept
|
||||
{
|
||||
switch (value) {
|
||||
case 0: out = pp::paint::BlendMode::normal; return true;
|
||||
case 1: out = pp::paint::BlendMode::multiply; return true;
|
||||
case 2: out = pp::paint::BlendMode::screen; return true;
|
||||
case 3: out = pp::paint::BlendMode::color_dodge; return true;
|
||||
case 4: out = pp::paint::BlendMode::overlay; return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] bool stroke_blend_mode_from_persisted_index(int value, pp::paint::StrokeBlendMode& out) noexcept
|
||||
{
|
||||
switch (value) {
|
||||
case 0: out = pp::paint::StrokeBlendMode::normal; return true;
|
||||
case 1: out = pp::paint::StrokeBlendMode::multiply; return true;
|
||||
case 2: out = pp::paint::StrokeBlendMode::subtract; return true;
|
||||
case 3: out = pp::paint::StrokeBlendMode::darken; return true;
|
||||
case 4: out = pp::paint::StrokeBlendMode::overlay; return true;
|
||||
case 5: out = pp::paint::StrokeBlendMode::color_dodge; return true;
|
||||
case 6: out = pp::paint::StrokeBlendMode::color_burn; return true;
|
||||
case 7: out = pp::paint::StrokeBlendMode::linear_burn; return true;
|
||||
case 8: out = pp::paint::StrokeBlendMode::hard_mix; return true;
|
||||
case 9: out = pp::paint::StrokeBlendMode::linear_height; return true;
|
||||
case 10: out = pp::paint::StrokeBlendMode::height; return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] pp::foundation::Result<std::size_t> expected_pixel_count(pp::renderer::Extent2D extent) noexcept
|
||||
{
|
||||
const auto extent_status = pp::renderer::validate_extent(extent);
|
||||
@@ -77,6 +107,22 @@ namespace {
|
||||
return StrokeCompositePath::fixed_function_blend;
|
||||
}
|
||||
|
||||
void apply_stroke_plan(CanvasBlendGatePlan& gate, const StrokeCompositePlan& stroke) noexcept
|
||||
{
|
||||
gate.path = stroke.path;
|
||||
gate.reads_destination_color = stroke.reads_destination_color;
|
||||
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 mark_shader_blend_fallback(CanvasBlendGatePlan& gate) noexcept
|
||||
{
|
||||
gate.shader_blend = true;
|
||||
gate.complex_blend = true;
|
||||
gate.compatibility_fallback = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pp::foundation::Status composite_layer(
|
||||
@@ -170,6 +216,82 @@ pp::foundation::Result<StrokeCompositePlan> plan_stroke_composite(
|
||||
return pp::foundation::Result<StrokeCompositePlan>::success(plan);
|
||||
}
|
||||
|
||||
pp::foundation::Result<CanvasBlendGatePlan> plan_canvas_blend_gate(
|
||||
pp::renderer::RenderDeviceFeatures features,
|
||||
CanvasBlendGateRequest request) noexcept
|
||||
{
|
||||
CanvasBlendGatePlan gate;
|
||||
|
||||
for (std::size_t i = 0; i < request.layer_blend_modes.size(); ++i) {
|
||||
pp::paint::BlendMode layer_blend = pp::paint::BlendMode::normal;
|
||||
if (!paint_blend_mode_from_persisted_index(request.layer_blend_modes[i], layer_blend)) {
|
||||
if (request.layer_blend_modes[i] != 0) {
|
||||
gate.first_complex_layer_index = static_cast<int>(i);
|
||||
mark_shader_blend_fallback(gate);
|
||||
return pp::foundation::Result<CanvasBlendGatePlan>::success(gate);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (layer_blend == pp::paint::BlendMode::normal) {
|
||||
continue;
|
||||
}
|
||||
|
||||
gate.shader_blend = true;
|
||||
gate.complex_blend = true;
|
||||
gate.first_complex_layer_index = static_cast<int>(i);
|
||||
const auto stroke = plan_stroke_composite(
|
||||
features,
|
||||
StrokeCompositeRequest {
|
||||
.extent = request.extent,
|
||||
.layer_blend_mode = layer_blend,
|
||||
});
|
||||
if (stroke) {
|
||||
apply_stroke_plan(gate, stroke.value());
|
||||
} else {
|
||||
gate.compatibility_fallback = true;
|
||||
}
|
||||
return pp::foundation::Result<CanvasBlendGatePlan>::success(gate);
|
||||
}
|
||||
|
||||
pp::paint::StrokeBlendMode stroke_blend = pp::paint::StrokeBlendMode::normal;
|
||||
if (request.has_stroke_blend_mode) {
|
||||
if (!stroke_blend_mode_from_persisted_index(request.stroke_blend_mode, stroke_blend)) {
|
||||
if (request.stroke_blend_mode != 0) {
|
||||
gate.stroke_complex = true;
|
||||
mark_shader_blend_fallback(gate);
|
||||
return pp::foundation::Result<CanvasBlendGatePlan>::success(gate);
|
||||
}
|
||||
} else if (stroke_blend != pp::paint::StrokeBlendMode::normal) {
|
||||
gate.stroke_complex = true;
|
||||
}
|
||||
}
|
||||
|
||||
gate.dual_brush_complex = request.dual_brush_blend;
|
||||
gate.pattern_complex = request.pattern_blend;
|
||||
if (!gate.stroke_complex && !gate.dual_brush_complex && !gate.pattern_complex) {
|
||||
return pp::foundation::Result<CanvasBlendGatePlan>::success(gate);
|
||||
}
|
||||
|
||||
gate.shader_blend = true;
|
||||
gate.complex_blend = true;
|
||||
const auto stroke = plan_stroke_composite(
|
||||
features,
|
||||
StrokeCompositeRequest {
|
||||
.extent = request.extent,
|
||||
.stroke_blend_mode = stroke_blend,
|
||||
.dual_brush_blend = request.dual_brush_blend,
|
||||
.pattern_blend = request.pattern_blend,
|
||||
});
|
||||
if (stroke) {
|
||||
apply_stroke_plan(gate, stroke.value());
|
||||
} else {
|
||||
gate.compatibility_fallback = true;
|
||||
}
|
||||
|
||||
return pp::foundation::Result<CanvasBlendGatePlan>::success(gate);
|
||||
}
|
||||
|
||||
const char* stroke_composite_path_name(StrokeCompositePath path) noexcept
|
||||
{
|
||||
switch (path) {
|
||||
|
||||
@@ -50,6 +50,30 @@ struct StrokeCompositePlan {
|
||||
bool requires_explicit_transition = false;
|
||||
};
|
||||
|
||||
struct CanvasBlendGateRequest {
|
||||
pp::renderer::Extent2D extent {};
|
||||
std::span<const int> layer_blend_modes;
|
||||
bool has_stroke_blend_mode = false;
|
||||
int stroke_blend_mode = 0;
|
||||
bool dual_brush_blend = false;
|
||||
bool pattern_blend = false;
|
||||
};
|
||||
|
||||
struct CanvasBlendGatePlan {
|
||||
bool shader_blend = false;
|
||||
bool complex_blend = false;
|
||||
bool compatibility_fallback = false;
|
||||
bool stroke_complex = false;
|
||||
bool dual_brush_complex = false;
|
||||
bool pattern_complex = false;
|
||||
int first_complex_layer_index = -1;
|
||||
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;
|
||||
};
|
||||
|
||||
[[nodiscard]] pp::foundation::Status composite_layer(
|
||||
std::span<pp::paint::Rgba> destination,
|
||||
pp::renderer::Extent2D extent,
|
||||
@@ -65,6 +89,10 @@ struct StrokeCompositePlan {
|
||||
pp::renderer::RenderDeviceFeatures features,
|
||||
StrokeCompositeRequest request) noexcept;
|
||||
|
||||
[[nodiscard]] pp::foundation::Result<CanvasBlendGatePlan> plan_canvas_blend_gate(
|
||||
pp::renderer::RenderDeviceFeatures features,
|
||||
CanvasBlendGateRequest request) noexcept;
|
||||
|
||||
[[nodiscard]] const char* stroke_composite_path_name(StrokeCompositePath path) noexcept;
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user