mirror of
https://github.com/servo/servo.git
synced 2025-08-05 21:50:18 +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 std::rc::Rc;
|
||||||
|
|
||||||
use canvas_traits::canvas::{
|
use canvas_traits::canvas::{
|
||||||
CompositionOptions, FillOrStrokeStyle, FillRule, LineOptions, Path, ShadowOptions,
|
CompositionOptions, CompositionOrBlending, CompositionStyle, FillOrStrokeStyle, FillRule,
|
||||||
|
LineOptions, Path, ShadowOptions,
|
||||||
};
|
};
|
||||||
use compositing_traits::SerializableImageData;
|
use compositing_traits::SerializableImageData;
|
||||||
use euclid::default::{Point2D, Rect, Size2D, Transform2D};
|
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(
|
self.scene.push_layer(
|
||||||
draw_options.composition_operation.convert(),
|
composition_operation.convert(),
|
||||||
1.0,
|
1.0,
|
||||||
kurbo::Affine::IDENTITY,
|
kurbo::Affine::IDENTITY,
|
||||||
&kurbo::Rect::ZERO.with_size(self.size.cast()),
|
&kurbo::Rect::ZERO.with_size(self.size.cast()),
|
||||||
|
@ -304,7 +315,7 @@ impl GenericDrawTarget for VelloDrawTarget {
|
||||||
self.ensure_drawing();
|
self.ensure_drawing();
|
||||||
let scale_up = dest.size.width > source.size.width || dest.size.height > source.size.height;
|
let scale_up = dest.size.width > source.size.width || dest.size.height > source.size.height;
|
||||||
let shape: kurbo::Rect = dest.into();
|
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(
|
self_.scene.fill(
|
||||||
peniko::Fill::NonZero,
|
peniko::Fill::NonZero,
|
||||||
transform.cast().into(),
|
transform.cast().into(),
|
||||||
|
@ -359,7 +370,7 @@ impl GenericDrawTarget for VelloDrawTarget {
|
||||||
transform: Transform2D<f32>,
|
transform: Transform2D<f32>,
|
||||||
) {
|
) {
|
||||||
self.ensure_drawing();
|
self.ensure_drawing();
|
||||||
self.with_draw_options(&composition_options, |self_| {
|
self.with_composition(composition_options.composition_operation, |self_| {
|
||||||
self_.scene.fill(
|
self_.scene.fill(
|
||||||
fill_rule.convert(),
|
fill_rule.convert(),
|
||||||
transform.cast().into(),
|
transform.cast().into(),
|
||||||
|
@ -381,7 +392,7 @@ impl GenericDrawTarget for VelloDrawTarget {
|
||||||
self.ensure_drawing();
|
self.ensure_drawing();
|
||||||
let pattern = convert_to_brush(style, composition_options);
|
let pattern = convert_to_brush(style, composition_options);
|
||||||
let transform = transform.cast().into();
|
let transform = transform.cast().into();
|
||||||
self.with_draw_options(&composition_options, |self_| {
|
self.with_composition(composition_options.composition_operation, |self_| {
|
||||||
let mut advance = 0.;
|
let mut advance = 0.;
|
||||||
for run in text_runs.iter() {
|
for run in text_runs.iter() {
|
||||||
let glyphs = &run.glyphs;
|
let glyphs = &run.glyphs;
|
||||||
|
@ -443,7 +454,7 @@ impl GenericDrawTarget for VelloDrawTarget {
|
||||||
let pattern = convert_to_brush(style, composition_options);
|
let pattern = convert_to_brush(style, composition_options);
|
||||||
let transform = transform.cast().into();
|
let transform = transform.cast().into();
|
||||||
let rect: kurbo::Rect = rect.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_
|
self_
|
||||||
.scene
|
.scene
|
||||||
.fill(peniko::Fill::NonZero, transform, &pattern, None, &rect);
|
.fill(peniko::Fill::NonZero, transform, &pattern, None, &rect);
|
||||||
|
@ -489,7 +500,7 @@ impl GenericDrawTarget for VelloDrawTarget {
|
||||||
transform: Transform2D<f32>,
|
transform: Transform2D<f32>,
|
||||||
) {
|
) {
|
||||||
self.ensure_drawing();
|
self.ensure_drawing();
|
||||||
self.with_draw_options(&composition_options, |self_| {
|
self.with_composition(composition_options.composition_operation, |self_| {
|
||||||
self_.scene.stroke(
|
self_.scene.stroke(
|
||||||
&line_options.convert(),
|
&line_options.convert(),
|
||||||
transform.cast().into(),
|
transform.cast().into(),
|
||||||
|
@ -510,7 +521,7 @@ impl GenericDrawTarget for VelloDrawTarget {
|
||||||
) {
|
) {
|
||||||
self.ensure_drawing();
|
self.ensure_drawing();
|
||||||
let rect: kurbo::Rect = rect.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.stroke(
|
self_.scene.stroke(
|
||||||
&line_options.convert(),
|
&line_options.convert(),
|
||||||
transform.cast().into(),
|
transform.cast().into(),
|
||||||
|
|
|
@ -7,7 +7,8 @@ use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use canvas_traits::canvas::{
|
use canvas_traits::canvas::{
|
||||||
CompositionOptions, FillOrStrokeStyle, FillRule, LineOptions, Path, ShadowOptions,
|
CompositionOptions, CompositionOrBlending, CompositionStyle, FillOrStrokeStyle, FillRule,
|
||||||
|
LineOptions, Path, ShadowOptions,
|
||||||
};
|
};
|
||||||
use compositing_traits::SerializableImageData;
|
use compositing_traits::SerializableImageData;
|
||||||
use euclid::default::{Point2D, Rect, Size2D, Transform2D};
|
use euclid::default::{Point2D, Rect, Size2D, Transform2D};
|
||||||
|
@ -58,15 +59,16 @@ pub(crate) struct VelloCPUDrawTarget {
|
||||||
impl VelloCPUDrawTarget {
|
impl VelloCPUDrawTarget {
|
||||||
fn with_composition(
|
fn with_composition(
|
||||||
&mut self,
|
&mut self,
|
||||||
composition_options: &CompositionOptions,
|
composition_operation: CompositionOrBlending,
|
||||||
f: impl FnOnce(&mut Self),
|
f: impl FnOnce(&mut Self),
|
||||||
) {
|
) {
|
||||||
self.ctx.push_layer(
|
// Fast-path for default and most common composition operation
|
||||||
None,
|
if composition_operation == CompositionOrBlending::Composition(CompositionStyle::SourceOver)
|
||||||
Some(composition_options.composition_operation.convert()),
|
{
|
||||||
Some(composition_options.alpha as f32),
|
f(self);
|
||||||
None,
|
return;
|
||||||
);
|
}
|
||||||
|
self.ctx.push_blend_layer(composition_operation.convert());
|
||||||
f(self);
|
f(self);
|
||||||
self.ctx.pop_layer();
|
self.ctx.pop_layer();
|
||||||
}
|
}
|
||||||
|
@ -210,7 +212,7 @@ impl GenericDrawTarget for VelloCPUDrawTarget {
|
||||||
|
|
||||||
fn draw_surface(
|
fn draw_surface(
|
||||||
&mut self,
|
&mut self,
|
||||||
surface: Self::SourceSurface,
|
mut surface: Self::SourceSurface,
|
||||||
dest: Rect<f64>,
|
dest: Rect<f64>,
|
||||||
source: Rect<f64>,
|
source: Rect<f64>,
|
||||||
filter: Filter,
|
filter: Filter,
|
||||||
|
@ -219,7 +221,12 @@ impl GenericDrawTarget for VelloCPUDrawTarget {
|
||||||
) {
|
) {
|
||||||
self.ensure_drawing();
|
self.ensure_drawing();
|
||||||
let scale_up = dest.size.width > source.size.width || dest.size.height > source.size.height;
|
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_transform(transform.cast().into());
|
||||||
self_.ctx.set_paint(vello_cpu::Image {
|
self_.ctx.set_paint(vello_cpu::Image {
|
||||||
source: vello_cpu::ImageSource::Pixmap(surface),
|
source: vello_cpu::ImageSource::Pixmap(surface),
|
||||||
|
@ -268,11 +275,10 @@ impl GenericDrawTarget for VelloCPUDrawTarget {
|
||||||
transform: Transform2D<f32>,
|
transform: Transform2D<f32>,
|
||||||
) {
|
) {
|
||||||
self.ensure_drawing();
|
self.ensure_drawing();
|
||||||
let paint: vello_cpu::PaintType = style.convert();
|
self.with_composition(composition_options.composition_operation, |self_| {
|
||||||
self.with_composition(&composition_options, |self_| {
|
|
||||||
self_.ctx.set_transform(transform.cast().into());
|
self_.ctx.set_transform(transform.cast().into());
|
||||||
self_.ctx.set_fill_rule(fill_rule.convert());
|
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.fill_path(&path.0);
|
||||||
});
|
});
|
||||||
self.ctx.set_fill_rule(peniko::Fill::NonZero);
|
self.ctx.set_fill_rule(peniko::Fill::NonZero);
|
||||||
|
@ -287,10 +293,9 @@ impl GenericDrawTarget for VelloCPUDrawTarget {
|
||||||
transform: Transform2D<f32>,
|
transform: Transform2D<f32>,
|
||||||
) {
|
) {
|
||||||
self.ensure_drawing();
|
self.ensure_drawing();
|
||||||
let style: vello_cpu::PaintType = style.convert();
|
self.ctx.set_paint(paint(style, composition_options.alpha));
|
||||||
self.ctx.set_paint(style);
|
|
||||||
self.ctx.set_transform(transform.cast().into());
|
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.;
|
let mut advance = 0.;
|
||||||
for run in text_runs.iter() {
|
for run in text_runs.iter() {
|
||||||
let glyphs = &run.glyphs;
|
let glyphs = &run.glyphs;
|
||||||
|
@ -346,10 +351,9 @@ impl GenericDrawTarget for VelloCPUDrawTarget {
|
||||||
transform: Transform2D<f32>,
|
transform: Transform2D<f32>,
|
||||||
) {
|
) {
|
||||||
self.ensure_drawing();
|
self.ensure_drawing();
|
||||||
let paint: vello_cpu::PaintType = style.convert();
|
self.with_composition(composition_options.composition_operation, |self_| {
|
||||||
self.with_composition(&composition_options, |self_| {
|
|
||||||
self_.ctx.set_transform(transform.cast().into());
|
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());
|
self_.ctx.fill_rect(&rect.cast().into());
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -395,10 +399,9 @@ impl GenericDrawTarget for VelloCPUDrawTarget {
|
||||||
transform: Transform2D<f32>,
|
transform: Transform2D<f32>,
|
||||||
) {
|
) {
|
||||||
self.ensure_drawing();
|
self.ensure_drawing();
|
||||||
let paint: vello_cpu::PaintType = style.convert();
|
self.with_composition(composition_options.composition_operation, |self_| {
|
||||||
self.with_composition(&composition_options, |self_| {
|
|
||||||
self_.ctx.set_transform(transform.cast().into());
|
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.set_stroke(line_options.convert());
|
||||||
self_.ctx.stroke_path(&path.0);
|
self_.ctx.stroke_path(&path.0);
|
||||||
})
|
})
|
||||||
|
@ -413,10 +416,9 @@ impl GenericDrawTarget for VelloCPUDrawTarget {
|
||||||
transform: Transform2D<f32>,
|
transform: Transform2D<f32>,
|
||||||
) {
|
) {
|
||||||
self.ensure_drawing();
|
self.ensure_drawing();
|
||||||
let paint: vello_cpu::PaintType = style.convert();
|
self.with_composition(composition_options.composition_operation, |self_| {
|
||||||
self.with_composition(&composition_options, |self_| {
|
|
||||||
self_.ctx.set_transform(transform.cast().into());
|
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.set_stroke(line_options.convert());
|
||||||
self_.ctx.stroke_rect(&rect.cast().into());
|
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