1473 lines
54 KiB
Rust
1473 lines
54 KiB
Rust
/* 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 std::cell::Cell;
|
|
use std::fmt;
|
|
use std::marker::PhantomData;
|
|
use std::path::PathBuf;
|
|
use std::sync::Arc;
|
|
use std::u32;
|
|
use api::{MinimapData, SnapshotImageKey};
|
|
use time::precise_time_ns;
|
|
use crate::api::channel::{Sender, single_msg_channel, unbounded_channel};
|
|
use crate::api::{BuiltDisplayList, IdNamespace, ExternalScrollId, Parameter, BoolParameter};
|
|
use crate::api::{FontKey, FontInstanceKey, NativeFontHandle};
|
|
use crate::api::{BlobImageData, BlobImageKey, ImageData, ImageDescriptor, ImageKey, Epoch, QualitySettings};
|
|
use crate::api::{BlobImageParams, BlobImageRequest, BlobImageResult, AsyncBlobImageRasterizer, BlobImageHandler};
|
|
use crate::api::{DocumentId, PipelineId, PropertyBindingId, PropertyBindingKey, ExternalEvent};
|
|
use crate::api::{HitTestResult, HitTesterRequest, ApiHitTester, PropertyValue, DynamicProperties};
|
|
use crate::api::{SampledScrollOffset, TileSize, NotificationRequest, DebugFlags};
|
|
use crate::api::{GlyphDimensionRequest, GlyphIndexRequest, GlyphIndex, GlyphDimensions};
|
|
use crate::api::{FontInstanceOptions, FontInstancePlatformOptions, FontVariation, RenderReasons};
|
|
use crate::api::DEFAULT_TILE_SIZE;
|
|
use crate::api::units::*;
|
|
use crate::api_resources::ApiResources;
|
|
use glyph_rasterizer::SharedFontResources;
|
|
use crate::scene_builder_thread::{SceneBuilderRequest, SceneBuilderResult};
|
|
use crate::intern::InterningMemoryReport;
|
|
use crate::profiler::{self, TransactionProfile};
|
|
|
|
#[repr(C)]
|
|
#[derive(Clone, Copy, Debug)]
|
|
#[cfg_attr(any(feature = "serde"), derive(Deserialize, Serialize))]
|
|
struct ResourceId(pub u32);
|
|
|
|
/// Update of a persistent resource in WebRender.
|
|
///
|
|
/// ResourceUpdate changes keep theirs effect across display list changes.
|
|
#[derive(Clone)]
|
|
#[cfg_attr(any(feature = "serde"), derive(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 `AddSnapshotImage`.
|
|
AddSnapshotImage(AddSnapshotImage),
|
|
/// See `AddSnapshotImage`.
|
|
DeleteSnapshotImage(SnapshotImageKey),
|
|
/// 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(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(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::AddSnapshotImage(..) => f.write_str("ResourceUpdate::AddSnapshotImage"),
|
|
ResourceUpdate::DeleteSnapshotImage(..) => f.write_str("ResourceUpdate::DeleteSnapshotImage"),
|
|
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"),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Whether to generate a frame, and if so, an id that allows tracking this
|
|
/// transaction through the various frame stages.
|
|
#[derive(Clone, Debug)]
|
|
pub enum GenerateFrame {
|
|
/// Generate a frame if something changed.
|
|
Yes {
|
|
/// An id that allows tracking the frame transaction through the various
|
|
/// frame stages. Specified by the caller of generate_frame().
|
|
id: u64,
|
|
/// If false, (a subset of) the frame will be rendered, but nothing will
|
|
/// be presented on the window.
|
|
present: bool,
|
|
},
|
|
/// Don't generate a frame even if something has changed.
|
|
No,
|
|
}
|
|
|
|
impl GenerateFrame {
|
|
///
|
|
pub fn as_bool(&self) -> bool {
|
|
match self {
|
|
GenerateFrame::Yes { .. } => true,
|
|
GenerateFrame::No => false,
|
|
}
|
|
}
|
|
|
|
/// If false, a frame may be (partially) generated but it will not be
|
|
/// presented to the window.
|
|
pub fn present(&self) -> bool {
|
|
match self {
|
|
GenerateFrame::Yes { present, .. } => *present,
|
|
GenerateFrame::No => false,
|
|
}
|
|
}
|
|
|
|
/// Return the frame ID, if a frame is generated.
|
|
pub fn id(&self) -> Option<u64> {
|
|
match self {
|
|
GenerateFrame::Yes { id, .. } => Some(*id),
|
|
GenerateFrame::No => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// 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>,
|
|
|
|
/// True if the transaction needs the scene building thread's attention.
|
|
/// False for things that can skip the scene builder, like APZ changes and
|
|
/// async images.
|
|
///
|
|
/// Before this `Transaction` is converted to a `TransactionMsg`, we look
|
|
/// over its contents and set this if we're doing anything the scene builder
|
|
/// needs to know about, so this is only a default.
|
|
use_scene_builder_thread: bool,
|
|
|
|
/// Whether to generate a frame, and if so, an id that allows tracking this
|
|
/// transaction through the various frame stages. Specified by the caller of
|
|
/// generate_frame().
|
|
generate_frame: GenerateFrame,
|
|
|
|
/// Time when this transaction was constructed.
|
|
creation_time: u64,
|
|
|
|
/// 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,
|
|
|
|
///
|
|
pub render_reasons: RenderReasons,
|
|
}
|
|
|
|
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: GenerateFrame::No,
|
|
creation_time: precise_time_ns(),
|
|
invalidate_rendered_frame: false,
|
|
low_priority: false,
|
|
render_reasons: RenderReasons::empty(),
|
|
}
|
|
}
|
|
|
|
/// 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.as_bool() &&
|
|
!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};
|
|
/// # use webrender::api::units::{DeviceIntSize};
|
|
/// # use webrender::render_api::{RenderApiSender, Transaction};
|
|
/// # 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.
|
|
/// * `pipeline_id`: The ID of the pipeline that is supplying this display list.
|
|
/// * `display_list`: The root Display list used in this frame.
|
|
pub fn set_display_list(
|
|
&mut self,
|
|
epoch: Epoch,
|
|
(pipeline_id, mut display_list): (PipelineId, BuiltDisplayList),
|
|
) {
|
|
display_list.set_send_time_ns(precise_time_ns());
|
|
self.scene_ops.push(
|
|
SceneMsg::SetDisplayList {
|
|
display_list,
|
|
epoch,
|
|
pipeline_id,
|
|
}
|
|
);
|
|
}
|
|
|
|
/// 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,
|
|
) {
|
|
window_size_sanity_check(device_rect.size());
|
|
self.scene_ops.push(
|
|
SceneMsg::SetDocumentView {
|
|
device_rect,
|
|
},
|
|
);
|
|
}
|
|
|
|
/// Set multiple scroll offsets with generations to the node identified by
|
|
/// the given external scroll id, the scroll offsets are relative to the
|
|
/// pre-scrolled offset for the scrolling layer.
|
|
pub fn set_scroll_offsets(
|
|
&mut self,
|
|
id: ExternalScrollId,
|
|
sampled_scroll_offsets: Vec<SampledScrollOffset>,
|
|
) {
|
|
self.frame_ops.push(FrameMsg::SetScrollOffsets(id, sampled_scroll_offsets));
|
|
}
|
|
|
|
/// 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_is_transform_async_zooming(&mut self, is_zooming: bool, animation_id: PropertyBindingId) {
|
|
self.frame_ops.push(FrameMsg::SetIsTransformAsyncZooming(is_zooming, animation_id));
|
|
}
|
|
|
|
/// Specify data for APZ minimap debug overlay to be composited
|
|
pub fn set_minimap_data(&mut self, id: ExternalScrollId, minimap_data: MinimapData) {
|
|
self.frame_ops.push(FrameMsg::SetMinimapData(id, minimap_data));
|
|
}
|
|
|
|
/// 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, id: u64, present: bool, reasons: RenderReasons) {
|
|
self.generate_frame = GenerateFrame::Yes{ id, present };
|
|
self.render_reasons |= reasons;
|
|
}
|
|
|
|
/// 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, reasons: RenderReasons) {
|
|
self.invalidate_rendered_frame = true;
|
|
self.render_reasons |= reasons
|
|
}
|
|
|
|
/// Reset the list of animated property bindings that should be used to resolve
|
|
/// bindings in the current display list.
|
|
pub fn reset_dynamic_properties(&mut self) {
|
|
self.frame_ops.push(FrameMsg::ResetDynamicProperties);
|
|
}
|
|
|
|
/// Add to the list of animated property bindings that should be used to resolve
|
|
/// bindings in the current display list.
|
|
pub fn append_dynamic_properties(&mut self, properties: DynamicProperties) {
|
|
self.frame_ops.push(FrameMsg::AppendDynamicProperties(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,
|
|
creation_time: Some(self.creation_time),
|
|
invalidate_rendered_frame: self.invalidate_rendered_frame,
|
|
low_priority: self.low_priority,
|
|
blob_rasterizer: None,
|
|
blob_requests: Vec::new(),
|
|
rasterized_blobs: Vec::new(),
|
|
profile: TransactionProfile::new(),
|
|
render_reasons: self.render_reasons,
|
|
})
|
|
}
|
|
|
|
/// 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::AddSnapshotImage`.
|
|
pub fn add_snapshot_image(
|
|
&mut self,
|
|
key: SnapshotImageKey,
|
|
) {
|
|
self.resource_updates.push(
|
|
ResourceUpdate::AddSnapshotImage(AddSnapshotImage { key })
|
|
);
|
|
}
|
|
|
|
/// See `ResourceUpdate::DeleteSnapshotImage`.
|
|
pub fn delete_snapshot_image(&mut self, key: SnapshotImageKey) {
|
|
self.resource_updates.push(ResourceUpdate::DeleteSnapshotImage(key));
|
|
}
|
|
|
|
/// See `ResourceUpdate::AddFont`.
|
|
pub fn add_raw_font(&mut self, key: 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: FontKey, native_handle: NativeFontHandle) {
|
|
self.resource_updates
|
|
.push(ResourceUpdate::AddFont(AddFont::Native(key, native_handle)));
|
|
}
|
|
|
|
/// See `ResourceUpdate::DeleteFont`.
|
|
pub fn delete_font(&mut self, key: FontKey) {
|
|
self.resource_updates.push(ResourceUpdate::DeleteFont(key));
|
|
}
|
|
|
|
/// See `ResourceUpdate::AddFontInstance`.
|
|
pub fn add_font_instance(
|
|
&mut self,
|
|
key: FontInstanceKey,
|
|
font_key: FontKey,
|
|
glyph_size: f32,
|
|
options: Option<FontInstanceOptions>,
|
|
platform_options: Option<FontInstancePlatformOptions>,
|
|
variations: Vec<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: 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: GenerateFrame,
|
|
/// Creation time of this transaction.
|
|
pub creation_time: Option<u64>,
|
|
/// 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)>,
|
|
/// Collect various data along the rendering pipeline to display it in the embedded profiler.
|
|
pub profile: TransactionProfile,
|
|
/// Keep track of who asks rendering to happen.
|
|
pub render_reasons: RenderReasons,
|
|
}
|
|
|
|
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.as_bool() &&
|
|
!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)]
|
|
#[cfg_attr(any(feature = "serde"), derive(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)]
|
|
#[cfg_attr(any(feature = "serde"), derive(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)]
|
|
#[cfg_attr(any(feature = "serde"), derive(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)]
|
|
#[cfg_attr(any(feature = "serde"), derive(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 snapshot image resource.
|
|
///
|
|
/// Must be matched with a `DeleteSnapshotImage` at some point to prevent memory leaks.
|
|
#[derive(Clone)]
|
|
#[cfg_attr(any(feature = "serde"), derive(Deserialize, Serialize))]
|
|
pub struct AddSnapshotImage {
|
|
/// The key identfying the snapshot resource.
|
|
pub key: SnapshotImageKey,
|
|
}
|
|
|
|
/// Creates a font resource.
|
|
///
|
|
/// Must be matched with a corresponding `ResourceUpdate::DeleteFont` at some point to prevent
|
|
/// memory leaks.
|
|
#[derive(Clone)]
|
|
#[cfg_attr(any(feature = "serde"), derive(Deserialize, Serialize))]
|
|
pub enum AddFont {
|
|
///
|
|
Raw(FontKey, Arc<Vec<u8>>, u32),
|
|
///
|
|
Native(FontKey, NativeFontHandle),
|
|
}
|
|
|
|
/// Creates a font instance resource.
|
|
///
|
|
/// Must be matched with a corresponding `DeleteFontInstance` at some point
|
|
/// to prevent memory leaks.
|
|
#[derive(Clone)]
|
|
#[cfg_attr(any(feature = "serde"), derive(Deserialize, Serialize))]
|
|
pub struct AddFontInstance {
|
|
/// A key to identify the font instance.
|
|
pub key: FontInstanceKey,
|
|
/// The font resource's key.
|
|
pub font_key: FontKey,
|
|
/// Glyph size in app units.
|
|
pub glyph_size: f32,
|
|
///
|
|
pub options: Option<FontInstanceOptions>,
|
|
///
|
|
pub platform_options: Option<FontInstancePlatformOptions>,
|
|
///
|
|
pub variations: Vec<FontVariation>,
|
|
}
|
|
|
|
/// Frame messages affect building the scene.
|
|
pub enum SceneMsg {
|
|
///
|
|
UpdateEpoch(PipelineId, Epoch),
|
|
///
|
|
SetRootPipeline(PipelineId),
|
|
///
|
|
RemovePipeline(PipelineId),
|
|
///
|
|
SetDisplayList {
|
|
///
|
|
display_list: BuiltDisplayList,
|
|
///
|
|
epoch: Epoch,
|
|
///
|
|
pipeline_id: PipelineId,
|
|
},
|
|
///
|
|
SetDocumentView {
|
|
///
|
|
device_rect: DeviceIntRect,
|
|
},
|
|
/// 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(WorldPoint, Sender<HitTestResult>),
|
|
///
|
|
RequestHitTester(Sender<Arc<dyn ApiHitTester>>),
|
|
///
|
|
SetScrollOffsets(ExternalScrollId, Vec<SampledScrollOffset>),
|
|
///
|
|
ResetDynamicProperties,
|
|
///
|
|
AppendDynamicProperties(DynamicProperties),
|
|
///
|
|
AppendDynamicTransformProperties(Vec<PropertyValue<LayoutTransform>>),
|
|
///
|
|
SetIsTransformAsyncZooming(bool, PropertyBindingId),
|
|
///
|
|
SetMinimapData(ExternalScrollId, MinimapData)
|
|
}
|
|
|
|
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::RemovePipeline(..) => "SceneMsg::RemovePipeline",
|
|
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::SetScrollOffsets(..) => "FrameMsg::SetScrollOffsets",
|
|
FrameMsg::ResetDynamicProperties => "FrameMsg::ResetDynamicProperties",
|
|
FrameMsg::AppendDynamicProperties(..) => "FrameMsg::AppendDynamicProperties",
|
|
FrameMsg::AppendDynamicTransformProperties(..) => "FrameMsg::AppendDynamicTransformProperties",
|
|
FrameMsg::SetIsTransformAsyncZooming(..) => "FrameMsg::SetIsTransformAsyncZooming",
|
|
FrameMsg::SetMinimapData(..) => "FrameMsg::SetMinimapData",
|
|
})
|
|
}
|
|
}
|
|
|
|
bitflags!{
|
|
/// Bit flags for WR stages to store in a capture.
|
|
// Note: capturing `FRAME` without `SCENE` is not currently supported.
|
|
#[derive(Debug, Copy, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)]
|
|
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.
|
|
#[derive(Debug, Copy, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)]
|
|
pub struct ClearCache: u8 {
|
|
///
|
|
const IMAGES = 0b1;
|
|
///
|
|
const GLYPHS = 0b10;
|
|
///
|
|
const GLYPH_DIMENSIONS = 0b100;
|
|
///
|
|
const RENDER_TASKS = 0b1000;
|
|
///
|
|
const TEXTURE_CACHE = 0b10000;
|
|
/// Clear render target pool
|
|
const RENDER_TARGETS = 0b100000;
|
|
}
|
|
}
|
|
|
|
/// 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),
|
|
/// 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),
|
|
/// 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),
|
|
/// Set an override tile size to use for picture caches
|
|
SetPictureTileSize(Option<DeviceIntSize>),
|
|
/// Set an override for max off-screen surface size
|
|
SetMaximumSurfaceSize(Option<usize>),
|
|
}
|
|
|
|
/// Message sent by the `RenderApi` to the render backend thread.
|
|
pub enum ApiMsg {
|
|
/// 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),
|
|
/// A message targeted at a particular document.
|
|
UpdateDocuments(Vec<Box<TransactionMsg>>),
|
|
/// 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),
|
|
/// Message from the scene builder thread.
|
|
SceneBuilderResult(SceneBuilderResult),
|
|
}
|
|
|
|
impl fmt::Debug for ApiMsg {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
f.write_str(match *self {
|
|
ApiMsg::CloneApi(..) => "ApiMsg::CloneApi",
|
|
ApiMsg::CloneApiByClient(..) => "ApiMsg::CloneApiByClient",
|
|
ApiMsg::AddDocument(..) => "ApiMsg::AddDocument",
|
|
ApiMsg::UpdateDocuments(..) => "ApiMsg::UpdateDocuments",
|
|
ApiMsg::MemoryPressure => "ApiMsg::MemoryPressure",
|
|
ApiMsg::ReportMemory(..) => "ApiMsg::ReportMemory",
|
|
ApiMsg::DebugCommand(..) => "ApiMsg::DebugCommand",
|
|
ApiMsg::SceneBuilderResult(..) => "ApiMsg::SceneBuilderResult",
|
|
})
|
|
}
|
|
}
|
|
|
|
/// 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>,
|
|
scene_sender: Sender<SceneBuilderRequest>,
|
|
low_priority_scene_sender: Sender<SceneBuilderRequest>,
|
|
blob_image_handler: Option<Box<dyn BlobImageHandler>>,
|
|
fonts: SharedFontResources,
|
|
}
|
|
|
|
impl RenderApiSender {
|
|
/// Used internally by the `Renderer`.
|
|
pub fn new(
|
|
api_sender: Sender<ApiMsg>,
|
|
scene_sender: Sender<SceneBuilderRequest>,
|
|
low_priority_scene_sender: Sender<SceneBuilderRequest>,
|
|
blob_image_handler: Option<Box<dyn BlobImageHandler>>,
|
|
fonts: SharedFontResources,
|
|
) -> Self {
|
|
RenderApiSender {
|
|
api_sender,
|
|
scene_sender,
|
|
low_priority_scene_sender,
|
|
blob_image_handler,
|
|
fonts,
|
|
}
|
|
}
|
|
|
|
/// Creates a new resource API object with a dedicated namespace.
|
|
pub fn create_api(&self) -> RenderApi {
|
|
let (sync_tx, sync_rx) = single_msg_channel();
|
|
let msg = ApiMsg::CloneApi(sync_tx);
|
|
self.api_sender.send(msg).expect("Failed to send CloneApi message");
|
|
let namespace_id = sync_rx.recv().expect("Failed to receive CloneApi reply");
|
|
RenderApi {
|
|
api_sender: self.api_sender.clone(),
|
|
scene_sender: self.scene_sender.clone(),
|
|
low_priority_scene_sender: self.low_priority_scene_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.fonts.clone(),
|
|
),
|
|
}
|
|
}
|
|
|
|
/// Creates a new resource API object with a dedicated namespace.
|
|
/// Namespace id is allocated by client.
|
|
///
|
|
/// The function could be used only when WebRenderOptions::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(),
|
|
scene_sender: self.scene_sender.clone(),
|
|
low_priority_scene_sender: self.low_priority_scene_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.fonts.clone(),
|
|
),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// The main entry point to interact with WebRender.
|
|
pub struct RenderApi {
|
|
api_sender: Sender<ApiMsg>,
|
|
scene_sender: Sender<SceneBuilderRequest>,
|
|
low_priority_scene_sender: Sender<SceneBuilderRequest>,
|
|
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.scene_sender.clone(),
|
|
self.low_priority_scene_sender.clone(),
|
|
self.resources.blob_image_handler.as_ref().map(|handler| handler.create_similar()),
|
|
self.resources.get_fonts(),
|
|
)
|
|
}
|
|
|
|
/// 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) -> DocumentId {
|
|
let new_id = self.next_unique_id();
|
|
self.add_document_with_id(initial_size, new_id)
|
|
}
|
|
|
|
/// See `add_document`
|
|
pub fn add_document_with_id(&self,
|
|
initial_size: DeviceIntSize,
|
|
id: u32) -> DocumentId {
|
|
window_size_sanity_check(initial_size);
|
|
|
|
let document_id = DocumentId::new(self.namespace_id, id);
|
|
|
|
// We send this message to both the render backend and the scene builder instead of having
|
|
// the scene builder thread forward it to the render backend as we do elswhere. This is because
|
|
// some transactions can skip the scene builder thread and we want to avoid them arriving before
|
|
// the render backend knows about the existence of the corresponding document id.
|
|
// It may not be necessary, though.
|
|
self.api_sender.send(
|
|
ApiMsg::AddDocument(document_id, initial_size)
|
|
).unwrap();
|
|
self.scene_sender.send(
|
|
SceneBuilderRequest::AddDocument(document_id, initial_size)
|
|
).unwrap();
|
|
|
|
document_id
|
|
}
|
|
|
|
/// Delete a document.
|
|
pub fn delete_document(&self, document_id: DocumentId) {
|
|
self.low_priority_scene_sender.send(
|
|
SceneBuilderRequest::DeleteDocument(document_id)
|
|
).unwrap();
|
|
}
|
|
|
|
/// Generate a new font key
|
|
pub fn generate_font_key(&self) -> FontKey {
|
|
let new_id = self.next_unique_id();
|
|
FontKey::new(self.namespace_id, new_id)
|
|
}
|
|
|
|
/// Generate a new font instance key
|
|
pub fn generate_font_instance_key(&self) -> FontInstanceKey {
|
|
let new_id = self.next_unique_id();
|
|
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: FontInstanceKey,
|
|
glyph_indices: Vec<GlyphIndex>,
|
|
) -> Vec<Option<GlyphDimensions>> {
|
|
let (sender, rx) = single_msg_channel();
|
|
let msg = SceneBuilderRequest::GetGlyphDimensions(GlyphDimensionRequest {
|
|
key,
|
|
glyph_indices,
|
|
sender
|
|
});
|
|
self.low_priority_scene_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: FontKey, text: &str) -> Vec<Option<u32>> {
|
|
let (sender, rx) = single_msg_channel();
|
|
let msg = SceneBuilderRequest::GetGlyphIndices(GlyphIndexRequest {
|
|
key,
|
|
text: text.to_string(),
|
|
sender,
|
|
});
|
|
self.low_priority_scene_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 = SceneBuilderRequest::ExternalEvent(evt);
|
|
self.low_priority_scene_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, _ops: malloc_size_of::MallocSizeOfOps) -> MemoryReport {
|
|
let (tx, rx) = single_msg_channel();
|
|
self.api_sender.send(ApiMsg::ReportMemory(tx)).unwrap();
|
|
*rx.recv().unwrap()
|
|
}
|
|
|
|
/// Update debugging flags.
|
|
pub fn set_debug_flags(&mut self, flags: DebugFlags) {
|
|
self.resources.set_debug_flags(flags);
|
|
let cmd = DebugCommand::SetFlags(flags);
|
|
self.api_sender.send(ApiMsg::DebugCommand(cmd)).unwrap();
|
|
self.scene_sender.send(SceneBuilderRequest ::SetFlags(flags)).unwrap();
|
|
self.low_priority_scene_sender.send(SceneBuilderRequest ::SetFlags(flags)).unwrap();
|
|
}
|
|
|
|
/// Stop RenderBackend's task until shut down
|
|
pub fn stop_render_backend(&self) {
|
|
self.low_priority_scene_sender.send(SceneBuilderRequest::StopRenderBackend).unwrap();
|
|
}
|
|
|
|
/// Shut the WebRender instance down.
|
|
pub fn shut_down(&self, synchronously: bool) {
|
|
if synchronously {
|
|
let (tx, rx) = single_msg_channel();
|
|
self.low_priority_scene_sender.send(SceneBuilderRequest::ShutDown(Some(tx))).unwrap();
|
|
rx.recv().unwrap();
|
|
} else {
|
|
self.low_priority_scene_sender.send(SceneBuilderRequest::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: GenerateFrame::No,
|
|
creation_time: None,
|
|
invalidate_rendered_frame: false,
|
|
use_scene_builder_thread: false,
|
|
low_priority: false,
|
|
blob_rasterizer: None,
|
|
blob_requests: Vec::new(),
|
|
rasterized_blobs: Vec::new(),
|
|
profile: TransactionProfile::new(),
|
|
render_reasons: RenderReasons::empty(),
|
|
})
|
|
}
|
|
|
|
/// 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);
|
|
|
|
if transaction.generate_frame.as_bool() {
|
|
transaction.profile.start_time(profiler::API_SEND_TIME);
|
|
transaction.profile.start_time(profiler::TOTAL_FRAME_CPU_TIME);
|
|
}
|
|
|
|
if transaction.use_scene_builder_thread {
|
|
let sender = if transaction.low_priority {
|
|
&mut self.low_priority_scene_sender
|
|
} else {
|
|
&mut self.scene_sender
|
|
};
|
|
|
|
sender.send(SceneBuilderRequest::Transactions(vec![transaction]))
|
|
.expect("send by scene sender failed");
|
|
} else {
|
|
self.api_sender.send(ApiMsg::UpdateDocuments(vec![transaction]))
|
|
.expect("send by api sender failed");
|
|
}
|
|
}
|
|
|
|
/// 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.
|
|
pub fn hit_test(&self,
|
|
document_id: DocumentId,
|
|
point: WorldPoint,
|
|
) -> HitTestResult {
|
|
let (tx, rx) = single_msg_channel();
|
|
|
|
self.send_frame_msg(
|
|
document_id,
|
|
FrameMsg::HitTest(point, 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) = single_msg_channel();
|
|
self.send_frame_msg(
|
|
document_id,
|
|
FrameMsg::RequestHitTester(tx)
|
|
);
|
|
|
|
HitTesterRequest { rx }
|
|
}
|
|
|
|
// 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.scene_sender.send(SceneBuilderRequest::WakeUp).unwrap();
|
|
}
|
|
|
|
/// 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) = single_msg_channel();
|
|
self.low_priority_scene_sender.send(SceneBuilderRequest::Flush(tx)).unwrap();
|
|
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) = unbounded_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(&self, cmd: DebugCommand) {
|
|
let msg = ApiMsg::DebugCommand(cmd);
|
|
self.send_message(msg);
|
|
}
|
|
|
|
/// Update a instance-global parameter.
|
|
pub fn set_parameter(&mut self, parameter: Parameter) {
|
|
if let Parameter::Bool(BoolParameter::Multithreading, enabled) = parameter {
|
|
self.resources.enable_multithreading(enabled);
|
|
}
|
|
|
|
let _ = self.low_priority_scene_sender.send(
|
|
SceneBuilderRequest::SetParameter(parameter)
|
|
);
|
|
}
|
|
}
|
|
|
|
impl Drop for RenderApi {
|
|
fn drop(&mut self) {
|
|
let msg = SceneBuilderRequest::ClearNamespace(self.namespace_id);
|
|
let _ = self.low_priority_scene_sender.send(msg);
|
|
}
|
|
}
|
|
|
|
|
|
fn window_size_sanity_check(size: DeviceIntSize) {
|
|
// Anything bigger than this will crash later when attempting to create
|
|
// a render task.
|
|
use crate::api::MAX_RENDER_TASK_SIZE;
|
|
if size.width > MAX_RENDER_TASK_SIZE || size.height > MAX_RENDER_TASK_SIZE {
|
|
panic!("Attempting to create a {}x{} window/document", size.width, size.height);
|
|
}
|
|
}
|
|
|
|
/// Collection of heap sizes, in bytes.
|
|
/// cbindgen:derive-eq=false
|
|
/// cbindgen:derive-ostream=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 hit_testers: usize,
|
|
pub fonts: usize,
|
|
pub weak_fonts: usize,
|
|
pub images: usize,
|
|
pub rasterized_blobs: usize,
|
|
pub shader_cache: usize,
|
|
pub interning: InterningMemoryReport,
|
|
pub display_list: usize,
|
|
pub upload_staging_memory: usize,
|
|
pub swgl: usize,
|
|
pub frame_allocator: usize,
|
|
pub render_tasks: usize,
|
|
|
|
//
|
|
// GPU memory.
|
|
//
|
|
pub gpu_cache_textures: usize,
|
|
pub vertex_data_textures: usize,
|
|
pub render_target_textures: usize,
|
|
pub picture_tile_textures: usize,
|
|
pub atlas_textures: usize,
|
|
pub standalone_textures: usize,
|
|
pub texture_cache_structures: usize,
|
|
pub depth_target_textures: usize,
|
|
pub texture_upload_pbos: usize,
|
|
pub swap_chain: usize,
|
|
pub render_texture_hosts: usize,
|
|
pub upload_staging_textures: usize,
|
|
}
|