aboutsummaryrefslogtreecommitdiffstats
path: root/third_party/webrender/webrender_api
diff options
context:
space:
mode:
authorMukilan Thiyagarajan <mukilan@igalia.com>2023-09-14 15:00:42 +0530
committerMukilan Thiyagarajan <mukilan@igalia.com>2023-09-14 15:00:42 +0530
commitc385b3c9737c17d59cb02e520c3b68b232cb6497 (patch)
treead598ffbbdfbcecd6a4cf458abe2afc702d92c27 /third_party/webrender/webrender_api
parent988e05a68b48c9e744bf49459faf41a1bd9b81d7 (diff)
downloadservo-revert-webrender.tar.gz
servo-revert-webrender.zip
Revert "Upgrade WebRender to e491e1ae637b2eed1e7195855d88357e5eb3ddf9 (#30323)"revert-webrender
This reverts commit a9d37cb85ac2c55fc630fccffe1ba60ff00f555b.
Diffstat (limited to 'third_party/webrender/webrender_api')
-rw-r--r--third_party/webrender/webrender_api/Cargo.toml1
-rw-r--r--third_party/webrender/webrender_api/src/api.rs2121
-rw-r--r--third_party/webrender/webrender_api/src/channel.rs63
-rw-r--r--third_party/webrender/webrender_api/src/display_item.rs183
-rw-r--r--third_party/webrender/webrender_api/src/display_list.rs130
-rw-r--r--third_party/webrender/webrender_api/src/font.rs5
-rw-r--r--third_party/webrender/webrender_api/src/gradient_builder.rs4
-rw-r--r--third_party/webrender/webrender_api/src/image.rs31
-rw-r--r--third_party/webrender/webrender_api/src/image_tiling.rs815
-rw-r--r--third_party/webrender/webrender_api/src/lib.rs636
-rw-r--r--third_party/webrender/webrender_api/src/resources.rs327
-rw-r--r--third_party/webrender/webrender_api/src/units.rs3
12 files changed, 3350 insertions, 969 deletions
diff --git a/third_party/webrender/webrender_api/Cargo.toml b/third_party/webrender/webrender_api/Cargo.toml
index 2d045bb7548..1a48083a10b 100644
--- a/third_party/webrender/webrender_api/Cargo.toml
+++ b/third_party/webrender/webrender_api/Cargo.toml
@@ -26,7 +26,6 @@ serde_bytes = "0.11"
time = "0.1"
malloc_size_of = { version = "0.0.1", path = "../wr_malloc_size_of", package = "wr_malloc_size_of" }
peek-poke = { version = "0.2", path = "../peek-poke", features = ["extras"] }
-crossbeam-channel = "0.5"
[target.'cfg(target_os = "macos")'.dependencies]
core-foundation = "0.9"
diff --git a/third_party/webrender/webrender_api/src/api.rs b/third_party/webrender/webrender_api/src/api.rs
new file mode 100644
index 00000000000..94962ad7128
--- /dev/null
+++ b/third_party/webrender/webrender_api/src/api.rs
@@ -0,0 +1,2121 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#![deny(missing_docs)]
+
+use peek_poke::PeekPoke;
+use std::cell::Cell;
+use std::fmt;
+use std::marker::PhantomData;
+use std::os::raw::c_void;
+use std::path::PathBuf;
+use std::sync::Arc;
+use std::u32;
+use std::sync::mpsc::{Sender, Receiver, channel};
+use time::precise_time_ns;
+// local imports
+use crate::{display_item as di, font};
+use crate::color::{ColorU, ColorF};
+use crate::display_list::BuiltDisplayList;
+use crate::font::SharedFontInstanceMap;
+use crate::image::{BlobImageData, BlobImageKey, ImageData, ImageDescriptor, ImageKey};
+use crate::image::{BlobImageParams, BlobImageRequest, BlobImageResult, AsyncBlobImageRasterizer, BlobImageHandler};
+use crate::image::DEFAULT_TILE_SIZE;
+use crate::resources::ApiResources;
+use crate::units::*;
+
+/// Width and height in device pixels of image tiles.
+pub type TileSize = u16;
+
+/// Documents are rendered in the ascending order of their associated layer values.
+pub type DocumentLayer = i8;
+
+/// Various settings that the caller can select based on desired tradeoffs
+/// between rendering quality and performance / power usage.
+#[derive(Copy, Clone, Deserialize, Serialize)]
+pub struct QualitySettings {
+ /// If true, disable creating separate picture cache slices when the
+ /// scroll root changes. This gives maximum opportunity to find an
+ /// opaque background, which enables subpixel AA. However, it is
+ /// usually significantly more expensive to render when scrolling.
+ pub force_subpixel_aa_where_possible: bool,
+}
+
+impl Default for QualitySettings {
+ fn default() -> Self {
+ QualitySettings {
+ // Prefer performance over maximum subpixel AA quality, since WR
+ // already enables subpixel AA in more situations than other browsers.
+ force_subpixel_aa_where_possible: false,
+ }
+ }
+}
+
+/// Update of a persistent resource in WebRender.
+///
+/// ResourceUpdate changes keep theirs effect across display list changes.
+#[derive(Clone, Deserialize, Serialize)]
+pub enum ResourceUpdate {
+ /// See `AddImage`.
+ AddImage(AddImage),
+ /// See `UpdateImage`.
+ UpdateImage(UpdateImage),
+ /// Delete an existing image resource.
+ ///
+ /// It is invalid to continue referring to the image key in any display list
+ /// in the transaction that contains the `DeleteImage` message and subsequent
+ /// transactions.
+ DeleteImage(ImageKey),
+ /// See `AddBlobImage`.
+ AddBlobImage(AddBlobImage),
+ /// See `UpdateBlobImage`.
+ UpdateBlobImage(UpdateBlobImage),
+ /// Delete existing blob image resource.
+ DeleteBlobImage(BlobImageKey),
+ /// See `AddBlobImage::visible_area`.
+ SetBlobImageVisibleArea(BlobImageKey, DeviceIntRect),
+ /// See `AddFont`.
+ AddFont(AddFont),
+ /// Deletes an already existing font resource.
+ ///
+ /// It is invalid to continue referring to the font key in any display list
+ /// in the transaction that contains the `DeleteImage` message and subsequent
+ /// transactions.
+ DeleteFont(font::FontKey),
+ /// See `AddFontInstance`.
+ AddFontInstance(AddFontInstance),
+ /// Deletes an already existing font instance resource.
+ ///
+ /// It is invalid to continue referring to the font instance in any display
+ /// list in the transaction that contains the `DeleteImage` message and
+ /// subsequent transactions.
+ DeleteFontInstance(font::FontInstanceKey),
+}
+
+impl fmt::Debug for ResourceUpdate {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ ResourceUpdate::AddImage(ref i) => f.write_fmt(format_args!(
+ "ResourceUpdate::AddImage size({:?})",
+ &i.descriptor.size
+ )),
+ ResourceUpdate::UpdateImage(ref i) => f.write_fmt(format_args!(
+ "ResourceUpdate::UpdateImage size({:?})",
+ &i.descriptor.size
+ )),
+ ResourceUpdate::AddBlobImage(ref i) => f.write_fmt(format_args!(
+ "ResourceUFpdate::AddBlobImage size({:?})",
+ &i.descriptor.size
+ )),
+ ResourceUpdate::UpdateBlobImage(i) => f.write_fmt(format_args!(
+ "ResourceUpdate::UpdateBlobImage size({:?})",
+ &i.descriptor.size
+ )),
+ ResourceUpdate::DeleteImage(..) => f.write_str("ResourceUpdate::DeleteImage"),
+ ResourceUpdate::DeleteBlobImage(..) => f.write_str("ResourceUpdate::DeleteBlobImage"),
+ ResourceUpdate::SetBlobImageVisibleArea(..) => f.write_str("ResourceUpdate::SetBlobImageVisibleArea"),
+ ResourceUpdate::AddFont(..) => f.write_str("ResourceUpdate::AddFont"),
+ ResourceUpdate::DeleteFont(..) => f.write_str("ResourceUpdate::DeleteFont"),
+ ResourceUpdate::AddFontInstance(..) => f.write_str("ResourceUpdate::AddFontInstance"),
+ ResourceUpdate::DeleteFontInstance(..) => f.write_str("ResourceUpdate::DeleteFontInstance"),
+ }
+ }
+}
+
+/// A Transaction is a group of commands to apply atomically to a document.
+///
+/// This mechanism ensures that:
+/// - no other message can be interleaved between two commands that need to be applied together.
+/// - no redundant work is performed if two commands in the same transaction cause the scene or
+/// the frame to be rebuilt.
+pub struct Transaction {
+ /// Operations affecting the scene (applied before scene building).
+ scene_ops: Vec<SceneMsg>,
+ /// Operations affecting the generation of frames (applied after scene building).
+ frame_ops: Vec<FrameMsg>,
+
+ notifications: Vec<NotificationRequest>,
+
+ /// Persistent resource updates to apply as part of this transaction.
+ pub resource_updates: Vec<ResourceUpdate>,
+
+ /// If true the transaction is piped through the scene building thread, if false
+ /// it will be applied directly on the render backend.
+ use_scene_builder_thread: bool,
+
+ generate_frame: bool,
+
+ /// Set to true in order to force re-rendering even if WebRender can't internally
+ /// detect that something has changed.
+ pub invalidate_rendered_frame: bool,
+
+ low_priority: bool,
+}
+
+impl Transaction {
+ /// Constructor.
+ pub fn new() -> Self {
+ Transaction {
+ scene_ops: Vec::new(),
+ frame_ops: Vec::new(),
+ resource_updates: Vec::new(),
+ notifications: Vec::new(),
+ use_scene_builder_thread: true,
+ generate_frame: false,
+ invalidate_rendered_frame: false,
+ low_priority: false,
+ }
+ }
+
+ /// Marks this transaction to allow it to skip going through the scene builder
+ /// thread.
+ ///
+ /// This is useful to avoid jank in transaction associated with animated
+ /// property updates, panning and zooming.
+ ///
+ /// Note that transactions that skip the scene builder thread can race ahead of
+ /// transactions that don't skip it.
+ pub fn skip_scene_builder(&mut self) {
+ self.use_scene_builder_thread = false;
+ }
+
+ /// Marks this transaction to enforce going through the scene builder thread.
+ pub fn use_scene_builder_thread(&mut self) {
+ self.use_scene_builder_thread = true;
+ }
+
+ /// Returns true if the transaction has no effect.
+ pub fn is_empty(&self) -> bool {
+ !self.generate_frame &&
+ !self.invalidate_rendered_frame &&
+ self.scene_ops.is_empty() &&
+ self.frame_ops.is_empty() &&
+ self.resource_updates.is_empty() &&
+ self.notifications.is_empty()
+ }
+
+ /// Update a pipeline's epoch.
+ pub fn update_epoch(&mut self, pipeline_id: PipelineId, epoch: Epoch) {
+ // We track epochs before and after scene building.
+ // This one will be applied to the pending scene right away:
+ self.scene_ops.push(SceneMsg::UpdateEpoch(pipeline_id, epoch));
+ // And this one will be applied to the currently built scene at the end
+ // of the transaction (potentially long after the scene_ops one).
+ self.frame_ops.push(FrameMsg::UpdateEpoch(pipeline_id, epoch));
+ // We could avoid the duplication here by storing the epoch updates in a
+ // separate array and let the render backend schedule the updates at the
+ // proper times, but it wouldn't make things simpler.
+ }
+
+ /// Sets the root pipeline.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use webrender_api::{PipelineId, RenderApiSender, Transaction};
+ /// # use webrender_api::units::{DeviceIntSize};
+ /// # fn example() {
+ /// let pipeline_id = PipelineId(0, 0);
+ /// let mut txn = Transaction::new();
+ /// txn.set_root_pipeline(pipeline_id);
+ /// # }
+ /// ```
+ pub fn set_root_pipeline(&mut self, pipeline_id: PipelineId) {
+ self.scene_ops.push(SceneMsg::SetRootPipeline(pipeline_id));
+ }
+
+ /// Removes data associated with a pipeline from the internal data structures.
+ /// If the specified `pipeline_id` is for the root pipeline, the root pipeline
+ /// is reset back to `None`.
+ pub fn remove_pipeline(&mut self, pipeline_id: PipelineId) {
+ self.scene_ops.push(SceneMsg::RemovePipeline(pipeline_id));
+ }
+
+ /// Supplies a new frame to WebRender.
+ ///
+ /// Non-blocking, it notifies a worker process which processes the display list.
+ ///
+ /// Note: Scrolling doesn't require an own Frame.
+ ///
+ /// Arguments:
+ ///
+ /// * `epoch`: The unique Frame ID, monotonically increasing.
+ /// * `background`: The background color of this pipeline.
+ /// * `viewport_size`: The size of the viewport for this frame.
+ /// * `pipeline_id`: The ID of the pipeline that is supplying this display list.
+ /// * `content_size`: The total screen space size of this display list's display items.
+ /// * `display_list`: The root Display list used in this frame.
+ /// * `preserve_frame_state`: If a previous frame exists which matches this pipeline
+ /// id, this setting determines if frame state (such as scrolling
+ /// position) should be preserved for this new display list.
+ pub fn set_display_list(
+ &mut self,
+ epoch: Epoch,
+ background: Option<ColorF>,
+ viewport_size: LayoutSize,
+ (pipeline_id, content_size, mut display_list): (PipelineId, LayoutSize, BuiltDisplayList),
+ preserve_frame_state: bool,
+ ) {
+ display_list.set_send_time_ns(precise_time_ns());
+ self.scene_ops.push(
+ SceneMsg::SetDisplayList {
+ display_list,
+ epoch,
+ pipeline_id,
+ background,
+ viewport_size,
+ content_size,
+ preserve_frame_state,
+ }
+ );
+ }
+
+ /// Add a set of persistent resource updates to apply as part of this transaction.
+ pub fn update_resources(&mut self, mut resources: Vec<ResourceUpdate>) {
+ self.resource_updates.append(&mut resources);
+ }
+
+ // Note: Gecko uses this to get notified when a transaction that contains
+ // potentially long blob rasterization or scene build is ready to be rendered.
+ // so that the tab-switching integration can react adequately when tab
+ // switching takes too long. For this use case when matters is that the
+ // notification doesn't fire before scene building and blob rasterization.
+
+ /// Trigger a notification at a certain stage of the rendering pipeline.
+ ///
+ /// Not that notification requests are skipped during serialization, so is is
+ /// best to use them for synchronization purposes and not for things that could
+ /// affect the WebRender's state.
+ pub fn notify(&mut self, event: NotificationRequest) {
+ self.notifications.push(event);
+ }
+
+ /// Setup the output region in the framebuffer for a given document.
+ pub fn set_document_view(
+ &mut self,
+ device_rect: DeviceIntRect,
+ device_pixel_ratio: f32,
+ ) {
+ self.scene_ops.push(
+ SceneMsg::SetDocumentView {
+ device_rect,
+ device_pixel_ratio,
+ },
+ );
+ }
+
+ /// Enable copying of the output of this pipeline id to
+ /// an external texture for callers to consume.
+ pub fn enable_frame_output(&mut self, pipeline_id: PipelineId, enable: bool) {
+ self.scene_ops.push(SceneMsg::EnableFrameOutput(pipeline_id, enable));
+ }
+
+ /// Scrolls the scrolling layer under the `cursor`
+ ///
+ /// WebRender looks for the layer closest to the user
+ /// which has `ScrollPolicy::Scrollable` set.
+ pub fn scroll(&mut self, scroll_location: ScrollLocation, cursor: WorldPoint) {
+ self.frame_ops.push(FrameMsg::Scroll(scroll_location, cursor));
+ }
+
+ ///
+ pub fn scroll_node_with_id(
+ &mut self,
+ origin: LayoutPoint,
+ id: di::ExternalScrollId,
+ clamp: ScrollClamping,
+ ) {
+ self.frame_ops.push(FrameMsg::ScrollNodeWithId(origin, id, clamp));
+ }
+
+ /// Set the current quality / performance settings for this document.
+ pub fn set_quality_settings(&mut self, settings: QualitySettings) {
+ self.scene_ops.push(SceneMsg::SetQualitySettings { settings });
+ }
+
+ ///
+ pub fn set_page_zoom(&mut self, page_zoom: ZoomFactor) {
+ self.scene_ops.push(SceneMsg::SetPageZoom(page_zoom));
+ }
+
+ ///
+ pub fn set_pinch_zoom(&mut self, pinch_zoom: ZoomFactor) {
+ self.frame_ops.push(FrameMsg::SetPinchZoom(pinch_zoom));
+ }
+
+ ///
+ pub fn set_is_transform_async_zooming(&mut self, is_zooming: bool, animation_id: PropertyBindingId) {
+ self.frame_ops.push(FrameMsg::SetIsTransformAsyncZooming(is_zooming, animation_id));
+ }
+
+ ///
+ pub fn set_pan(&mut self, pan: DeviceIntPoint) {
+ self.frame_ops.push(FrameMsg::SetPan(pan));
+ }
+
+ /// Generate a new frame. When it's done and a RenderNotifier has been set
+ /// in `webrender::Renderer`, [new_frame_ready()][notifier] gets called.
+ /// Note that the notifier is called even if the frame generation was a
+ /// no-op; the arguments passed to `new_frame_ready` will provide information
+ /// as to when happened.
+ ///
+ /// [notifier]: trait.RenderNotifier.html#tymethod.new_frame_ready
+ pub fn generate_frame(&mut self) {
+ self.generate_frame = true;
+ }
+
+ /// Invalidate rendered frame. It ensure that frame will be rendered during
+ /// next frame generation. WebRender could skip frame rendering if there
+ /// is no update.
+ /// But there are cases that needs to force rendering.
+ /// - Content of image is updated by reusing same ExternalImageId.
+ /// - Platform requests it if pixels become stale (like wakeup from standby).
+ pub fn invalidate_rendered_frame(&mut self) {
+ self.invalidate_rendered_frame = true;
+ }
+
+ /// Supply a list of animated property bindings that should be used to resolve
+ /// bindings in the current display list.
+ pub fn update_dynamic_properties(&mut self, properties: DynamicProperties) {
+ self.frame_ops.push(FrameMsg::UpdateDynamicProperties(properties));
+ }
+
+ /// Add to the list of animated property bindings that should be used to
+ /// resolve bindings in the current display list. This is a convenience method
+ /// so the caller doesn't have to figure out all the dynamic properties before
+ /// setting them on the transaction but can do them incrementally.
+ pub fn append_dynamic_transform_properties(&mut self, transforms: Vec<PropertyValue<LayoutTransform>>) {
+ self.frame_ops.push(FrameMsg::AppendDynamicTransformProperties(transforms));
+ }
+
+ /// Consumes this object and just returns the frame ops.
+ pub fn get_frame_ops(self) -> Vec<FrameMsg> {
+ self.frame_ops
+ }
+
+ fn finalize(self, document_id: DocumentId) -> Box<TransactionMsg> {
+ Box::new(TransactionMsg {
+ document_id,
+ scene_ops: self.scene_ops,
+ frame_ops: self.frame_ops,
+ resource_updates: self.resource_updates,
+ notifications: self.notifications,
+ use_scene_builder_thread: self.use_scene_builder_thread,
+ generate_frame: self.generate_frame,
+ invalidate_rendered_frame: self.invalidate_rendered_frame,
+ low_priority: self.low_priority,
+ blob_rasterizer: None,
+ blob_requests: Vec::new(),
+ rasterized_blobs: Vec::new(),
+ })
+ }
+
+ /// See `ResourceUpdate::AddImage`.
+ pub fn add_image(
+ &mut self,
+ key: ImageKey,
+ descriptor: ImageDescriptor,
+ data: ImageData,
+ tiling: Option<TileSize>,
+ ) {
+ self.resource_updates.push(ResourceUpdate::AddImage(AddImage {
+ key,
+ descriptor,
+ data,
+ tiling,
+ }));
+ }
+
+ /// See `ResourceUpdate::UpdateImage`.
+ pub fn update_image(
+ &mut self,
+ key: ImageKey,
+ descriptor: ImageDescriptor,
+ data: ImageData,
+ dirty_rect: &ImageDirtyRect,
+ ) {
+ self.resource_updates.push(ResourceUpdate::UpdateImage(UpdateImage {
+ key,
+ descriptor,
+ data,
+ dirty_rect: *dirty_rect,
+ }));
+ }
+
+ /// See `ResourceUpdate::DeleteImage`.
+ pub fn delete_image(&mut self, key: ImageKey) {
+ self.resource_updates.push(ResourceUpdate::DeleteImage(key));
+ }
+
+ /// See `ResourceUpdate::AddBlobImage`.
+ pub fn add_blob_image(
+ &mut self,
+ key: BlobImageKey,
+ descriptor: ImageDescriptor,
+ data: Arc<BlobImageData>,
+ visible_rect: DeviceIntRect,
+ tile_size: Option<TileSize>,
+ ) {
+ self.resource_updates.push(
+ ResourceUpdate::AddBlobImage(AddBlobImage {
+ key,
+ descriptor,
+ data,
+ visible_rect,
+ tile_size: tile_size.unwrap_or(DEFAULT_TILE_SIZE),
+ })
+ );
+ }
+
+ /// See `ResourceUpdate::UpdateBlobImage`.
+ pub fn update_blob_image(
+ &mut self,
+ key: BlobImageKey,
+ descriptor: ImageDescriptor,
+ data: Arc<BlobImageData>,
+ visible_rect: DeviceIntRect,
+ dirty_rect: &BlobDirtyRect,
+ ) {
+ self.resource_updates.push(
+ ResourceUpdate::UpdateBlobImage(UpdateBlobImage {
+ key,
+ descriptor,
+ data,
+ visible_rect,
+ dirty_rect: *dirty_rect,
+ })
+ );
+ }
+
+ /// See `ResourceUpdate::DeleteBlobImage`.
+ pub fn delete_blob_image(&mut self, key: BlobImageKey) {
+ self.resource_updates.push(ResourceUpdate::DeleteBlobImage(key));
+ }
+
+ /// See `ResourceUpdate::SetBlobImageVisibleArea`.
+ pub fn set_blob_image_visible_area(&mut self, key: BlobImageKey, area: DeviceIntRect) {
+ self.resource_updates.push(ResourceUpdate::SetBlobImageVisibleArea(key, area))
+ }
+
+ /// See `ResourceUpdate::AddFont`.
+ pub fn add_raw_font(&mut self, key: font::FontKey, bytes: Vec<u8>, index: u32) {
+ self.resource_updates
+ .push(ResourceUpdate::AddFont(AddFont::Raw(key, Arc::new(bytes), index)));
+ }
+
+ /// See `ResourceUpdate::AddFont`.
+ pub fn add_native_font(&mut self, key: font::FontKey, native_handle: font::NativeFontHandle) {
+ self.resource_updates
+ .push(ResourceUpdate::AddFont(AddFont::Native(key, native_handle)));
+ }
+
+ /// See `ResourceUpdate::DeleteFont`.
+ pub fn delete_font(&mut self, key: font::FontKey) {
+ self.resource_updates.push(ResourceUpdate::DeleteFont(key));
+ }
+
+ /// See `ResourceUpdate::AddFontInstance`.
+ pub fn add_font_instance(
+ &mut self,
+ key: font::FontInstanceKey,
+ font_key: font::FontKey,
+ glyph_size: f32,
+ options: Option<font::FontInstanceOptions>,
+ platform_options: Option<font::FontInstancePlatformOptions>,
+ variations: Vec<font::FontVariation>,
+ ) {
+ self.resource_updates
+ .push(ResourceUpdate::AddFontInstance(AddFontInstance {
+ key,
+ font_key,
+ glyph_size,
+ options,
+ platform_options,
+ variations,
+ }));
+ }
+
+ /// See `ResourceUpdate::DeleteFontInstance`.
+ pub fn delete_font_instance(&mut self, key: font::FontInstanceKey) {
+ self.resource_updates.push(ResourceUpdate::DeleteFontInstance(key));
+ }
+
+ /// A hint that this transaction can be processed at a lower priority. High-
+ /// priority transactions can jump ahead of regular-priority transactions,
+ /// but both high- and regular-priority transactions are processed in order
+ /// relative to other transactions of the same priority.
+ pub fn set_low_priority(&mut self, low_priority: bool) {
+ self.low_priority = low_priority;
+ }
+
+ /// Returns whether this transaction is marked as low priority.
+ pub fn is_low_priority(&self) -> bool {
+ self.low_priority
+ }
+}
+
+///
+pub struct DocumentTransaction {
+ ///
+ pub document_id: DocumentId,
+ ///
+ pub transaction: Transaction,
+}
+
+/// Represents a transaction in the format sent through the channel.
+pub struct TransactionMsg {
+ ///
+ pub document_id: DocumentId,
+ /// Changes that require re-building the scene.
+ pub scene_ops: Vec<SceneMsg>,
+ /// Changes to animated properties that do not require re-building the scene.
+ pub frame_ops: Vec<FrameMsg>,
+ /// Updates to resources that persist across display lists.
+ pub resource_updates: Vec<ResourceUpdate>,
+ /// Whether to trigger frame building and rendering if something has changed.
+ pub generate_frame: bool,
+ /// Whether to force frame building and rendering even if no changes are internally
+ /// observed.
+ pub invalidate_rendered_frame: bool,
+ /// Whether to enforce that this transaction go through the scene builder.
+ pub use_scene_builder_thread: bool,
+ ///
+ pub low_priority: bool,
+
+ /// Handlers to notify at certain points of the pipeline.
+ pub notifications: Vec<NotificationRequest>,
+ ///
+ pub blob_rasterizer: Option<Box<dyn AsyncBlobImageRasterizer>>,
+ ///
+ pub blob_requests: Vec<BlobImageParams>,
+ ///
+ pub rasterized_blobs: Vec<(BlobImageRequest, BlobImageResult)>,
+}
+
+impl fmt::Debug for TransactionMsg {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ writeln!(f, "threaded={}, genframe={}, invalidate={}, low_priority={}",
+ self.use_scene_builder_thread,
+ self.generate_frame,
+ self.invalidate_rendered_frame,
+ self.low_priority,
+ ).unwrap();
+ for scene_op in &self.scene_ops {
+ writeln!(f, "\t\t{:?}", scene_op).unwrap();
+ }
+
+ for frame_op in &self.frame_ops {
+ writeln!(f, "\t\t{:?}", frame_op).unwrap();
+ }
+
+ for resource_update in &self.resource_updates {
+ writeln!(f, "\t\t{:?}", resource_update).unwrap();
+ }
+ Ok(())
+ }
+}
+
+impl TransactionMsg {
+ /// Returns true if this transaction has no effect.
+ pub fn is_empty(&self) -> bool {
+ !self.generate_frame &&
+ !self.invalidate_rendered_frame &&
+ self.scene_ops.is_empty() &&
+ self.frame_ops.is_empty() &&
+ self.resource_updates.is_empty() &&
+ self.notifications.is_empty()
+ }
+}
+
+/// Creates an image resource with provided parameters.
+///
+/// Must be matched with a `DeleteImage` at some point to prevent memory leaks.
+#[derive(Clone, Deserialize, Serialize)]
+pub struct AddImage {
+ /// A key to identify the image resource.
+ pub key: ImageKey,
+ /// Properties of the image.
+ pub descriptor: ImageDescriptor,
+ /// The pixels of the image.
+ pub data: ImageData,
+ /// An optional tiling scheme to apply when storing the image's data
+ /// on the GPU. Applies to both width and heights of the tiles.
+ ///
+ /// Note that WebRender may internally chose to tile large images
+ /// even if this member is set to `None`.
+ pub tiling: Option<TileSize>,
+}
+
+/// Updates an already existing image resource.
+#[derive(Clone, Deserialize, Serialize)]
+pub struct UpdateImage {
+ /// The key identfying the image resource to update.
+ pub key: ImageKey,
+ /// Properties of the image.
+ pub descriptor: ImageDescriptor,
+ /// The pixels of the image.
+ pub data: ImageData,
+ /// An optional dirty rect that lets WebRender optimize the amount of
+ /// data to transfer to the GPU.
+ ///
+ /// The data provided must still represent the entire image.
+ pub dirty_rect: ImageDirtyRect,
+}
+
+/// Creates a blob-image resource with provided parameters.
+///
+/// Must be matched with a `DeleteImage` at some point to prevent memory leaks.
+#[derive(Clone, Deserialize, Serialize)]
+pub struct AddBlobImage {
+ /// A key to identify the blob-image resource.
+ pub key: BlobImageKey,
+ /// Properties of the image.
+ pub descriptor: ImageDescriptor,
+ /// The blob-image's serialized commands.
+ pub data: Arc<BlobImageData>,
+ /// The portion of the plane in the blob-image's internal coordinate
+ /// system that is stretched to fill the image display item.
+ ///
+ /// Unlike regular images, blob images are not limited in size. The
+ /// top-left corner of their internal coordinate system is also not
+ /// necessary at (0, 0).
+ /// This means that blob images can be updated to insert/remove content
+ /// in any direction to support panning and zooming.
+ pub visible_rect: DeviceIntRect,
+ /// The blob image's tile size to apply when rasterizing the blob-image
+ /// and when storing its rasterized data on the GPU.
+ /// Applies to both width and heights of the tiles.
+ ///
+ /// All blob images are tiled.
+ pub tile_size: TileSize,
+}
+
+/// Updates an already existing blob-image resource.
+#[derive(Clone, Deserialize, Serialize)]
+pub struct UpdateBlobImage {
+ /// The key identfying the blob-image resource to update.
+ pub key: BlobImageKey,
+ /// Properties of the image.
+ pub descriptor: ImageDescriptor,
+ /// The blob-image's serialized commands.
+ pub data: Arc<BlobImageData>,
+ /// See `AddBlobImage::visible_rect`.
+ pub visible_rect: DeviceIntRect,
+ /// An optional dirty rect that lets WebRender optimize the amount of
+ /// data to to rasterize and transfer to the GPU.
+ pub dirty_rect: BlobDirtyRect,
+}
+
+/// Creates a font resource.
+///
+/// Must be matched with a corresponding `ResourceUpdate::DeleteFont` at some point to prevent
+/// memory leaks.
+#[derive(Clone, Deserialize, Serialize)]
+pub enum AddFont {
+ ///
+ Raw(font::FontKey, Arc<Vec<u8>>, u32),
+ ///
+ Native(font::FontKey, font::NativeFontHandle),
+}
+
+/// Describe an item that matched a hit-test query.
+#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
+pub struct HitTestItem {
+ /// The pipeline that the display item that was hit belongs to.
+ pub pipeline: PipelineId,
+
+ /// The tag of the hit display item.
+ pub tag: di::ItemTag,
+
+ /// The hit point in the coordinate space of the "viewport" of the display item. The
+ /// viewport is the scroll node formed by the root reference frame of the display item's
+ /// pipeline.
+ pub point_in_viewport: LayoutPoint,
+
+ /// The coordinates of the original hit test point relative to the origin of this item.
+ /// This is useful for calculating things like text offsets in the client.
+ pub point_relative_to_item: LayoutPoint,
+}
+
+/// Returned by `RenderApi::hit_test`.
+#[derive(Clone, Debug, Default, Deserialize, Serialize)]
+pub struct HitTestResult {
+ /// List of items that are match the hit-test query.
+ pub items: Vec<HitTestItem>,
+}
+
+bitflags! {
+ #[derive(Deserialize, MallocSizeOf, Serialize)]
+ ///
+ pub struct HitTestFlags: u8 {
+ ///
+ const FIND_ALL = 0b00000001;
+ ///
+ const POINT_RELATIVE_TO_PIPELINE_VIEWPORT = 0b00000010;
+ }
+}
+
+/// Creates a font instance resource.
+///
+/// Must be matched with a corresponding `DeleteFontInstance` at some point
+/// to prevent memory leaks.
+#[derive(Clone, Deserialize, Serialize)]
+pub struct AddFontInstance {
+ /// A key to identify the font instance.
+ pub key: font::FontInstanceKey,
+ /// The font resource's key.
+ pub font_key: font::FontKey,
+ /// Glyph size in app units.
+ pub glyph_size: f32,
+ ///
+ pub options: Option<font::FontInstanceOptions>,
+ ///
+ pub platform_options: Option<font::FontInstancePlatformOptions>,
+ ///
+ pub variations: Vec<font::FontVariation>,
+}
+
+/// Frame messages affect building the scene.
+pub enum SceneMsg {
+ ///
+ UpdateEpoch(PipelineId, Epoch),
+ ///
+ SetPageZoom(ZoomFactor),
+ ///
+ SetRootPipeline(PipelineId),
+ ///
+ RemovePipeline(PipelineId),
+ ///
+ EnableFrameOutput(PipelineId, bool),
+ ///
+ SetDisplayList {
+ ///
+ display_list: BuiltDisplayList,
+ ///
+ epoch: Epoch,
+ ///
+ pipeline_id: PipelineId,
+ ///
+ background: Option<ColorF>,
+ ///
+ viewport_size: LayoutSize,
+ ///
+ content_size: LayoutSize,
+ ///
+ preserve_frame_state: bool,
+ },
+ ///
+ SetDocumentView {
+ ///
+ device_rect: DeviceIntRect,
+ ///
+ device_pixel_ratio: f32,
+ },
+ /// Set the current quality / performance configuration for this document.
+ SetQualitySettings {
+ /// The set of available quality / performance config values.
+ settings: QualitySettings,
+ },
+}
+
+/// Frame messages affect frame generation (applied after building the scene).
+pub enum FrameMsg {
+ ///
+ UpdateEpoch(PipelineId, Epoch),
+ ///
+ HitTest(Option<PipelineId>, WorldPoint, HitTestFlags, Sender<HitTestResult>),
+ ///
+ RequestHitTester(Sender<Arc<dyn ApiHitTester>>),
+ ///
+ SetPan(DeviceIntPoint),
+ ///
+ Scroll(ScrollLocation, WorldPoint),
+ ///
+ ScrollNodeWithId(LayoutPoint, di::ExternalScrollId, ScrollClamping),
+ ///
+ GetScrollNodeState(Sender<Vec<ScrollNodeState>>),
+ ///
+ UpdateDynamicProperties(DynamicProperties),
+ ///
+ AppendDynamicTransformProperties(Vec<PropertyValue<LayoutTransform>>),
+ ///
+ SetPinchZoom(ZoomFactor),
+ ///
+ SetIsTransformAsyncZooming(bool, PropertyBindingId),
+}
+
+impl fmt::Debug for SceneMsg {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.write_str(match *self {
+ SceneMsg::UpdateEpoch(..) => "SceneMsg::UpdateEpoch",
+ SceneMsg::SetDisplayList { .. } => "SceneMsg::SetDisplayList",
+ SceneMsg::SetPageZoom(..) => "SceneMsg::SetPageZoom",
+ SceneMsg::RemovePipeline(..) => "SceneMsg::RemovePipeline",
+ SceneMsg::EnableFrameOutput(..) => "SceneMsg::EnableFrameOutput",
+ SceneMsg::SetDocumentView { .. } => "SceneMsg::SetDocumentView",
+ SceneMsg::SetRootPipeline(..) => "SceneMsg::SetRootPipeline",
+ SceneMsg::SetQualitySettings { .. } => "SceneMsg::SetQualitySettings",
+ })
+ }
+}
+
+impl fmt::Debug for FrameMsg {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.write_str(match *self {
+ FrameMsg::UpdateEpoch(..) => "FrameMsg::UpdateEpoch",
+ FrameMsg::HitTest(..) => "FrameMsg::HitTest",
+ FrameMsg::RequestHitTester(..) => "FrameMsg::RequestHitTester",
+ FrameMsg::SetPan(..) => "FrameMsg::SetPan",
+ FrameMsg::Scroll(..) => "FrameMsg::Scroll",
+ FrameMsg::ScrollNodeWithId(..) => "FrameMsg::ScrollNodeWithId",
+ FrameMsg::GetScrollNodeState(..) => "FrameMsg::GetScrollNodeState",
+ FrameMsg::UpdateDynamicProperties(..) => "FrameMsg::UpdateDynamicProperties",
+ FrameMsg::AppendDynamicTransformProperties(..) => "FrameMsg::AppendDynamicTransformProperties",
+ FrameMsg::SetPinchZoom(..) => "FrameMsg::SetPinchZoom",
+ FrameMsg::SetIsTransformAsyncZooming(..) => "FrameMsg::SetIsTransformAsyncZooming",
+ })
+ }
+}
+
+bitflags!{
+ /// Bit flags for WR stages to store in a capture.
+ // Note: capturing `FRAME` without `SCENE` is not currently supported.
+ pub struct CaptureBits: u8 {
+ ///
+ const SCENE = 0x1;
+ ///
+ const FRAME = 0x2;
+ ///
+ const TILE_CACHE = 0x4;
+ ///
+ const EXTERNAL_RESOURCES = 0x8;
+ }
+}
+
+bitflags!{
+ /// Mask for clearing caches in debug commands.
+ pub struct ClearCache: u8 {
+ ///
+ const IMAGES = 0b1;
+ ///
+ const GLYPHS = 0b01;
+ ///
+ const GLYPH_DIMENSIONS = 0b001;
+ ///
+ const RENDER_TASKS = 0b0001;
+ ///
+ const TEXTURE_CACHE = 0b00001;
+ }
+}
+
+/// Information about a loaded capture of each document
+/// that is returned by `RenderBackend`.
+#[derive(Clone, Debug)]
+pub struct CapturedDocument {
+ ///
+ pub document_id: DocumentId,
+ ///
+ pub root_pipeline_id: Option<PipelineId>,
+}
+
+/// Update of the state of built-in debugging facilities.
+#[derive(Clone)]
+pub enum DebugCommand {
+ /// Sets the provided debug flags.
+ SetFlags(DebugFlags),
+ /// Configure if dual-source blending is used, if available.
+ EnableDualSourceBlending(bool),
+ /// Fetch current documents and display lists.
+ FetchDocuments,
+ /// Fetch current passes and batches.
+ FetchPasses,
+ // TODO: This should be called FetchClipScrollTree. However, that requires making
+ // changes to webrender's web debugger ui, touching a 4Mb minified file that
+ // is too big to submit through the conventional means.
+ /// Fetch the spatial tree.
+ FetchClipScrollTree,
+ /// Fetch render tasks.
+ FetchRenderTasks,
+ /// Fetch screenshot.
+ FetchScreenshot,
+ /// Save a capture of all the documents state.
+ SaveCapture(PathBuf, CaptureBits),
+ /// Load a capture of all the documents state.
+ LoadCapture(PathBuf, Option<(u32, u32)>, Sender<CapturedDocument>),
+ /// Start capturing a sequence of scene/frame changes.
+ StartCaptureSequence(PathBuf, CaptureBits),
+ /// Stop capturing a sequence of scene/frame changes.
+ StopCaptureSequence,
+ /// Clear cached resources, forcing them to be re-uploaded from templates.
+ ClearCaches(ClearCache),
+ /// Enable/disable native compositor usage
+ EnableNativeCompositor(bool),
+ /// Enable/disable parallel job execution with rayon.
+ EnableMultithreading(bool),
+ /// Sets the maximum amount of existing batches to visit before creating a new one.
+ SetBatchingLookback(u32),
+ /// Invalidate GPU cache, forcing the update from the CPU mirror.
+ InvalidateGpuCache,
+ /// Causes the scene builder to pause for a given amount of milliseconds each time it
+ /// processes a transaction.
+ SimulateLongSceneBuild(u32),
+ /// Causes the low priority scene builder to pause for a given amount of milliseconds
+ /// each time it processes a transaction.
+ SimulateLongLowPrioritySceneBuild(u32),
+ /// Set an override tile size to use for picture caches
+ SetPictureTileSize(Option<DeviceIntSize>),
+}
+
+/// Message sent by the `RenderApi` to the render backend thread.
+pub enum ApiMsg {
+ /// Gets the glyph dimensions
+ GetGlyphDimensions(font::GlyphDimensionRequest),
+ /// Gets the glyph indices from a string
+ GetGlyphIndices(font::GlyphIndexRequest),
+ /// Adds a new document namespace.
+ CloneApi(Sender<IdNamespace>),
+ /// Adds a new document namespace.
+ CloneApiByClient(IdNamespace),
+ /// Adds a new document with given initial size.
+ AddDocument(DocumentId, DeviceIntSize, DocumentLayer),
+ /// A message targeted at a particular document.
+ UpdateDocuments(Vec<Box<TransactionMsg>>),
+ /// Deletes an existing document.
+ DeleteDocument(DocumentId),
+ /// An opaque handle that must be passed to the render notifier. It is used by Gecko
+ /// to forward gecko-specific messages to the render thread preserving the ordering
+ /// within the other messages.
+ ExternalEvent(ExternalEvent),
+ /// Removes all resources associated with a namespace.
+ ClearNamespace(IdNamespace),
+ /// Flush from the caches anything that isn't necessary, to free some memory.
+ MemoryPressure,
+ /// Collects a memory report.
+ ReportMemory(Sender<Box<MemoryReport>>),
+ /// Change debugging options.
+ DebugCommand(DebugCommand),
+ /// Wakes the render backend's event loop up. Needed when an event is communicated
+ /// through another channel.
+ WakeUp,
+ /// See `RenderApi::wake_scene_builder`.
+ WakeSceneBuilder,
+ /// Block until a round-trip to the scene builder thread has completed. This
+ /// ensures that any transactions (including ones deferred to the scene
+ /// builder thread) have been processed.
+ FlushSceneBuilder(Sender<()>),
+ /// Shut the WebRender instance down.
+ ShutDown(Option<Sender<()>>),
+}
+
+impl fmt::Debug for ApiMsg {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.write_str(match *self {
+ ApiMsg::GetGlyphDimensions(..) => "ApiMsg::GetGlyphDimensions",
+ ApiMsg::GetGlyphIndices(..) => "ApiMsg::GetGlyphIndices",
+ ApiMsg::CloneApi(..) => "ApiMsg::CloneApi",
+ ApiMsg::CloneApiByClient(..) => "ApiMsg::CloneApiByClient",
+ ApiMsg::AddDocument(..) => "ApiMsg::AddDocument",
+ ApiMsg::UpdateDocuments(..) => "ApiMsg::UpdateDocuments",
+ ApiMsg::DeleteDocument(..) => "ApiMsg::DeleteDocument",
+ ApiMsg::ExternalEvent(..) => "ApiMsg::ExternalEvent",
+ ApiMsg::ClearNamespace(..) => "ApiMsg::ClearNamespace",
+ ApiMsg::MemoryPressure => "ApiMsg::MemoryPressure",
+ ApiMsg::ReportMemory(..) => "ApiMsg::ReportMemory",
+ ApiMsg::DebugCommand(..) => "ApiMsg::DebugCommand",
+ ApiMsg::ShutDown(..) => "ApiMsg::ShutDown",
+ ApiMsg::WakeUp => "ApiMsg::WakeUp",
+ ApiMsg::WakeSceneBuilder => "ApiMsg::WakeSceneBuilder",
+ ApiMsg::FlushSceneBuilder(..) => "ApiMsg::FlushSceneBuilder",
+ })
+ }
+}
+
+/// An epoch identifies the state of a pipeline in time.
+///
+/// This is mostly used as a synchronization mechanism to observe how/when particular pipeline
+/// updates propagate through WebRender and are applied at various stages.
+#[repr(C)]
+#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
+pub struct Epoch(pub u32);
+
+impl Epoch {
+ /// Magic invalid epoch value.
+ pub fn invalid() -> Epoch {
+ Epoch(u32::MAX)
+ }
+}
+
+/// ID namespaces uniquely identify different users of WebRender's API.
+///
+/// For example in Gecko each content process uses a separate id namespace.
+#[repr(C)]
+#[derive(Clone, Copy, Debug, Default, Eq, MallocSizeOf, PartialEq, Hash, Ord, PartialOrd, PeekPoke)]
+#[derive(Deserialize, Serialize)]
+pub struct IdNamespace(pub u32);
+
+/// A key uniquely identifying a WebRender document.
+///
+/// Instances can manage one or several documents (using the same render backend thread).
+/// Each document will internally correspond to a single scene, and scenes are made of
+/// one or several pipelines.
+#[repr(C)]
+#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
+pub struct DocumentId {
+ ///
+ pub namespace_id: IdNamespace,
+ ///
+ pub id: u32,
+}
+
+impl DocumentId {
+ ///
+ pub fn new(namespace_id: IdNamespace, id: u32) -> Self {
+ DocumentId {
+ namespace_id,
+ id,
+ }
+ }
+
+ ///
+ pub const INVALID: DocumentId = DocumentId { namespace_id: IdNamespace(0), id: 0 };
+}
+
+/// This type carries no valuable semantics for WR. However, it reflects the fact that
+/// clients (Servo) may generate pipelines by different semi-independent sources.
+/// These pipelines still belong to the same `IdNamespace` and the same `DocumentId`.
+/// Having this extra Id field enables them to generate `PipelineId` without collision.
+pub type PipelineSourceId = u32;
+
+/// From the point of view of WR, `PipelineId` is completely opaque and generic as long as
+/// it's clonable, serializable, comparable, and hashable.
+#[repr(C)]
+#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
+pub struct PipelineId(pub PipelineSourceId, pub u32);
+
+impl Default for PipelineId {
+ fn default() -> Self {
+ PipelineId::dummy()
+ }
+}
+
+impl PipelineId {
+ ///
+ pub fn dummy() -> Self {
+ PipelineId(0, 0)
+ }
+}
+
+///
+#[derive(Copy, Clone, Debug, MallocSizeOf, Serialize, Deserialize)]
+pub enum ClipIntern {}
+
+///
+#[derive(Copy, Clone, Debug, MallocSizeOf, Serialize, Deserialize)]
+pub enum FilterDataIntern {}
+
+/// Information specific to a primitive type that
+/// uniquely identifies a primitive template by key.
+#[derive(Debug, Clone, Eq, MallocSizeOf, PartialEq, Hash, Serialize, Deserialize)]
+pub enum PrimitiveKeyKind {
+ /// Clear an existing rect, used for special effects on some platforms.
+ Clear,
+ ///
+ Rectangle {
+ ///
+ color: PropertyBinding<ColorU>,
+ },
+}
+
+/// Meta-macro to enumerate the various interner identifiers and types.
+///
+/// IMPORTANT: Keep this synchronized with the list in mozilla-central located at
+/// gfx/webrender_bindings/webrender_ffi.h
+///
+/// Note that this could be a lot less verbose if concat_idents! were stable. :-(
+#[macro_export]
+macro_rules! enumerate_interners {
+ ($macro_name: ident) => {
+ $macro_name! {
+ clip: ClipIntern,
+ prim: PrimitiveKeyKind,
+ normal_border: NormalBorderPrim,
+ image_border: ImageBorder,
+ image: Image,
+ yuv_image: YuvImage,
+ line_decoration: LineDecoration,
+ linear_grad: LinearGradient,
+ radial_grad: RadialGradient,
+ conic_grad: ConicGradient,
+ picture: Picture,
+ text_run: TextRun,
+ filter_data: FilterDataIntern,
+ backdrop: Backdrop,
+ }
+ }
+}
+
+macro_rules! declare_interning_memory_report {
+ ( $( $name:ident: $ty:ident, )+ ) => {
+ ///
+ #[repr(C)]
+ #[derive(AddAssign, Clone, Debug, Default)]
+ pub struct InternerSubReport {
+ $(
+ ///
+ pub $name: usize,
+ )+
+ }
+ }
+}
+
+enumerate_interners!(declare_interning_memory_report);
+
+/// Memory report for interning-related data structures.
+/// cbindgen:derive-eq=false
+#[repr(C)]
+#[derive(Clone, Debug, Default)]
+pub struct InterningMemoryReport {
+ ///
+ pub interners: InternerSubReport,
+ ///
+ pub data_stores: InternerSubReport,
+}
+
+impl ::std::ops::AddAssign for InterningMemoryReport {
+ fn add_assign(&mut self, other: InterningMemoryReport) {
+ self.interners += other.interners;
+ self.data_stores += other.data_stores;
+ }
+}
+
+/// Collection of heap sizes, in bytes.
+/// cbindgen:derive-eq=false
+#[repr(C)]
+#[allow(missing_docs)]
+#[derive(AddAssign, Clone, Debug, Default)]
+pub struct MemoryReport {
+ //
+ // CPU Memory.
+ //
+ pub clip_stores: usize,
+ pub gpu_cache_metadata: usize,
+ pub gpu_cache_cpu_mirror: usize,
+ pub render_tasks: usize,
+ pub hit_testers: usize,
+ pub fonts: usize,
+ pub images: usize,
+ pub rasterized_blobs: usize,
+ pub shader_cache: usize,
+ pub interning: InterningMemoryReport,
+ pub display_list: usize,
+
+ //
+ // GPU memory.
+ //
+ pub gpu_cache_textures: usize,
+ pub vertex_data_textures: usize,
+ pub render_target_textures: usize,
+ pub texture_cache_textures: usize,
+ pub depth_target_textures: usize,
+ pub swap_chain: usize,
+}
+
+/// A C function that takes a pointer to a heap allocation and returns its size.
+///
+/// This is borrowed from the malloc_size_of crate, upon which we want to avoid
+/// a dependency from WebRender.
+pub type VoidPtrToSizeFn = unsafe extern "C" fn(ptr: *const c_void) -> usize;
+
+#[repr(C)]
+#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
+struct ResourceId(pub u32);
+
+/// An opaque pointer-sized value.
+#[repr(C)]
+#[derive(Clone)]
+pub struct ExternalEvent {
+ raw: usize,
+}
+
+unsafe impl Send for ExternalEvent {}
+
+impl ExternalEvent {
+ /// Creates the event from an opaque pointer-sized value.
+ pub fn from_raw(raw: usize) -> Self {
+ ExternalEvent { raw }
+ }
+ /// Consumes self to make it obvious that the event should be forwarded only once.
+ pub fn unwrap(self) -> usize {
+ self.raw
+ }
+}
+
+/// Describe whether or not scrolling should be clamped by the content bounds.
+#[derive(Clone, Deserialize, Serialize)]
+pub enum ScrollClamping {
+ ///
+ ToContentBounds,
+ ///
+ NoClamping,
+}
+
+/// Allows the API to communicate with WebRender.
+///
+/// This object is created along with the `Renderer` and it's main use from a
+/// user perspective is to create one or several `RenderApi` objects.
+pub struct RenderApiSender {
+ api_sender: Sender<ApiMsg>,
+ blob_image_handler: Option<Box<dyn BlobImageHandler>>,
+ shared_font_instances: SharedFontInstanceMap,
+}
+
+impl RenderApiSender {
+ /// Used internally by the `Renderer`.
+ pub fn new(
+ api_sender: Sender<ApiMsg>,
+ blob_image_handler: Option<Box<dyn BlobImageHandler>>,
+ shared_font_instances: SharedFontInstanceMap,
+ ) -> Self {
+ RenderApiSender {
+ api_sender,
+ blob_image_handler,
+ shared_font_instances,
+ }
+ }
+
+ /// Creates a new resource API object with a dedicated namespace.
+ pub fn create_api(&self) -> RenderApi {
+ let (sync_tx, sync_rx) = channel();
+ let msg = ApiMsg::CloneApi(sync_tx);
+ self.api_sender.send(msg).expect("Failed to send CloneApi message");
+ let namespace_id = match sync_rx.recv() {
+ Ok(id) => id,
+ Err(e) => {
+ // This is used to discover the underlying cause of https://github.com/servo/servo/issues/13480.
+ let webrender_is_alive = self.api_sender.send(ApiMsg::WakeUp);
+ if webrender_is_alive.is_err() {
+ panic!("WebRender was shut down before processing CloneApi: {}", e);
+ } else {
+ panic!("CloneApi message response was dropped while WebRender was still alive: {}", e);
+ }
+ }
+ };
+ RenderApi {
+ api_sender: self.api_sender.clone(),
+ namespace_id,
+ next_id: Cell::new(ResourceId(0)),
+ resources: ApiResources::new(
+ self.blob_image_handler.as_ref().map(|handler| handler.create_similar()),
+ self.shared_font_instances.clone(),
+ ),
+ }
+ }
+
+ /// Creates a new resource API object with a dedicated namespace.
+ /// Namespace id is allocated by client.
+ ///
+ /// The function could be used only when RendererOptions::namespace_alloc_by_client is true.
+ /// When the option is true, create_api() could not be used to prevent namespace id conflict.
+ pub fn create_api_by_client(&self, namespace_id: IdNamespace) -> RenderApi {
+ let msg = ApiMsg::CloneApiByClient(namespace_id);
+ self.api_sender.send(msg).expect("Failed to send CloneApiByClient message");
+ RenderApi {
+ api_sender: self.api_sender.clone(),
+ namespace_id,
+ next_id: Cell::new(ResourceId(0)),
+ resources: ApiResources::new(
+ self.blob_image_handler.as_ref().map(|handler| handler.create_similar()),
+ self.shared_font_instances.clone(),
+ ),
+ }
+ }
+}
+
+bitflags! {
+ /// Flags to enable/disable various builtin debugging tools.
+ #[repr(C)]
+ #[derive(Default, Deserialize, MallocSizeOf, Serialize)]
+ pub struct DebugFlags: u32 {
+ /// Display the frame profiler on screen.
+ const PROFILER_DBG = 1 << 0;
+ /// Display intermediate render targets on screen.
+ const RENDER_TARGET_DBG = 1 << 1;
+ /// Display all texture cache pages on screen.
+ const TEXTURE_CACHE_DBG = 1 << 2;
+ /// Display GPU timing results.
+ const GPU_TIME_QUERIES = 1 << 3;
+ /// Query the number of pixels that pass the depth test divided and show it
+ /// in the profiler as a percentage of the number of pixels in the screen
+ /// (window width times height).
+ const GPU_SAMPLE_QUERIES = 1 << 4;
+ /// Render each quad with their own draw call.
+ ///
+ /// Terrible for performance but can help with understanding the drawing
+ /// order when inspecting renderdoc or apitrace recordings.
+ const DISABLE_BATCHING = 1 << 5;
+ /// Display the pipeline epochs.
+ const EPOCHS = 1 << 6;
+ /// Reduce the amount of information displayed by the profiler so that
+ /// it occupies less screen real-estate.
+ const COMPACT_PROFILER = 1 << 7;
+ /// Print driver messages to stdout.
+ const ECHO_DRIVER_MESSAGES = 1 << 8;
+ /// Show an indicator that moves every time a frame is rendered.
+ const NEW_FRAME_INDICATOR = 1 << 9;
+ /// Show an indicator that moves every time a scene is built.
+ const NEW_SCENE_INDICATOR = 1 << 10;
+ /// Show an overlay displaying overdraw amount.
+ const SHOW_OVERDRAW = 1 << 11;
+ /// Display the contents of GPU cache.
+ const GPU_CACHE_DBG = 1 << 12;
+ /// Show a red bar that moves each time a slow frame is detected.
+ const SLOW_FRAME_INDICATOR = 1 << 13;
+ /// Clear evicted parts of the texture cache for debugging purposes.
+ const TEXTURE_CACHE_DBG_CLEAR_EVICTED = 1 << 14;
+ /// Show picture caching debug overlay
+ const PICTURE_CACHING_DBG = 1 << 15;
+ /// Highlight all primitives with colors based on kind.
+ const PRIMITIVE_DBG = 1 << 16;
+ /// Draw a zoom widget showing part of the framebuffer zoomed in.
+ const ZOOM_DBG = 1 << 17;
+ /// Scale the debug renderer down for a smaller screen. This will disrupt
+ /// any mapping between debug display items and page content, so shouldn't
+ /// be used with overlays like the picture caching or primitive display.
+ const SMALL_SCREEN = 1 << 18;
+ /// Disable various bits of the WebRender pipeline, to help narrow
+ /// down where slowness might be coming from.
+ const DISABLE_OPAQUE_PASS = 1 << 19;
+ ///
+ const DISABLE_ALPHA_PASS = 1 << 20;
+ ///
+ const DISABLE_CLIP_MASKS = 1 << 21;
+ ///
+ const DISABLE_TEXT_PRIMS = 1 << 22;
+ ///
+ const DISABLE_GRADIENT_PRIMS = 1 << 23;
+ ///
+ const OBSCURE_IMAGES = 1 << 24;
+ /// Taint the transparent area of the glyphs with a random opacity to easily
+ /// see when glyphs are re-rasterized.
+ const GLYPH_FLASHING = 1 << 25;
+ /// The profiler only displays information that is out of the ordinary.
+ const SMART_PROFILER = 1 << 26;
+ /// Dynamically control whether picture caching is enabled.
+ const DISABLE_PICTURE_CACHING = 1 << 27;
+ /// If set, dump picture cache invalidation debug to console.
+ const INVALIDATION_DBG = 1 << 28;
+ /// Log tile cache to memory for later saving as part of wr-capture
+ const TILE_CACHE_LOGGING_DBG = 1 << 29;
+ /// For debugging, force-disable automatic scaling of establishes_raster_root
+ /// pictures that are too large (ie go back to old behavior that prevents those
+ /// large pictures from establishing a raster root).
+ const DISABLE_RASTER_ROOT_SCALING = 1 << 30;
+ }
+}
+
+/// The main entry point to interact with WebRender.
+pub struct RenderApi {
+ api_sender: Sender<ApiMsg>,
+ namespace_id: IdNamespace,
+ next_id: Cell<ResourceId>,
+ resources: ApiResources,
+}
+
+impl RenderApi {
+ /// Returns the namespace ID used by this API object.
+ pub fn get_namespace_id(&self) -> IdNamespace {
+ self.namespace_id
+ }
+
+ ///
+ pub fn create_sender(&self) -> RenderApiSender {
+ RenderApiSender::new(
+ self.api_sender.clone(),
+ self.resources.blob_image_handler.as_ref().map(|handler| handler.create_similar()),
+ self.resources.get_shared_font_instances(),
+ )
+ }
+
+ /// Add a document to the WebRender instance.
+ ///
+ /// Instances can manage one or several documents (using the same render backend thread).
+ /// Each document will internally correspond to a single scene, and scenes are made of
+ /// one or several pipelines.
+ pub fn add_document(&self, initial_size: DeviceIntSize, layer: DocumentLayer) -> DocumentId {
+ let new_id = self.next_unique_id();
+ self.add_document_with_id(initial_size, layer, new_id)
+ }
+
+ /// See `add_document`
+ pub fn add_document_with_id(&self,
+ initial_size: DeviceIntSize,
+ layer: DocumentLayer,
+ id: u32) -> DocumentId {
+ let document_id = DocumentId::new(self.namespace_id, id);
+
+ let msg = ApiMsg::AddDocument(document_id, initial_size, layer);
+ self.api_sender.send(msg).unwrap();
+
+ document_id
+ }
+
+ /// Delete a document.
+ pub fn delete_document(&self, document_id: DocumentId) {
+ let msg = ApiMsg::DeleteDocument(document_id);
+ self.api_sender.send(msg).unwrap();
+ }
+
+ /// Generate a new font key
+ pub fn generate_font_key(&self) -> font::FontKey {
+ let new_id = self.next_unique_id();
+ font::FontKey::new(self.namespace_id, new_id)
+ }
+
+ /// Generate a new font instance key
+ pub fn generate_font_instance_key(&self) -> font::FontInstanceKey {
+ let new_id = self.next_unique_id();
+ font::FontInstanceKey::new(self.namespace_id, new_id)
+ }
+
+ /// Gets the dimensions for the supplied glyph keys
+ ///
+ /// Note: Internally, the internal texture cache doesn't store
+ /// 'empty' textures (height or width = 0)
+ /// This means that glyph dimensions e.g. for spaces (' ') will mostly be None.
+ pub fn get_glyph_dimensions(
+ &self,
+ key: font::FontInstanceKey,
+ glyph_indices: Vec<font::GlyphIndex>,
+ ) -> Vec<Option<font::GlyphDimensions>> {
+ let (sender, rx) = channel();
+ let msg = ApiMsg::GetGlyphDimensions(font::GlyphDimensionRequest {
+ key,
+ glyph_indices,
+ sender
+ });
+ self.api_sender.send(msg).unwrap();
+ rx.recv().unwrap()
+ }
+
+ /// Gets the glyph indices for the supplied string. These
+ /// can be used to construct GlyphKeys.
+ pub fn get_glyph_indices(&self, key: font::FontKey, text: &str) -> Vec<Option<u32>> {
+ let (sender, rx) = channel();
+ let msg = ApiMsg::GetGlyphIndices(font::GlyphIndexRequest {
+ key,
+ text: text.to_string(),
+ sender,
+ });
+ self.api_sender.send(msg).unwrap();
+ rx.recv().unwrap()
+ }
+
+ /// Creates an `ImageKey`.
+ pub fn generate_image_key(&self) -> ImageKey {
+ let new_id = self.next_unique_id();
+ ImageKey::new(self.namespace_id, new_id)
+ }
+
+ /// Creates a `BlobImageKey`.
+ pub fn generate_blob_image_key(&self) -> BlobImageKey {
+ BlobImageKey(self.generate_image_key())
+ }
+
+ /// A Gecko-specific notification mechanism to get some code executed on the
+ /// `Renderer`'s thread, mostly replaced by `NotificationHandler`. You should
+ /// probably use the latter instead.
+ pub fn send_external_event(&self, evt: ExternalEvent) {
+ let msg = ApiMsg::ExternalEvent(evt);
+ self.api_sender.send(msg).unwrap();
+ }
+
+ /// Notify WebRender that now is a good time to flush caches and release
+ /// as much memory as possible.
+ pub fn notify_memory_pressure(&self) {
+ self.api_sender.send(ApiMsg::MemoryPressure).unwrap();
+ }
+
+ /// Synchronously requests memory report.
+ pub fn report_memory(&self) -> MemoryReport {
+ let (tx, rx) = channel();
+ self.api_sender.send(ApiMsg::ReportMemory(tx)).unwrap();
+ *rx.recv().unwrap()
+ }
+
+ /// Update debugging flags.
+ pub fn set_debug_flags(&self, flags: DebugFlags) {
+ let cmd = DebugCommand::SetFlags(flags);
+ self.api_sender.send(ApiMsg::DebugCommand(cmd)).unwrap();
+ }
+
+ /// Shut the WebRender instance down.
+ pub fn shut_down(&self, synchronously: bool) {
+ if synchronously {
+ let (tx, rx) = channel();
+ self.api_sender.send(ApiMsg::ShutDown(Some(tx))).unwrap();
+ rx.recv().unwrap();
+ } else {
+ self.api_sender.send(ApiMsg::ShutDown(None)).unwrap();
+ }
+ }
+
+ /// Create a new unique key that can be used for
+ /// animated property bindings.
+ pub fn generate_property_binding_key<T: Copy>(&self) -> PropertyBindingKey<T> {
+ let new_id = self.next_unique_id();
+ PropertyBindingKey {
+ id: PropertyBindingId {
+ namespace: self.namespace_id,
+ uid: new_id,
+ },
+ _phantom: PhantomData,
+ }
+ }
+
+ #[inline]
+ fn next_unique_id(&self) -> u32 {
+ let ResourceId(id) = self.next_id.get();
+ self.next_id.set(ResourceId(id + 1));
+ id
+ }
+
+ // For use in Wrench only
+ #[doc(hidden)]
+ pub fn send_message(&self, msg: ApiMsg) {
+ self.api_sender.send(msg).unwrap();
+ }
+
+ /// Creates a transaction message from a single frame message.
+ fn frame_message(&self, msg: FrameMsg, document_id: DocumentId) -> Box<TransactionMsg> {
+ Box::new(TransactionMsg {
+ document_id,
+ scene_ops: Vec::new(),
+ frame_ops: vec![msg],
+ resource_updates: Vec::new(),
+ notifications: Vec::new(),
+ generate_frame: false,
+ invalidate_rendered_frame: false,
+ use_scene_builder_thread: false,
+ low_priority: false,
+ blob_rasterizer: None,
+ blob_requests: Vec::new(),
+ rasterized_blobs: Vec::new(),
+ })
+ }
+
+ /// Creates a transaction message from a single scene message.
+ fn scene_message(&self, msg: SceneMsg, document_id: DocumentId) -> Box<TransactionMsg> {
+ Box::new(TransactionMsg {
+ document_id,
+ scene_ops: vec![msg],
+ frame_ops: Vec::new(),
+ resource_updates: Vec::new(),
+ notifications: Vec::new(),
+ generate_frame: false,
+ invalidate_rendered_frame: false,
+ use_scene_builder_thread: false,
+ low_priority: false,
+ blob_rasterizer: None,
+ blob_requests: Vec::new(),
+ rasterized_blobs: Vec::new(),
+ })
+ }
+
+ /// A helper method to send document messages.
+ fn send_scene_msg(&self, document_id: DocumentId, msg: SceneMsg) {
+ // This assertion fails on Servo use-cases, because it creates different
+ // `RenderApi` instances for layout and compositor.
+ //assert_eq!(document_id.0, self.namespace_id);
+ self.api_sender
+ .send(ApiMsg::UpdateDocuments(vec![self.scene_message(msg, document_id)]))
+ .unwrap()
+ }
+
+ /// A helper method to send document messages.
+ fn send_frame_msg(&self, document_id: DocumentId, msg: FrameMsg) {
+ // This assertion fails on Servo use-cases, because it creates different
+ // `RenderApi` instances for layout and compositor.
+ //assert_eq!(document_id.0, self.namespace_id);
+ self.api_sender
+ .send(ApiMsg::UpdateDocuments(vec![self.frame_message(msg, document_id)]))
+ .unwrap()
+ }
+
+ /// Send a transaction to WebRender.
+ pub fn send_transaction(&mut self, document_id: DocumentId, transaction: Transaction) {
+ let mut transaction = transaction.finalize(document_id);
+
+ self.resources.update(&mut transaction);
+
+ self.api_sender.send(ApiMsg::UpdateDocuments(vec![transaction])).unwrap();
+ }
+
+ /// Send multiple transactions.
+ pub fn send_transactions(&mut self, document_ids: Vec<DocumentId>, mut transactions: Vec<Transaction>) {
+ debug_assert!(document_ids.len() == transactions.len());
+ let msgs = transactions.drain(..).zip(document_ids)
+ .map(|(txn, id)| {
+ let mut txn = txn.finalize(id);
+ self.resources.update(&mut txn);
+
+ txn
+ })
+ .collect();
+
+ self.api_sender.send(ApiMsg::UpdateDocuments(msgs)).unwrap();
+ }
+
+ /// Does a hit test on display items in the specified document, at the given
+ /// point. If a pipeline_id is specified, it is used to further restrict the
+ /// hit results so that only items inside that pipeline are matched. If the
+ /// HitTestFlags argument contains the FIND_ALL flag, then the vector of hit
+ /// results will contain all display items that match, ordered from front
+ /// to back.
+ pub fn hit_test(&self,
+ document_id: DocumentId,
+ pipeline_id: Option<PipelineId>,
+ point: WorldPoint,
+ flags: HitTestFlags)
+ -> HitTestResult {
+ let (tx, rx) = channel();
+
+ self.send_frame_msg(
+ document_id,
+ FrameMsg::HitTest(pipeline_id, point, flags, tx)
+ );
+ rx.recv().unwrap()
+ }
+
+ /// Synchronously request an object that can perform fast hit testing queries.
+ pub fn request_hit_tester(&self, document_id: DocumentId) -> HitTesterRequest {
+ let (tx, rx) = channel();
+ self.send_frame_msg(
+ document_id,
+ FrameMsg::RequestHitTester(tx)
+ );
+
+ HitTesterRequest { rx }
+ }
+
+ /// Setup the output region in the framebuffer for a given document.
+ pub fn set_document_view(
+ &self,
+ document_id: DocumentId,
+ device_rect: DeviceIntRect,
+ device_pixel_ratio: f32,
+ ) {
+ self.send_scene_msg(
+ document_id,
+ SceneMsg::SetDocumentView { device_rect, device_pixel_ratio },
+ );
+ }
+
+ /// Setup the output region in the framebuffer for a given document.
+ /// Enable copying of the output of this pipeline id to
+ /// an external texture for callers to consume.
+ pub fn enable_frame_output(
+ &self,
+ document_id: DocumentId,
+ pipeline_id: PipelineId,
+ enable: bool,
+ ) {
+ self.send_scene_msg(
+ document_id,
+ SceneMsg::EnableFrameOutput(pipeline_id, enable),
+ );
+ }
+
+ ///
+ pub fn get_scroll_node_state(&self, document_id: DocumentId) -> Vec<ScrollNodeState> {
+ let (tx, rx) = channel();
+ self.send_frame_msg(document_id, FrameMsg::GetScrollNodeState(tx));
+ rx.recv().unwrap()
+ }
+
+ // Some internal scheduling magic that leaked into the API.
+ // Buckle up and see APZUpdater.cpp for more info about what this is about.
+ #[doc(hidden)]
+ pub fn wake_scene_builder(&self) {
+ self.send_message(ApiMsg::WakeSceneBuilder);
+ }
+
+ /// Block until a round-trip to the scene builder thread has completed. This
+ /// ensures that any transactions (including ones deferred to the scene
+ /// builder thread) have been processed.
+ pub fn flush_scene_builder(&self) {
+ let (tx, rx) = channel();
+ self.send_message(ApiMsg::FlushSceneBuilder(tx));
+ rx.recv().unwrap(); // block until done
+ }
+
+ /// Save a capture of the current frame state for debugging.
+ pub fn save_capture(&self, path: PathBuf, bits: CaptureBits) {
+ let msg = ApiMsg::DebugCommand(DebugCommand::SaveCapture(path, bits));
+ self.send_message(msg);
+ }
+
+ /// Load a capture of the current frame state for debugging.
+ pub fn load_capture(&self, path: PathBuf, ids: Option<(u32, u32)>) -> Vec<CapturedDocument> {
+ // First flush the scene builder otherwise async scenes might clobber
+ // the capture we are about to load.
+ self.flush_scene_builder();
+
+ let (tx, rx) = channel();
+ let msg = ApiMsg::DebugCommand(DebugCommand::LoadCapture(path, ids, tx));
+ self.send_message(msg);
+
+ let mut documents = Vec::new();
+ while let Ok(captured_doc) = rx.recv() {
+ documents.push(captured_doc);
+ }
+ documents
+ }
+
+ /// Start capturing a sequence of frames.
+ pub fn start_capture_sequence(&self, path: PathBuf, bits: CaptureBits) {
+ let msg = ApiMsg::DebugCommand(DebugCommand::StartCaptureSequence(path, bits));
+ self.send_message(msg);
+ }
+
+ /// Stop capturing sequences of frames.
+ pub fn stop_capture_sequence(&self) {
+ let msg = ApiMsg::DebugCommand(DebugCommand::StopCaptureSequence);
+ self.send_message(msg);
+ }
+
+ /// Update the state of builtin debugging facilities.
+ pub fn send_debug_cmd(&mut self, cmd: DebugCommand) {
+ if let DebugCommand::EnableMultithreading(enable) = cmd {
+ // TODO(nical) we should enable it for all RenderApis.
+ self.resources.enable_multithreading(enable);
+ }
+ let msg = ApiMsg::DebugCommand(cmd);
+ self.send_message(msg);
+ }
+}
+
+impl Drop for RenderApi {
+ fn drop(&mut self) {
+ let msg = ApiMsg::ClearNamespace(self.namespace_id);
+ let _ = self.api_sender.send(msg);
+ }
+}
+
+/// A hit tester requested to the render backend thread but not necessarily ready yet.
+///
+/// The request should be resolved as late as possible to reduce the likelihood of blocking.
+pub struct HitTesterRequest {
+ rx: Receiver<Arc<dyn ApiHitTester>>,
+}
+
+impl HitTesterRequest {
+ /// Block until the hit tester is available and return it, consuming teh request.
+ pub fn resolve(self) -> Arc<dyn ApiHitTester> {
+ self.rx.recv().unwrap()
+ }
+}
+
+///
+#[derive(Clone)]
+pub struct ScrollNodeState {
+ ///
+ pub id: di::ExternalScrollId,
+ ///
+ pub scroll_offset: LayoutVector2D,
+}
+
+///
+#[derive(Clone, Copy, Debug)]
+pub enum ScrollLocation {
+ /// Scroll by a certain amount.
+ Delta(LayoutVector2D),
+ /// Scroll to very top of element.
+ Start,
+ /// Scroll to very bottom of element.
+ End,
+}
+
+/// Represents a zoom factor.
+#[derive(Clone, Copy, Debug)]
+pub struct ZoomFactor(f32);
+
+impl ZoomFactor {
+ /// Construct a new zoom factor.
+ pub fn new(scale: f32) -> Self {
+ ZoomFactor(scale)
+ }
+
+ /// Get the zoom factor as an untyped float.
+ pub fn get(self) -> f32 {
+ self.0
+ }
+}
+
+/// A key to identify an animated property binding.
+#[repr(C)]
+#[derive(Clone, Copy, Debug, Default, Deserialize, MallocSizeOf, PartialEq, Serialize, Eq, Hash, PeekPoke)]
+pub struct PropertyBindingId {
+ namespace: IdNamespace,
+ uid: u32,
+}
+
+impl PropertyBindingId {
+ /// Constructor.
+ pub fn new(value: u64) -> Self {
+ PropertyBindingId {
+ namespace: IdNamespace((value >> 32) as u32),
+ uid: value as u32,
+ }
+ }
+}
+
+/// A unique key that is used for connecting animated property
+/// values to bindings in the display list.
+#[repr(C)]
+#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
+pub struct PropertyBindingKey<T> {
+ ///
+ pub id: PropertyBindingId,
+ _phantom: PhantomData<T>,
+}
+
+/// Construct a property value from a given key and value.
+impl<T: Copy> PropertyBindingKey<T> {
+ ///
+ pub fn with(self, value: T) -> PropertyValue<T> {
+ PropertyValue { key: self, value }
+ }
+}
+
+impl<T> PropertyBindingKey<T> {
+ /// Constructor.
+ pub fn new(value: u64) -> Self {
+ PropertyBindingKey {
+ id: PropertyBindingId::new(value),
+ _phantom: PhantomData,
+ }
+ }
+}
+
+/// A binding property can either be a specific value
+/// (the normal, non-animated case) or point to a binding location
+/// to fetch the current value from.
+/// Note that Binding has also a non-animated value, the value is
+/// used for the case where the animation is still in-delay phase
+/// (i.e. the animation doesn't produce any animation values).
+#[repr(C)]
+#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
+pub enum PropertyBinding<T> {
+ /// Non-animated value.
+ Value(T),
+ /// Animated binding.
+ Binding(PropertyBindingKey<T>, T),
+}
+
+impl<T: Default> Default for PropertyBinding<T> {
+ fn default() -> Self {
+ PropertyBinding::Value(Default::default())
+ }
+}
+
+impl<T> From<T> for PropertyBinding<T> {
+ fn from(value: T) -> PropertyBinding<T> {
+ PropertyBinding::Value(value)
+ }
+}
+
+impl From<PropertyBindingKey<ColorF>> for PropertyBindingKey<ColorU> {
+ fn from(key: PropertyBindingKey<ColorF>) -> PropertyBindingKey<ColorU> {
+ PropertyBindingKey {
+ id: key.id.clone(),
+ _phantom: PhantomData,
+ }
+ }
+}
+
+impl From<PropertyBindingKey<ColorU>> for PropertyBindingKey<ColorF> {
+ fn from(key: PropertyBindingKey<ColorU>) -> PropertyBindingKey<ColorF> {
+ PropertyBindingKey {
+ id: key.id.clone(),
+ _phantom: PhantomData,
+ }
+ }
+}
+
+impl From<PropertyBinding<ColorF>> for PropertyBinding<ColorU> {
+ fn from(value: PropertyBinding<ColorF>) -> PropertyBinding<ColorU> {
+ match value {
+ PropertyBinding::Value(value) => PropertyBinding::Value(value.into()),
+ PropertyBinding::Binding(k, v) => {
+ PropertyBinding::Binding(k.into(), v.into())
+ }
+ }
+ }
+}
+
+impl From<PropertyBinding<ColorU>> for PropertyBinding<ColorF> {
+ fn from(value: PropertyBinding<ColorU>) -> PropertyBinding<ColorF> {
+ match value {
+ PropertyBinding::Value(value) => PropertyBinding::Value(value.into()),
+ PropertyBinding::Binding(k, v) => {
+ PropertyBinding::Binding(k.into(), v.into())
+ }
+ }
+ }
+}
+
+/// The current value of an animated property. This is
+/// supplied by the calling code.
+#[derive(Clone, Copy, Debug, Deserialize, Serialize, PartialEq)]
+pub struct PropertyValue<T> {
+ ///
+ pub key: PropertyBindingKey<T>,
+ ///
+ pub value: T,
+}
+
+/// When using `generate_frame()`, a list of `PropertyValue` structures
+/// can optionally be supplied to provide the current value of any
+/// animated properties.
+#[derive(Clone, Deserialize, Serialize, Debug, PartialEq, Default)]
+pub struct DynamicProperties {
+ ///
+ pub transforms: Vec<PropertyValue<LayoutTransform>>,
+ /// opacity
+ pub floats: Vec<PropertyValue<f32>>,
+ /// background color
+ pub colors: Vec<PropertyValue<ColorF>>,
+}
+
+/// A handler to integrate WebRender with the thread that contains the `Renderer`.
+pub trait RenderNotifier: Send {
+ ///
+ fn clone(&self) -> Box<dyn RenderNotifier>;
+ /// Wake the thread containing the `Renderer` up (after updates have been put
+ /// in the renderer's queue).
+ fn wake_up(&self);
+ /// Notify the thread containing the `Renderer` that a new frame is ready.
+ fn new_frame_ready(&self, _: DocumentId, scrolled: bool, composite_needed: bool, render_time_ns: Option<u64>);
+ /// A Gecko-specific notification mechanism to get some code executed on the
+ /// `Renderer`'s thread, mostly replaced by `NotificationHandler`. You should
+ /// probably use the latter instead.
+ fn external_event(&self, _evt: ExternalEvent) {
+ unimplemented!()
+ }
+ /// Notify the thread containing the `Renderer` that the render backend has been
+ /// shut down.
+ fn shut_down(&self) {}
+}
+
+/// A stage of the rendering pipeline.
+#[repr(u32)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum Checkpoint {
+ ///
+ SceneBuilt,
+ ///
+ FrameBuilt,
+ ///
+ FrameTexturesUpdated,
+ ///
+ FrameRendered,
+ /// NotificationRequests get notified with this if they get dropped without having been
+ /// notified. This provides the guarantee that if a request is created it will get notified.
+ TransactionDropped,
+}
+
+/// A handler to notify when a transaction reaches certain stages of the rendering
+/// pipeline.
+pub trait NotificationHandler : Send + Sync {
+ /// Entry point of the handler to implement. Invoked by WebRender.
+ fn notify(&self, when: Checkpoint);
+}
+
+/// A request to notify a handler when the transaction reaches certain stages of the
+/// rendering pipeline.
+///
+/// The request is guaranteed to be notified once and only once, even if the transaction
+/// is dropped before the requested check-point.
+pub struct NotificationRequest {
+ handler: Option<Box<dyn NotificationHandler>>,
+ when: Checkpoint,
+}
+
+impl NotificationRequest {
+ /// Constructor.
+ pub fn new(when: Checkpoint, handler: Box<dyn NotificationHandler>) -> Self {
+ NotificationRequest {
+ handler: Some(handler),
+ when,
+ }
+ }
+
+ /// The specified stage at which point the handler should be notified.
+ pub fn when(&self) -> Checkpoint { self.when }
+
+ /// Called by WebRender at specified stages to notify the registered handler.
+ pub fn notify(mut self) {
+ if let Some(handler) = self.handler.take() {
+ handler.notify(self.when);
+ }
+ }
+}
+
+/// An object that can perform hit-testing without doing synchronous queries to
+/// the RenderBackendThread.
+pub trait ApiHitTester: Send + Sync {
+ /// Does a hit test on display items in the specified document, at the given
+ /// point. If a pipeline_id is specified, it is used to further restrict the
+ /// hit results so that only items inside that pipeline are matched. If the
+ /// HitTestFlags argument contains the FIND_ALL flag, then the vector of hit
+ /// results will contain all display items that match, ordered from front
+ /// to back.
+ fn hit_test(&self, pipeline_id: Option<PipelineId>, point: WorldPoint, flags: HitTestFlags) -> HitTestResult;
+}
+
+impl Drop for NotificationRequest {
+ fn drop(&mut self) {
+ if let Some(ref mut handler) = self.handler {
+ handler.notify(Checkpoint::TransactionDropped);
+ }
+ }
+}
+
+// This Clone impl yields an "empty" request because we don't want the requests
+// to be notified twice so the request is owned by only one of the API messages
+// (the original one) after the clone.
+// This works in practice because the notifications requests are used for
+// synchronization so we don't need to include them in the recording mechanism
+// in wrench that clones the messages.
+impl Clone for NotificationRequest {
+ fn clone(&self) -> Self {
+ NotificationRequest {
+ when: self.when,
+ handler: None,
+ }
+ }
+}
+
+
+bitflags! {
+ /// Each bit of the edge AA mask is:
+ /// 0, when the edge of the primitive needs to be considered for AA
+ /// 1, when the edge of the segment needs to be considered for AA
+ ///
+ /// *Note*: the bit values have to match the shader logic in
+ /// `write_transform_vertex()` function.
+ #[cfg_attr(feature = "serialize", derive(Serialize))]
+ #[cfg_attr(feature = "deserialize", derive(Deserialize))]
+ #[derive(MallocSizeOf)]
+ pub struct EdgeAaSegmentMask: u8 {
+ ///
+ const LEFT = 0x1;
+ ///
+ const TOP = 0x2;
+ ///
+ const RIGHT = 0x4;
+ ///
+ const BOTTOM = 0x8;
+ }
+}
diff --git a/third_party/webrender/webrender_api/src/channel.rs b/third_party/webrender/webrender_api/src/channel.rs
index 7d21c6e4339..2bc4a5f16b8 100644
--- a/third_party/webrender/webrender_api/src/channel.rs
+++ b/third_party/webrender/webrender_api/src/channel.rs
@@ -2,19 +2,12 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-use crate::{Epoch, PipelineId};
+use crate::api::{Epoch, PipelineId};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::io::{self, Cursor, Error, ErrorKind, Read};
use std::mem;
-
-pub use crossbeam_channel as crossbeam;
-
-#[cfg(not(target_os = "windows"))]
-pub use crossbeam_channel::{Sender, Receiver};
-
-#[cfg(target_os = "windows")]
-pub use std::sync::mpsc::{Sender, Receiver};
+use std::sync::mpsc;
#[derive(Clone)]
pub struct Payload {
@@ -81,7 +74,7 @@ pub type PayloadSender = MsgSender<Payload>;
pub type PayloadReceiver = MsgReceiver<Payload>;
pub struct MsgReceiver<T> {
- rx: Receiver<T>,
+ rx: mpsc::Receiver<T>,
}
impl<T> MsgReceiver<T> {
@@ -89,14 +82,14 @@ impl<T> MsgReceiver<T> {
self.rx.recv().map_err(|e| io::Error::new(ErrorKind::Other, e.to_string()))
}
- pub fn to_crossbeam_receiver(self) -> Receiver<T> {
+ pub fn to_mpsc_receiver(self) -> mpsc::Receiver<T> {
self.rx
}
}
#[derive(Clone)]
pub struct MsgSender<T> {
- tx: Sender<T>,
+ tx: mpsc::Sender<T>,
}
impl<T> MsgSender<T> {
@@ -106,12 +99,12 @@ impl<T> MsgSender<T> {
}
pub fn payload_channel() -> Result<(PayloadSender, PayloadReceiver), Error> {
- let (tx, rx) = unbounded_channel();
+ let (tx, rx) = mpsc::channel();
Ok((PayloadSender { tx }, PayloadReceiver { rx }))
}
pub fn msg_channel<T>() -> Result<(MsgSender<T>, MsgReceiver<T>), Error> {
- let (tx, rx) = unbounded_channel();
+ let (tx, rx) = mpsc::channel();
Ok((MsgSender { tx }, MsgReceiver { rx }))
}
@@ -136,45 +129,3 @@ impl<'de, T> Deserialize<'de> for MsgSender<T> {
unreachable!();
}
}
-
-/// A create a channel intended for one-shot uses, for example the channels
-/// created to block on a synchronous query and then discarded,
-#[cfg(not(target_os = "windows"))]
-pub fn single_msg_channel<T>() -> (Sender<T>, Receiver<T>) {
- crossbeam_channel::bounded(1)
-}
-
-/// A fast MPMC message channel that can hold a fixed number of messages.
-///
-/// If the channel is full, the sender will block upon sending extra messages
-/// until the receiver has consumed some messages.
-/// The capacity parameter should be chosen either:
-/// - high enough to avoid blocking on the common cases,
-/// - or, on the contrary, using the blocking behavior as a means to prevent
-/// fast producers from building up work faster than it is consumed.
-#[cfg(not(target_os = "windows"))]
-pub fn fast_channel<T>(capacity: usize) -> (Sender<T>, Receiver<T>) {
- crossbeam_channel::bounded(capacity)
-}
-
-/// Creates an MPMC channel that is a bit slower than the fast_channel but doesn't
-/// have a limit on the number of messages held at a given time and therefore
-/// doesn't block when sending.
-#[cfg(not(target_os = "windows"))]
-pub use crossbeam_channel::unbounded as unbounded_channel;
-
-
-#[cfg(target_os = "windows")]
-pub fn fast_channel<T>(_cap: usize) -> (Sender<T>, Receiver<T>) {
- std::sync::mpsc::channel()
-}
-
-#[cfg(target_os = "windows")]
-pub fn unbounded_channel<T>() -> (Sender<T>, Receiver<T>) {
- std::sync::mpsc::channel()
-}
-
-#[cfg(target_os = "windows")]
-pub fn single_msg_channel<T>() -> (Sender<T>, Receiver<T>) {
- std::sync::mpsc::channel()
-}
diff --git a/third_party/webrender/webrender_api/src/display_item.rs b/third_party/webrender/webrender_api/src/display_item.rs
index 5c425b9ed17..e6b8e036e46 100644
--- a/third_party/webrender/webrender_api/src/display_item.rs
+++ b/third_party/webrender/webrender_api/src/display_item.rs
@@ -2,16 +2,15 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-use euclid::{SideOffsets2D, Angle};
+use euclid::SideOffsets2D;
use peek_poke::PeekPoke;
use std::ops::Not;
// local imports
use crate::font;
-use crate::{PipelineId, PropertyBinding};
+use crate::api::{PipelineId, PropertyBinding};
use crate::color::ColorF;
use crate::image::{ColorDepth, ImageKey};
use crate::units::*;
-use std::hash::{Hash, Hasher};
// ******************************************************************
// * NOTE: some of these structs have an "IMPLICIT" comment. *
@@ -48,10 +47,6 @@ bitflags! {
/// compositor surface under certain (implementation specific) conditions. This
/// is typically used for large videos, and canvas elements.
const PREFER_COMPOSITOR_SURFACE = 1 << 3;
- /// If set, this primitive can be passed directly to the compositor via its
- /// ExternalImageId, and the compositor will use the native image directly.
- /// Used as a further extension on top of PREFER_COMPOSITOR_SURFACE.
- const SUPPORTS_EXTERNAL_COMPOSITOR_SURFACE = 1 << 4;
}
}
@@ -73,6 +68,10 @@ pub struct CommonItemProperties {
pub clip_id: ClipId,
/// The coordinate-space the item is in (yes, it can be really granular)
pub spatial_id: SpatialId,
+ /// Opaque bits for our clients to use for hit-testing. This is the most
+ /// dubious "common" field, but because it's an Option, it usually only
+ /// wastes a single byte (for None).
+ pub hit_info: Option<ItemTag>,
/// Various flags describing properties of this primitive.
pub flags: PrimitiveFlags,
}
@@ -87,6 +86,7 @@ impl CommonItemProperties {
clip_rect,
spatial_id: space_and_clip.spatial_id,
clip_id: space_and_clip.clip_id,
+ hit_info: None,
flags: PrimitiveFlags::default(),
}
}
@@ -155,7 +155,6 @@ pub enum DisplayItem {
SetFilterOps,
SetFilterData,
SetFilterPrimitives,
- SetPoints,
// These marker items terminate a scope introduced by a previous item.
PopReferenceFrame,
@@ -204,7 +203,6 @@ pub enum DebugDisplayItem {
SetFilterOps(Vec<FilterOp>),
SetFilterData(FilterData),
SetFilterPrimitives(Vec<FilterPrimitive>),
- SetPoints(Vec<LayoutPoint>),
PopReferenceFrame,
PopStackingContext,
@@ -216,8 +214,7 @@ pub struct ImageMaskClipDisplayItem {
pub id: ClipId,
pub parent_space_and_clip: SpaceAndClipInfo,
pub image_mask: ImageMask,
- pub fill_rule: FillRule,
-} // IMPLICIT points: Vec<LayoutPoint>
+}
#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
pub struct RectClipDisplayItem {
@@ -309,7 +306,7 @@ pub struct ScrollFrameDisplayItem {
pub content_rect: LayoutRect,
pub clip_rect: LayoutRect,
pub parent_space_and_clip: SpaceAndClipInfo,
- pub external_id: ExternalScrollId,
+ pub external_id: Option<ExternalScrollId>,
pub scroll_sensitivity: ScrollSensitivity,
/// The amount this scrollframe has already been scrolled by, in the caller.
/// This means that all the display items that are inside the scrollframe
@@ -341,7 +338,6 @@ pub struct ClearRectangleDisplayItem {
#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
pub struct HitTestDisplayItem {
pub common: CommonItemProperties,
- pub tag: ItemTag,
}
#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
@@ -627,15 +623,6 @@ pub struct Gradient {
pub extend_mode: ExtendMode,
} // IMPLICIT: stops: Vec<GradientStop>
-impl Gradient {
- pub fn is_valid(&self) -> bool {
- self.start_point.x.is_finite() &&
- self.start_point.y.is_finite() &&
- self.end_point.x.is_finite() &&
- self.end_point.y.is_finite()
- }
-}
-
/// The area
#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
pub struct GradientDisplayItem {
@@ -668,15 +655,6 @@ pub struct RadialGradient {
pub extend_mode: ExtendMode,
} // IMPLICIT stops: Vec<GradientStop>
-impl RadialGradient {
- pub fn is_valid(&self) -> bool {
- self.center.x.is_finite() &&
- self.center.y.is_finite() &&
- self.start_offset.is_finite() &&
- self.end_offset.is_finite()
- }
-}
-
#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
pub struct ConicGradient {
pub center: LayoutPoint,
@@ -686,16 +664,6 @@ pub struct ConicGradient {
pub extend_mode: ExtendMode,
} // IMPLICIT stops: Vec<GradientStop>
-impl ConicGradient {
- pub fn is_valid(&self) -> bool {
- self.center.x.is_finite() &&
- self.center.y.is_finite() &&
- self.angle.is_finite() &&
- self.start_offset.is_finite() &&
- self.end_offset.is_finite()
- }
-}
-
/// Just an abstraction for bundling up a bunch of clips into a "super clip".
#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
pub struct ClipChainItem {
@@ -743,87 +711,23 @@ pub struct ReferenceFrameDisplayListItem {
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, PeekPoke)]
pub enum ReferenceFrameKind {
+ /// Zoom reference frames must be a scale + translation only
+ Zoom,
/// A normal transform matrix, may contain perspective (the CSS transform property)
- Transform {
- /// Optionally marks the transform as only ever having a simple 2D scale or translation,
- /// allowing for optimizations.
- is_2d_scale_translation: bool,
- /// Marks that the transform should be snapped. Used for transforms which animate in
- /// response to scrolling, eg for zooming or dynamic toolbar fixed-positioning.
- should_snap: bool,
- },
+ Transform,
/// A perspective transform, that optionally scrolls relative to a specific scroll node
Perspective {
scrolling_relative_to: Option<ExternalScrollId>,
}
}
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, PeekPoke)]
-pub enum Rotation {
- Degree0,
- Degree90,
- Degree180,
- Degree270,
-}
-
-impl Rotation {
- pub fn to_matrix(
- &self,
- size: LayoutSize,
- ) -> LayoutTransform {
- let (shift_center_to_origin, angle) = match self {
- Rotation::Degree0 => {
- (LayoutTransform::translation(-size.width / 2., -size.height / 2., 0.), Angle::degrees(0.))
- },
- Rotation::Degree90 => {
- (LayoutTransform::translation(-size.height / 2., -size.width / 2., 0.), Angle::degrees(90.))
- },
- Rotation::Degree180 => {
- (LayoutTransform::translation(-size.width / 2., -size.height / 2., 0.), Angle::degrees(180.))
- },
- Rotation::Degree270 => {
- (LayoutTransform::translation(-size.height / 2., -size.width / 2., 0.), Angle::degrees(270.))
- },
- };
- let shift_origin_to_center = LayoutTransform::translation(size.width / 2., size.height / 2., 0.);
-
- shift_center_to_origin
- .then(&LayoutTransform::rotation(0., 0., 1.0, angle))
- .then(&shift_origin_to_center)
- }
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, PeekPoke)]
-pub enum ReferenceTransformBinding {
- /// Standard reference frame which contains a precomputed transform.
- Static {
- binding: PropertyBinding<LayoutTransform>,
- },
- /// Computed reference frame which dynamically calculates the transform
- /// based on the given parameters. The reference is the content size of
- /// the parent iframe, which is affected by snapping.
- Computed {
- scale_from: Option<LayoutSize>,
- vertical_flip: bool,
- rotation: Rotation,
- },
-}
-
-impl Default for ReferenceTransformBinding {
- fn default() -> Self {
- ReferenceTransformBinding::Static {
- binding: Default::default(),
- }
- }
-}
-
#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
pub struct ReferenceFrame {
pub kind: ReferenceFrameKind,
pub transform_style: TransformStyle,
/// The transform matrix, either the perspective matrix or the transform
/// matrix.
- pub transform: ReferenceTransformBinding,
+ pub transform: PropertyBinding<LayoutTransform>,
pub id: SpatialId,
}
@@ -858,7 +762,7 @@ pub enum TransformStyle {
/// when we want to cache the output, and performance is
/// important. Note that this is a performance hint only,
/// which WR may choose to ignore.
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, MallocSizeOf, Serialize, PeekPoke)]
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, PeekPoke)]
#[repr(u8)]
pub enum RasterSpace {
// Rasterize in local-space, applying supplied scale to primitives.
@@ -881,23 +785,6 @@ impl RasterSpace {
}
}
-impl Eq for RasterSpace {}
-
-impl Hash for RasterSpace {
- fn hash<H: Hasher>(&self, state: &mut H) {
- match self {
- RasterSpace::Screen => {
- 0.hash(state);
- }
- RasterSpace::Local(scale) => {
- // Note: this is inconsistent with the Eq impl for -0.0 (don't care).
- 1.hash(state);
- scale.to_bits().hash(state);
- }
- }
- }
-}
-
bitflags! {
#[repr(C)]
#[derive(Deserialize, MallocSizeOf, Serialize, PeekPoke)]
@@ -1024,8 +911,7 @@ impl FloodPrimitive {
#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
pub struct BlurPrimitive {
pub input: FilterPrimitiveInput,
- pub width: f32,
- pub height: f32,
+ pub radius: f32,
}
#[repr(C)]
@@ -1152,7 +1038,7 @@ pub enum FilterOp {
/// Filter that does no transformation of the colors, needed for
/// debug purposes only.
Identity,
- Blur(f32, f32),
+ Blur(f32),
Brightness(f32),
Contrast(f32),
Grayscale(f32),
@@ -1344,7 +1230,6 @@ pub enum YuvColorSpace {
Rec601 = 0,
Rec709 = 1,
Rec2020 = 2,
- Identity = 3, // aka RGB as per ISO/IEC 23091-2:2019
}
#[repr(u8)]
@@ -1519,34 +1404,6 @@ impl ComplexClipRegion {
}
}
-pub const POLYGON_CLIP_VERTEX_MAX: usize = 16;
-
-#[repr(u8)]
-#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize, Eq, Hash, PeekPoke)]
-pub enum FillRule {
- Nonzero = 0x1, // Behaves as the SVG fill-rule definition for nonzero.
- Evenodd = 0x2, // Behaves as the SVG fill-rule definition for evenodd.
-}
-
-impl From<u8> for FillRule {
- fn from(fill_rule: u8) -> Self {
- match fill_rule {
- 0x1 => FillRule::Nonzero,
- 0x2 => FillRule::Evenodd,
- _ => panic!("Unexpected FillRule value."),
- }
- }
-}
-
-impl From<FillRule> for u8 {
- fn from(fill_rule: FillRule) -> Self {
- match fill_rule {
- FillRule::Nonzero => 0x1,
- FillRule::Evenodd => 0x2,
- }
- }
-}
-
#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize, PeekPoke)]
pub struct ClipChainId(pub u64, pub PipelineId);
@@ -1674,7 +1531,6 @@ impl DisplayItem {
DisplayItem::SetFilterOps => "set_filter_ops",
DisplayItem::SetFilterData => "set_filter_data",
DisplayItem::SetFilterPrimitives => "set_filter_primitives",
- DisplayItem::SetPoints => "set_points",
DisplayItem::RadialGradient(..) => "radial_gradient",
DisplayItem::Rectangle(..) => "rectangle",
DisplayItem::ScrollFrame(..) => "scroll_frame",
@@ -1716,13 +1572,8 @@ impl_default_for_enums! {
FilterOp => Identity,
ComponentTransferFuncType => Identity,
ClipMode => Clip,
- FillRule => Nonzero,
ClipId => ClipId::invalid(),
- ReferenceFrameKind => Transform {
- is_2d_scale_translation: false,
- should_snap: false,
- },
- Rotation => Degree0,
+ ReferenceFrameKind => Transform,
TransformStyle => Flat,
RasterSpace => Local(f32::default()),
MixBlendMode => Normal,
diff --git a/third_party/webrender/webrender_api/src/display_list.rs b/third_party/webrender/webrender_api/src/display_list.rs
index e2498c5c194..0680a9875d3 100644
--- a/third_party/webrender/webrender_api/src/display_list.rs
+++ b/third_party/webrender/webrender_api/src/display_list.rs
@@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use euclid::SideOffsets2D;
-use peek_poke::{ensure_red_zone, peek_from_slice, poke_extend_vec, strip_red_zone};
+use peek_poke::{ensure_red_zone, peek_from_slice, poke_extend_vec};
use peek_poke::{poke_inplace_slice, poke_into_vec, Poke};
#[cfg(feature = "deserialize")]
use serde::de::Deserializer;
@@ -20,7 +20,7 @@ use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
// local imports
use crate::display_item as di;
use crate::display_item_cache::*;
-use crate::{PipelineId, PropertyBinding};
+use crate::api::{PipelineId, PropertyBinding};
use crate::gradient_builder::GradientBuilder;
use crate::color::ColorF;
use crate::font::{FontInstanceKey, GlyphInstance, GlyphOptions};
@@ -115,18 +115,6 @@ pub struct BuiltDisplayList {
descriptor: BuiltDisplayListDescriptor,
}
-#[repr(C)]
-#[derive(Copy, Clone, Deserialize, Serialize)]
-pub enum GeckoDisplayListType {
- None,
- Partial(f64),
- Full(f64),
-}
-
-impl Default for GeckoDisplayListType {
- fn default() -> Self { GeckoDisplayListType::None }
-}
-
/// Describes the memory layout of a display list.
///
/// A display list consists of some number of display list items, followed by a number of display
@@ -134,8 +122,6 @@ impl Default for GeckoDisplayListType {
#[repr(C)]
#[derive(Copy, Clone, Default, Deserialize, Serialize)]
pub struct BuiltDisplayListDescriptor {
- /// Gecko specific information about the display list.
- gecko_display_list_type: GeckoDisplayListType,
/// The first IPC time stamp: before any work has been done
builder_start_time: u64,
/// The second IPC time stamp: after serialization
@@ -182,10 +168,6 @@ impl DisplayListWithCache {
self.display_list.descriptor()
}
- pub fn times(&self) -> (u64, u64, u64) {
- self.display_list.times()
- }
-
pub fn data(&self) -> &[u8] {
self.display_list.data()
}
@@ -239,7 +221,6 @@ pub struct BuiltDisplayListIter<'a> {
cur_filter_primitives: ItemRange<'a, di::FilterPrimitive>,
cur_clip_chain_items: ItemRange<'a, di::ClipId>,
cur_complex_clip: ItemRange<'a, di::ComplexClipRegion>,
- cur_points: ItemRange<'a, LayoutPoint>,
peeking: Peek,
/// Should just be initialized but never populated in release builds
debug_stats: DebugStats,
@@ -282,7 +263,7 @@ impl DebugStats {
/// Logs the stats for the given serialized slice
#[cfg(feature = "display_list_stats")]
- fn log_slice<T: Copy + Default + peek_poke::Peek>(
+ fn log_slice<T: Peek>(
&mut self,
slice_name: &'static str,
range: &ItemRange<T>,
@@ -337,10 +318,6 @@ impl<'a, 'b> DisplayItemRef<'a, 'b> {
self.iter.cur_complex_clip
}
- pub fn points(&self) -> ItemRange<LayoutPoint> {
- self.iter.cur_points
- }
-
pub fn glyphs(&self) -> ItemRange<GlyphInstance> {
self.iter.glyphs()
}
@@ -414,14 +391,6 @@ impl BuiltDisplayList {
)
}
- pub fn gecko_display_list_stats(&self) -> (f64, bool) {
- match self.descriptor.gecko_display_list_type {
- GeckoDisplayListType::Full(duration) => (duration, true),
- GeckoDisplayListType::Partial(duration) => (duration, false),
- _ => (0.0, false)
- }
- }
-
pub fn total_clip_nodes(&self) -> usize {
self.descriptor.total_clip_nodes
}
@@ -503,9 +472,6 @@ impl BuiltDisplayList {
Real::SetGradientStops => Debug::SetGradientStops(
item.iter.cur_stops.iter().collect()
),
- Real::SetPoints => Debug::SetPoints(
- item.iter.cur_points.iter().collect()
- ),
Real::RectClip(v) => Debug::RectClip(v),
Real::RoundedRectClip(v) => Debug::RoundedRectClip(v),
Real::ImageMaskClip(v) => Debug::ImageMaskClip(v),
@@ -575,7 +541,6 @@ impl<'a> BuiltDisplayListIter<'a> {
cur_filter_primitives: ItemRange::default(),
cur_clip_chain_items: ItemRange::default(),
cur_complex_clip: ItemRange::default(),
- cur_points: ItemRange::default(),
peeking: Peek::NotPeeking,
debug_stats: DebugStats {
last_addr: data.as_ptr() as usize,
@@ -644,7 +609,6 @@ impl<'a> BuiltDisplayListIter<'a> {
self.cur_stops = ItemRange::default();
self.cur_complex_clip = ItemRange::default();
self.cur_clip_chain_items = ItemRange::default();
- self.cur_points = ItemRange::default();
self.cur_filters = ItemRange::default();
self.cur_filter_primitives = ItemRange::default();
self.cur_filter_data.clear();
@@ -655,8 +619,7 @@ impl<'a> BuiltDisplayListIter<'a> {
SetGradientStops |
SetFilterOps |
SetFilterData |
- SetFilterPrimitives |
- SetPoints => {
+ SetFilterPrimitives => {
// These are marker items for populating other display items, don't yield them.
continue;
}
@@ -718,10 +681,6 @@ impl<'a> BuiltDisplayListIter<'a> {
self.cur_filter_primitives = skip_slice::<di::FilterPrimitive>(&mut self.data);
self.debug_stats.log_slice("set_filter_primitives.primitives", &self.cur_filter_primitives);
}
- SetPoints => {
- self.cur_points = skip_slice::<LayoutPoint>(&mut self.data);
- self.debug_stats.log_slice("set_points.points", &self.cur_points);
- }
ClipChain(_) => {
self.cur_clip_chain_items = skip_slice::<di::ClipId>(&mut self.data);
self.debug_stats.log_slice("clip_chain.clip_ids", &self.cur_clip_chain_items);
@@ -935,10 +894,6 @@ impl<'de> Deserialize<'de> for BuiltDisplayList {
DisplayListBuilder::push_iter_impl(&mut temp, stops);
Real::SetGradientStops
},
- Debug::SetPoints(points) => {
- DisplayListBuilder::push_iter_impl(&mut temp, points);
- Real::SetPoints
- },
Debug::RectClip(v) => Real::RectClip(v),
Debug::RoundedRectClip(v) => Real::RoundedRectClip(v),
Debug::ImageMaskClip(v) => Real::ImageMaskClip(v),
@@ -976,7 +931,6 @@ impl<'de> Deserialize<'de> for BuiltDisplayList {
Ok(BuiltDisplayList {
data,
descriptor: BuiltDisplayListDescriptor {
- gecko_display_list_type: GeckoDisplayListType::None,
builder_start_time: 0,
builder_finish_time: 1,
send_start_time: 1,
@@ -1022,6 +976,9 @@ pub struct DisplayListBuilder {
next_clip_chain_id: u64,
builder_start_time: u64,
+ /// The size of the content of this display list. This is used to allow scrolling
+ /// outside the bounds of the display list items themselves.
+ content_size: LayoutSize,
save_state: Option<SaveState>,
cache_size: usize,
@@ -1029,12 +986,13 @@ pub struct DisplayListBuilder {
}
impl DisplayListBuilder {
- pub fn new(pipeline_id: PipelineId) -> Self {
- Self::with_capacity(pipeline_id, 0)
+ pub fn new(pipeline_id: PipelineId, content_size: LayoutSize) -> Self {
+ Self::with_capacity(pipeline_id, content_size, 0)
}
pub fn with_capacity(
pipeline_id: PipelineId,
+ content_size: LayoutSize,
capacity: usize,
) -> Self {
let start_time = precise_time_ns();
@@ -1051,12 +1009,18 @@ impl DisplayListBuilder {
next_spatial_index: FIRST_SPATIAL_NODE_INDEX,
next_clip_chain_id: 0,
builder_start_time: start_time,
+ content_size,
save_state: None,
cache_size: 0,
serialized_content_buffer: None,
}
}
+ /// Return the content size for this display list
+ pub fn content_size(&self) -> LayoutSize {
+ self.content_size
+ }
+
/// Saves the current display list state, so it may be `restore()`'d.
///
/// # Conditions:
@@ -1111,15 +1075,11 @@ impl DisplayListBuilder {
W: Write
{
let mut temp = BuiltDisplayList::default();
- ensure_red_zone::<di::DisplayItem>(&mut self.data);
- temp.descriptor.extra_data_offset = self.data.len();
mem::swap(&mut temp.data, &mut self.data);
let mut index: usize = 0;
{
- let mut cache = DisplayItemCache::new();
- cache.update(&temp);
- let mut iter = temp.iter_with_cache(&cache);
+ let mut iter = temp.iter();
while let Some(item) = iter.next_raw() {
if index >= range.start.unwrap_or(0) && range.end.map_or(true, |e| index < e) {
writeln!(sink, "{}{:?}", " ".repeat(indent), item.item()).unwrap();
@@ -1129,7 +1089,6 @@ impl DisplayListBuilder {
}
self.data = temp.data;
- strip_red_zone::<di::DisplayItem>(&mut self.data);
index
}
@@ -1276,11 +1235,9 @@ impl DisplayListBuilder {
pub fn push_hit_test(
&mut self,
common: &di::CommonItemProperties,
- tag: di::ItemTag,
) {
let item = di::DisplayItem::HitTest(di::HitTestDisplayItem {
common: *common,
- tag,
});
self.push_item(&item);
}
@@ -1577,9 +1534,7 @@ impl DisplayListBuilder {
origin,
reference_frame: di::ReferenceFrame {
transform_style,
- transform: di::ReferenceTransformBinding::Static {
- binding: transform,
- },
+ transform,
kind,
id,
},
@@ -1589,38 +1544,6 @@ impl DisplayListBuilder {
id
}
- pub fn push_computed_frame(
- &mut self,
- origin: LayoutPoint,
- parent_spatial_id: di::SpatialId,
- scale_from: Option<LayoutSize>,
- vertical_flip: bool,
- rotation: di::Rotation,
- ) -> di::SpatialId {
- let id = self.generate_spatial_index();
-
- let item = di::DisplayItem::PushReferenceFrame(di::ReferenceFrameDisplayListItem {
- parent_spatial_id,
- origin,
- reference_frame: di::ReferenceFrame {
- transform_style: di::TransformStyle::Flat,
- transform: di::ReferenceTransformBinding::Computed {
- scale_from,
- vertical_flip,
- rotation,
- },
- kind: di::ReferenceFrameKind::Transform {
- is_2d_scale_translation: false,
- should_snap: false,
- },
- id,
- },
- });
-
- self.push_item(&item);
- id
- }
-
pub fn pop_reference_frame(&mut self) {
self.push_item(&di::DisplayItem::PopReferenceFrame);
}
@@ -1773,7 +1696,7 @@ impl DisplayListBuilder {
pub fn define_scroll_frame(
&mut self,
parent_space_and_clip: &di::SpaceAndClipInfo,
- external_id: di::ExternalScrollId,
+ external_id: Option<di::ExternalScrollId>,
content_rect: LayoutRect,
clip_rect: LayoutRect,
scroll_sensitivity: di::ScrollSensitivity,
@@ -1819,25 +1742,14 @@ impl DisplayListBuilder {
&mut self,
parent_space_and_clip: &di::SpaceAndClipInfo,
image_mask: di::ImageMask,
- points: &[LayoutPoint],
- fill_rule: di::FillRule,
) -> di::ClipId {
let id = self.generate_clip_index();
let item = di::DisplayItem::ImageMaskClip(di::ImageMaskClipDisplayItem {
id,
parent_space_and_clip: *parent_space_and_clip,
image_mask,
- fill_rule,
});
- // We only need to supply points if there are at least 3, which is the
- // minimum to specify a polygon. BuiltDisplayListIter.next ensures that points
- // are cleared between processing other display items, so we'll correctly get
- // zero points when no SetPoints item has been pushed.
- if points.len() >= 3 {
- self.push_item(&di::DisplayItem::SetPoints);
- self.push_iter(points);
- }
self.push_item(&item);
id
}
@@ -2016,7 +1928,7 @@ impl DisplayListBuilder {
self.cache_size = cache_size;
}
- pub fn finalize(mut self) -> (PipelineId, BuiltDisplayList) {
+ pub fn finalize(mut self) -> (PipelineId, LayoutSize, BuiltDisplayList) {
assert!(self.save_state.is_none(), "Finalized DisplayListBuilder with a pending save");
if let Some(content) = self.serialized_content_buffer.take() {
@@ -2039,9 +1951,9 @@ impl DisplayListBuilder {
let end_time = precise_time_ns();
(
self.pipeline_id,
+ self.content_size,
BuiltDisplayList {
descriptor: BuiltDisplayListDescriptor {
- gecko_display_list_type: GeckoDisplayListType::None,
builder_start_time: self.builder_start_time,
builder_finish_time: end_time,
send_start_time: end_time,
diff --git a/third_party/webrender/webrender_api/src/font.rs b/third_party/webrender/webrender_api/src/font.rs
index 3052db8f9e1..7f24736f09e 100644
--- a/third_party/webrender/webrender_api/src/font.rs
+++ b/third_party/webrender/webrender_api/src/font.rs
@@ -15,11 +15,10 @@ use std::cmp::Ordering;
use std::hash::{Hash, Hasher};
#[cfg(not(target_os = "macos"))]
use std::path::PathBuf;
-use std::sync::{Arc, RwLock, RwLockReadGuard};
+use std::sync::{Arc, RwLock, RwLockReadGuard, mpsc::Sender};
use std::collections::HashMap;
// local imports
-use crate::IdNamespace;
-use crate::channel::Sender;
+use crate::api::IdNamespace;
use crate::color::ColorU;
use crate::units::LayoutPoint;
diff --git a/third_party/webrender/webrender_api/src/gradient_builder.rs b/third_party/webrender/webrender_api/src/gradient_builder.rs
index 6347396f791..883acbafa38 100644
--- a/third_party/webrender/webrender_api/src/gradient_builder.rs
+++ b/third_party/webrender/webrender_api/src/gradient_builder.rs
@@ -132,9 +132,7 @@ impl GradientBuilder {
let first = *stops.first().unwrap();
let last = *stops.last().unwrap();
- // Express the assertion so that if one of the offsets is NaN, we don't panic
- // and instead take the branch that handles degenerate gradients.
- assert!(!(first.offset > last.offset));
+ assert!(first.offset <= last.offset);
let stops_delta = last.offset - first.offset;
diff --git a/third_party/webrender/webrender_api/src/image.rs b/third_party/webrender/webrender_api/src/image.rs
index 23c660b647e..4a664bddcfd 100644
--- a/third_party/webrender/webrender_api/src/image.rs
+++ b/third_party/webrender/webrender_api/src/image.rs
@@ -9,7 +9,7 @@ use peek_poke::PeekPoke;
use std::ops::{Add, Sub};
use std::sync::Arc;
// local imports
-use crate::{IdNamespace, TileSize};
+use crate::api::{IdNamespace, PipelineId, TileSize};
use crate::display_item::ImageRendering;
use crate::font::{FontInstanceKey, FontInstanceData, FontKey, FontTemplate};
use crate::units::*;
@@ -100,24 +100,38 @@ pub trait ExternalImageHandler {
fn unlock(&mut self, key: ExternalImageId, channel_index: u8);
}
+/// Allows callers to receive a texture with the contents of a specific
+/// pipeline copied to it.
+pub trait OutputImageHandler {
+ /// Return the native texture handle and the size of the texture.
+ fn lock(&mut self, pipeline_id: PipelineId) -> Option<(u32, FramebufferIntSize)>;
+ /// Unlock will only be called if the lock() call succeeds, when WR has issued
+ /// the GL commands to copy the output to the texture handle.
+ fn unlock(&mut self, pipeline_id: PipelineId);
+}
+
/// Specifies the type of texture target in driver terms.
#[repr(u8)]
-#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
-pub enum ImageBufferKind {
+#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
+pub enum TextureTarget {
/// Standard texture. This maps to GL_TEXTURE_2D in OpenGL.
- Texture2D = 0,
+ Default = 0,
+ /// Array texture. This maps to GL_TEXTURE_2D_ARRAY in OpenGL. See
+ /// https://www.khronos.org/opengl/wiki/Array_Texture for background
+ /// on Array textures.
+ Array = 1,
/// Rectangle texture. This maps to GL_TEXTURE_RECTANGLE in OpenGL. This
/// is similar to a standard texture, with a few subtle differences
/// (no mipmaps, non-power-of-two dimensions, different coordinate space)
/// that make it useful for representing the kinds of textures we use
/// in WebRender. See https://www.khronos.org/opengl/wiki/Rectangle_Texture
/// for background on Rectangle textures.
- TextureRect = 1,
+ Rect = 2,
/// External texture. This maps to GL_TEXTURE_EXTERNAL_OES in OpenGL, which
/// is an extension. This is used for image formats that OpenGL doesn't
/// understand, particularly YUV. See
/// https://www.khronos.org/registry/OpenGL/extensions/OES/OES_EGL_image_external.txt
- TextureExternal = 2,
+ External = 3,
}
/// Storage format identifier for externally-managed images.
@@ -125,7 +139,7 @@ pub enum ImageBufferKind {
#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub enum ExternalImageType {
/// The image is texture-backed.
- TextureHandle(ImageBufferKind),
+ TextureHandle(TextureTarget),
/// The image is heap-allocated by the embedding.
Buffer,
}
@@ -326,9 +340,10 @@ pub enum ImageData {
}
mod serde_image_data_raw {
- use serde::{Deserializer, Serializer};
use serde_bytes;
+
use std::sync::Arc;
+ use serde::{Deserializer, Serializer};
pub fn serialize<S: Serializer>(bytes: &Arc<Vec<u8>>, serializer: S) -> Result<S::Ok, S::Error> {
serde_bytes::serialize(bytes.as_slice(), serializer)
diff --git a/third_party/webrender/webrender_api/src/image_tiling.rs b/third_party/webrender/webrender_api/src/image_tiling.rs
new file mode 100644
index 00000000000..8fdc82ef24d
--- /dev/null
+++ b/third_party/webrender/webrender_api/src/image_tiling.rs
@@ -0,0 +1,815 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use crate::{TileSize, EdgeAaSegmentMask};
+use crate::units::*;
+use euclid::{point2, size2};
+use std::i32;
+use std::ops::Range;
+
+/// If repetitions are far enough apart that only one is within
+/// the primitive rect, then we can simplify the parameters and
+/// treat the primitive as not repeated.
+/// This can let us avoid unnecessary work later to handle some
+/// of the parameters.
+pub fn simplify_repeated_primitive(
+ stretch_size: &LayoutSize,
+ tile_spacing: &mut LayoutSize,
+ prim_rect: &mut LayoutRect,
+) {
+ let stride = *stretch_size + *tile_spacing;
+
+ if stride.width >= prim_rect.size.width {
+ tile_spacing.width = 0.0;
+ prim_rect.size.width = f32::min(prim_rect.size.width, stretch_size.width);
+ }
+ if stride.height >= prim_rect.size.height {
+ tile_spacing.height = 0.0;
+ prim_rect.size.height = f32::min(prim_rect.size.height, stretch_size.height);
+ }
+}
+
+pub struct Repetition {
+ pub origin: LayoutPoint,
+ pub edge_flags: EdgeAaSegmentMask,
+}
+
+pub struct RepetitionIterator {
+ current_x: i32,
+ x_count: i32,
+ current_y: i32,
+ y_count: i32,
+ row_flags: EdgeAaSegmentMask,
+ current_origin: LayoutPoint,
+ initial_origin: LayoutPoint,
+ stride: LayoutSize,
+}
+
+impl Iterator for RepetitionIterator {
+ type Item = Repetition;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ if self.current_x == self.x_count {
+ self.current_y += 1;
+ if self.current_y >= self.y_count {
+ return None;
+ }
+ self.current_x = 0;
+
+ self.row_flags = EdgeAaSegmentMask::empty();
+ if self.current_y == self.y_count - 1 {
+ self.row_flags |= EdgeAaSegmentMask::BOTTOM;
+ }
+
+ self.current_origin.x = self.initial_origin.x;
+ self.current_origin.y += self.stride.height;
+ }
+
+ let mut edge_flags = self.row_flags;
+ if self.current_x == 0 {
+ edge_flags |= EdgeAaSegmentMask::LEFT;
+ }
+
+ if self.current_x == self.x_count - 1 {
+ edge_flags |= EdgeAaSegmentMask::RIGHT;
+ }
+
+ let repetition = Repetition {
+ origin: self.current_origin,
+ edge_flags,
+ };
+
+ self.current_origin.x += self.stride.width;
+ self.current_x += 1;
+
+ Some(repetition)
+ }
+}
+
+pub fn repetitions(
+ prim_rect: &LayoutRect,
+ visible_rect: &LayoutRect,
+ stride: LayoutSize,
+) -> RepetitionIterator {
+ assert!(stride.width > 0.0);
+ assert!(stride.height > 0.0);
+
+ let visible_rect = match prim_rect.intersection(&visible_rect) {
+ Some(rect) => rect,
+ None => {
+ return RepetitionIterator {
+ current_origin: LayoutPoint::zero(),
+ initial_origin: LayoutPoint::zero(),
+ current_x: 0,
+ current_y: 0,
+ x_count: 0,
+ y_count: 0,
+ stride,
+ row_flags: EdgeAaSegmentMask::empty(),
+ }
+ }
+ };
+
+ let nx = if visible_rect.origin.x > prim_rect.origin.x {
+ f32::floor((visible_rect.origin.x - prim_rect.origin.x) / stride.width)
+ } else {
+ 0.0
+ };
+
+ let ny = if visible_rect.origin.y > prim_rect.origin.y {
+ f32::floor((visible_rect.origin.y - prim_rect.origin.y) / stride.height)
+ } else {
+ 0.0
+ };
+
+ let x0 = prim_rect.origin.x + nx * stride.width;
+ let y0 = prim_rect.origin.y + ny * stride.height;
+
+ let x_most = visible_rect.max_x();
+ let y_most = visible_rect.max_y();
+
+ let x_count = f32::ceil((x_most - x0) / stride.width) as i32;
+ let y_count = f32::ceil((y_most - y0) / stride.height) as i32;
+
+ let mut row_flags = EdgeAaSegmentMask::TOP;
+ if y_count == 1 {
+ row_flags |= EdgeAaSegmentMask::BOTTOM;
+ }
+
+ RepetitionIterator {
+ current_origin: LayoutPoint::new(x0, y0),
+ initial_origin: LayoutPoint::new(x0, y0),
+ current_x: 0,
+ current_y: 0,
+ x_count,
+ y_count,
+ row_flags,
+ stride,
+ }
+}
+
+#[derive(Debug)]
+pub struct Tile {
+ pub rect: LayoutRect,
+ pub offset: TileOffset,
+ pub edge_flags: EdgeAaSegmentMask,
+}
+
+#[derive(Debug)]
+pub struct TileIteratorExtent {
+ /// Range of visible tiles to iterate over in number of tiles.
+ tile_range: Range<i32>,
+ /// Range of tiles of the full image including tiles that are culled out.
+ image_tiles: Range<i32>,
+ /// Size of the first tile in layout space.
+ first_tile_layout_size: f32,
+ /// Size of the last tile in layout space.
+ last_tile_layout_size: f32,
+ /// Position of blob point (0, 0) in layout space.
+ layout_tiling_origin: f32,
+ /// Position of the top-left corner of the primitive rect in layout space.
+ layout_prim_start: f32,
+}
+
+#[derive(Debug)]
+pub struct TileIterator {
+ current_tile: TileOffset,
+ x: TileIteratorExtent,
+ y: TileIteratorExtent,
+ regular_tile_size: LayoutSize,
+}
+
+impl Iterator for TileIterator {
+ type Item = Tile;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ // If we reach the end of a row, reset to the beginning of the next row.
+ if self.current_tile.x >= self.x.tile_range.end {
+ self.current_tile.y += 1;
+ self.current_tile.x = self.x.tile_range.start;
+ }
+
+ // Stop iterating if we reach the last tile. We may start here if there
+ // were no tiles to iterate over.
+ if self.current_tile.x >= self.x.tile_range.end || self.current_tile.y >= self.y.tile_range.end {
+ return None;
+ }
+
+ let tile_offset = self.current_tile;
+
+ let mut segment_rect = LayoutRect {
+ origin: LayoutPoint::new(
+ self.x.layout_tiling_origin + tile_offset.x as f32 * self.regular_tile_size.width,
+ self.y.layout_tiling_origin + tile_offset.y as f32 * self.regular_tile_size.height,
+ ),
+ size: self.regular_tile_size,
+ };
+
+ let mut edge_flags = EdgeAaSegmentMask::empty();
+
+ if tile_offset.x == self.x.image_tiles.start {
+ edge_flags |= EdgeAaSegmentMask::LEFT;
+ segment_rect.size.width = self.x.first_tile_layout_size;
+ segment_rect.origin.x = self.x.layout_prim_start;
+ }
+ if tile_offset.x == self.x.image_tiles.end - 1 {
+ edge_flags |= EdgeAaSegmentMask::RIGHT;
+ segment_rect.size.width = self.x.last_tile_layout_size;
+ }
+
+ if tile_offset.y == self.y.image_tiles.start {
+ segment_rect.size.height = self.y.first_tile_layout_size;
+ segment_rect.origin.y = self.y.layout_prim_start;
+ edge_flags |= EdgeAaSegmentMask::TOP;
+ }
+ if tile_offset.y == self.y.image_tiles.end - 1 {
+ segment_rect.size.height = self.y.last_tile_layout_size;
+ edge_flags |= EdgeAaSegmentMask::BOTTOM;
+ }
+
+ assert!(tile_offset.y < self.y.tile_range.end);
+ let tile = Tile {
+ rect: segment_rect,
+ offset: tile_offset,
+ edge_flags,
+ };
+
+ self.current_tile.x += 1;
+
+ Some(tile)
+ }
+}
+
+pub fn tiles(
+ prim_rect: &LayoutRect,
+ visible_rect: &LayoutRect,
+ image_rect: &DeviceIntRect,
+ device_tile_size: i32,
+) -> TileIterator {
+ // The image resource is tiled. We have to generate an image primitive
+ // for each tile.
+ // We need to do this because the image is broken up into smaller tiles in the texture
+ // cache and the image shader is not able to work with this type of sparse representation.
+
+ // The tiling logic works as follows:
+ //
+ // +-#################-+ -+
+ // | #//| | |//# | | image size
+ // | #//| | |//# | |
+ // +-#--+----+----+--#-+ | -+
+ // | #//| | |//# | | | regular tile size
+ // | #//| | |//# | | |
+ // +-#--+----+----+--#-+ | -+-+
+ // | #//|////|////|//# | | | "leftover" height
+ // | ################# | -+ ---+
+ // +----+----+----+----+
+ //
+ // In the ascii diagram above, a large image is split into tiles of almost regular size.
+ // The tiles on the edges (hatched in the diagram) can be smaller than the regular tiles
+ // and are handled separately in the code (we'll call them boundary tiles).
+ //
+ // Each generated segment corresponds to a tile in the texture cache, with the
+ // assumption that the boundary tiles are sized to fit their own irregular size in the
+ // texture cache.
+ //
+ // Because we can have very large virtual images we iterate over the visible portion of
+ // the image in layer space instead of iterating over all device tiles.
+
+ let visible_rect = match prim_rect.intersection(&visible_rect) {
+ Some(rect) => rect,
+ None => {
+ return TileIterator {
+ current_tile: TileOffset::zero(),
+ x: TileIteratorExtent {
+ tile_range: 0..0,
+ image_tiles: 0..0,
+ first_tile_layout_size: 0.0,
+ last_tile_layout_size: 0.0,
+ layout_tiling_origin: 0.0,
+ layout_prim_start: prim_rect.origin.x,
+ },
+ y: TileIteratorExtent {
+ tile_range: 0..0,
+ image_tiles: 0..0,
+ first_tile_layout_size: 0.0,
+ last_tile_layout_size: 0.0,
+ layout_tiling_origin: 0.0,
+ layout_prim_start: prim_rect.origin.y,
+ },
+ regular_tile_size: LayoutSize::zero(),
+ }
+ }
+ };
+
+ // Size of regular tiles in layout space.
+ let layout_tile_size = LayoutSize::new(
+ device_tile_size as f32 / image_rect.size.width as f32 * prim_rect.size.width,
+ device_tile_size as f32 / image_rect.size.height as f32 * prim_rect.size.height,
+ );
+
+ // The decomposition logic is exactly the same on each axis so we reduce
+ // this to a 1-dimensional problem in an attempt to make the code simpler.
+
+ let x_extent = tiles_1d(
+ layout_tile_size.width,
+ visible_rect.x_range(),
+ prim_rect.min_x(),
+ image_rect.x_range(),
+ device_tile_size,
+ );
+
+ let y_extent = tiles_1d(
+ layout_tile_size.height,
+ visible_rect.y_range(),
+ prim_rect.min_y(),
+ image_rect.y_range(),
+ device_tile_size,
+ );
+
+ TileIterator {
+ current_tile: point2(
+ x_extent.tile_range.start,
+ y_extent.tile_range.start,
+ ),
+ x: x_extent,
+ y: y_extent,
+ regular_tile_size: layout_tile_size,
+ }
+}
+
+/// Decompose tiles along an arbitrary axis.
+///
+/// This does most of the heavy lifting needed for `tiles` but in a single dimension for
+/// the sake of simplicity since the problem is independent on the x and y axes.
+fn tiles_1d(
+ layout_tile_size: f32,
+ layout_visible_range: Range<f32>,
+ layout_prim_start: f32,
+ device_image_range: Range<i32>,
+ device_tile_size: i32,
+) -> TileIteratorExtent {
+ // A few sanity checks.
+ debug_assert!(layout_tile_size > 0.0);
+ debug_assert!(layout_visible_range.end >= layout_visible_range.start);
+ debug_assert!(device_image_range.end > device_image_range.start);
+ debug_assert!(device_tile_size > 0);
+
+ // Sizes of the boundary tiles in pixels.
+ let first_tile_device_size = first_tile_size_1d(&device_image_range, device_tile_size);
+ let last_tile_device_size = last_tile_size_1d(&device_image_range, device_tile_size);
+
+ // [start..end[ Range of tiles of this row/column (in number of tiles) without
+ // taking culling into account.
+ let image_tiles = tile_range_1d(&device_image_range, device_tile_size);
+
+ // Layout offset of tile (0, 0) with respect to the top-left corner of the display item.
+ let layout_offset = device_image_range.start as f32 * layout_tile_size / device_tile_size as f32;
+ // Position in layout space of tile (0, 0).
+ let layout_tiling_origin = layout_prim_start - layout_offset;
+
+ // [start..end[ Range of the visible tiles (because of culling).
+ let visible_tiles_start = f32::floor((layout_visible_range.start - layout_tiling_origin) / layout_tile_size) as i32;
+ let visible_tiles_end = f32::ceil((layout_visible_range.end - layout_tiling_origin) / layout_tile_size) as i32;
+
+ // Combine the above two to get the tiles in the image that are visible this frame.
+ let mut tiles_start = i32::max(image_tiles.start, visible_tiles_start);
+ let tiles_end = i32::min(image_tiles.end, visible_tiles_end);
+ if tiles_start > tiles_end {
+ tiles_start = tiles_end;
+ }
+
+ // The size in layout space of the boundary tiles.
+ let first_tile_layout_size = if tiles_start == image_tiles.start {
+ first_tile_device_size as f32 * layout_tile_size / device_tile_size as f32
+ } else {
+ // boundary tile was culled out, so the new first tile is a regularly sized tile.
+ layout_tile_size
+ };
+
+ // Same here.
+ let last_tile_layout_size = if tiles_end == image_tiles.end {
+ last_tile_device_size as f32 * layout_tile_size / device_tile_size as f32
+ } else {
+ layout_tile_size
+ };
+
+ TileIteratorExtent {
+ tile_range: tiles_start..tiles_end,
+ image_tiles,
+ first_tile_layout_size,
+ last_tile_layout_size,
+ layout_tiling_origin,
+ layout_prim_start,
+ }
+}
+
+/// Compute the range of tiles (in number of tiles) that intersect the provided
+/// image range (in pixels) in an arbitrary dimension.
+///
+/// ```ignore
+///
+/// 0
+/// :
+/// #-+---+---+---+---+---+--#
+/// # | | | | | | #
+/// #-+---+---+---+---+---+--#
+/// ^ : ^
+///
+/// +------------------------+ image_range
+/// +---+ regular_tile_size
+///
+/// ```
+fn tile_range_1d(
+ image_range: &Range<i32>,
+ regular_tile_size: i32,
+) -> Range<i32> {
+ // Integer division truncates towards zero so with negative values if the first/last
+ // tile isn't a full tile we can get offset by one which we account for here.
+
+ let mut start = image_range.start / regular_tile_size;
+ if image_range.start % regular_tile_size < 0 {
+ start -= 1;
+ }
+
+ let mut end = image_range.end / regular_tile_size;
+ if image_range.end % regular_tile_size > 0 {
+ end += 1;
+ }
+
+ start..end
+}
+
+// Sizes of the first boundary tile in pixels.
+//
+// It can be smaller than the regular tile size if the image is not a multiple
+// of the regular tile size.
+fn first_tile_size_1d(
+ image_range: &Range<i32>,
+ regular_tile_size: i32,
+) -> i32 {
+ // We have to account for how the % operation behaves for negative values.
+ let image_size = image_range.end - image_range.start;
+ i32::min(
+ match image_range.start % regular_tile_size {
+ // . #------+------+ .
+ // . #//////| | .
+ 0 => regular_tile_size,
+ // (zero) -> 0 . #--+------+ .
+ // . . #//| | .
+ // %(m): ~~>
+ m if m > 0 => regular_tile_size - m,
+ // . . #--+------+ 0 <- (zero)
+ // . . #//| | .
+ // %(m): <~~
+ m => -m,
+ },
+ image_size
+ )
+}
+
+// Sizes of the last boundary tile in pixels.
+//
+// It can be smaller than the regular tile size if the image is not a multiple
+// of the regular tile size.
+fn last_tile_size_1d(
+ image_range: &Range<i32>,
+ regular_tile_size: i32,
+) -> i32 {
+ // We have to account for how the modulo operation behaves for negative values.
+ let image_size = image_range.end - image_range.start;
+ i32::min(
+ match image_range.end % regular_tile_size {
+ // +------+------# .
+ // tiles: . | |//////# .
+ 0 => regular_tile_size,
+ // . +------+--# . 0 <- (zero)
+ // . | |//# . .
+ // modulo (m): <~~
+ m if m < 0 => regular_tile_size + m,
+ // (zero) -> 0 +------+--# . .
+ // . | |//# . .
+ // modulo (m): ~~>
+ m => m,
+ },
+ image_size,
+ )
+}
+
+pub fn compute_tile_rect(
+ image_rect: &DeviceIntRect,
+ regular_tile_size: TileSize,
+ tile: TileOffset,
+) -> DeviceIntRect {
+ let regular_tile_size = regular_tile_size as i32;
+ DeviceIntRect {
+ origin: point2(
+ compute_tile_origin_1d(image_rect.x_range(), regular_tile_size, tile.x as i32),
+ compute_tile_origin_1d(image_rect.y_range(), regular_tile_size, tile.y as i32),
+ ),
+ size: size2(
+ compute_tile_size_1d(image_rect.x_range(), regular_tile_size, tile.x as i32),
+ compute_tile_size_1d(image_rect.y_range(), regular_tile_size, tile.y as i32),
+ ),
+ }
+}
+
+fn compute_tile_origin_1d(
+ img_range: Range<i32>,
+ regular_tile_size: i32,
+ tile_offset: i32,
+) -> i32 {
+ let tile_range = tile_range_1d(&img_range, regular_tile_size);
+ if tile_offset == tile_range.start {
+ img_range.start
+ } else {
+ tile_offset * regular_tile_size
+ }
+}
+
+// Compute the width and height in pixels of a tile depending on its position in the image.
+pub fn compute_tile_size(
+ image_rect: &DeviceIntRect,
+ regular_tile_size: TileSize,
+ tile: TileOffset,
+) -> DeviceIntSize {
+ let regular_tile_size = regular_tile_size as i32;
+ size2(
+ compute_tile_size_1d(image_rect.x_range(), regular_tile_size, tile.x as i32),
+ compute_tile_size_1d(image_rect.y_range(), regular_tile_size, tile.y as i32),
+ )
+}
+
+fn compute_tile_size_1d(
+ img_range: Range<i32>,
+ regular_tile_size: i32,
+ tile_offset: i32,
+) -> i32 {
+ let tile_range = tile_range_1d(&img_range, regular_tile_size);
+
+ // Most tiles are going to have base_size as width and height,
+ // except for tiles around the edges that are shrunk to fit the image data.
+ let actual_size = if tile_offset == tile_range.start {
+ first_tile_size_1d(&img_range, regular_tile_size)
+ } else if tile_offset == tile_range.end - 1 {
+ last_tile_size_1d(&img_range, regular_tile_size)
+ } else {
+ regular_tile_size
+ };
+
+ assert!(actual_size > 0);
+
+ actual_size
+}
+
+pub fn compute_tile_range(
+ visible_area: &DeviceIntRect,
+ tile_size: u16,
+) -> TileRange {
+ let tile_size = tile_size as i32;
+ let x_range = tile_range_1d(&visible_area.x_range(), tile_size);
+ let y_range = tile_range_1d(&visible_area.y_range(), tile_size);
+
+ TileRange {
+ origin: point2(x_range.start, y_range.start),
+ size: size2(x_range.end - x_range.start, y_range.end - y_range.start),
+ }
+}
+
+pub fn for_each_tile_in_range(
+ range: &TileRange,
+ mut callback: impl FnMut(TileOffset),
+) {
+ for y in range.y_range() {
+ for x in range.x_range() {
+ callback(point2(x, y));
+ }
+ }
+}
+
+pub fn compute_valid_tiles_if_bounds_change(
+ prev_rect: &DeviceIntRect,
+ new_rect: &DeviceIntRect,
+ tile_size: u16,
+) -> Option<TileRange> {
+ let intersection = match prev_rect.intersection(new_rect) {
+ Some(rect) => rect,
+ None => {
+ return Some(TileRange::zero());
+ }
+ };
+
+ let left = prev_rect.min_x() != new_rect.min_x();
+ let right = prev_rect.max_x() != new_rect.max_x();
+ let top = prev_rect.min_y() != new_rect.min_y();
+ let bottom = prev_rect.max_y() != new_rect.max_y();
+
+ if !left && !right && !top && !bottom {
+ // Bounds have not changed.
+ return None;
+ }
+
+ let tw = 1.0 / (tile_size as f32);
+ let th = 1.0 / (tile_size as f32);
+
+ let tiles = intersection
+ .cast::<f32>()
+ .scale(tw, th);
+
+ let min_x = if left { f32::ceil(tiles.min_x()) } else { f32::floor(tiles.min_x()) };
+ let min_y = if top { f32::ceil(tiles.min_y()) } else { f32::floor(tiles.min_y()) };
+ let max_x = if right { f32::floor(tiles.max_x()) } else { f32::ceil(tiles.max_x()) };
+ let max_y = if bottom { f32::floor(tiles.max_y()) } else { f32::ceil(tiles.max_y()) };
+
+ Some(TileRange {
+ origin: point2(min_x as i32, min_y as i32),
+ size: size2((max_x - min_x) as i32, (max_y - min_y) as i32),
+ })
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use std::collections::HashSet;
+ use euclid::rect;
+
+ // this checks some additional invariants
+ fn checked_for_each_tile(
+ prim_rect: &LayoutRect,
+ visible_rect: &LayoutRect,
+ device_image_rect: &DeviceIntRect,
+ device_tile_size: i32,
+ callback: &mut dyn FnMut(&LayoutRect, TileOffset, EdgeAaSegmentMask),
+ ) {
+ let mut coverage = LayoutRect::zero();
+ let mut seen_tiles = HashSet::new();
+ for tile in tiles(
+ prim_rect,
+ visible_rect,
+ device_image_rect,
+ device_tile_size,
+ ) {
+ // make sure we don't get sent duplicate tiles
+ assert!(!seen_tiles.contains(&tile.offset));
+ seen_tiles.insert(tile.offset);
+ coverage = coverage.union(&tile.rect);
+ assert!(prim_rect.contains_rect(&tile.rect));
+ callback(&tile.rect, tile.offset, tile.edge_flags);
+ }
+ assert!(prim_rect.contains_rect(&coverage));
+ assert!(coverage.contains_rect(&visible_rect.intersection(&prim_rect).unwrap_or(LayoutRect::zero())));
+ }
+
+ #[test]
+ fn basic() {
+ let mut count = 0;
+ checked_for_each_tile(&rect(0., 0., 1000., 1000.),
+ &rect(75., 75., 400., 400.),
+ &rect(0, 0, 400, 400),
+ 36,
+ &mut |_tile_rect, _tile_offset, _tile_flags| {
+ count += 1;
+ },
+ );
+ assert_eq!(count, 36);
+ }
+
+ #[test]
+ fn empty() {
+ let mut count = 0;
+ checked_for_each_tile(&rect(0., 0., 74., 74.),
+ &rect(75., 75., 400., 400.),
+ &rect(0, 0, 400, 400),
+ 36,
+ &mut |_tile_rect, _tile_offset, _tile_flags| {
+ count += 1;
+ },
+ );
+ assert_eq!(count, 0);
+ }
+
+ #[test]
+ fn test_tiles_1d() {
+ // Exactly one full tile at positive offset.
+ let result = tiles_1d(64.0, -10000.0..10000.0, 0.0, 0..64, 64);
+ assert_eq!(result.tile_range.start, 0);
+ assert_eq!(result.tile_range.end, 1);
+ assert_eq!(result.first_tile_layout_size, 64.0);
+ assert_eq!(result.last_tile_layout_size, 64.0);
+
+ // Exactly one full tile at negative offset.
+ let result = tiles_1d(64.0, -10000.0..10000.0, -64.0, -64..0, 64);
+ assert_eq!(result.tile_range.start, -1);
+ assert_eq!(result.tile_range.end, 0);
+ assert_eq!(result.first_tile_layout_size, 64.0);
+ assert_eq!(result.last_tile_layout_size, 64.0);
+
+ // Two full tiles at negative and positive offsets.
+ let result = tiles_1d(64.0, -10000.0..10000.0, -64.0, -64..64, 64);
+ assert_eq!(result.tile_range.start, -1);
+ assert_eq!(result.tile_range.end, 1);
+ assert_eq!(result.first_tile_layout_size, 64.0);
+ assert_eq!(result.last_tile_layout_size, 64.0);
+
+ // One partial tile at positive offset, non-zero origin, culled out.
+ let result = tiles_1d(64.0, -100.0..10.0, 64.0, 64..310, 64);
+ assert_eq!(result.tile_range.start, result.tile_range.end);
+
+ // Two tiles at negative and positive offsets, one of which is culled out.
+ // The remaining tile is partially culled but it should still generate a full tile.
+ let result = tiles_1d(64.0, 10.0..10000.0, -64.0, -64..64, 64);
+ assert_eq!(result.tile_range.start, 0);
+ assert_eq!(result.tile_range.end, 1);
+ assert_eq!(result.first_tile_layout_size, 64.0);
+ assert_eq!(result.last_tile_layout_size, 64.0);
+ let result = tiles_1d(64.0, -10000.0..-10.0, -64.0, -64..64, 64);
+ assert_eq!(result.tile_range.start, -1);
+ assert_eq!(result.tile_range.end, 0);
+ assert_eq!(result.first_tile_layout_size, 64.0);
+ assert_eq!(result.last_tile_layout_size, 64.0);
+
+ // Stretched tile in layout space device tile size is 64 and layout tile size is 128.
+ // So the resulting tile sizes in layout space should be multiplied by two.
+ let result = tiles_1d(128.0, -10000.0..10000.0, -64.0, -64..32, 64);
+ assert_eq!(result.tile_range.start, -1);
+ assert_eq!(result.tile_range.end, 1);
+ assert_eq!(result.first_tile_layout_size, 128.0);
+ assert_eq!(result.last_tile_layout_size, 64.0);
+
+ // Two visible tiles (the rest is culled out).
+ let result = tiles_1d(10.0, 0.0..20.0, 0.0, 0..64, 64);
+ assert_eq!(result.tile_range.start, 0);
+ assert_eq!(result.tile_range.end, 1);
+ assert_eq!(result.first_tile_layout_size, 10.0);
+ assert_eq!(result.last_tile_layout_size, 10.0);
+
+ // Two visible tiles at negative layout offsets (the rest is culled out).
+ let result = tiles_1d(10.0, -20.0..0.0, -20.0, 0..64, 64);
+ assert_eq!(result.tile_range.start, 0);
+ assert_eq!(result.tile_range.end, 1);
+ assert_eq!(result.first_tile_layout_size, 10.0);
+ assert_eq!(result.last_tile_layout_size, 10.0);
+ }
+
+ #[test]
+ fn test_tile_range_1d() {
+ assert_eq!(tile_range_1d(&(0..256), 256), 0..1);
+ assert_eq!(tile_range_1d(&(0..257), 256), 0..2);
+ assert_eq!(tile_range_1d(&(-1..257), 256), -1..2);
+ assert_eq!(tile_range_1d(&(-256..256), 256), -1..1);
+ assert_eq!(tile_range_1d(&(-20..-10), 6), -4..-1);
+ assert_eq!(tile_range_1d(&(20..100), 256), 0..1);
+ }
+
+ #[test]
+ fn test_first_last_tile_size_1d() {
+ assert_eq!(first_tile_size_1d(&(0..10), 64), 10);
+ assert_eq!(first_tile_size_1d(&(-20..0), 64), 20);
+
+ assert_eq!(last_tile_size_1d(&(0..10), 64), 10);
+ assert_eq!(last_tile_size_1d(&(-20..0), 64), 20);
+ }
+
+ #[test]
+ fn doubly_partial_tiles() {
+ // In the following tests the image is a single tile and none of the sides of the tile
+ // align with the tile grid.
+ // This can only happen when we have a single non-aligned partial tile and no regular
+ // tiles.
+ assert_eq!(first_tile_size_1d(&(300..310), 64), 10);
+ assert_eq!(first_tile_size_1d(&(-20..-10), 64), 10);
+
+ assert_eq!(last_tile_size_1d(&(300..310), 64), 10);
+ assert_eq!(last_tile_size_1d(&(-20..-10), 64), 10);
+
+
+ // One partial tile at positve offset, non-zero origin.
+ let result = tiles_1d(64.0, -10000.0..10000.0, 0.0, 300..310, 64);
+ assert_eq!(result.tile_range.start, 4);
+ assert_eq!(result.tile_range.end, 5);
+ assert_eq!(result.first_tile_layout_size, 10.0);
+ assert_eq!(result.last_tile_layout_size, 10.0);
+ }
+
+ #[test]
+ fn smaller_than_tile_size_at_origin() {
+ let r = compute_tile_rect(
+ &rect(0, 0, 80, 80),
+ 256,
+ point2(0, 0),
+ );
+
+ assert_eq!(r, rect(0, 0, 80, 80));
+ }
+
+ #[test]
+ fn smaller_than_tile_size_with_offset() {
+ let r = compute_tile_rect(
+ &rect(20, 20, 80, 80),
+ 256,
+ point2(0, 0),
+ );
+
+ assert_eq!(r, rect(20, 20, 80, 80));
+ }
+}
diff --git a/third_party/webrender/webrender_api/src/lib.rs b/third_party/webrender/webrender_api/src/lib.rs
index f5cda1fbf65..848f4740c90 100644
--- a/third_party/webrender/webrender_api/src/lib.rs
+++ b/third_party/webrender/webrender_api/src/lib.rs
@@ -15,21 +15,25 @@
#![cfg_attr(feature = "cargo-clippy", allow(clippy::float_cmp, clippy::too_many_arguments))]
#![cfg_attr(feature = "cargo-clippy", allow(clippy::unreadable_literal, clippy::new_without_default))]
-pub use crossbeam_channel;
-pub use euclid;
-
#[macro_use]
extern crate bitflags;
#[cfg(feature = "nightly")]
-extern crate core;
+use core;
+#[cfg(target_os = "macos")]
+use core_foundation;
+#[cfg(target_os = "macos")]
+use core_graphics;
+#[macro_use]
+extern crate derive_more;
+pub use euclid;
#[macro_use]
extern crate malloc_size_of_derive;
#[macro_use]
extern crate serde_derive;
use malloc_size_of;
-use peek_poke;
+mod api;
pub mod channel;
mod color;
mod display_item;
@@ -38,8 +42,13 @@ mod display_list;
mod font;
mod gradient_builder;
mod image;
+mod resources;
pub mod units;
+#[doc(hidden)]
+pub mod image_tiling;
+
+pub use crate::api::*;
pub use crate::color::*;
pub use crate::display_item::*;
pub use crate::display_item_cache::DisplayItemCache;
@@ -47,619 +56,4 @@ pub use crate::display_list::*;
pub use crate::font::*;
pub use crate::gradient_builder::*;
pub use crate::image::*;
-
-use crate::units::*;
-use crate::channel::Receiver;
-use std::marker::PhantomData;
-use std::sync::Arc;
-use std::os::raw::c_void;
-use peek_poke::PeekPoke;
-
-/// Width and height in device pixels of image tiles.
-pub type TileSize = u16;
-
-/// Various settings that the caller can select based on desired tradeoffs
-/// between rendering quality and performance / power usage.
-#[derive(Copy, Clone, Deserialize, Serialize)]
-pub struct QualitySettings {
- /// If true, disable creating separate picture cache slices when the
- /// scroll root changes. This gives maximum opportunity to find an
- /// opaque background, which enables subpixel AA. However, it is
- /// usually significantly more expensive to render when scrolling.
- pub force_subpixel_aa_where_possible: bool,
-}
-
-impl Default for QualitySettings {
- fn default() -> Self {
- QualitySettings {
- // Prefer performance over maximum subpixel AA quality, since WR
- // already enables subpixel AA in more situations than other browsers.
- force_subpixel_aa_where_possible: false,
- }
- }
-}
-
-/// An epoch identifies the state of a pipeline in time.
-///
-/// This is mostly used as a synchronization mechanism to observe how/when particular pipeline
-/// updates propagate through WebRender and are applied at various stages.
-#[repr(C)]
-#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
-pub struct Epoch(pub u32);
-
-impl Epoch {
- /// Magic invalid epoch value.
- pub fn invalid() -> Epoch {
- Epoch(u32::MAX)
- }
-}
-
-/// ID namespaces uniquely identify different users of WebRender's API.
-///
-/// For example in Gecko each content process uses a separate id namespace.
-#[repr(C)]
-#[derive(Clone, Copy, Debug, Default, Eq, MallocSizeOf, PartialEq, Hash, Ord, PartialOrd, PeekPoke)]
-#[derive(Deserialize, Serialize)]
-pub struct IdNamespace(pub u32);
-
-/// A key uniquely identifying a WebRender document.
-///
-/// Instances can manage one or several documents (using the same render backend thread).
-/// Each document will internally correspond to a single scene, and scenes are made of
-/// one or several pipelines.
-#[repr(C)]
-#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
-pub struct DocumentId {
- ///
- pub namespace_id: IdNamespace,
- ///
- pub id: u32,
-}
-
-impl DocumentId {
- ///
- pub fn new(namespace_id: IdNamespace, id: u32) -> Self {
- DocumentId {
- namespace_id,
- id,
- }
- }
-
- ///
- pub const INVALID: DocumentId = DocumentId { namespace_id: IdNamespace(0), id: 0 };
-}
-
-/// This type carries no valuable semantics for WR. However, it reflects the fact that
-/// clients (Servo) may generate pipelines by different semi-independent sources.
-/// These pipelines still belong to the same `IdNamespace` and the same `DocumentId`.
-/// Having this extra Id field enables them to generate `PipelineId` without collision.
-pub type PipelineSourceId = u32;
-
-/// From the point of view of WR, `PipelineId` is completely opaque and generic as long as
-/// it's clonable, serializable, comparable, and hashable.
-#[repr(C)]
-#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
-pub struct PipelineId(pub PipelineSourceId, pub u32);
-
-impl Default for PipelineId {
- fn default() -> Self {
- PipelineId::dummy()
- }
-}
-
-impl PipelineId {
- ///
- pub fn dummy() -> Self {
- PipelineId(!0, !0)
- }
-}
-
-
-/// An opaque pointer-sized value.
-#[repr(C)]
-#[derive(Clone)]
-pub struct ExternalEvent {
- raw: usize,
-}
-
-unsafe impl Send for ExternalEvent {}
-
-impl ExternalEvent {
- /// Creates the event from an opaque pointer-sized value.
- pub fn from_raw(raw: usize) -> Self {
- ExternalEvent { raw }
- }
- /// Consumes self to make it obvious that the event should be forwarded only once.
- pub fn unwrap(self) -> usize {
- self.raw
- }
-}
-
-/// Describe whether or not scrolling should be clamped by the content bounds.
-#[derive(Clone, Deserialize, Serialize)]
-pub enum ScrollClamping {
- ///
- ToContentBounds,
- ///
- NoClamping,
-}
-
-/// A handler to integrate WebRender with the thread that contains the `Renderer`.
-pub trait RenderNotifier: Send {
- ///
- fn clone(&self) -> Box<dyn RenderNotifier>;
- /// Wake the thread containing the `Renderer` up (after updates have been put
- /// in the renderer's queue).
- fn wake_up(
- &self,
- composite_needed: bool,
- );
- /// Notify the thread containing the `Renderer` that a new frame is ready.
- fn new_frame_ready(&self, _: DocumentId, scrolled: bool, composite_needed: bool, render_time_ns: Option<u64>);
- /// A Gecko-specific notification mechanism to get some code executed on the
- /// `Renderer`'s thread, mostly replaced by `NotificationHandler`. You should
- /// probably use the latter instead.
- fn external_event(&self, _evt: ExternalEvent) {
- unimplemented!()
- }
- /// Notify the thread containing the `Renderer` that the render backend has been
- /// shut down.
- fn shut_down(&self) {}
-}
-
-/// A stage of the rendering pipeline.
-#[repr(u32)]
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub enum Checkpoint {
- ///
- SceneBuilt,
- ///
- FrameBuilt,
- ///
- FrameTexturesUpdated,
- ///
- FrameRendered,
- /// NotificationRequests get notified with this if they get dropped without having been
- /// notified. This provides the guarantee that if a request is created it will get notified.
- TransactionDropped,
-}
-
-/// A handler to notify when a transaction reaches certain stages of the rendering
-/// pipeline.
-pub trait NotificationHandler : Send + Sync {
- /// Entry point of the handler to implement. Invoked by WebRender.
- fn notify(&self, when: Checkpoint);
-}
-
-/// A request to notify a handler when the transaction reaches certain stages of the
-/// rendering pipeline.
-///
-/// The request is guaranteed to be notified once and only once, even if the transaction
-/// is dropped before the requested check-point.
-pub struct NotificationRequest {
- handler: Option<Box<dyn NotificationHandler>>,
- when: Checkpoint,
-}
-
-impl NotificationRequest {
- /// Constructor.
- pub fn new(when: Checkpoint, handler: Box<dyn NotificationHandler>) -> Self {
- NotificationRequest {
- handler: Some(handler),
- when,
- }
- }
-
- /// The specified stage at which point the handler should be notified.
- pub fn when(&self) -> Checkpoint { self.when }
-
- /// Called by WebRender at specified stages to notify the registered handler.
- pub fn notify(mut self) {
- if let Some(handler) = self.handler.take() {
- handler.notify(self.when);
- }
- }
-}
-
-/// An object that can perform hit-testing without doing synchronous queries to
-/// the RenderBackendThread.
-pub trait ApiHitTester: Send + Sync {
- /// Does a hit test on display items in the specified document, at the given
- /// point. If a pipeline_id is specified, it is used to further restrict the
- /// hit results so that only items inside that pipeline are matched. The vector
- /// of hit results will contain all display items that match, ordered from
- /// front to back.
- fn hit_test(&self, pipeline_id: Option<PipelineId>, point: WorldPoint, flags: HitTestFlags) -> HitTestResult;
-}
-
-/// A hit tester requested to the render backend thread but not necessarily ready yet.
-///
-/// The request should be resolved as late as possible to reduce the likelihood of blocking.
-pub struct HitTesterRequest {
- #[doc(hidden)]
- pub rx: Receiver<Arc<dyn ApiHitTester>>,
-}
-
-impl HitTesterRequest {
- /// Block until the hit tester is available and return it, consuming teh request.
- pub fn resolve(self) -> Arc<dyn ApiHitTester> {
- self.rx.recv().unwrap()
- }
-}
-
-/// Describe an item that matched a hit-test query.
-#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
-pub struct HitTestItem {
- /// The pipeline that the display item that was hit belongs to.
- pub pipeline: PipelineId,
-
- /// The tag of the hit display item.
- pub tag: ItemTag,
-
- /// The hit point in the coordinate space of the "viewport" of the display item. The
- /// viewport is the scroll node formed by the root reference frame of the display item's
- /// pipeline.
- pub point_in_viewport: LayoutPoint,
-
- /// The coordinates of the original hit test point relative to the origin of this item.
- /// This is useful for calculating things like text offsets in the client.
- pub point_relative_to_item: LayoutPoint,
-}
-
-/// Returned by `RenderApi::hit_test`.
-#[derive(Clone, Debug, Default, Deserialize, Serialize)]
-pub struct HitTestResult {
- /// List of items that are match the hit-test query.
- pub items: Vec<HitTestItem>,
-}
-
-bitflags! {
- #[derive(Deserialize, MallocSizeOf, Serialize)]
- ///
- pub struct HitTestFlags: u8 {
- ///
- const FIND_ALL = 0b00000001;
- ///
- const POINT_RELATIVE_TO_PIPELINE_VIEWPORT = 0b00000010;
- }
-}
-
-impl Drop for NotificationRequest {
- fn drop(&mut self) {
- if let Some(ref mut handler) = self.handler {
- handler.notify(Checkpoint::TransactionDropped);
- }
- }
-}
-
-// This Clone impl yields an "empty" request because we don't want the requests
-// to be notified twice so the request is owned by only one of the API messages
-// (the original one) after the clone.
-// This works in practice because the notifications requests are used for
-// synchronization so we don't need to include them in the recording mechanism
-// in wrench that clones the messages.
-impl Clone for NotificationRequest {
- fn clone(&self) -> Self {
- NotificationRequest {
- when: self.when,
- handler: None,
- }
- }
-}
-
-
-/// A key to identify an animated property binding.
-#[repr(C)]
-#[derive(Clone, Copy, Debug, Default, Deserialize, MallocSizeOf, PartialEq, Serialize, Eq, Hash, PeekPoke)]
-pub struct PropertyBindingId {
- pub namespace: IdNamespace,
- pub uid: u32,
-}
-
-impl PropertyBindingId {
- /// Constructor.
- pub fn new(value: u64) -> Self {
- PropertyBindingId {
- namespace: IdNamespace((value >> 32) as u32),
- uid: value as u32,
- }
- }
-}
-
-/// A unique key that is used for connecting animated property
-/// values to bindings in the display list.
-#[repr(C)]
-#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
-pub struct PropertyBindingKey<T> {
- ///
- pub id: PropertyBindingId,
- #[doc(hidden)]
- pub _phantom: PhantomData<T>,
-}
-
-/// Construct a property value from a given key and value.
-impl<T: Copy> PropertyBindingKey<T> {
- ///
- pub fn with(self, value: T) -> PropertyValue<T> {
- PropertyValue { key: self, value }
- }
-}
-
-impl<T> PropertyBindingKey<T> {
- /// Constructor.
- pub fn new(value: u64) -> Self {
- PropertyBindingKey {
- id: PropertyBindingId::new(value),
- _phantom: PhantomData,
- }
- }
-}
-
-/// A binding property can either be a specific value
-/// (the normal, non-animated case) or point to a binding location
-/// to fetch the current value from.
-/// Note that Binding has also a non-animated value, the value is
-/// used for the case where the animation is still in-delay phase
-/// (i.e. the animation doesn't produce any animation values).
-#[repr(C)]
-#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
-pub enum PropertyBinding<T> {
- /// Non-animated value.
- Value(T),
- /// Animated binding.
- Binding(PropertyBindingKey<T>, T),
-}
-
-impl<T: Default> Default for PropertyBinding<T> {
- fn default() -> Self {
- PropertyBinding::Value(Default::default())
- }
-}
-
-impl<T> From<T> for PropertyBinding<T> {
- fn from(value: T) -> PropertyBinding<T> {
- PropertyBinding::Value(value)
- }
-}
-
-impl From<PropertyBindingKey<ColorF>> for PropertyBindingKey<ColorU> {
- fn from(key: PropertyBindingKey<ColorF>) -> PropertyBindingKey<ColorU> {
- PropertyBindingKey {
- id: key.id.clone(),
- _phantom: PhantomData,
- }
- }
-}
-
-impl From<PropertyBindingKey<ColorU>> for PropertyBindingKey<ColorF> {
- fn from(key: PropertyBindingKey<ColorU>) -> PropertyBindingKey<ColorF> {
- PropertyBindingKey {
- id: key.id.clone(),
- _phantom: PhantomData,
- }
- }
-}
-
-impl From<PropertyBinding<ColorF>> for PropertyBinding<ColorU> {
- fn from(value: PropertyBinding<ColorF>) -> PropertyBinding<ColorU> {
- match value {
- PropertyBinding::Value(value) => PropertyBinding::Value(value.into()),
- PropertyBinding::Binding(k, v) => {
- PropertyBinding::Binding(k.into(), v.into())
- }
- }
- }
-}
-
-impl From<PropertyBinding<ColorU>> for PropertyBinding<ColorF> {
- fn from(value: PropertyBinding<ColorU>) -> PropertyBinding<ColorF> {
- match value {
- PropertyBinding::Value(value) => PropertyBinding::Value(value.into()),
- PropertyBinding::Binding(k, v) => {
- PropertyBinding::Binding(k.into(), v.into())
- }
- }
- }
-}
-
-/// The current value of an animated property. This is
-/// supplied by the calling code.
-#[derive(Clone, Copy, Debug, Deserialize, Serialize, PartialEq)]
-pub struct PropertyValue<T> {
- ///
- pub key: PropertyBindingKey<T>,
- ///
- pub value: T,
-}
-
-/// When using `generate_frame()`, a list of `PropertyValue` structures
-/// can optionally be supplied to provide the current value of any
-/// animated properties.
-#[derive(Clone, Deserialize, Serialize, Debug, PartialEq, Default)]
-pub struct DynamicProperties {
- ///
- pub transforms: Vec<PropertyValue<LayoutTransform>>,
- /// opacity
- pub floats: Vec<PropertyValue<f32>>,
- /// background color
- pub colors: Vec<PropertyValue<ColorF>>,
-}
-
-/// A C function that takes a pointer to a heap allocation and returns its size.
-///
-/// This is borrowed from the malloc_size_of crate, upon which we want to avoid
-/// a dependency from WebRender.
-pub type VoidPtrToSizeFn = unsafe extern "C" fn(ptr: *const c_void) -> usize;
-
-bitflags! {
- /// Flags to enable/disable various builtin debugging tools.
- #[repr(C)]
- #[derive(Default, Deserialize, MallocSizeOf, Serialize)]
- pub struct DebugFlags: u32 {
- /// Display the frame profiler on screen.
- const PROFILER_DBG = 1 << 0;
- /// Display intermediate render targets on screen.
- const RENDER_TARGET_DBG = 1 << 1;
- /// Display all texture cache pages on screen.
- const TEXTURE_CACHE_DBG = 1 << 2;
- /// Display GPU timing results.
- const GPU_TIME_QUERIES = 1 << 3;
- /// Query the number of pixels that pass the depth test divided and show it
- /// in the profiler as a percentage of the number of pixels in the screen
- /// (window width times height).
- const GPU_SAMPLE_QUERIES = 1 << 4;
- /// Render each quad with their own draw call.
- ///
- /// Terrible for performance but can help with understanding the drawing
- /// order when inspecting renderdoc or apitrace recordings.
- const DISABLE_BATCHING = 1 << 5;
- /// Display the pipeline epochs.
- const EPOCHS = 1 << 6;
- /// Print driver messages to stdout.
- const ECHO_DRIVER_MESSAGES = 1 << 7;
- /// Show an overlay displaying overdraw amount.
- const SHOW_OVERDRAW = 1 << 8;
- /// Display the contents of GPU cache.
- const GPU_CACHE_DBG = 1 << 9;
- /// Clear evicted parts of the texture cache for debugging purposes.
- const TEXTURE_CACHE_DBG_CLEAR_EVICTED = 1 << 10;
- /// Show picture caching debug overlay
- const PICTURE_CACHING_DBG = 1 << 11;
- /// Highlight all primitives with colors based on kind.
- const PRIMITIVE_DBG = 1 << 12;
- /// Draw a zoom widget showing part of the framebuffer zoomed in.
- const ZOOM_DBG = 1 << 13;
- /// Scale the debug renderer down for a smaller screen. This will disrupt
- /// any mapping between debug display items and page content, so shouldn't
- /// be used with overlays like the picture caching or primitive display.
- const SMALL_SCREEN = 1 << 14;
- /// Disable various bits of the WebRender pipeline, to help narrow
- /// down where slowness might be coming from.
- const DISABLE_OPAQUE_PASS = 1 << 15;
- ///
- const DISABLE_ALPHA_PASS = 1 << 16;
- ///
- const DISABLE_CLIP_MASKS = 1 << 17;
- ///
- const DISABLE_TEXT_PRIMS = 1 << 18;
- ///
- const DISABLE_GRADIENT_PRIMS = 1 << 19;
- ///
- const OBSCURE_IMAGES = 1 << 20;
- /// Taint the transparent area of the glyphs with a random opacity to easily
- /// see when glyphs are re-rasterized.
- const GLYPH_FLASHING = 1 << 21;
- /// The profiler only displays information that is out of the ordinary.
- const SMART_PROFILER = 1 << 22;
- /// If set, dump picture cache invalidation debug to console.
- const INVALIDATION_DBG = 1 << 23;
- /// Log tile cache to memory for later saving as part of wr-capture
- const TILE_CACHE_LOGGING_DBG = 1 << 24;
- /// Collect and dump profiler statistics to captures.
- const PROFILER_CAPTURE = (1 as u32) << 25; // need "as u32" until we have cbindgen#556
- /// Invalidate picture tiles every frames (useful when inspecting GPU work in external tools).
- const FORCE_PICTURE_INVALIDATION = (1 as u32) << 26;
- const USE_BATCHED_TEXTURE_UPLOADS = (1 as u32) << 27;
- const USE_DRAW_CALLS_FOR_TEXTURE_COPY = (1 as u32) << 28;
- }
-}
-
-/// Information specific to a primitive type that
-/// uniquely identifies a primitive template by key.
-#[derive(Debug, Clone, Eq, MallocSizeOf, PartialEq, Hash, Serialize, Deserialize)]
-pub enum PrimitiveKeyKind {
- /// Clear an existing rect, used for special effects on some platforms.
- Clear,
- ///
- Rectangle {
- ///
- color: PropertyBinding<ColorU>,
- },
-}
-
-///
-#[derive(Clone)]
-pub struct ScrollNodeState {
- ///
- pub id: ExternalScrollId,
- ///
- pub scroll_offset: LayoutVector2D,
-}
-
-///
-#[derive(Clone, Copy, Debug)]
-pub enum ScrollLocation {
- /// Scroll by a certain amount.
- Delta(LayoutVector2D),
- /// Scroll to very top of element.
- Start,
- /// Scroll to very bottom of element.
- End,
-}
-
-/// Represents a zoom factor.
-#[derive(Clone, Copy, Debug)]
-pub struct ZoomFactor(f32);
-
-impl ZoomFactor {
- /// Construct a new zoom factor.
- pub fn new(scale: f32) -> Self {
- ZoomFactor(scale)
- }
-
- /// Get the zoom factor as an untyped float.
- pub fn get(self) -> f32 {
- self.0
- }
-}
-
-/// Crash annotations included in crash reports.
-#[repr(C)]
-#[derive(Clone, Copy)]
-pub enum CrashAnnotation {
- CompileShader = 0,
- DrawShader = 1,
-}
-
-/// Handler to expose support for annotating crash reports.
-pub trait CrashAnnotator : Send {
- fn set(&self, annotation: CrashAnnotation, value: &std::ffi::CStr);
- fn clear(&self, annotation: CrashAnnotation);
- fn box_clone(&self) -> Box<dyn CrashAnnotator>;
-}
-
-impl Clone for Box<dyn CrashAnnotator> {
- fn clone(&self) -> Box<dyn CrashAnnotator> {
- self.box_clone()
- }
-}
-
-/// Guard to add a crash annotation at creation, and clear it at destruction.
-pub struct CrashAnnotatorGuard<'a> {
- annotator: &'a Option<Box<dyn CrashAnnotator>>,
- annotation: CrashAnnotation,
-}
-
-impl<'a> CrashAnnotatorGuard<'a> {
- pub fn new(
- annotator: &'a Option<Box<dyn CrashAnnotator>>,
- annotation: CrashAnnotation,
- value: &std::ffi::CStr,
- ) -> Self {
- if let Some(ref annotator) = annotator {
- annotator.set(annotation, value);
- }
- Self {
- annotator,
- annotation,
- }
- }
-}
-
-impl<'a> Drop for CrashAnnotatorGuard<'a> {
- fn drop(&mut self) {
- if let Some(ref annotator) = self.annotator {
- annotator.clear(self.annotation);
- }
- }
-}
+pub use crate::resources::DEFAULT_TILE_SIZE;
diff --git a/third_party/webrender/webrender_api/src/resources.rs b/third_party/webrender/webrender_api/src/resources.rs
new file mode 100644
index 00000000000..c41fdc3009e
--- /dev/null
+++ b/third_party/webrender/webrender_api/src/resources.rs
@@ -0,0 +1,327 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use crate::{BlobImageKey, ImageDescriptor, DirtyRect, TileSize, ResourceUpdate};
+use crate::{BlobImageHandler, AsyncBlobImageRasterizer, BlobImageData, BlobImageParams};
+use crate::{BlobImageRequest, BlobImageDescriptor, BlobImageResources, TransactionMsg};
+use crate::{FontKey, FontTemplate, FontInstanceData, FontInstanceKey, AddFont};
+use crate::image_tiling::*;
+use crate::units::*;
+use crate::font::SharedFontInstanceMap;
+use crate::euclid::{point2, size2};
+
+pub const DEFAULT_TILE_SIZE: TileSize = 512;
+
+use std::collections::HashMap;
+use std::sync::Arc;
+
+/// We use this to generate the async blob rendering requests.
+struct BlobImageTemplate {
+ descriptor: ImageDescriptor,
+ tile_size: TileSize,
+ dirty_rect: BlobDirtyRect,
+ /// See ImageResource::visible_rect.
+ visible_rect: DeviceIntRect,
+ // If the active rect of the blob changes, this represents the
+ // range of tiles that remain valid. This must be taken into
+ // account in addition to the valid rect when submitting blob
+ // rasterization requests.
+ // `None` means the bounds have not changed (tiles are still valid).
+ // `Some(TileRange::zero())` means all of the tiles are invalid.
+ valid_tiles_after_bounds_change: Option<TileRange>,
+}
+
+struct FontResources {
+ templates: HashMap<FontKey, FontTemplate>,
+ instances: SharedFontInstanceMap,
+}
+
+pub struct ApiResources {
+ blob_image_templates: HashMap<BlobImageKey, BlobImageTemplate>,
+ pub blob_image_handler: Option<Box<dyn BlobImageHandler>>,
+ fonts: FontResources,
+}
+
+impl BlobImageResources for FontResources {
+ fn get_font_data(&self, key: FontKey) -> &FontTemplate {
+ self.templates.get(&key).unwrap()
+ }
+ fn get_font_instance_data(&self, key: FontInstanceKey) -> Option<FontInstanceData> {
+ self.instances.get_font_instance_data(key)
+ }
+}
+
+impl ApiResources {
+ pub fn new(
+ blob_image_handler: Option<Box<dyn BlobImageHandler>>,
+ instances: SharedFontInstanceMap,
+ ) -> Self {
+ ApiResources {
+ blob_image_templates: HashMap::new(),
+ blob_image_handler,
+ fonts: FontResources {
+ templates: HashMap::new(),
+ instances,
+ }
+ }
+ }
+
+ pub fn get_shared_font_instances(&self) -> SharedFontInstanceMap {
+ self.fonts.instances.clone()
+ }
+
+ pub fn update(&mut self, transaction: &mut TransactionMsg) {
+ let mut blobs_to_rasterize = Vec::new();
+ for update in &transaction.resource_updates {
+ match *update {
+ ResourceUpdate::AddBlobImage(ref img) => {
+ self.blob_image_handler
+ .as_mut()
+ .unwrap()
+ .add(img.key, Arc::clone(&img.data), &img.visible_rect, img.tile_size);
+
+ self.blob_image_templates.insert(
+ img.key,
+ BlobImageTemplate {
+ descriptor: img.descriptor,
+ tile_size: img.tile_size,
+ dirty_rect: DirtyRect::All,
+ valid_tiles_after_bounds_change: None,
+ visible_rect: img.visible_rect,
+ },
+ );
+ blobs_to_rasterize.push(img.key);
+ }
+ ResourceUpdate::UpdateBlobImage(ref img) => {
+ debug_assert_eq!(img.visible_rect.size, img.descriptor.size);
+ self.update_blob_image(
+ img.key,
+ Some(&img.descriptor),
+ Some(&img.dirty_rect),
+ Some(Arc::clone(&img.data)),
+ &img.visible_rect,
+ );
+ blobs_to_rasterize.push(img.key);
+ }
+ ResourceUpdate::DeleteBlobImage(key) => {
+ self.blob_image_templates.remove(&key);
+ }
+ ResourceUpdate::SetBlobImageVisibleArea(ref key, ref area) => {
+ self.update_blob_image(*key, None, None, None, area);
+ blobs_to_rasterize.push(*key);
+ }
+ ResourceUpdate::AddFont(ref font) => {
+ match font {
+ AddFont::Raw(key, bytes, index) => {
+ self.fonts.templates.insert(
+ *key,
+ FontTemplate::Raw(Arc::clone(bytes), *index),
+ );
+ }
+ AddFont::Native(key, native_font_handle) => {
+ self.fonts.templates.insert(
+ *key,
+ FontTemplate::Native(native_font_handle.clone()),
+ );
+ }
+ }
+ }
+ ResourceUpdate::AddFontInstance(ref instance) => {
+ // TODO(nical): Don't clone these.
+ self.fonts.instances.add_font_instance(
+ instance.key,
+ instance.font_key,
+ instance.glyph_size,
+ instance.options.clone(),
+ instance.platform_options.clone(),
+ instance.variations.clone(),
+ );
+ }
+ ResourceUpdate::DeleteFont(key) => {
+ self.fonts.templates.remove(&key);
+ if let Some(ref mut handler) = self.blob_image_handler {
+ handler.delete_font(key);
+ }
+ }
+ ResourceUpdate::DeleteFontInstance(key) => {
+ // We will delete from the shared font instance map in the resource cache
+ // after scene swap.
+
+ if let Some(ref mut r) = self.blob_image_handler {
+ r.delete_font_instance(key);
+ }
+ }
+ _ => {}
+ }
+ }
+
+ let (rasterizer, requests) = self.create_blob_scene_builder_requests(&blobs_to_rasterize);
+ transaction.blob_rasterizer = rasterizer;
+ transaction.blob_requests = requests;
+ }
+
+ pub fn enable_multithreading(&mut self, enable: bool) {
+ if let Some(ref mut handler) = self.blob_image_handler {
+ handler.enable_multithreading(enable);
+ }
+ }
+
+ fn update_blob_image(
+ &mut self,
+ key: BlobImageKey,
+ descriptor: Option<&ImageDescriptor>,
+ dirty_rect: Option<&BlobDirtyRect>,
+ data: Option<Arc<BlobImageData>>,
+ visible_rect: &DeviceIntRect,
+ ) {
+ if let Some(data) = data {
+ let dirty_rect = dirty_rect.unwrap();
+ self.blob_image_handler.as_mut().unwrap().update(key, data, visible_rect, dirty_rect);
+ }
+
+ let image = self.blob_image_templates
+ .get_mut(&key)
+ .expect("Attempt to update non-existent blob image");
+
+ let mut valid_tiles_after_bounds_change = compute_valid_tiles_if_bounds_change(
+ &image.visible_rect,
+ visible_rect,
+ image.tile_size,
+ );
+
+ match (image.valid_tiles_after_bounds_change, valid_tiles_after_bounds_change) {
+ (Some(old), Some(ref mut new)) => {
+ *new = new.intersection(&old).unwrap_or_else(TileRange::zero);
+ }
+ (Some(old), None) => {
+ valid_tiles_after_bounds_change = Some(old);
+ }
+ _ => {}
+ }
+
+ let blob_size = visible_rect.size;
+
+ if let Some(descriptor) = descriptor {
+ image.descriptor = *descriptor;
+ } else {
+ // make sure the descriptor size matches the visible rect.
+ // This might not be necessary but let's stay on the safe side.
+ image.descriptor.size = blob_size;
+ }
+
+ if let Some(dirty_rect) = dirty_rect {
+ image.dirty_rect = image.dirty_rect.union(dirty_rect);
+ }
+
+ image.valid_tiles_after_bounds_change = valid_tiles_after_bounds_change;
+ image.visible_rect = *visible_rect;
+ }
+
+ pub fn create_blob_scene_builder_requests(
+ &mut self,
+ keys: &[BlobImageKey]
+ ) -> (Option<Box<dyn AsyncBlobImageRasterizer>>, Vec<BlobImageParams>) {
+ if self.blob_image_handler.is_none() || keys.is_empty() {
+ return (None, Vec::new());
+ }
+
+ let mut blob_request_params = Vec::new();
+ for key in keys {
+ let template = self.blob_image_templates.get_mut(key).unwrap();
+
+ // If we know that only a portion of the blob image is in the viewport,
+ // only request these visible tiles since blob images can be huge.
+ let tiles = compute_tile_range(
+ &template.visible_rect,
+ template.tile_size,
+ );
+
+ // Don't request tiles that weren't invalidated.
+ let dirty_tiles = match template.dirty_rect {
+ DirtyRect::Partial(dirty_rect) => {
+ compute_tile_range(
+ &dirty_rect.cast_unit(),
+ template.tile_size,
+ )
+ }
+ DirtyRect::All => tiles,
+ };
+
+ for_each_tile_in_range(&tiles, |tile| {
+ let still_valid = template.valid_tiles_after_bounds_change
+ .map(|valid_tiles| valid_tiles.contains(tile))
+ .unwrap_or(true);
+
+ if still_valid && !dirty_tiles.contains(tile) {
+ return;
+ }
+
+ let descriptor = BlobImageDescriptor {
+ rect: compute_tile_rect(
+ &template.visible_rect,
+ template.tile_size,
+ tile,
+ ).cast_unit(),
+ format: template.descriptor.format,
+ };
+
+ assert!(descriptor.rect.size.width > 0 && descriptor.rect.size.height > 0);
+ blob_request_params.push(
+ BlobImageParams {
+ request: BlobImageRequest { key: *key, tile },
+ descriptor,
+ dirty_rect: DirtyRect::All,
+ }
+ );
+ });
+
+ template.dirty_rect = DirtyRect::empty();
+ template.valid_tiles_after_bounds_change = None;
+ }
+
+ let handler = self.blob_image_handler.as_mut().unwrap();
+ handler.prepare_resources(&self.fonts, &blob_request_params);
+ (Some(handler.create_blob_rasterizer()), blob_request_params)
+ }
+}
+
+fn compute_valid_tiles_if_bounds_change(
+ prev_rect: &DeviceIntRect,
+ new_rect: &DeviceIntRect,
+ tile_size: u16,
+) -> Option<TileRange> {
+ let intersection = match prev_rect.intersection(new_rect) {
+ Some(rect) => rect,
+ None => {
+ return Some(TileRange::zero());
+ }
+ };
+
+ let left = prev_rect.min_x() != new_rect.min_x();
+ let right = prev_rect.max_x() != new_rect.max_x();
+ let top = prev_rect.min_y() != new_rect.min_y();
+ let bottom = prev_rect.max_y() != new_rect.max_y();
+
+ if !left && !right && !top && !bottom {
+ // Bounds have not changed.
+ return None;
+ }
+
+ let tw = 1.0 / (tile_size as f32);
+ let th = 1.0 / (tile_size as f32);
+
+ let tiles = intersection
+ .cast::<f32>()
+ .scale(tw, th);
+
+ let min_x = if left { f32::ceil(tiles.min_x()) } else { f32::floor(tiles.min_x()) };
+ let min_y = if top { f32::ceil(tiles.min_y()) } else { f32::floor(tiles.min_y()) };
+ let max_x = if right { f32::floor(tiles.max_x()) } else { f32::ceil(tiles.max_x()) };
+ let max_y = if bottom { f32::floor(tiles.max_y()) } else { f32::ceil(tiles.max_y()) };
+
+ Some(TileRange {
+ origin: point2(min_x as i32, min_y as i32),
+ size: size2((max_x - min_x) as i32, (max_y - min_y) as i32),
+ })
+}
diff --git a/third_party/webrender/webrender_api/src/units.rs b/third_party/webrender/webrender_api/src/units.rs
index 5ec6a80e920..9913e4e6647 100644
--- a/third_party/webrender/webrender_api/src/units.rs
+++ b/third_party/webrender/webrender_api/src/units.rs
@@ -32,7 +32,6 @@ pub type DeviceIntLength = Length<i32, DevicePixel>;
pub type DeviceIntSideOffsets = SideOffsets2D<i32, DevicePixel>;
pub type DeviceRect = Rect<f32, DevicePixel>;
-pub type DeviceBox2D = Box2D<f32, DevicePixel>;
pub type DevicePoint = Point2D<f32, DevicePixel>;
pub type DeviceVector2D = Vector2D<f32, DevicePixel>;
pub type DeviceSize = Size2D<f32, DevicePixel>;
@@ -150,7 +149,7 @@ pub type BlobToDeviceTranslation = Translation2D<i32, LayoutPixel, DevicePixel>;
/// may grow. Storing them as texel coords and normalizing
/// the UVs in the vertex shader means nothing needs to be
/// updated on the CPU when the texture size changes.
-#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
+#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
pub struct TexelRect {
pub uv0: DevicePoint,
pub uv1: DevicePoint,