mirror of
https://github.com/servo/servo.git
synced 2025-08-05 13:40:08 +01:00
canvas: Do not use vello layers for opacity or default composition (#38440)
In this PR we moved global alpha handling using temporary layers to mutation of paint in vello_cpu (we were already doing this in vello classic). This + not using temporary layer for SrcOver (default and most common composition operation) allows us to remove most temporary layers from `with_composition`. This slightly improves performance of vello backend, but drastically improves performance of vello_cpu. We are now able to render bunnymark (100 bunnies) with 60 FPS. In the future we could cache current layer and change it when compositing operation changes, although that would complicate clips, so improvement is questionable. Testing: Existing WPT tests for functionality, but we do not have any performance tests. Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
This commit is contained in:
parent
fe9341dd46
commit
874645ae86
2 changed files with 73 additions and 34 deletions
|
@ -17,7 +17,8 @@ use std::num::NonZeroUsize;
|
|||
use std::rc::Rc;
|
||||
|
||||
use canvas_traits::canvas::{
|
||||
CompositionOptions, FillOrStrokeStyle, FillRule, LineOptions, Path, ShadowOptions,
|
||||
CompositionOptions, CompositionOrBlending, CompositionStyle, FillOrStrokeStyle, FillRule,
|
||||
LineOptions, Path, ShadowOptions,
|
||||
};
|
||||
use compositing_traits::SerializableImageData;
|
||||
use euclid::default::{Point2D, Rect, Size2D, Transform2D};
|
||||
|
@ -134,9 +135,19 @@ impl VelloDrawTarget {
|
|||
}
|
||||
}
|
||||
|
||||
fn with_draw_options<F: FnOnce(&mut Self)>(&mut self, draw_options: &CompositionOptions, f: F) {
|
||||
fn with_composition<F: FnOnce(&mut Self)>(
|
||||
&mut self,
|
||||
composition_operation: CompositionOrBlending,
|
||||
f: F,
|
||||
) {
|
||||
// Fast-path for default and most common composition operation
|
||||
if composition_operation == CompositionOrBlending::Composition(CompositionStyle::SourceOver)
|
||||
{
|
||||
f(self);
|
||||
return;
|
||||
}
|
||||
self.scene.push_layer(
|
||||
draw_options.composition_operation.convert(),
|
||||
composition_operation.convert(),
|
||||
1.0,
|
||||
kurbo::Affine::IDENTITY,
|
||||
&kurbo::Rect::ZERO.with_size(self.size.cast()),
|
||||
|
@ -304,7 +315,7 @@ impl GenericDrawTarget for VelloDrawTarget {
|
|||
self.ensure_drawing();
|
||||
let scale_up = dest.size.width > source.size.width || dest.size.height > source.size.height;
|
||||
let shape: kurbo::Rect = dest.into();
|
||||
self.with_draw_options(&composition_options, move |self_| {
|
||||
self.with_composition(composition_options.composition_operation, move |self_| {
|
||||
self_.scene.fill(
|
||||
peniko::Fill::NonZero,
|
||||
transform.cast().into(),
|
||||
|
@ -359,7 +370,7 @@ impl GenericDrawTarget for VelloDrawTarget {
|
|||
transform: Transform2D<f32>,
|
||||
) {
|
||||
self.ensure_drawing();
|
||||
self.with_draw_options(&composition_options, |self_| {
|
||||
self.with_composition(composition_options.composition_operation, |self_| {
|
||||
self_.scene.fill(
|
||||
fill_rule.convert(),
|
||||
transform.cast().into(),
|
||||
|
@ -381,7 +392,7 @@ impl GenericDrawTarget for VelloDrawTarget {
|
|||
self.ensure_drawing();
|
||||
let pattern = convert_to_brush(style, composition_options);
|
||||
let transform = transform.cast().into();
|
||||
self.with_draw_options(&composition_options, |self_| {
|
||||
self.with_composition(composition_options.composition_operation, |self_| {
|
||||
let mut advance = 0.;
|
||||
for run in text_runs.iter() {
|
||||
let glyphs = &run.glyphs;
|
||||
|
@ -443,7 +454,7 @@ impl GenericDrawTarget for VelloDrawTarget {
|
|||
let pattern = convert_to_brush(style, composition_options);
|
||||
let transform = transform.cast().into();
|
||||
let rect: kurbo::Rect = rect.cast().into();
|
||||
self.with_draw_options(&composition_options, |self_| {
|
||||
self.with_composition(composition_options.composition_operation, |self_| {
|
||||
self_
|
||||
.scene
|
||||
.fill(peniko::Fill::NonZero, transform, &pattern, None, &rect);
|
||||
|
@ -489,7 +500,7 @@ impl GenericDrawTarget for VelloDrawTarget {
|
|||
transform: Transform2D<f32>,
|
||||
) {
|
||||
self.ensure_drawing();
|
||||
self.with_draw_options(&composition_options, |self_| {
|
||||
self.with_composition(composition_options.composition_operation, |self_| {
|
||||
self_.scene.stroke(
|
||||
&line_options.convert(),
|
||||
transform.cast().into(),
|
||||
|
@ -510,7 +521,7 @@ impl GenericDrawTarget for VelloDrawTarget {
|
|||
) {
|
||||
self.ensure_drawing();
|
||||
let rect: kurbo::Rect = rect.cast().into();
|
||||
self.with_draw_options(&composition_options, |self_| {
|
||||
self.with_composition(composition_options.composition_operation, |self_| {
|
||||
self_.scene.stroke(
|
||||
&line_options.convert(),
|
||||
transform.cast().into(),
|
||||
|
|
|
@ -7,7 +7,8 @@ use std::collections::HashMap;
|
|||
use std::sync::Arc;
|
||||
|
||||
use canvas_traits::canvas::{
|
||||
CompositionOptions, FillOrStrokeStyle, FillRule, LineOptions, Path, ShadowOptions,
|
||||
CompositionOptions, CompositionOrBlending, CompositionStyle, FillOrStrokeStyle, FillRule,
|
||||
LineOptions, Path, ShadowOptions,
|
||||
};
|
||||
use compositing_traits::SerializableImageData;
|
||||
use euclid::default::{Point2D, Rect, Size2D, Transform2D};
|
||||
|
@ -58,15 +59,16 @@ pub(crate) struct VelloCPUDrawTarget {
|
|||
impl VelloCPUDrawTarget {
|
||||
fn with_composition(
|
||||
&mut self,
|
||||
composition_options: &CompositionOptions,
|
||||
composition_operation: CompositionOrBlending,
|
||||
f: impl FnOnce(&mut Self),
|
||||
) {
|
||||
self.ctx.push_layer(
|
||||
None,
|
||||
Some(composition_options.composition_operation.convert()),
|
||||
Some(composition_options.alpha as f32),
|
||||
None,
|
||||
);
|
||||
// Fast-path for default and most common composition operation
|
||||
if composition_operation == CompositionOrBlending::Composition(CompositionStyle::SourceOver)
|
||||
{
|
||||
f(self);
|
||||
return;
|
||||
}
|
||||
self.ctx.push_blend_layer(composition_operation.convert());
|
||||
f(self);
|
||||
self.ctx.pop_layer();
|
||||
}
|
||||
|
@ -210,7 +212,7 @@ impl GenericDrawTarget for VelloCPUDrawTarget {
|
|||
|
||||
fn draw_surface(
|
||||
&mut self,
|
||||
surface: Self::SourceSurface,
|
||||
mut surface: Self::SourceSurface,
|
||||
dest: Rect<f64>,
|
||||
source: Rect<f64>,
|
||||
filter: Filter,
|
||||
|
@ -219,7 +221,12 @@ impl GenericDrawTarget for VelloCPUDrawTarget {
|
|||
) {
|
||||
self.ensure_drawing();
|
||||
let scale_up = dest.size.width > source.size.width || dest.size.height > source.size.height;
|
||||
self.with_composition(&composition_options, move |self_| {
|
||||
if composition_options.alpha != 1.0 {
|
||||
Arc::get_mut(&mut surface)
|
||||
.expect("surface should be owned")
|
||||
.multiply_alpha((composition_options.alpha * 255.0) as u8);
|
||||
}
|
||||
self.with_composition(composition_options.composition_operation, move |self_| {
|
||||
self_.ctx.set_transform(transform.cast().into());
|
||||
self_.ctx.set_paint(vello_cpu::Image {
|
||||
source: vello_cpu::ImageSource::Pixmap(surface),
|
||||
|
@ -268,11 +275,10 @@ impl GenericDrawTarget for VelloCPUDrawTarget {
|
|||
transform: Transform2D<f32>,
|
||||
) {
|
||||
self.ensure_drawing();
|
||||
let paint: vello_cpu::PaintType = style.convert();
|
||||
self.with_composition(&composition_options, |self_| {
|
||||
self.with_composition(composition_options.composition_operation, |self_| {
|
||||
self_.ctx.set_transform(transform.cast().into());
|
||||
self_.ctx.set_fill_rule(fill_rule.convert());
|
||||
self_.ctx.set_paint(paint);
|
||||
self_.ctx.set_paint(paint(style, composition_options.alpha));
|
||||
self_.ctx.fill_path(&path.0);
|
||||
});
|
||||
self.ctx.set_fill_rule(peniko::Fill::NonZero);
|
||||
|
@ -287,10 +293,9 @@ impl GenericDrawTarget for VelloCPUDrawTarget {
|
|||
transform: Transform2D<f32>,
|
||||
) {
|
||||
self.ensure_drawing();
|
||||
let style: vello_cpu::PaintType = style.convert();
|
||||
self.ctx.set_paint(style);
|
||||
self.ctx.set_paint(paint(style, composition_options.alpha));
|
||||
self.ctx.set_transform(transform.cast().into());
|
||||
self.with_composition(&composition_options, |self_| {
|
||||
self.with_composition(composition_options.composition_operation, |self_| {
|
||||
let mut advance = 0.;
|
||||
for run in text_runs.iter() {
|
||||
let glyphs = &run.glyphs;
|
||||
|
@ -346,10 +351,9 @@ impl GenericDrawTarget for VelloCPUDrawTarget {
|
|||
transform: Transform2D<f32>,
|
||||
) {
|
||||
self.ensure_drawing();
|
||||
let paint: vello_cpu::PaintType = style.convert();
|
||||
self.with_composition(&composition_options, |self_| {
|
||||
self.with_composition(composition_options.composition_operation, |self_| {
|
||||
self_.ctx.set_transform(transform.cast().into());
|
||||
self_.ctx.set_paint(paint);
|
||||
self_.ctx.set_paint(paint(style, composition_options.alpha));
|
||||
self_.ctx.fill_rect(&rect.cast().into());
|
||||
})
|
||||
}
|
||||
|
@ -395,10 +399,9 @@ impl GenericDrawTarget for VelloCPUDrawTarget {
|
|||
transform: Transform2D<f32>,
|
||||
) {
|
||||
self.ensure_drawing();
|
||||
let paint: vello_cpu::PaintType = style.convert();
|
||||
self.with_composition(&composition_options, |self_| {
|
||||
self.with_composition(composition_options.composition_operation, |self_| {
|
||||
self_.ctx.set_transform(transform.cast().into());
|
||||
self_.ctx.set_paint(paint);
|
||||
self_.ctx.set_paint(paint(style, composition_options.alpha));
|
||||
self_.ctx.set_stroke(line_options.convert());
|
||||
self_.ctx.stroke_path(&path.0);
|
||||
})
|
||||
|
@ -413,10 +416,9 @@ impl GenericDrawTarget for VelloCPUDrawTarget {
|
|||
transform: Transform2D<f32>,
|
||||
) {
|
||||
self.ensure_drawing();
|
||||
let paint: vello_cpu::PaintType = style.convert();
|
||||
self.with_composition(&composition_options, |self_| {
|
||||
self.with_composition(composition_options.composition_operation, |self_| {
|
||||
self_.ctx.set_transform(transform.cast().into());
|
||||
self_.ctx.set_paint(paint);
|
||||
self_.ctx.set_paint(paint(style, composition_options.alpha));
|
||||
self_.ctx.set_stroke(line_options.convert());
|
||||
self_.ctx.stroke_rect(&rect.cast().into());
|
||||
})
|
||||
|
@ -520,3 +522,29 @@ impl Convert<vello_cpu::PaintType> for FillOrStrokeStyle {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn paint(style: FillOrStrokeStyle, alpha: f64) -> vello_cpu::PaintType {
|
||||
assert!((0.0..=1.0).contains(&alpha));
|
||||
let paint = style.convert();
|
||||
if alpha == 1.0 {
|
||||
paint
|
||||
} else {
|
||||
match paint {
|
||||
vello_cpu::PaintType::Solid(alpha_color) => {
|
||||
vello_cpu::PaintType::Solid(alpha_color.multiply_alpha(alpha as f32))
|
||||
},
|
||||
vello_cpu::PaintType::Gradient(gradient) => {
|
||||
vello_cpu::PaintType::Gradient(gradient.multiply_alpha(alpha as f32))
|
||||
},
|
||||
vello_cpu::PaintType::Image(mut image) => {
|
||||
match &mut image.source {
|
||||
vello_cpu::ImageSource::Pixmap(pixmap) => Arc::get_mut(pixmap)
|
||||
.expect("pixmap should not be shared with anyone at this point")
|
||||
.multiply_alpha((alpha * 255.0) as u8),
|
||||
vello_cpu::ImageSource::OpaqueId(_) => unimplemented!(),
|
||||
};
|
||||
vello_cpu::PaintType::Image(image)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue