Add stroke composite feedback planner

This commit is contained in:
2026-06-03 18:07:08 +02:00
parent 94a6877e7c
commit 2ec11e5099
9 changed files with 531 additions and 3 deletions

View File

@@ -6,6 +6,40 @@ namespace pp::paint_renderer {
namespace {
[[nodiscard]] bool is_valid_blend_mode(pp::paint::BlendMode mode) noexcept
{
switch (mode) {
case pp::paint::BlendMode::normal:
case pp::paint::BlendMode::multiply:
case pp::paint::BlendMode::screen:
case pp::paint::BlendMode::color_dodge:
case pp::paint::BlendMode::overlay:
return true;
}
return false;
}
[[nodiscard]] bool is_valid_stroke_blend_mode(pp::paint::StrokeBlendMode mode) noexcept
{
switch (mode) {
case pp::paint::StrokeBlendMode::normal:
case pp::paint::StrokeBlendMode::multiply:
case pp::paint::StrokeBlendMode::subtract:
case pp::paint::StrokeBlendMode::darken:
case pp::paint::StrokeBlendMode::overlay:
case pp::paint::StrokeBlendMode::color_dodge:
case pp::paint::StrokeBlendMode::color_burn:
case pp::paint::StrokeBlendMode::linear_burn:
case pp::paint::StrokeBlendMode::hard_mix:
case pp::paint::StrokeBlendMode::linear_height:
case pp::paint::StrokeBlendMode::height:
return true;
}
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);
@@ -29,6 +63,20 @@ namespace {
return pp::foundation::Result<std::size_t>::success(static_cast<std::size_t>(count));
}
[[nodiscard]] StrokeCompositePath composite_path_from_feedback(pp::renderer::PaintFeedbackPath path) noexcept
{
switch (path) {
case pp::renderer::PaintFeedbackPath::none:
return StrokeCompositePath::fixed_function_blend;
case pp::renderer::PaintFeedbackPath::framebuffer_fetch:
return StrokeCompositePath::framebuffer_fetch;
case pp::renderer::PaintFeedbackPath::ping_pong_textures:
return StrokeCompositePath::ping_pong_textures;
}
return StrokeCompositePath::fixed_function_blend;
}
}
pp::foundation::Status composite_layer(
@@ -62,4 +110,78 @@ pp::foundation::Status composite_layer(
return pp::foundation::Status::success();
}
bool stroke_composite_requires_feedback(
pp::paint::BlendMode layer_blend_mode,
pp::paint::StrokeBlendMode stroke_blend_mode,
bool dual_brush_blend,
bool pattern_blend) noexcept
{
return layer_blend_mode != pp::paint::BlendMode::normal
|| stroke_blend_mode != pp::paint::StrokeBlendMode::normal
|| dual_brush_blend
|| pattern_blend;
}
pp::foundation::Result<StrokeCompositePlan> plan_stroke_composite(
pp::renderer::RenderDeviceFeatures features,
StrokeCompositeRequest request) noexcept
{
if (!is_valid_blend_mode(request.layer_blend_mode)) {
return pp::foundation::Result<StrokeCompositePlan>::failure(
pp::foundation::Status::invalid_argument("unknown layer blend mode"));
}
if (!is_valid_stroke_blend_mode(request.stroke_blend_mode)) {
return pp::foundation::Result<StrokeCompositePlan>::failure(
pp::foundation::Status::invalid_argument("unknown stroke blend mode"));
}
const pp::renderer::TextureDesc target_desc {
.extent = request.extent,
.format = request.target_format,
.usage = request.target_usage,
.debug_name = "stroke-composite-target",
};
const auto complex_blend = stroke_composite_requires_feedback(
request.layer_blend_mode,
request.stroke_blend_mode,
request.dual_brush_blend,
request.pattern_blend);
const auto feedback = pp::renderer::plan_paint_feedback(features, target_desc, complex_blend);
if (!feedback) {
return pp::foundation::Result<StrokeCompositePlan>::failure(feedback.status());
}
StrokeCompositePlan plan;
plan.path = composite_path_from_feedback(feedback.value().path);
plan.feedback = feedback.value();
plan.target_desc = target_desc;
plan.target_bytes = feedback.value().target_bytes;
plan.auxiliary_bytes = feedback.value().requires_auxiliary_texture
? feedback.value().target_bytes
: 0U;
plan.estimated_working_bytes = plan.target_bytes + plan.auxiliary_bytes;
plan.complex_blend = complex_blend;
plan.reads_destination_color = feedback.value().reads_destination_color;
plan.requires_auxiliary_texture = feedback.value().requires_auxiliary_texture;
plan.requires_texture_copy = feedback.value().requires_texture_copy;
plan.requires_render_target_blit = feedback.value().requires_render_target_blit;
plan.requires_explicit_transition = feedback.value().requires_explicit_transition;
return pp::foundation::Result<StrokeCompositePlan>::success(plan);
}
const char* stroke_composite_path_name(StrokeCompositePath path) noexcept
{
switch (path) {
case StrokeCompositePath::fixed_function_blend:
return "fixed_function_blend";
case StrokeCompositePath::framebuffer_fetch:
return "framebuffer_fetch";
case StrokeCompositePath::ping_pong_textures:
return "ping_pong_textures";
}
return "unknown";
}
}