diff options
Diffstat (limited to 'third_party/webrender/webrender_api/src/api.rs')
-rw-r--r-- | third_party/webrender/webrender_api/src/api.rs | 2121 |
1 files changed, 2121 insertions, 0 deletions
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; + } +} |