canvas: Make script/canvas thread boundry mostly stateless (#38164)

This PR removes all `Set*` commands as we will send needed options on
each request. This will allow us to remove most of state handling from
canvas paint thread (currently it's still stateful).

Testing: Existing WPT tests
work towards #38022

try run: https://github.com/sagudev/servo/actions/runs/16413823963

---------

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
This commit is contained in:
sagudev 2025-07-22 14:30:15 +02:00 committed by GitHub
parent 8a1cc69717
commit cd340fa8b9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 379 additions and 116 deletions

View file

@ -5,7 +5,7 @@
use std::borrow::ToOwned;
use std::collections::HashMap;
use std::sync::Arc;
use std::thread;
use std::{f32, thread};
use canvas_traits::ConstellationCanvasMsg;
use canvas_traits::canvas::*;
@ -134,76 +134,149 @@ impl<'a> CanvasPaintThread<'a> {
fn process_canvas_2d_message(&mut self, message: Canvas2dMsg, canvas_id: CanvasId) {
match message {
Canvas2dMsg::FillText(text, x, y, max_width, style, is_rtl) => {
self.canvas(canvas_id).set_fill_style(style);
self.canvas(canvas_id)
.fill_text(text, x, y, max_width, is_rtl);
Canvas2dMsg::FillText(
text,
x,
y,
max_width,
style,
is_rtl,
text_options,
shadow_options,
composition_options,
transform,
) => {
let canvas = self.canvas(canvas_id);
canvas.set_transform(&transform);
canvas.set_fill_style(style);
canvas.set_text_options(text_options);
canvas.set_shadow_options(shadow_options);
canvas.set_composition_options(composition_options);
canvas.fill_text(text, x, y, max_width, is_rtl);
},
Canvas2dMsg::FillRect(rect, style) => {
self.canvas(canvas_id).set_fill_style(style);
self.canvas(canvas_id).fill_rect(&rect);
Canvas2dMsg::FillRect(rect, style, shadow_options, composition_options, transform) => {
let canvas = self.canvas(canvas_id);
canvas.set_transform(&transform);
canvas.set_fill_style(style);
canvas.set_shadow_options(shadow_options);
canvas.set_composition_options(composition_options);
canvas.fill_rect(&rect);
},
Canvas2dMsg::StrokeRect(rect, style) => {
self.canvas(canvas_id).set_stroke_style(style);
self.canvas(canvas_id).stroke_rect(&rect);
Canvas2dMsg::StrokeRect(
rect,
style,
line_options,
shadow_options,
composition_options,
transform,
) => {
let canvas = self.canvas(canvas_id);
canvas.set_transform(&transform);
canvas.set_stroke_style(style);
canvas.set_line_options(line_options);
canvas.set_shadow_options(shadow_options);
canvas.set_composition_options(composition_options);
canvas.stroke_rect(&rect);
},
Canvas2dMsg::ClearRect(ref rect) => self.canvas(canvas_id).clear_rect(rect),
Canvas2dMsg::FillPath(style, path) => {
self.canvas(canvas_id).set_fill_style(style);
self.canvas(canvas_id).fill_path(&path);
Canvas2dMsg::ClearRect(ref rect, transform) => {
self.canvas(canvas_id).set_transform(&transform);
self.canvas(canvas_id).clear_rect(rect)
},
Canvas2dMsg::StrokePath(style, path) => {
self.canvas(canvas_id).set_stroke_style(style);
self.canvas(canvas_id).stroke_path(&path);
Canvas2dMsg::FillPath(style, path, shadow_options, composition_options, transform) => {
let canvas = self.canvas(canvas_id);
canvas.set_transform(&transform);
canvas.set_fill_style(style);
canvas.set_shadow_options(shadow_options);
canvas.set_composition_options(composition_options);
canvas.fill_path(&path);
},
Canvas2dMsg::ClipPath(path) => self.canvas(canvas_id).clip_path(&path),
Canvas2dMsg::DrawImage(snapshot, dest_rect, source_rect, smoothing_enabled) => {
self.canvas(canvas_id).draw_image(
Canvas2dMsg::StrokePath(
path,
style,
line_options,
shadow_options,
composition_options,
transform,
) => {
let canvas = self.canvas(canvas_id);
canvas.set_transform(&transform);
canvas.set_stroke_style(style);
canvas.set_line_options(line_options);
canvas.set_shadow_options(shadow_options);
canvas.set_composition_options(composition_options);
canvas.stroke_path(&path);
},
Canvas2dMsg::ClipPath(path, transform) => {
let canvas = self.canvas(canvas_id);
canvas.set_transform(&transform);
canvas.clip_path(&path);
},
Canvas2dMsg::DrawImage(
snapshot,
dest_rect,
source_rect,
smoothing_enabled,
shadow_options,
composition_options,
transform,
) => {
let canvas = self.canvas(canvas_id);
canvas.set_transform(&transform);
canvas.set_shadow_options(shadow_options);
canvas.set_composition_options(composition_options);
canvas.draw_image(
snapshot.to_owned(),
dest_rect,
source_rect,
smoothing_enabled,
)
},
Canvas2dMsg::DrawEmptyImage(image_size, dest_rect, source_rect) => self
.canvas(canvas_id)
.draw_image(Snapshot::cleared(image_size), dest_rect, source_rect, false),
Canvas2dMsg::DrawEmptyImage(
image_size,
dest_rect,
source_rect,
shadow_options,
composition_options,
transform,
) => {
let canvas = self.canvas(canvas_id);
canvas.set_transform(&transform);
canvas.set_shadow_options(shadow_options);
canvas.set_composition_options(composition_options);
self.canvas(canvas_id).draw_image(
Snapshot::cleared(image_size),
dest_rect,
source_rect,
false,
)
},
Canvas2dMsg::DrawImageInOther(
other_canvas_id,
image_size,
dest_rect,
source_rect,
smoothing,
shadow_options,
composition_options,
transform,
) => {
let snapshot = self
.canvas(canvas_id)
.read_pixels(Some(source_rect.to_u32()), Some(image_size));
self.canvas(other_canvas_id).draw_image(
snapshot,
dest_rect,
source_rect,
smoothing,
);
let canvas = self.canvas(other_canvas_id);
canvas.set_transform(&transform);
canvas.set_composition_options(composition_options);
canvas.set_shadow_options(shadow_options);
canvas.draw_image(snapshot, dest_rect, source_rect, smoothing);
},
Canvas2dMsg::MeasureText(text, sender) => {
let metrics = self.canvas(canvas_id).measure_text(text);
Canvas2dMsg::MeasureText(text, sender, text_options) => {
let canvas = self.canvas(canvas_id);
canvas.set_text_options(text_options);
let metrics = canvas.measure_text(text);
sender.send(metrics).unwrap();
},
Canvas2dMsg::RestoreContext => self.canvas(canvas_id).restore_context_state(),
Canvas2dMsg::SaveContext => self.canvas(canvas_id).save_context_state(),
Canvas2dMsg::SetLineWidth(width) => self.canvas(canvas_id).set_line_width(width),
Canvas2dMsg::SetLineCap(cap) => self.canvas(canvas_id).set_line_cap(cap),
Canvas2dMsg::SetLineJoin(join) => self.canvas(canvas_id).set_line_join(join),
Canvas2dMsg::SetMiterLimit(limit) => self.canvas(canvas_id).set_miter_limit(limit),
Canvas2dMsg::SetLineDash(items) => self.canvas(canvas_id).set_line_dash(items),
Canvas2dMsg::SetLineDashOffset(offset) => {
self.canvas(canvas_id).set_line_dash_offset(offset)
},
Canvas2dMsg::SetTransform(ref matrix) => self.canvas(canvas_id).set_transform(matrix),
Canvas2dMsg::SetGlobalAlpha(alpha) => self.canvas(canvas_id).set_global_alpha(alpha),
Canvas2dMsg::SetGlobalComposition(op) => {
self.canvas(canvas_id).set_global_composition(op)
},
Canvas2dMsg::GetImageData(dest_rect, canvas_size, sender) => {
let snapshot = self
.canvas(canvas_id)
@ -214,21 +287,6 @@ impl<'a> CanvasPaintThread<'a> {
self.canvas(canvas_id)
.put_image_data(snapshot.to_owned(), rect);
},
Canvas2dMsg::SetShadowOffsetX(value) => {
self.canvas(canvas_id).set_shadow_offset_x(value)
},
Canvas2dMsg::SetShadowOffsetY(value) => {
self.canvas(canvas_id).set_shadow_offset_y(value)
},
Canvas2dMsg::SetShadowBlur(value) => self.canvas(canvas_id).set_shadow_blur(value),
Canvas2dMsg::SetShadowColor(color) => self.canvas(canvas_id).set_shadow_color(color),
Canvas2dMsg::SetFont(font_style) => self.canvas(canvas_id).set_font(font_style),
Canvas2dMsg::SetTextAlign(text_align) => {
self.canvas(canvas_id).set_text_align(text_align)
},
Canvas2dMsg::SetTextBaseline(text_baseline) => {
self.canvas(canvas_id).set_text_baseline(text_baseline)
},
Canvas2dMsg::UpdateImage(sender) => {
self.canvas(canvas_id).update_image_rendering();
sender.send(()).unwrap();
@ -455,4 +513,41 @@ impl Canvas<'_> {
Canvas::Raqote(canvas_data) => canvas_data.recreate(size),
}
}
fn set_text_options(&mut self, text_options: TextOptions) {
if let Some(font) = text_options.font {
self.set_font(font);
}
self.set_text_align(text_options.align);
self.set_text_baseline(text_options.baseline);
}
fn set_shadow_options(&mut self, shadow_options: ShadowOptions) {
self.set_shadow_color(shadow_options.color);
self.set_shadow_offset_x(shadow_options.offset_x);
self.set_shadow_offset_y(shadow_options.offset_y);
self.set_shadow_blur(shadow_options.blur);
}
fn set_composition_options(&mut self, composition_options: CompositionOptions) {
self.set_global_alpha(composition_options.alpha as f32);
self.set_global_composition(composition_options.composition_operation);
}
fn set_line_options(&mut self, line_options: LineOptions) {
let LineOptions {
width,
cap_style,
join_style,
miter_limit,
dash,
dash_offset,
} = line_options;
self.set_line_width(width as f32);
self.set_line_cap(cap_style);
self.set_line_join(join_style);
self.set_miter_limit(miter_limit as f32);
self.set_line_dash(dash);
self.set_line_dash_offset(dash_offset as f32);
}
}