Upgrade WebRender to e491e1ae637b2eed1e7195855d88357e5eb3ddf9 (#30323)

* Upgrade vendored version of WebRender

* Patch WebRender: upgrade version of gleam

* Restore hit testing implementation

* Fix WebRender warnings

* Adapt Servo to new WebRender

* Update results

* Add a workaround for #30313

This slightly expands text boundaries in order to take into account the
fact that layout isn't measuring glyph boundaries.
This commit is contained in:
Martin Robinson 2023-09-10 14:38:56 +02:00 committed by GitHub
parent c079acb3c3
commit a9d37cb85a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
563 changed files with 48524 additions and 51657 deletions

View file

@ -13,7 +13,8 @@ use std::default::Default;
use std::sync::{Arc, Mutex};
use surfman::chains::{SwapChainAPI, SwapChains, SwapChainsAPI};
use surfman::{Device, SurfaceInfo, SurfaceTexture};
use webrender_api::{DocumentId, RenderApiSender};
use webrender::RenderApiSender;
use webrender_api::DocumentId;
use webrender_surfman::WebrenderSurfman;
use webrender_traits::{
WebrenderExternalImageApi, WebrenderExternalImageRegistry, WebrenderImageSource,

View file

@ -63,10 +63,11 @@ use surfman::{
self, Adapter, Connection, Context, ContextAttributeFlags, ContextAttributes, Device,
GLVersion, SurfaceAccess, SurfaceInfo, SurfaceType,
};
use webrender::{RenderApi, RenderApiSender, Transaction};
use webrender_api::{
units::DeviceIntSize, DirtyRect, DocumentId, ExternalImageData, ExternalImageId,
ExternalImageType, ImageData, ImageDescriptor, ImageDescriptorFlags, ImageFormat, ImageKey,
RenderApi, RenderApiSender, TextureTarget, Transaction,
ExternalImageType, ImageBufferKind, ImageData, ImageDescriptor, ImageDescriptorFlags,
ImageFormat, ImageKey,
};
use webrender_traits::{WebrenderExternalImageRegistry, WebrenderImageHandlerType};
use webxr::SurfmanGL as WebXRSurfman;
@ -608,7 +609,7 @@ impl WebGLThread {
let descriptor_attributes = self.device.context_descriptor_attributes(&descriptor);
let gl_version = descriptor_attributes.version;
let has_alpha = requested_flags.contains(ContextAttributeFlags::ALPHA);
let texture_target = current_wr_texture_target(&self.device);
let image_buffer_kind = current_wr_image_buffer_kind(&self.device);
self.device.make_context_current(&ctx).unwrap();
let framebuffer = self
@ -667,7 +668,7 @@ impl WebGLThread {
size.to_i32(),
has_alpha,
id,
texture_target,
image_buffer_kind,
);
self.cached_context_info
@ -723,7 +724,7 @@ impl WebGLThread {
.state
.requested_flags
.contains(ContextAttributeFlags::ALPHA);
let texture_target = current_wr_texture_target(&self.device);
let image_buffer_kind = current_wr_image_buffer_kind(&self.device);
Self::update_wr_external_image(
&mut self.webrender_api,
self.webrender_doc,
@ -731,7 +732,7 @@ impl WebGLThread {
has_alpha,
context_id,
info.image_key,
texture_target,
image_buffer_kind,
);
debug_assert_eq!(data.gl.get_error(), gl::NO_ERROR);
@ -927,10 +928,10 @@ impl WebGLThread {
size: Size2D<i32>,
alpha: bool,
context_id: WebGLContextId,
target: TextureTarget,
image_buffer_kind: ImageBufferKind,
) -> ImageKey {
let descriptor = Self::image_descriptor(size, alpha);
let data = Self::external_image_data(context_id, target);
let data = Self::external_image_data(context_id, image_buffer_kind);
let image_key = webrender_api.generate_image_key();
let mut txn = Transaction::new();
@ -948,10 +949,10 @@ impl WebGLThread {
alpha: bool,
context_id: WebGLContextId,
image_key: ImageKey,
target: TextureTarget,
image_buffer_kind: ImageBufferKind,
) {
let descriptor = Self::image_descriptor(size, alpha);
let data = Self::external_image_data(context_id, target);
let data = Self::external_image_data(context_id, image_buffer_kind);
let mut txn = Transaction::new();
txn.update_image(image_key, descriptor, data, &DirtyRect::All);
@ -972,11 +973,14 @@ impl WebGLThread {
}
/// Helper function to create a `ImageData::External` instance.
fn external_image_data(context_id: WebGLContextId, target: TextureTarget) -> ImageData {
fn external_image_data(
context_id: WebGLContextId,
image_buffer_kind: ImageBufferKind,
) -> ImageData {
let data = ExternalImageData {
id: ExternalImageId(context_id.0 as u64),
channel_index: 0,
image_type: ExternalImageType::TextureHandle(target),
image_type: ExternalImageType::TextureHandle(image_buffer_kind),
};
ImageData::External(data)
}
@ -1006,10 +1010,10 @@ struct WebGLContextInfo {
}
// TODO(pcwalton): Add `GL_TEXTURE_EXTERNAL_OES`?
fn current_wr_texture_target(device: &Device) -> TextureTarget {
fn current_wr_image_buffer_kind(device: &Device) -> ImageBufferKind {
match device.surface_gl_texture_target() {
gl::TEXTURE_RECTANGLE => TextureTarget::Rect,
_ => TextureTarget::Default,
gl::TEXTURE_RECTANGLE => ImageBufferKind::TextureRect,
_ => ImageBufferKind::Texture2D,
}
}

View file

@ -48,13 +48,14 @@ use std::num::NonZeroU32;
use std::rc::Rc;
use style_traits::{CSSPixel, DevicePixel, PinchZoomFactor};
use time::{now, precise_time_ns, precise_time_s};
use webrender;
use webrender::{CaptureBits, RenderApi, Transaction};
use webrender_api::units::{
DeviceIntPoint, DeviceIntSize, DevicePoint, LayoutPoint, LayoutVector2D, WorldPoint,
};
use webrender_api::{
self, BuiltDisplayList, CaptureBits, DirtyRect, DocumentId, Epoch as WebRenderEpoch,
ExternalScrollId, HitTestFlags, PipelineId as WebRenderPipelineId, RenderApi, ScrollClamping,
ScrollLocation, Transaction, ZoomFactor,
self, BuiltDisplayList, DirtyRect, DocumentId, Epoch as WebRenderEpoch, ExternalScrollId,
HitTestFlags, PipelineId as WebRenderPipelineId, ScrollClamping, ScrollLocation, ZoomFactor,
};
use webrender_surfman::WebrenderSurfman;
@ -649,58 +650,55 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
WebRenderEpoch(0),
None,
Default::default(),
(pipeline, Default::default(), Default::default()),
(pipeline, Default::default()),
false,
);
self.webrender_api
.send_transaction(self.webrender_document, txn);
},
WebrenderMsg::Layout(script_traits::WebrenderMsg::SendScrollNode(
point,
scroll_id,
clamping,
)) => {
WebrenderMsg::Layout(script_traits::WebrenderMsg::SendScrollNode(point, scroll_id)) => {
self.waiting_for_results_of_scroll = true;
let mut txn = Transaction::new();
txn.scroll_node_with_id(point, scroll_id, clamping);
txn.generate_frame();
txn.scroll_node_with_id(point, scroll_id, ScrollClamping::ToContentBounds);
txn.generate_frame(0);
self.webrender_api
.send_transaction(self.webrender_document, txn);
},
WebrenderMsg::Layout(script_traits::WebrenderMsg::SendDisplayList {
display_list_info,
content_size,
display_list_descriptor,
display_list_receiver,
}) => match display_list_receiver.recv() {
Ok(data) => {
self.waiting_on_pending_frame = true;
}) => {
let display_list_data = match display_list_receiver.recv() {
Ok(display_list_data) => display_list_data,
_ => return warn!("Could not recieve WebRender display list."),
};
let pipeline_id = display_list_info.pipeline_id;
let details = self.pipeline_details(PipelineId::from_webrender(pipeline_id));
details.hit_test_items = display_list_info.hit_test_info;
details.install_new_scroll_tree(display_list_info.scroll_tree);
self.waiting_on_pending_frame = true;
let mut txn = Transaction::new();
txn.set_display_list(
display_list_info.epoch,
None,
display_list_info.viewport_size,
(
pipeline_id,
content_size,
BuiltDisplayList::from_data(data, display_list_descriptor),
),
true,
);
txn.generate_frame();
self.webrender_api
.send_transaction(self.webrender_document, txn);
},
Err(e) => warn!("error receiving display list data: {e:?}"),
let pipeline_id = display_list_info.pipeline_id;
let details = self.pipeline_details(PipelineId::from_webrender(pipeline_id));
details.hit_test_items = display_list_info.hit_test_info;
details.install_new_scroll_tree(display_list_info.scroll_tree);
let mut txn = Transaction::new();
txn.set_display_list(
display_list_info.epoch,
None,
display_list_info.viewport_size,
(
pipeline_id,
BuiltDisplayList::from_data(display_list_data, display_list_descriptor),
),
true,
);
txn.generate_frame(0);
self.webrender_api
.send_transaction(self.webrender_document, txn);
},
WebrenderMsg::Layout(script_traits::WebrenderMsg::HitTest(
@ -876,7 +874,7 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
let pipeline_id = frame_tree.pipeline.id.to_webrender();
let mut txn = Transaction::new();
txn.set_root_pipeline(pipeline_id);
txn.generate_frame();
txn.generate_frame(0);
self.webrender_api
.send_transaction(self.webrender_document, txn);
@ -925,14 +923,16 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
self.pipeline_details.remove(&pipeline_id);
}
fn send_window_size(&self, size_type: WindowSizeType) {
fn send_window_size(&mut self, size_type: WindowSizeType) {
let dppx = self.page_zoom * self.embedder_coordinates.hidpi_factor;
self.webrender_api.set_document_view(
self.webrender_document,
let mut transaction = Transaction::new();
transaction.set_document_view(
self.embedder_coordinates.get_viewport(),
self.embedder_coordinates.hidpi_factor.get(),
);
self.webrender_api
.send_transaction(self.webrender_document, transaction);
let initial_viewport = self.embedder_coordinates.viewport.size.to_f32() / dppx;
@ -1195,17 +1195,17 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
pub fn on_scroll_event(
&mut self,
delta: ScrollLocation,
scroll_location: ScrollLocation,
cursor: DeviceIntPoint,
phase: TouchEventType,
) {
match phase {
TouchEventType::Move => self.on_scroll_window_event(delta, cursor),
TouchEventType::Move => self.on_scroll_window_event(scroll_location, cursor),
TouchEventType::Up | TouchEventType::Cancel => {
self.on_scroll_window_event(delta, cursor);
self.on_scroll_window_event(scroll_location, cursor);
},
TouchEventType::Down => {
self.on_scroll_window_event(delta, cursor);
self.on_scroll_window_event(scroll_location, cursor);
},
}
}
@ -1213,7 +1213,7 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
fn on_scroll_window_event(&mut self, scroll_location: ScrollLocation, cursor: DeviceIntPoint) {
self.pending_scroll_zoom_events.push(ScrollZoomEvent {
magnification: 1.0,
scroll_location: scroll_location,
scroll_location,
cursor: cursor,
event_count: 1,
});
@ -1307,7 +1307,7 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
self.set_pinch_zoom_level(old_zoom * combined_event.magnification);
txn.set_pinch_zoom(ZoomFactor::new(self.pinch_zoom_level()));
}
txn.generate_frame();
txn.generate_frame(0);
self.webrender_api
.send_transaction(self.webrender_document, txn);
self.waiting_for_results_of_scroll = true
@ -1395,7 +1395,7 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
fn update_page_zoom_for_webrender(&mut self) {
let page_zoom = ZoomFactor::new(self.page_zoom.get());
let mut txn = Transaction::new();
let mut txn = webrender::Transaction::new();
txn.set_page_zoom(page_zoom);
self.webrender_api
.send_transaction(self.webrender_document, txn);
@ -1609,7 +1609,7 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
// Paint the scene.
// TODO(gw): Take notice of any errors the renderer returns!
self.clear_background();
self.webrender.render(size).ok();
self.webrender.render(size, 0 /* buffer_age */).ok();
},
);
@ -1904,7 +1904,7 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
self.webrender.set_debug_flags(flags);
let mut txn = Transaction::new();
txn.generate_frame();
txn.generate_frame(0);
self.webrender_api
.send_transaction(self.webrender_document, txn);
}

View file

@ -12,8 +12,8 @@ use crossbeam_channel::Sender;
use profile_traits::mem;
use profile_traits::time;
use std::rc::Rc;
use webrender::RenderApi;
use webrender_api::DocumentId;
use webrender_api::RenderApi;
use webrender_surfman::WebrenderSurfman;
mod compositor;

View file

@ -42,6 +42,7 @@ servo_remutex = { path = "../remutex" }
servo_url = { path = "../url" }
style_traits = { path = "../style_traits" }
webgpu = { path = "../webgpu" }
webrender = { workspace = true }
webrender_api = { workspace = true }
webrender_traits = { path = "../webrender_traits" }
webxr-api = { git = "https://github.com/servo/webxr", features = ["ipc"] }

View file

@ -179,7 +179,8 @@ use std::sync::{Arc, Mutex};
use std::thread;
use style_traits::CSSPixel;
use webgpu::{self, WebGPU, WebGPURequest};
use webrender_api::{DocumentId, RenderApi, RenderApiSender};
use webrender::{RenderApi, RenderApiSender};
use webrender_api::DocumentId;
use webrender_traits::WebrenderExternalImageRegistry;
type PendingApprovalNavigations = HashMap<PipelineId, (LoadData, HistoryEntryReplacement)>;

View file

@ -71,10 +71,11 @@ use style::values::specified::ui::CursorKind;
use style::values::RGBA;
use style_traits::{CSSPixel, ToCss};
use webrender_api::units::{LayoutRect, LayoutTransform, LayoutVector2D};
use webrender_api::{self, BorderDetails, BorderRadius, BorderSide, BoxShadowClipMode, ColorF};
use webrender_api::{ColorU, ExternalScrollId, FilterOp, GlyphInstance, ImageRendering, LineStyle};
use webrender_api::{NinePatchBorder, NinePatchBorderSource, NormalBorder, PropertyBinding};
use webrender_api::{ScrollSensitivity, StickyOffsetBounds};
use webrender_api::{
self, BorderDetails, BorderRadius, BorderSide, BoxShadowClipMode, ColorF, ColorU,
ExternalScrollId, FilterOp, GlyphInstance, ImageRendering, LineStyle, NinePatchBorder,
NinePatchBorderSource, NormalBorder, PropertyBinding, ScrollSensitivity, StickyOffsetBounds,
};
static THREAD_TINT_COLORS: [ColorF; 8] = [
ColorF {
@ -2117,7 +2118,7 @@ impl Fragment {
}
// Text
let mut glyphs = convert_text_run_to_glyphs(
let (largest_advance, mut glyphs) = convert_text_run_to_glyphs(
text_fragment.run.clone(),
text_fragment.range,
baseline_origin,
@ -2131,6 +2132,22 @@ impl Fragment {
};
state.indexable_text.insert(self.node, indexable_text);
// FIXME(mrobinson, #30313): This is a serious hack to enable a WebRender upgrade.
// Servo is not calculating glyph boundaries and is instead relying on the
// measured size of the content box here -- which is based on the positioning
// of the text. The issue is that glyphs can extend beyond the boundaries
// established by their brush origin and advance. Servo should be measuring
// the ink boundary rectangle based on the brush origin and the glyph extents
// instead.
//
// We don't yet have that information here, so in the meantime simply expand
// the boundary rectangle of the text by the largest character advance of the
// painted text run in all directions. This is used as a heuristic for a
// reasonable amount of "fudge" space to include the entire text run.
let inflated_bounds = stacking_relative_content_box
.inflate(largest_advance, largest_advance)
.to_layout();
// Process glyphs in chunks to avoid overflowing WebRender's internal limits (#17230).
while !glyphs.is_empty() {
let mut rest_of_glyphs = vec![];
@ -2142,7 +2159,7 @@ impl Fragment {
state.add_display_item(DisplayItem::Text(CommonDisplayItem::with_data(
base.clone(),
webrender_api::TextDisplayItem {
bounds: stacking_relative_content_box.to_layout(),
bounds: inflated_bounds,
common: items::empty_common_item_properties(),
font_key: text_fragment.run.font_key,
color: text_color.to_layout(),
@ -3013,7 +3030,8 @@ fn convert_text_run_to_glyphs(
text_run: Arc<TextRun>,
range: Range<ByteIndex>,
mut origin: Point2D<Au>,
) -> Vec<GlyphInstance> {
) -> (Au, Vec<GlyphInstance>) {
let mut largest_advance = Au(0);
let mut glyphs = vec![];
for slice in text_run.natural_word_slices_in_visual_order(&range) {
@ -3023,6 +3041,8 @@ fn convert_text_run_to_glyphs(
} else {
glyph.advance()
};
largest_advance = largest_advance.max(glyph.advance());
if !slice.glyphs.is_whitespace() {
let glyph_offset = glyph.offset().unwrap_or(Point2D::zero());
let point = origin + glyph_offset.to_vector();
@ -3035,7 +3055,7 @@ fn convert_text_run_to_glyphs(
origin.x += glyph_advance;
}
}
return glyphs;
(largest_advance, glyphs)
}
pub struct IndexableTextItem {

View file

@ -39,7 +39,7 @@ impl ToLayout for Filter {
type Type = wr::FilterOp;
fn to_layout(&self) -> Self::Type {
match *self {
Filter::Blur(radius) => wr::FilterOp::Blur(radius.px()),
Filter::Blur(radius) => wr::FilterOp::Blur(radius.px(), radius.px()),
Filter::Brightness(amount) => wr::FilterOp::Brightness(amount.0),
Filter::Contrast(amount) => wr::FilterOp::Contrast(amount.0),
Filter::Grayscale(amount) => wr::FilterOp::Grayscale(amount.0),

View file

@ -29,11 +29,10 @@ use style::computed_values::_servo_top_layer::T as InTopLayer;
use webrender_api as wr;
use webrender_api::units::{LayoutPixel, LayoutRect, LayoutTransform};
use webrender_api::{
BorderRadius, ClipId, ClipMode, CommonItemProperties, ComplexClipRegion, ExternalScrollId,
FilterOp, GlyphInstance, GradientStop, ImageKey, MixBlendMode, PrimitiveFlags,
ScrollSensitivity, Shadow, SpatialId, StickyOffsetBounds, TransformStyle,
BorderRadius, ClipChainId, ClipId, ClipMode, CommonItemProperties, ComplexClipRegion,
ExternalScrollId, FilterOp, GlyphInstance, GradientStop, ImageKey, MixBlendMode,
PrimitiveFlags, ScrollSensitivity, Shadow, SpatialId, StickyOffsetBounds, TransformStyle,
};
use wr::ClipChainId;
pub use style::dom::OpaqueNode;
@ -497,7 +496,6 @@ pub fn empty_common_item_properties() -> CommonItemProperties {
clip_rect: LayoutRect::max_rect(),
clip_id: ClipId::root(wr::PipelineId::dummy()),
spatial_id: SpatialId::root_scroll_node(wr::PipelineId::dummy()),
hit_info: None,
flags: PrimitiveFlags::empty(),
}
}

View file

@ -46,6 +46,7 @@ impl<'a> ClipScrollState<'a> {
let root_clip_chain =
builder.define_clip_chain(None, [ClipId::root(state.compositor_info.pipeline_id)]);
state.add_clip_node_mapping(0, root_clip_chain);
state.add_clip_node_mapping(1, root_clip_chain);
@ -111,11 +112,7 @@ impl DisplayList {
epoch: Epoch,
) -> (DisplayListBuilder, CompositorDisplayListInfo, IsContentful) {
let webrender_pipeline = pipeline_id.to_webrender();
let mut builder = DisplayListBuilder::with_capacity(
webrender_pipeline,
self.bounds().size,
1024 * 1024, // 1 MB of space
);
let mut builder = DisplayListBuilder::new(webrender_pipeline);
let content_size = self.bounds().size;
let mut state = ClipScrollState::new(
@ -153,17 +150,16 @@ impl DisplayItem {
let internal_clip_id = clip_and_scroll_indices
.clipping
.unwrap_or(clip_and_scroll_indices.scrolling);
let current_clip_id = state.webrender_clip_id_for_index(internal_clip_id.to_index());
let current_clip_chain_id = state.webrender_clip_id_for_index(internal_clip_id.to_index());
let hit_test_bounds = self.bounds().intersection(&self.base().clip_rect);
let build_common_item_properties = |base: &BaseDisplayItem| {
CommonItemProperties {
clip_rect: base.clip_rect,
spatial_id: current_scroll_node_id.spatial_id,
clip_id: ClipId::ClipChain(current_clip_id),
clip_id: ClipId::ClipChain(current_clip_chain_id),
// TODO(gw): Make use of the WR backface visibility functionality.
flags: PrimitiveFlags::default(),
hit_info: None,
}
};
@ -184,10 +180,15 @@ impl DisplayItem {
current_scroll_node_id,
);
let mut common = build_common_item_properties(base);
common.hit_info = Some((hit_test_index as u64, 0u16));
common.clip_rect = bounds;
builder.push_hit_test(&common);
builder.push_hit_test(
&CommonItemProperties {
clip_rect: bounds,
spatial_id: current_scroll_node_id.spatial_id,
clip_id: ClipId::ClipChain(current_clip_chain_id),
flags: PrimitiveFlags::default(),
},
(hit_test_index as u64, 0u16),
);
};
match *self {
@ -299,7 +300,13 @@ impl DisplayItem {
scrolling_relative_to: None,
},
),
(Some(t), None) => (t, ReferenceFrameKind::Transform),
(Some(t), None) => (
t,
ReferenceFrameKind::Transform {
is_2d_scale_translation: false,
should_snap: false,
},
),
(Some(t), Some(p)) => (
p.then(&t),
ReferenceFrameKind::Perspective {
@ -318,7 +325,7 @@ impl DisplayItem {
);
let index = frame_index.to_index();
state.add_clip_node_mapping(index, current_clip_id);
state.add_clip_node_mapping(index, current_clip_chain_id);
state.register_spatial_node(
index,
new_spatial_id,
@ -405,7 +412,7 @@ impl DisplayItem {
let spatial_id = builder
.define_scroll_frame(
&parent_space_and_clip_info,
Some(external_id),
external_id,
node.content_rect,
item_rect,
scroll_sensitivity,

View file

@ -64,8 +64,8 @@ pub(super) fn painting_area<'a>(
};
// The 'backgound-clip' property maps directly to `clip_rect` in `CommonItemProperties`:
let mut common = builder.common_properties(*painting_area, &fb.fragment.style);
if let Some(clip_id) = clip {
common.clip_id = clip_id
if let Some(clip_chain_id) = clip {
common.clip_id = wr::ClipId::ClipChain(clip_chain_id)
}
(painting_area, common)
}

View file

@ -19,7 +19,7 @@ impl ToWebRender for ComputedFilter {
type Type = FilterOp;
fn to_webrender(&self) -> Self::Type {
match *self {
ComputedFilter::Blur(radius) => FilterOp::Blur(radius.px()),
ComputedFilter::Blur(radius) => FilterOp::Blur(radius.px(), radius.px()),
ComputedFilter::Brightness(amount) => FilterOp::Brightness(amount.0),
ComputedFilter::Contrast(amount) => FilterOp::Contrast(amount.0),
ComputedFilter::Grayscale(amount) => FilterOp::Grayscale(amount.0),

View file

@ -26,7 +26,7 @@ use style::values::computed::{BorderStyle, Color, Length, LengthPercentage, Outl
use style::values::specified::text::TextDecorationLine;
use style::values::specified::ui::CursorKind;
use style_traits::CSSPixel;
use webrender_api::{self as wr, units};
use webrender_api::{self as wr, units, ClipChainId, ClipId, CommonItemProperties};
mod background;
mod conversions;
@ -71,7 +71,7 @@ impl DisplayList {
epoch: wr::Epoch,
) -> Self {
Self {
wr: wr::DisplayListBuilder::new(pipeline_id, content_size),
wr: wr::DisplayListBuilder::new(pipeline_id),
compositor_info: CompositorDisplayListInfo::new(
viewport_size,
content_size,
@ -93,7 +93,7 @@ pub(crate) struct DisplayListBuilder<'a> {
/// only passing the builder instead passing the containing
/// [stacking_context::StackingContextFragment] as an argument to display
/// list building functions.
current_clip_id: wr::ClipId,
current_clip_chain_id: ClipChainId,
/// The [OpaqueNode] handle to the node used to paint the page background
/// if the background was a canvas.
@ -126,8 +126,8 @@ impl DisplayList {
root_stacking_context: &StackingContext,
) -> (FnvHashMap<BrowsingContextId, Size2D<f32, CSSPixel>>, bool) {
let mut builder = DisplayListBuilder {
current_scroll_node_id: self.compositor_info.root_scroll_node_id,
current_clip_id: wr::ClipId::root(self.wr.pipeline_id),
current_scroll_node_id: self.compositor_info.root_reference_frame_id,
current_clip_chain_id: ClipChainId(0, self.compositor_info.pipeline_id),
element_for_canvas_background: fragment_tree.canvas_background.from_element,
is_contentful: false,
context,
@ -155,8 +155,7 @@ impl<'a> DisplayListBuilder<'a> {
wr::CommonItemProperties {
clip_rect,
spatial_id: self.current_scroll_node_id.spatial_id,
clip_id: self.current_clip_id,
hit_info: None,
clip_id: ClipId::ClipChain(self.current_clip_chain_id),
flags: style.get_webrender_primitive_flags(),
}
}
@ -281,12 +280,21 @@ impl Fragment {
return;
}
let common = builder.common_properties(rect.to_webrender(), &fragment.parent_style);
let hit_info = builder.hit_info(&fragment.parent_style, fragment.base.tag, Cursor::Text);
let mut hit_test_common = common.clone();
hit_test_common.hit_info = hit_info;
builder.wr().push_hit_test(&hit_test_common);
if let Some(hit_info) =
builder.hit_info(&fragment.parent_style, fragment.base.tag, Cursor::Text)
{
let clip_chain_id = builder.current_clip_chain_id;
let spatial_id = builder.current_scroll_node_id.spatial_id;
builder.wr().push_hit_test(
&CommonItemProperties {
clip_rect: rect.to_webrender(),
clip_id: ClipId::ClipChain(clip_chain_id),
spatial_id,
flags: fragment.parent_style.get_webrender_primitive_flags(),
},
hit_info,
);
}
let color = fragment.parent_style.clone_color();
let font_metrics = &fragment.font_metrics;
@ -318,6 +326,7 @@ impl Fragment {
}
// Text.
let common = builder.common_properties(rect.to_webrender(), &fragment.parent_style);
builder.wr().push_text(
&common,
rect.to_webrender(),
@ -376,9 +385,9 @@ struct BuilderForBoxFragment<'a> {
padding_rect: OnceCell<units::LayoutRect>,
content_rect: OnceCell<units::LayoutRect>,
border_radius: wr::BorderRadius,
border_edge_clip_id: OnceCell<Option<wr::ClipId>>,
padding_edge_clip_id: OnceCell<Option<wr::ClipId>>,
content_edge_clip_id: OnceCell<Option<wr::ClipId>>,
border_edge_clip_chain_id: OnceCell<Option<ClipChainId>>,
padding_edge_clip_chain_id: OnceCell<Option<ClipChainId>>,
content_edge_clip_chain_id: OnceCell<Option<ClipChainId>>,
}
impl<'a> BuilderForBoxFragment<'a> {
@ -433,9 +442,9 @@ impl<'a> BuilderForBoxFragment<'a> {
border_radius,
padding_rect: OnceCell::new(),
content_rect: OnceCell::new(),
border_edge_clip_id: OnceCell::new(),
padding_edge_clip_id: OnceCell::new(),
content_edge_clip_id: OnceCell::new(),
border_edge_clip_chain_id: OnceCell::new(),
padding_edge_clip_chain_id: OnceCell::new(),
content_edge_clip_chain_id: OnceCell::new(),
}
}
@ -459,14 +468,14 @@ impl<'a> BuilderForBoxFragment<'a> {
})
}
fn border_edge_clip(&self, builder: &mut DisplayListBuilder) -> Option<wr::ClipId> {
fn border_edge_clip(&self, builder: &mut DisplayListBuilder) -> Option<ClipChainId> {
*self
.border_edge_clip_id
.border_edge_clip_chain_id
.get_or_init(|| clip_for_radii(self.border_radius, self.border_rect, builder))
}
fn padding_edge_clip(&self, builder: &mut DisplayListBuilder) -> Option<wr::ClipId> {
*self.padding_edge_clip_id.get_or_init(|| {
fn padding_edge_clip(&self, builder: &mut DisplayListBuilder) -> Option<ClipChainId> {
*self.padding_edge_clip_chain_id.get_or_init(|| {
clip_for_radii(
inner_radii(
self.border_radius,
@ -481,8 +490,8 @@ impl<'a> BuilderForBoxFragment<'a> {
})
}
fn content_edge_clip(&self, builder: &mut DisplayListBuilder) -> Option<wr::ClipId> {
*self.content_edge_clip_id.get_or_init(|| {
fn content_edge_clip(&self, builder: &mut DisplayListBuilder) -> Option<ClipChainId> {
*self.content_edge_clip_chain_id.get_or_init(|| {
clip_for_radii(
inner_radii(
self.border_radius,
@ -512,14 +521,16 @@ impl<'a> BuilderForBoxFragment<'a> {
self.fragment.base.tag,
Cursor::Default,
);
if hit_info.is_some() {
let mut common = builder.common_properties(self.border_rect, &self.fragment.style);
common.hit_info = hit_info;
if let Some(clip_id) = self.border_edge_clip(builder) {
common.clip_id = clip_id
}
builder.wr().push_hit_test(&common)
let hit_info = match hit_info {
Some(hit_info) => hit_info,
None => return,
};
let mut common = builder.common_properties(self.border_rect, &self.fragment.style);
if let Some(clip_chain_id) = self.border_edge_clip(builder) {
common.clip_id = ClipId::ClipChain(clip_chain_id);
}
builder.wr().push_hit_test(&common, hit_info);
}
fn build_background(&mut self, builder: &mut DisplayListBuilder) {
@ -868,21 +879,27 @@ fn clip_for_radii(
radii: wr::BorderRadius,
rect: units::LayoutRect,
builder: &mut DisplayListBuilder,
) -> Option<wr::ClipId> {
) -> Option<ClipChainId> {
if radii.is_zero() {
None
} else {
let clip_chain_id = builder.current_clip_chain_id.clone();
let parent_space_and_clip = wr::SpaceAndClipInfo {
spatial_id: builder.current_scroll_node_id.spatial_id,
clip_id: builder.current_clip_id,
clip_id: ClipId::ClipChain(clip_chain_id),
};
Some(builder.wr().define_clip_rounded_rect(
let new_clip_id = builder.wr().define_clip_rounded_rect(
&parent_space_and_clip,
wr::ComplexClipRegion {
rect,
radii,
mode: wr::ClipMode::Clip,
},
))
);
Some(
builder
.wr()
.define_clip_chain(Some(clip_chain_id), [new_clip_id]),
)
}
}

View file

@ -28,6 +28,7 @@ use style::values::generics::transform;
use style::values::specified::box_::DisplayOutside;
use webrender_api as wr;
use webrender_api::units::{LayoutPoint, LayoutRect, LayoutTransform, LayoutVector2D};
use webrender_api::ScrollSensitivity;
#[derive(Clone)]
pub(crate) struct ContainingBlock {
@ -150,17 +151,16 @@ impl DisplayList {
external_id: wr::ExternalScrollId,
content_rect: LayoutRect,
clip_rect: LayoutRect,
scroll_sensitivity: wr::ScrollSensitivity,
external_scroll_offset: LayoutVector2D,
scroll_sensitivity: ScrollSensitivity,
) -> (ScrollTreeNodeId, wr::ClipChainId) {
let parent_space_and_clip_info = wr::SpaceAndClipInfo {
spatial_id: parent_scroll_node_id.spatial_id,
clip_id: wr::ClipId::root(self.wr.pipeline_id),
};
let new_clip_id = self
.wr
.define_clip_rect(&parent_space_and_clip_info, clip_rect);
let new_clip_chain_id = self
.wr
.define_clip_chain(Some(*parent_clip_chain_id), [new_clip_id]);
@ -169,11 +169,11 @@ impl DisplayList {
.wr
.define_scroll_frame(
&parent_space_and_clip_info,
Some(external_id),
external_id,
content_rect,
clip_rect,
scroll_sensitivity,
external_scroll_offset,
LayoutVector2D::zero(), /* external_scroll_offset */
)
.spatial_id;
@ -202,7 +202,7 @@ pub(crate) struct StackingContextFragment {
impl StackingContextFragment {
fn build_display_list(&self, builder: &mut DisplayListBuilder) {
builder.current_scroll_node_id = self.scroll_node_id;
builder.current_clip_id = wr::ClipId::ClipChain(self.clip_chain_id);
builder.current_clip_chain_id = self.clip_chain_id;
self.fragment
.borrow()
.build_display_list(builder, &self.containing_block, self.section);
@ -964,9 +964,9 @@ impl BoxFragment {
let sensitivity =
if ComputedOverflow::Hidden == overflow_x && ComputedOverflow::Hidden == overflow_y {
wr::ScrollSensitivity::Script
ScrollSensitivity::Script
} else {
wr::ScrollSensitivity::ScriptAndInputEvents
ScrollSensitivity::ScriptAndInputEvents
};
let padding_rect = self
@ -984,7 +984,6 @@ impl BoxFragment {
.to_webrender(),
padding_rect,
sensitivity,
LayoutVector2D::zero(),
),
)
}
@ -1013,7 +1012,13 @@ impl BoxFragment {
scrolling_relative_to: None,
},
),
(Some(transform), None) => (transform, wr::ReferenceFrameKind::Transform),
(Some(transform), None) => (
transform,
wr::ReferenceFrameKind::Transform {
is_2d_scale_translation: false,
should_snap: false,
},
),
(Some(transform), Some(perspective)) => (
perspective.then(&transform),
wr::ReferenceFrameKind::Perspective {

View file

@ -109,7 +109,7 @@ use style::traversal_flags::TraversalFlags;
use style_traits::CSSPixel;
use style_traits::DevicePixel;
use style_traits::SpeculativePainter;
use webrender_api::{units, ColorF, HitTestFlags, ScrollClamping};
use webrender_api::{units, ColorF, HitTestFlags};
/// Information needed by the layout thread.
pub struct LayoutThread {
@ -1073,7 +1073,7 @@ impl LayoutThread {
.maybe_observe_paint_time(self, epoch, is_contentful.0);
self.webrender_api
.send_display_list(compositor_info, builder.finalize());
.send_display_list(compositor_info, builder.finalize().1);
},
);
}
@ -1508,11 +1508,8 @@ impl LayoutThread {
.insert(state.scroll_id, state.scroll_offset);
let point = Point2D::new(-state.scroll_offset.x, -state.scroll_offset.y);
self.webrender_api.send_scroll_node(
units::LayoutPoint::from_untyped(point),
state.scroll_id,
ScrollClamping::ToContentBounds,
);
self.webrender_api
.send_scroll_node(units::LayoutPoint::from_untyped(point), state.scroll_id);
}
fn set_scroll_states<'a, 'b>(

View file

@ -95,7 +95,7 @@ use style::traversal_flags::TraversalFlags;
use style_traits::CSSPixel;
use style_traits::DevicePixel;
use style_traits::SpeculativePainter;
use webrender_api::{units, HitTestFlags, ScrollClamping};
use webrender_api::{units, HitTestFlags};
/// Information needed by the layout thread.
pub struct LayoutThread {
@ -1173,11 +1173,8 @@ impl LayoutThread {
.insert(state.scroll_id, state.scroll_offset);
let point = Point2D::new(-state.scroll_offset.x, -state.scroll_offset.y);
self.webrender_api.send_scroll_node(
units::LayoutPoint::from_untyped(point),
state.scroll_id,
ScrollClamping::ToContentBounds,
);
self.webrender_api
.send_scroll_node(units::LayoutPoint::from_untyped(point), state.scroll_id);
}
fn set_scroll_states<'a, 'b>(
@ -1284,7 +1281,7 @@ impl LayoutThread {
.maybe_observe_paint_time(self, epoch, is_contentful);
self.webrender_api
.send_display_list(display_list.compositor_info, display_list.wr.finalize());
.send_display_list(display_list.compositor_info, display_list.wr.finalize().1);
self.update_iframe_sizes(iframe_sizes);

View file

@ -62,7 +62,7 @@ use webrender_api::{
BorderRadius, BorderStyle, BoxShadowClipMode, ColorF, ComplexClipRegion, ExtendMode,
ExternalScrollId, FilterOp, FontInstanceKey, GlyphInstance, GradientStop, ImageKey,
ImageRendering, LineStyle, MixBlendMode, NinePatchBorder, NormalBorder, RepeatMode,
ScrollSensitivity, StickyOffsetBounds, TransformStyle,
StickyOffsetBounds, TransformStyle,
};
/// A C function that takes a pointer to a heap allocation and returns its size.
@ -844,8 +844,6 @@ malloc_size_of_is_0!(NormalBorder);
#[cfg(feature = "webrender_api")]
malloc_size_of_is_0!(RepeatMode);
#[cfg(feature = "webrender_api")]
malloc_size_of_is_0!(ScrollSensitivity);
#[cfg(feature = "webrender_api")]
malloc_size_of_is_0!(StickyOffsetBounds);
#[cfg(feature = "webrender_api")]
malloc_size_of_is_0!(TransformStyle);

View file

@ -96,7 +96,7 @@ use std::rc::Rc;
use std::sync::{Arc, Mutex};
use time::{self, Duration, Timespec};
use webrender_api::ImageKey;
use webrender_api::{ExternalImageData, ExternalImageId, ExternalImageType, TextureTarget};
use webrender_api::{ExternalImageData, ExternalImageId, ExternalImageType, ImageBufferKind};
use webrender_api::{ImageData, ImageDescriptor, ImageDescriptorFlags, ImageFormat};
#[derive(PartialEq)]
@ -225,9 +225,9 @@ impl VideoFrameRenderer for MediaFrameRenderer {
let image_data = if frame.is_gl_texture() && self.player_id.is_some() {
let texture_target = if frame.is_external_oes() {
TextureTarget::External
ImageBufferKind::TextureExternal
} else {
TextureTarget::Default
ImageBufferKind::Texture2D
};
ImageData::External(ExternalImageData {
@ -254,9 +254,9 @@ impl VideoFrameRenderer for MediaFrameRenderer {
let image_data = if frame.is_gl_texture() && self.player_id.is_some() {
let texture_target = if frame.is_external_oes() {
TextureTarget::External
ImageBufferKind::TextureExternal
} else {
TextureTarget::Default
ImageBufferKind::Texture2D
};
ImageData::External(ExternalImageData {

View file

@ -220,6 +220,9 @@ pub struct CompositorDisplayListInfo {
/// The size of the viewport that this display list renders into.
pub viewport_size: LayoutSize,
/// The size of this display list's content.
pub content_size: LayoutSize,
/// The epoch of the display list.
pub epoch: Epoch,
@ -270,6 +273,7 @@ impl CompositorDisplayListInfo {
CompositorDisplayListInfo {
pipeline_id,
viewport_size,
content_size,
epoch,
hit_test_info: Default::default(),
scroll_tree,

View file

@ -69,13 +69,10 @@ use std::sync::Arc;
use style_traits::CSSPixel;
use style_traits::SpeculativePainter;
use webgpu::identity::WebGPUMsg;
use webrender_api::units::{
DeviceIntSize, DevicePixel, LayoutPixel, LayoutPoint, LayoutSize, WorldPoint,
};
use webrender_api::units::{DeviceIntSize, DevicePixel, LayoutPixel, LayoutPoint, WorldPoint};
use webrender_api::{
BuiltDisplayList, BuiltDisplayListDescriptor, DocumentId, ExternalImageData, ExternalScrollId,
HitTestFlags, ImageData, ImageDescriptor, ImageKey, PipelineId as WebRenderPipelineId,
ScrollClamping,
};
/// The address of a node. Layout sends these back. They must be validated via
@ -1125,13 +1122,11 @@ pub enum WebrenderMsg {
/// Inform WebRender of the existence of this pipeline.
SendInitialTransaction(WebRenderPipelineId),
/// Perform a scroll operation.
SendScrollNode(LayoutPoint, ExternalScrollId, ScrollClamping),
SendScrollNode(LayoutPoint, ExternalScrollId),
/// Inform WebRender of a new display list for the given pipeline.
SendDisplayList {
/// The [CompositorDisplayListInfo] that describes the display list being sent.
display_list_info: CompositorDisplayListInfo,
/// The content size of this display list as calculated by WebRender.
content_size: LayoutSize,
/// A descriptor of this display list used to construct this display list from raw data.
display_list_descriptor: BuiltDisplayListDescriptor,
/// An [ipc::IpcBytesReceiver] used to send the raw data of the display list.
@ -1170,16 +1165,8 @@ impl WebrenderIpcSender {
}
/// Perform a scroll operation.
pub fn send_scroll_node(
&self,
point: LayoutPoint,
scroll_id: ExternalScrollId,
clamping: ScrollClamping,
) {
if let Err(e) = self
.0
.send(WebrenderMsg::SendScrollNode(point, scroll_id, clamping))
{
pub fn send_scroll_node(&self, point: LayoutPoint, scroll_id: ExternalScrollId) {
if let Err(e) = self.0.send(WebrenderMsg::SendScrollNode(point, scroll_id)) {
warn!("Error sending scroll node: {}", e);
}
}
@ -1188,13 +1175,12 @@ impl WebrenderIpcSender {
pub fn send_display_list(
&self,
display_list_info: CompositorDisplayListInfo,
(_, content_size, list): (WebRenderPipelineId, LayoutSize, BuiltDisplayList),
list: BuiltDisplayList,
) {
let (display_list_data, display_list_descriptor) = list.into_data();
let (display_list_sender, display_list_receiver) = ipc::bytes_channel().unwrap();
if let Err(e) = self.0.send(WebrenderMsg::SendDisplayList {
display_list_info,
content_size,
display_list_descriptor,
display_list_receiver,
}) {

View file

@ -29,11 +29,14 @@ pub use devtools_traits;
pub use embedder_traits;
pub use euclid;
pub use gfx;
pub use gleam::gl;
pub use ipc_channel;
pub use keyboard_types;
pub use layout_thread_2013;
pub use layout_thread_2020;
pub use media;
pub use msg;
pub use msg::constellation_msg::TopLevelBrowsingContextId as BrowserId;
pub use net;
pub use net_traits;
pub use profile;
@ -41,25 +44,18 @@ pub use profile_traits;
pub use script;
pub use script_layout_interface;
pub use script_traits;
pub use servo_config as config;
pub use servo_config;
pub use servo_geometry;
pub use servo_url as url;
pub use servo_url;
pub use style;
pub use style_traits;
pub use webgpu;
pub use webrender_api;
use webrender_api::{DocumentId, FontInstanceKey, FontKey, ImageKey, RenderApiSender};
pub use webrender_surfman;
pub use webrender_traits;
#[cfg(feature = "webdriver")]
fn webdriver(port: u16, constellation: Sender<ConstellationMsg>) {
webdriver_server::start_server(port, constellation);
}
#[cfg(not(feature = "webdriver"))]
fn webdriver(_port: u16, _constellation: Sender<ConstellationMsg>) {}
use bluetooth::BluetoothThreadFactory;
use bluetooth_traits::BluetoothRequest;
use canvas::canvas_paint_thread::{self, CanvasPaintThread};
@ -84,7 +80,7 @@ use constellation::{FromCompositorLogger, FromScriptLogger};
use crossbeam_channel::{unbounded, Sender};
use embedder_traits::{EmbedderMsg, EmbedderProxy, EmbedderReceiver, EventLoopWaker};
use env_logger::Builder as EnvLoggerBuilder;
use euclid::{Scale, Size2D};
use euclid::Scale;
#[cfg(all(
not(target_os = "windows"),
not(target_os = "ios"),
@ -119,16 +115,19 @@ use std::rc::Rc;
use std::sync::Arc;
use std::sync::Mutex;
use surfman::GLApi;
use webrender::ShaderPrecacheFlags;
use webrender::{RenderApiSender, ShaderPrecacheFlags};
use webrender_api::{DocumentId, FontInstanceKey, FontKey, ImageKey};
use webrender_traits::WebrenderExternalImageHandlers;
use webrender_traits::WebrenderExternalImageRegistry;
use webrender_traits::WebrenderImageHandlerType;
pub use gleam::gl;
pub use keyboard_types;
pub use msg::constellation_msg::TopLevelBrowsingContextId as BrowserId;
pub use servo_config as config;
pub use servo_url as url;
#[cfg(feature = "webdriver")]
fn webdriver(port: u16, constellation: Sender<ConstellationMsg>) {
webdriver_server::start_server(port, constellation);
}
#[cfg(not(feature = "webdriver"))]
fn webdriver(_port: u16, _constellation: Sender<ConstellationMsg>) {}
#[cfg(feature = "media-gstreamer")]
mod media_platform {
@ -217,9 +216,11 @@ impl webrender_api::RenderNotifier for RenderNotifier {
Box::new(RenderNotifier::new(self.compositor_proxy.clone()))
}
fn wake_up(&self) {
self.compositor_proxy
.recomposite(CompositingReason::NewWebRenderFrame);
fn wake_up(&self, composite_needed: bool) {
if composite_needed {
self.compositor_proxy
.recomposite(CompositingReason::NewWebRenderFrame);
}
}
fn new_frame_ready(
@ -233,7 +234,7 @@ impl webrender_api::RenderNotifier for RenderNotifier {
self.compositor_proxy
.send(CompositorMsg::NewScrollFrameReady(composite_needed));
} else {
self.wake_up();
self.wake_up(true);
}
}
}
@ -333,7 +334,7 @@ where
None
};
let coordinates = window.get_coordinates();
let coordinates: compositing::windowing::EmbedderCoordinates = window.get_coordinates();
let device_pixel_ratio = coordinates.hidpi_factor.get();
let viewport_size = coordinates.viewport.size.to_f32() / device_pixel_ratio;
@ -345,10 +346,6 @@ where
);
let render_notifier = Box::new(RenderNotifier::new(compositor_proxy.clone()));
// Cast from `DeviceIndependentPixel` to `DevicePixel`
let window_size = Size2D::from_untyped(viewport_size.to_i32().to_untyped());
webrender::Renderer::new(
webrender_gl.clone(),
render_notifier,
@ -369,20 +366,12 @@ where
..Default::default()
},
None,
window_size,
)
.expect("Unable to initialize webrender!")
};
let webrender_api = webrender_api_sender.create_api();
let wr_document_layer = 0; //TODO
let webrender_document =
webrender_api.add_document(coordinates.framebuffer, wr_document_layer);
webrender_api.set_document_view(
webrender_document,
coordinates.get_viewport(),
coordinates.hidpi_factor.get(),
);
let webrender_document = webrender_api.add_document(coordinates.get_viewport().size);
// Important that this call is done in a single-threaded fashion, we
// can't defer it after `create_constellation` has started.
@ -575,8 +564,9 @@ where
self.compositor.on_wheel_event(delta, location);
},
EmbedderEvent::Scroll(delta, cursor, phase) => {
self.compositor.on_scroll_event(delta, cursor, phase);
EmbedderEvent::Scroll(scroll_location, cursor, phase) => {
self.compositor
.on_scroll_event(scroll_location, cursor, phase);
},
EmbedderEvent::Zoom(magnification) => {

View file

@ -20,6 +20,7 @@ msg = { path = "../msg" }
serde = { workspace = true, features = ["serde_derive"] }
servo_config = { path = "../config" }
smallvec = { workspace = true, features = ["serde"] }
webrender = { workspace = true }
webrender_api = { workspace = true }
webrender_traits = { path = "../webrender_traits" }
wgpu-core = { version = "0.17", features = ["replay", "trace", "serial-pass", "wgsl"] }

View file

@ -26,10 +26,8 @@ use std::rc::Rc;
use std::slice;
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
use webrender_api::{
DirtyRect, DocumentId, ExternalImageId, ImageData, ImageDescriptor, ImageKey, RenderApi,
RenderApiSender, Transaction,
};
use webrender::{RenderApi, RenderApiSender, Transaction};
use webrender_api::{DirtyRect, DocumentId, ExternalImageId, ImageData, ImageDescriptor, ImageKey};
use webrender_traits::{
WebrenderExternalImageApi, WebrenderExternalImageRegistry, WebrenderImageHandlerType,
WebrenderImageSource,