Plan live stroke rasterization boundaries

This commit is contained in:
2026-06-12 22:13:21 +02:00
parent 57c6128d11
commit 81726d30a5
8 changed files with 348 additions and 22 deletions

View File

@@ -1186,6 +1186,56 @@ pp::foundation::Result<StrokeCompositePlan> plan_stroke_composite(
return pp::foundation::Result<StrokeCompositePlan>::success(plan);
}
CanvasStrokeMaterialPlan plan_canvas_stroke_material(CanvasStrokeMaterialRequest request) noexcept
{
CanvasStrokeMaterialPlan plan;
auto bind = [&plan](CanvasStrokeTextureRole role, std::uint8_t slot) noexcept {
if (plan.texture_binding_count >= plan.texture_bindings.size()) {
return;
}
plan.texture_bindings[plan.texture_binding_count] = CanvasStrokeTextureBindingPlan {
.role = role,
.slot = slot,
};
++plan.texture_binding_count;
};
bind(CanvasStrokeTextureRole::main_brush_tip, 0);
plan.stroke_pass.uses_destination_feedback = request.destination_feedback_needed;
if (request.destination_feedback_needed) {
bind(CanvasStrokeTextureRole::destination_feedback, 1);
}
plan.stroke_pass.uses_pattern = request.pattern_enabled && request.pattern_eachsample;
if (plan.stroke_pass.uses_pattern) {
bind(CanvasStrokeTextureRole::pattern, 2);
}
plan.stroke_pass.uses_mixer = request.wet_blend || request.mix_blend || request.noise_enabled;
if (plan.stroke_pass.uses_mixer) {
bind(CanvasStrokeTextureRole::mixer, 3);
}
plan.dual_pass.enabled = request.dual_brush_enabled;
plan.dual_pass.uses_pattern = false;
if (request.dual_brush_enabled) {
bind(CanvasStrokeTextureRole::dual_brush_tip, 4);
}
plan.composite_pass.use_dual = request.dual_brush_enabled;
plan.composite_pass.use_pattern = request.pattern_enabled && !request.pattern_eachsample;
plan.composite_pass.dual_blend_mode = request.dual_blend_mode;
plan.composite_pass.pattern_blend_mode = request.pattern_blend_mode;
plan.composite_pass.dual_alpha = request.dual_alpha;
if (plan.composite_pass.use_pattern && !plan.stroke_pass.uses_pattern) {
bind(CanvasStrokeTextureRole::pattern, 2);
}
return plan;
}
pp::foundation::Result<CanvasBlendGatePlan> plan_canvas_blend_gate(
pp::renderer::RenderDeviceFeatures features,
CanvasBlendGateRequest request) noexcept
@@ -1301,6 +1351,26 @@ pp::foundation::Result<CanvasStrokeFeedbackPlan> plan_canvas_stroke_feedback(
return pp::foundation::Result<CanvasStrokeFeedbackPlan>::success(fallback);
}
pp::foundation::Result<CanvasStrokeRasterizationPlan> plan_canvas_stroke_rasterization(
pp::renderer::RenderDeviceFeatures features,
pp::renderer::Extent2D extent) noexcept
{
const auto feedback = plan_canvas_stroke_feedback(features, extent);
if (!feedback) {
return pp::foundation::Result<CanvasStrokeRasterizationPlan>::failure(feedback.status());
}
CanvasStrokeRasterizationPlan plan;
plan.feedback = feedback.value();
plan.copy_stroke_destination = !plan.feedback.reads_destination_color;
plan.can_route_feedback_through_renderer =
plan.feedback.reads_destination_color
|| plan.feedback.requires_texture_copy
|| plan.feedback.requires_render_target_blit;
plan.compatibility_fallback = plan.feedback.compatibility_fallback;
return pp::foundation::Result<CanvasStrokeRasterizationPlan>::success(plan);
}
const char* stroke_composite_path_name(StrokeCompositePath path) noexcept
{
switch (path) {

View File

@@ -56,6 +56,59 @@ struct StrokeCompositePlan {
bool requires_explicit_transition = false;
};
enum class CanvasStrokeTextureRole : std::uint8_t {
main_brush_tip,
destination_feedback,
pattern,
mixer,
dual_brush_tip,
};
struct CanvasStrokeTextureBindingPlan {
CanvasStrokeTextureRole role = CanvasStrokeTextureRole::main_brush_tip;
std::uint8_t slot = 0;
};
struct CanvasStrokeMaterialRequest {
bool destination_feedback_needed = false;
bool pattern_enabled = false;
bool pattern_eachsample = false;
bool wet_blend = false;
bool mix_blend = false;
bool noise_enabled = false;
bool dual_brush_enabled = false;
int dual_blend_mode = 0;
int pattern_blend_mode = 0;
float dual_alpha = 1.0F;
};
struct CanvasStrokeShaderPassPlan {
bool uses_destination_feedback = false;
bool uses_pattern = false;
bool uses_mixer = false;
};
struct CanvasStrokeDualPassPlan {
bool enabled = false;
bool uses_pattern = false;
};
struct CanvasStrokeCompositePassPlan {
bool use_dual = false;
bool use_pattern = false;
int dual_blend_mode = 0;
int pattern_blend_mode = 0;
float dual_alpha = 1.0F;
};
struct CanvasStrokeMaterialPlan {
CanvasStrokeShaderPassPlan stroke_pass {};
CanvasStrokeDualPassPlan dual_pass {};
CanvasStrokeCompositePassPlan composite_pass {};
std::array<CanvasStrokeTextureBindingPlan, 5> texture_bindings {};
std::size_t texture_binding_count = 0;
};
struct CanvasBlendGateRequest {
pp::renderer::Extent2D extent {};
std::span<const int> layer_blend_modes;
@@ -89,6 +142,13 @@ struct CanvasStrokeFeedbackPlan {
bool compatibility_fallback = false;
};
struct CanvasStrokeRasterizationPlan {
CanvasStrokeFeedbackPlan feedback {};
bool copy_stroke_destination = true;
bool can_route_feedback_through_renderer = false;
bool compatibility_fallback = false;
};
struct DocumentFaceCompositeRequest {
const pp::document::CanvasDocument* document = nullptr;
std::size_t frame_index = 0;
@@ -318,6 +378,9 @@ export_document_animation_frames_equirectangular_pngs(
pp::renderer::RenderDeviceFeatures features,
StrokeCompositeRequest request) noexcept;
[[nodiscard]] CanvasStrokeMaterialPlan plan_canvas_stroke_material(
CanvasStrokeMaterialRequest request) noexcept;
[[nodiscard]] pp::foundation::Result<CanvasBlendGatePlan> plan_canvas_blend_gate(
pp::renderer::RenderDeviceFeatures features,
CanvasBlendGateRequest request) noexcept;
@@ -326,6 +389,10 @@ export_document_animation_frames_equirectangular_pngs(
pp::renderer::RenderDeviceFeatures features,
pp::renderer::Extent2D extent) noexcept;
[[nodiscard]] pp::foundation::Result<CanvasStrokeRasterizationPlan> plan_canvas_stroke_rasterization(
pp::renderer::RenderDeviceFeatures features,
pp::renderer::Extent2D extent) noexcept;
[[nodiscard]] const char* stroke_composite_path_name(StrokeCompositePath path) noexcept;
}