mirror of
https://github.com/servo/servo.git
synced 2025-08-06 06:00:15 +01:00
Upgrade WebRender to e491e1ae637b2eed1e7195855d88357e5eb3ddf9 (#30323)
* Upgrade vendored version of WebRender * Patch WebRender: upgrade version of gleam * Restore hit testing implementation * Fix WebRender warnings * Adapt Servo to new WebRender * Update results * Add a workaround for #30313 This slightly expands text boundaries in order to take into account the fact that layout isn't measuring glyph boundaries.
This commit is contained in:
parent
c079acb3c3
commit
a9d37cb85a
563 changed files with 48524 additions and 51657 deletions
|
@ -26,6 +26,7 @@ serde_bytes = "0.11"
|
|||
time = "0.1"
|
||||
malloc_size_of = { version = "0.0.1", path = "../wr_malloc_size_of", package = "wr_malloc_size_of" }
|
||||
peek-poke = { version = "0.2", path = "../peek-poke", features = ["extras"] }
|
||||
crossbeam-channel = "0.5"
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
core-foundation = "0.9"
|
||||
|
|
2121
third_party/webrender/webrender_api/src/api.rs
vendored
2121
third_party/webrender/webrender_api/src/api.rs
vendored
File diff suppressed because it is too large
Load diff
|
@ -2,12 +2,19 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use crate::api::{Epoch, PipelineId};
|
||||
use crate::{Epoch, PipelineId};
|
||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use std::io::{self, Cursor, Error, ErrorKind, Read};
|
||||
use std::mem;
|
||||
use std::sync::mpsc;
|
||||
|
||||
pub use crossbeam_channel as crossbeam;
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
pub use crossbeam_channel::{Sender, Receiver};
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub use std::sync::mpsc::{Sender, Receiver};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Payload {
|
||||
|
@ -74,7 +81,7 @@ pub type PayloadSender = MsgSender<Payload>;
|
|||
pub type PayloadReceiver = MsgReceiver<Payload>;
|
||||
|
||||
pub struct MsgReceiver<T> {
|
||||
rx: mpsc::Receiver<T>,
|
||||
rx: Receiver<T>,
|
||||
}
|
||||
|
||||
impl<T> MsgReceiver<T> {
|
||||
|
@ -82,14 +89,14 @@ impl<T> MsgReceiver<T> {
|
|||
self.rx.recv().map_err(|e| io::Error::new(ErrorKind::Other, e.to_string()))
|
||||
}
|
||||
|
||||
pub fn to_mpsc_receiver(self) -> mpsc::Receiver<T> {
|
||||
pub fn to_crossbeam_receiver(self) -> Receiver<T> {
|
||||
self.rx
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MsgSender<T> {
|
||||
tx: mpsc::Sender<T>,
|
||||
tx: Sender<T>,
|
||||
}
|
||||
|
||||
impl<T> MsgSender<T> {
|
||||
|
@ -99,12 +106,12 @@ impl<T> MsgSender<T> {
|
|||
}
|
||||
|
||||
pub fn payload_channel() -> Result<(PayloadSender, PayloadReceiver), Error> {
|
||||
let (tx, rx) = mpsc::channel();
|
||||
let (tx, rx) = unbounded_channel();
|
||||
Ok((PayloadSender { tx }, PayloadReceiver { rx }))
|
||||
}
|
||||
|
||||
pub fn msg_channel<T>() -> Result<(MsgSender<T>, MsgReceiver<T>), Error> {
|
||||
let (tx, rx) = mpsc::channel();
|
||||
let (tx, rx) = unbounded_channel();
|
||||
Ok((MsgSender { tx }, MsgReceiver { rx }))
|
||||
}
|
||||
|
||||
|
@ -129,3 +136,45 @@ impl<'de, T> Deserialize<'de> for MsgSender<T> {
|
|||
unreachable!();
|
||||
}
|
||||
}
|
||||
|
||||
/// A create a channel intended for one-shot uses, for example the channels
|
||||
/// created to block on a synchronous query and then discarded,
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
pub fn single_msg_channel<T>() -> (Sender<T>, Receiver<T>) {
|
||||
crossbeam_channel::bounded(1)
|
||||
}
|
||||
|
||||
/// A fast MPMC message channel that can hold a fixed number of messages.
|
||||
///
|
||||
/// If the channel is full, the sender will block upon sending extra messages
|
||||
/// until the receiver has consumed some messages.
|
||||
/// The capacity parameter should be chosen either:
|
||||
/// - high enough to avoid blocking on the common cases,
|
||||
/// - or, on the contrary, using the blocking behavior as a means to prevent
|
||||
/// fast producers from building up work faster than it is consumed.
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
pub fn fast_channel<T>(capacity: usize) -> (Sender<T>, Receiver<T>) {
|
||||
crossbeam_channel::bounded(capacity)
|
||||
}
|
||||
|
||||
/// Creates an MPMC channel that is a bit slower than the fast_channel but doesn't
|
||||
/// have a limit on the number of messages held at a given time and therefore
|
||||
/// doesn't block when sending.
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
pub use crossbeam_channel::unbounded as unbounded_channel;
|
||||
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn fast_channel<T>(_cap: usize) -> (Sender<T>, Receiver<T>) {
|
||||
std::sync::mpsc::channel()
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn unbounded_channel<T>() -> (Sender<T>, Receiver<T>) {
|
||||
std::sync::mpsc::channel()
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn single_msg_channel<T>() -> (Sender<T>, Receiver<T>) {
|
||||
std::sync::mpsc::channel()
|
||||
}
|
||||
|
|
|
@ -2,15 +2,16 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use euclid::SideOffsets2D;
|
||||
use euclid::{SideOffsets2D, Angle};
|
||||
use peek_poke::PeekPoke;
|
||||
use std::ops::Not;
|
||||
// local imports
|
||||
use crate::font;
|
||||
use crate::api::{PipelineId, PropertyBinding};
|
||||
use crate::{PipelineId, PropertyBinding};
|
||||
use crate::color::ColorF;
|
||||
use crate::image::{ColorDepth, ImageKey};
|
||||
use crate::units::*;
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
// ******************************************************************
|
||||
// * NOTE: some of these structs have an "IMPLICIT" comment. *
|
||||
|
@ -47,6 +48,10 @@ bitflags! {
|
|||
/// compositor surface under certain (implementation specific) conditions. This
|
||||
/// is typically used for large videos, and canvas elements.
|
||||
const PREFER_COMPOSITOR_SURFACE = 1 << 3;
|
||||
/// If set, this primitive can be passed directly to the compositor via its
|
||||
/// ExternalImageId, and the compositor will use the native image directly.
|
||||
/// Used as a further extension on top of PREFER_COMPOSITOR_SURFACE.
|
||||
const SUPPORTS_EXTERNAL_COMPOSITOR_SURFACE = 1 << 4;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -68,10 +73,6 @@ pub struct CommonItemProperties {
|
|||
pub clip_id: ClipId,
|
||||
/// The coordinate-space the item is in (yes, it can be really granular)
|
||||
pub spatial_id: SpatialId,
|
||||
/// Opaque bits for our clients to use for hit-testing. This is the most
|
||||
/// dubious "common" field, but because it's an Option, it usually only
|
||||
/// wastes a single byte (for None).
|
||||
pub hit_info: Option<ItemTag>,
|
||||
/// Various flags describing properties of this primitive.
|
||||
pub flags: PrimitiveFlags,
|
||||
}
|
||||
|
@ -86,7 +87,6 @@ impl CommonItemProperties {
|
|||
clip_rect,
|
||||
spatial_id: space_and_clip.spatial_id,
|
||||
clip_id: space_and_clip.clip_id,
|
||||
hit_info: None,
|
||||
flags: PrimitiveFlags::default(),
|
||||
}
|
||||
}
|
||||
|
@ -155,6 +155,7 @@ pub enum DisplayItem {
|
|||
SetFilterOps,
|
||||
SetFilterData,
|
||||
SetFilterPrimitives,
|
||||
SetPoints,
|
||||
|
||||
// These marker items terminate a scope introduced by a previous item.
|
||||
PopReferenceFrame,
|
||||
|
@ -203,6 +204,7 @@ pub enum DebugDisplayItem {
|
|||
SetFilterOps(Vec<FilterOp>),
|
||||
SetFilterData(FilterData),
|
||||
SetFilterPrimitives(Vec<FilterPrimitive>),
|
||||
SetPoints(Vec<LayoutPoint>),
|
||||
|
||||
PopReferenceFrame,
|
||||
PopStackingContext,
|
||||
|
@ -214,7 +216,8 @@ pub struct ImageMaskClipDisplayItem {
|
|||
pub id: ClipId,
|
||||
pub parent_space_and_clip: SpaceAndClipInfo,
|
||||
pub image_mask: ImageMask,
|
||||
}
|
||||
pub fill_rule: FillRule,
|
||||
} // IMPLICIT points: Vec<LayoutPoint>
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
|
||||
pub struct RectClipDisplayItem {
|
||||
|
@ -306,7 +309,7 @@ pub struct ScrollFrameDisplayItem {
|
|||
pub content_rect: LayoutRect,
|
||||
pub clip_rect: LayoutRect,
|
||||
pub parent_space_and_clip: SpaceAndClipInfo,
|
||||
pub external_id: Option<ExternalScrollId>,
|
||||
pub external_id: ExternalScrollId,
|
||||
pub scroll_sensitivity: ScrollSensitivity,
|
||||
/// The amount this scrollframe has already been scrolled by, in the caller.
|
||||
/// This means that all the display items that are inside the scrollframe
|
||||
|
@ -338,6 +341,7 @@ pub struct ClearRectangleDisplayItem {
|
|||
#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
|
||||
pub struct HitTestDisplayItem {
|
||||
pub common: CommonItemProperties,
|
||||
pub tag: ItemTag,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
|
||||
|
@ -623,6 +627,15 @@ pub struct Gradient {
|
|||
pub extend_mode: ExtendMode,
|
||||
} // IMPLICIT: stops: Vec<GradientStop>
|
||||
|
||||
impl Gradient {
|
||||
pub fn is_valid(&self) -> bool {
|
||||
self.start_point.x.is_finite() &&
|
||||
self.start_point.y.is_finite() &&
|
||||
self.end_point.x.is_finite() &&
|
||||
self.end_point.y.is_finite()
|
||||
}
|
||||
}
|
||||
|
||||
/// The area
|
||||
#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
|
||||
pub struct GradientDisplayItem {
|
||||
|
@ -655,6 +668,15 @@ pub struct RadialGradient {
|
|||
pub extend_mode: ExtendMode,
|
||||
} // IMPLICIT stops: Vec<GradientStop>
|
||||
|
||||
impl RadialGradient {
|
||||
pub fn is_valid(&self) -> bool {
|
||||
self.center.x.is_finite() &&
|
||||
self.center.y.is_finite() &&
|
||||
self.start_offset.is_finite() &&
|
||||
self.end_offset.is_finite()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
|
||||
pub struct ConicGradient {
|
||||
pub center: LayoutPoint,
|
||||
|
@ -664,6 +686,16 @@ pub struct ConicGradient {
|
|||
pub extend_mode: ExtendMode,
|
||||
} // IMPLICIT stops: Vec<GradientStop>
|
||||
|
||||
impl ConicGradient {
|
||||
pub fn is_valid(&self) -> bool {
|
||||
self.center.x.is_finite() &&
|
||||
self.center.y.is_finite() &&
|
||||
self.angle.is_finite() &&
|
||||
self.start_offset.is_finite() &&
|
||||
self.end_offset.is_finite()
|
||||
}
|
||||
}
|
||||
|
||||
/// Just an abstraction for bundling up a bunch of clips into a "super clip".
|
||||
#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
|
||||
pub struct ClipChainItem {
|
||||
|
@ -711,23 +743,87 @@ pub struct ReferenceFrameDisplayListItem {
|
|||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, PeekPoke)]
|
||||
pub enum ReferenceFrameKind {
|
||||
/// Zoom reference frames must be a scale + translation only
|
||||
Zoom,
|
||||
/// A normal transform matrix, may contain perspective (the CSS transform property)
|
||||
Transform,
|
||||
Transform {
|
||||
/// Optionally marks the transform as only ever having a simple 2D scale or translation,
|
||||
/// allowing for optimizations.
|
||||
is_2d_scale_translation: bool,
|
||||
/// Marks that the transform should be snapped. Used for transforms which animate in
|
||||
/// response to scrolling, eg for zooming or dynamic toolbar fixed-positioning.
|
||||
should_snap: bool,
|
||||
},
|
||||
/// A perspective transform, that optionally scrolls relative to a specific scroll node
|
||||
Perspective {
|
||||
scrolling_relative_to: Option<ExternalScrollId>,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, PeekPoke)]
|
||||
pub enum Rotation {
|
||||
Degree0,
|
||||
Degree90,
|
||||
Degree180,
|
||||
Degree270,
|
||||
}
|
||||
|
||||
impl Rotation {
|
||||
pub fn to_matrix(
|
||||
&self,
|
||||
size: LayoutSize,
|
||||
) -> LayoutTransform {
|
||||
let (shift_center_to_origin, angle) = match self {
|
||||
Rotation::Degree0 => {
|
||||
(LayoutTransform::translation(-size.width / 2., -size.height / 2., 0.), Angle::degrees(0.))
|
||||
},
|
||||
Rotation::Degree90 => {
|
||||
(LayoutTransform::translation(-size.height / 2., -size.width / 2., 0.), Angle::degrees(90.))
|
||||
},
|
||||
Rotation::Degree180 => {
|
||||
(LayoutTransform::translation(-size.width / 2., -size.height / 2., 0.), Angle::degrees(180.))
|
||||
},
|
||||
Rotation::Degree270 => {
|
||||
(LayoutTransform::translation(-size.height / 2., -size.width / 2., 0.), Angle::degrees(270.))
|
||||
},
|
||||
};
|
||||
let shift_origin_to_center = LayoutTransform::translation(size.width / 2., size.height / 2., 0.);
|
||||
|
||||
shift_center_to_origin
|
||||
.then(&LayoutTransform::rotation(0., 0., 1.0, angle))
|
||||
.then(&shift_origin_to_center)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, PeekPoke)]
|
||||
pub enum ReferenceTransformBinding {
|
||||
/// Standard reference frame which contains a precomputed transform.
|
||||
Static {
|
||||
binding: PropertyBinding<LayoutTransform>,
|
||||
},
|
||||
/// Computed reference frame which dynamically calculates the transform
|
||||
/// based on the given parameters. The reference is the content size of
|
||||
/// the parent iframe, which is affected by snapping.
|
||||
Computed {
|
||||
scale_from: Option<LayoutSize>,
|
||||
vertical_flip: bool,
|
||||
rotation: Rotation,
|
||||
},
|
||||
}
|
||||
|
||||
impl Default for ReferenceTransformBinding {
|
||||
fn default() -> Self {
|
||||
ReferenceTransformBinding::Static {
|
||||
binding: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
|
||||
pub struct ReferenceFrame {
|
||||
pub kind: ReferenceFrameKind,
|
||||
pub transform_style: TransformStyle,
|
||||
/// The transform matrix, either the perspective matrix or the transform
|
||||
/// matrix.
|
||||
pub transform: PropertyBinding<LayoutTransform>,
|
||||
pub transform: ReferenceTransformBinding,
|
||||
pub id: SpatialId,
|
||||
}
|
||||
|
||||
|
@ -762,7 +858,7 @@ pub enum TransformStyle {
|
|||
/// when we want to cache the output, and performance is
|
||||
/// important. Note that this is a performance hint only,
|
||||
/// which WR may choose to ignore.
|
||||
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, PeekPoke)]
|
||||
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, MallocSizeOf, Serialize, PeekPoke)]
|
||||
#[repr(u8)]
|
||||
pub enum RasterSpace {
|
||||
// Rasterize in local-space, applying supplied scale to primitives.
|
||||
|
@ -785,6 +881,23 @@ impl RasterSpace {
|
|||
}
|
||||
}
|
||||
|
||||
impl Eq for RasterSpace {}
|
||||
|
||||
impl Hash for RasterSpace {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
match self {
|
||||
RasterSpace::Screen => {
|
||||
0.hash(state);
|
||||
}
|
||||
RasterSpace::Local(scale) => {
|
||||
// Note: this is inconsistent with the Eq impl for -0.0 (don't care).
|
||||
1.hash(state);
|
||||
scale.to_bits().hash(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[repr(C)]
|
||||
#[derive(Deserialize, MallocSizeOf, Serialize, PeekPoke)]
|
||||
|
@ -911,7 +1024,8 @@ impl FloodPrimitive {
|
|||
#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
|
||||
pub struct BlurPrimitive {
|
||||
pub input: FilterPrimitiveInput,
|
||||
pub radius: f32,
|
||||
pub width: f32,
|
||||
pub height: f32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
|
@ -1038,7 +1152,7 @@ pub enum FilterOp {
|
|||
/// Filter that does no transformation of the colors, needed for
|
||||
/// debug purposes only.
|
||||
Identity,
|
||||
Blur(f32),
|
||||
Blur(f32, f32),
|
||||
Brightness(f32),
|
||||
Contrast(f32),
|
||||
Grayscale(f32),
|
||||
|
@ -1230,6 +1344,7 @@ pub enum YuvColorSpace {
|
|||
Rec601 = 0,
|
||||
Rec709 = 1,
|
||||
Rec2020 = 2,
|
||||
Identity = 3, // aka RGB as per ISO/IEC 23091-2:2019
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
|
@ -1404,6 +1519,34 @@ impl ComplexClipRegion {
|
|||
}
|
||||
}
|
||||
|
||||
pub const POLYGON_CLIP_VERTEX_MAX: usize = 16;
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize, Eq, Hash, PeekPoke)]
|
||||
pub enum FillRule {
|
||||
Nonzero = 0x1, // Behaves as the SVG fill-rule definition for nonzero.
|
||||
Evenodd = 0x2, // Behaves as the SVG fill-rule definition for evenodd.
|
||||
}
|
||||
|
||||
impl From<u8> for FillRule {
|
||||
fn from(fill_rule: u8) -> Self {
|
||||
match fill_rule {
|
||||
0x1 => FillRule::Nonzero,
|
||||
0x2 => FillRule::Evenodd,
|
||||
_ => panic!("Unexpected FillRule value."),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FillRule> for u8 {
|
||||
fn from(fill_rule: FillRule) -> Self {
|
||||
match fill_rule {
|
||||
FillRule::Nonzero => 0x1,
|
||||
FillRule::Evenodd => 0x2,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize, PeekPoke)]
|
||||
pub struct ClipChainId(pub u64, pub PipelineId);
|
||||
|
||||
|
@ -1531,6 +1674,7 @@ impl DisplayItem {
|
|||
DisplayItem::SetFilterOps => "set_filter_ops",
|
||||
DisplayItem::SetFilterData => "set_filter_data",
|
||||
DisplayItem::SetFilterPrimitives => "set_filter_primitives",
|
||||
DisplayItem::SetPoints => "set_points",
|
||||
DisplayItem::RadialGradient(..) => "radial_gradient",
|
||||
DisplayItem::Rectangle(..) => "rectangle",
|
||||
DisplayItem::ScrollFrame(..) => "scroll_frame",
|
||||
|
@ -1572,8 +1716,13 @@ impl_default_for_enums! {
|
|||
FilterOp => Identity,
|
||||
ComponentTransferFuncType => Identity,
|
||||
ClipMode => Clip,
|
||||
FillRule => Nonzero,
|
||||
ClipId => ClipId::invalid(),
|
||||
ReferenceFrameKind => Transform,
|
||||
ReferenceFrameKind => Transform {
|
||||
is_2d_scale_translation: false,
|
||||
should_snap: false,
|
||||
},
|
||||
Rotation => Degree0,
|
||||
TransformStyle => Flat,
|
||||
RasterSpace => Local(f32::default()),
|
||||
MixBlendMode => Normal,
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use euclid::SideOffsets2D;
|
||||
use peek_poke::{ensure_red_zone, peek_from_slice, poke_extend_vec};
|
||||
use peek_poke::{ensure_red_zone, peek_from_slice, poke_extend_vec, strip_red_zone};
|
||||
use peek_poke::{poke_inplace_slice, poke_into_vec, Poke};
|
||||
#[cfg(feature = "deserialize")]
|
||||
use serde::de::Deserializer;
|
||||
|
@ -20,7 +20,7 @@ use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
|
|||
// local imports
|
||||
use crate::display_item as di;
|
||||
use crate::display_item_cache::*;
|
||||
use crate::api::{PipelineId, PropertyBinding};
|
||||
use crate::{PipelineId, PropertyBinding};
|
||||
use crate::gradient_builder::GradientBuilder;
|
||||
use crate::color::ColorF;
|
||||
use crate::font::{FontInstanceKey, GlyphInstance, GlyphOptions};
|
||||
|
@ -115,6 +115,18 @@ pub struct BuiltDisplayList {
|
|||
descriptor: BuiltDisplayListDescriptor,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Deserialize, Serialize)]
|
||||
pub enum GeckoDisplayListType {
|
||||
None,
|
||||
Partial(f64),
|
||||
Full(f64),
|
||||
}
|
||||
|
||||
impl Default for GeckoDisplayListType {
|
||||
fn default() -> Self { GeckoDisplayListType::None }
|
||||
}
|
||||
|
||||
/// Describes the memory layout of a display list.
|
||||
///
|
||||
/// A display list consists of some number of display list items, followed by a number of display
|
||||
|
@ -122,6 +134,8 @@ pub struct BuiltDisplayList {
|
|||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Default, Deserialize, Serialize)]
|
||||
pub struct BuiltDisplayListDescriptor {
|
||||
/// Gecko specific information about the display list.
|
||||
gecko_display_list_type: GeckoDisplayListType,
|
||||
/// The first IPC time stamp: before any work has been done
|
||||
builder_start_time: u64,
|
||||
/// The second IPC time stamp: after serialization
|
||||
|
@ -168,6 +182,10 @@ impl DisplayListWithCache {
|
|||
self.display_list.descriptor()
|
||||
}
|
||||
|
||||
pub fn times(&self) -> (u64, u64, u64) {
|
||||
self.display_list.times()
|
||||
}
|
||||
|
||||
pub fn data(&self) -> &[u8] {
|
||||
self.display_list.data()
|
||||
}
|
||||
|
@ -221,6 +239,7 @@ pub struct BuiltDisplayListIter<'a> {
|
|||
cur_filter_primitives: ItemRange<'a, di::FilterPrimitive>,
|
||||
cur_clip_chain_items: ItemRange<'a, di::ClipId>,
|
||||
cur_complex_clip: ItemRange<'a, di::ComplexClipRegion>,
|
||||
cur_points: ItemRange<'a, LayoutPoint>,
|
||||
peeking: Peek,
|
||||
/// Should just be initialized but never populated in release builds
|
||||
debug_stats: DebugStats,
|
||||
|
@ -263,7 +282,7 @@ impl DebugStats {
|
|||
|
||||
/// Logs the stats for the given serialized slice
|
||||
#[cfg(feature = "display_list_stats")]
|
||||
fn log_slice<T: Peek>(
|
||||
fn log_slice<T: Copy + Default + peek_poke::Peek>(
|
||||
&mut self,
|
||||
slice_name: &'static str,
|
||||
range: &ItemRange<T>,
|
||||
|
@ -318,6 +337,10 @@ impl<'a, 'b> DisplayItemRef<'a, 'b> {
|
|||
self.iter.cur_complex_clip
|
||||
}
|
||||
|
||||
pub fn points(&self) -> ItemRange<LayoutPoint> {
|
||||
self.iter.cur_points
|
||||
}
|
||||
|
||||
pub fn glyphs(&self) -> ItemRange<GlyphInstance> {
|
||||
self.iter.glyphs()
|
||||
}
|
||||
|
@ -391,6 +414,14 @@ impl BuiltDisplayList {
|
|||
)
|
||||
}
|
||||
|
||||
pub fn gecko_display_list_stats(&self) -> (f64, bool) {
|
||||
match self.descriptor.gecko_display_list_type {
|
||||
GeckoDisplayListType::Full(duration) => (duration, true),
|
||||
GeckoDisplayListType::Partial(duration) => (duration, false),
|
||||
_ => (0.0, false)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn total_clip_nodes(&self) -> usize {
|
||||
self.descriptor.total_clip_nodes
|
||||
}
|
||||
|
@ -472,6 +503,9 @@ impl BuiltDisplayList {
|
|||
Real::SetGradientStops => Debug::SetGradientStops(
|
||||
item.iter.cur_stops.iter().collect()
|
||||
),
|
||||
Real::SetPoints => Debug::SetPoints(
|
||||
item.iter.cur_points.iter().collect()
|
||||
),
|
||||
Real::RectClip(v) => Debug::RectClip(v),
|
||||
Real::RoundedRectClip(v) => Debug::RoundedRectClip(v),
|
||||
Real::ImageMaskClip(v) => Debug::ImageMaskClip(v),
|
||||
|
@ -541,6 +575,7 @@ impl<'a> BuiltDisplayListIter<'a> {
|
|||
cur_filter_primitives: ItemRange::default(),
|
||||
cur_clip_chain_items: ItemRange::default(),
|
||||
cur_complex_clip: ItemRange::default(),
|
||||
cur_points: ItemRange::default(),
|
||||
peeking: Peek::NotPeeking,
|
||||
debug_stats: DebugStats {
|
||||
last_addr: data.as_ptr() as usize,
|
||||
|
@ -609,6 +644,7 @@ impl<'a> BuiltDisplayListIter<'a> {
|
|||
self.cur_stops = ItemRange::default();
|
||||
self.cur_complex_clip = ItemRange::default();
|
||||
self.cur_clip_chain_items = ItemRange::default();
|
||||
self.cur_points = ItemRange::default();
|
||||
self.cur_filters = ItemRange::default();
|
||||
self.cur_filter_primitives = ItemRange::default();
|
||||
self.cur_filter_data.clear();
|
||||
|
@ -619,7 +655,8 @@ impl<'a> BuiltDisplayListIter<'a> {
|
|||
SetGradientStops |
|
||||
SetFilterOps |
|
||||
SetFilterData |
|
||||
SetFilterPrimitives => {
|
||||
SetFilterPrimitives |
|
||||
SetPoints => {
|
||||
// These are marker items for populating other display items, don't yield them.
|
||||
continue;
|
||||
}
|
||||
|
@ -681,6 +718,10 @@ impl<'a> BuiltDisplayListIter<'a> {
|
|||
self.cur_filter_primitives = skip_slice::<di::FilterPrimitive>(&mut self.data);
|
||||
self.debug_stats.log_slice("set_filter_primitives.primitives", &self.cur_filter_primitives);
|
||||
}
|
||||
SetPoints => {
|
||||
self.cur_points = skip_slice::<LayoutPoint>(&mut self.data);
|
||||
self.debug_stats.log_slice("set_points.points", &self.cur_points);
|
||||
}
|
||||
ClipChain(_) => {
|
||||
self.cur_clip_chain_items = skip_slice::<di::ClipId>(&mut self.data);
|
||||
self.debug_stats.log_slice("clip_chain.clip_ids", &self.cur_clip_chain_items);
|
||||
|
@ -894,6 +935,10 @@ impl<'de> Deserialize<'de> for BuiltDisplayList {
|
|||
DisplayListBuilder::push_iter_impl(&mut temp, stops);
|
||||
Real::SetGradientStops
|
||||
},
|
||||
Debug::SetPoints(points) => {
|
||||
DisplayListBuilder::push_iter_impl(&mut temp, points);
|
||||
Real::SetPoints
|
||||
},
|
||||
Debug::RectClip(v) => Real::RectClip(v),
|
||||
Debug::RoundedRectClip(v) => Real::RoundedRectClip(v),
|
||||
Debug::ImageMaskClip(v) => Real::ImageMaskClip(v),
|
||||
|
@ -931,6 +976,7 @@ impl<'de> Deserialize<'de> for BuiltDisplayList {
|
|||
Ok(BuiltDisplayList {
|
||||
data,
|
||||
descriptor: BuiltDisplayListDescriptor {
|
||||
gecko_display_list_type: GeckoDisplayListType::None,
|
||||
builder_start_time: 0,
|
||||
builder_finish_time: 1,
|
||||
send_start_time: 1,
|
||||
|
@ -976,9 +1022,6 @@ pub struct DisplayListBuilder {
|
|||
next_clip_chain_id: u64,
|
||||
builder_start_time: u64,
|
||||
|
||||
/// The size of the content of this display list. This is used to allow scrolling
|
||||
/// outside the bounds of the display list items themselves.
|
||||
content_size: LayoutSize,
|
||||
save_state: Option<SaveState>,
|
||||
|
||||
cache_size: usize,
|
||||
|
@ -986,13 +1029,12 @@ pub struct DisplayListBuilder {
|
|||
}
|
||||
|
||||
impl DisplayListBuilder {
|
||||
pub fn new(pipeline_id: PipelineId, content_size: LayoutSize) -> Self {
|
||||
Self::with_capacity(pipeline_id, content_size, 0)
|
||||
pub fn new(pipeline_id: PipelineId) -> Self {
|
||||
Self::with_capacity(pipeline_id, 0)
|
||||
}
|
||||
|
||||
pub fn with_capacity(
|
||||
pipeline_id: PipelineId,
|
||||
content_size: LayoutSize,
|
||||
capacity: usize,
|
||||
) -> Self {
|
||||
let start_time = precise_time_ns();
|
||||
|
@ -1009,18 +1051,12 @@ impl DisplayListBuilder {
|
|||
next_spatial_index: FIRST_SPATIAL_NODE_INDEX,
|
||||
next_clip_chain_id: 0,
|
||||
builder_start_time: start_time,
|
||||
content_size,
|
||||
save_state: None,
|
||||
cache_size: 0,
|
||||
serialized_content_buffer: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the content size for this display list
|
||||
pub fn content_size(&self) -> LayoutSize {
|
||||
self.content_size
|
||||
}
|
||||
|
||||
/// Saves the current display list state, so it may be `restore()`'d.
|
||||
///
|
||||
/// # Conditions:
|
||||
|
@ -1075,11 +1111,15 @@ impl DisplayListBuilder {
|
|||
W: Write
|
||||
{
|
||||
let mut temp = BuiltDisplayList::default();
|
||||
ensure_red_zone::<di::DisplayItem>(&mut self.data);
|
||||
temp.descriptor.extra_data_offset = self.data.len();
|
||||
mem::swap(&mut temp.data, &mut self.data);
|
||||
|
||||
let mut index: usize = 0;
|
||||
{
|
||||
let mut iter = temp.iter();
|
||||
let mut cache = DisplayItemCache::new();
|
||||
cache.update(&temp);
|
||||
let mut iter = temp.iter_with_cache(&cache);
|
||||
while let Some(item) = iter.next_raw() {
|
||||
if index >= range.start.unwrap_or(0) && range.end.map_or(true, |e| index < e) {
|
||||
writeln!(sink, "{}{:?}", " ".repeat(indent), item.item()).unwrap();
|
||||
|
@ -1089,6 +1129,7 @@ impl DisplayListBuilder {
|
|||
}
|
||||
|
||||
self.data = temp.data;
|
||||
strip_red_zone::<di::DisplayItem>(&mut self.data);
|
||||
index
|
||||
}
|
||||
|
||||
|
@ -1235,9 +1276,11 @@ impl DisplayListBuilder {
|
|||
pub fn push_hit_test(
|
||||
&mut self,
|
||||
common: &di::CommonItemProperties,
|
||||
tag: di::ItemTag,
|
||||
) {
|
||||
let item = di::DisplayItem::HitTest(di::HitTestDisplayItem {
|
||||
common: *common,
|
||||
tag,
|
||||
});
|
||||
self.push_item(&item);
|
||||
}
|
||||
|
@ -1534,7 +1577,9 @@ impl DisplayListBuilder {
|
|||
origin,
|
||||
reference_frame: di::ReferenceFrame {
|
||||
transform_style,
|
||||
transform,
|
||||
transform: di::ReferenceTransformBinding::Static {
|
||||
binding: transform,
|
||||
},
|
||||
kind,
|
||||
id,
|
||||
},
|
||||
|
@ -1544,6 +1589,38 @@ impl DisplayListBuilder {
|
|||
id
|
||||
}
|
||||
|
||||
pub fn push_computed_frame(
|
||||
&mut self,
|
||||
origin: LayoutPoint,
|
||||
parent_spatial_id: di::SpatialId,
|
||||
scale_from: Option<LayoutSize>,
|
||||
vertical_flip: bool,
|
||||
rotation: di::Rotation,
|
||||
) -> di::SpatialId {
|
||||
let id = self.generate_spatial_index();
|
||||
|
||||
let item = di::DisplayItem::PushReferenceFrame(di::ReferenceFrameDisplayListItem {
|
||||
parent_spatial_id,
|
||||
origin,
|
||||
reference_frame: di::ReferenceFrame {
|
||||
transform_style: di::TransformStyle::Flat,
|
||||
transform: di::ReferenceTransformBinding::Computed {
|
||||
scale_from,
|
||||
vertical_flip,
|
||||
rotation,
|
||||
},
|
||||
kind: di::ReferenceFrameKind::Transform {
|
||||
is_2d_scale_translation: false,
|
||||
should_snap: false,
|
||||
},
|
||||
id,
|
||||
},
|
||||
});
|
||||
|
||||
self.push_item(&item);
|
||||
id
|
||||
}
|
||||
|
||||
pub fn pop_reference_frame(&mut self) {
|
||||
self.push_item(&di::DisplayItem::PopReferenceFrame);
|
||||
}
|
||||
|
@ -1696,7 +1773,7 @@ impl DisplayListBuilder {
|
|||
pub fn define_scroll_frame(
|
||||
&mut self,
|
||||
parent_space_and_clip: &di::SpaceAndClipInfo,
|
||||
external_id: Option<di::ExternalScrollId>,
|
||||
external_id: di::ExternalScrollId,
|
||||
content_rect: LayoutRect,
|
||||
clip_rect: LayoutRect,
|
||||
scroll_sensitivity: di::ScrollSensitivity,
|
||||
|
@ -1742,14 +1819,25 @@ impl DisplayListBuilder {
|
|||
&mut self,
|
||||
parent_space_and_clip: &di::SpaceAndClipInfo,
|
||||
image_mask: di::ImageMask,
|
||||
points: &[LayoutPoint],
|
||||
fill_rule: di::FillRule,
|
||||
) -> di::ClipId {
|
||||
let id = self.generate_clip_index();
|
||||
let item = di::DisplayItem::ImageMaskClip(di::ImageMaskClipDisplayItem {
|
||||
id,
|
||||
parent_space_and_clip: *parent_space_and_clip,
|
||||
image_mask,
|
||||
fill_rule,
|
||||
});
|
||||
|
||||
// We only need to supply points if there are at least 3, which is the
|
||||
// minimum to specify a polygon. BuiltDisplayListIter.next ensures that points
|
||||
// are cleared between processing other display items, so we'll correctly get
|
||||
// zero points when no SetPoints item has been pushed.
|
||||
if points.len() >= 3 {
|
||||
self.push_item(&di::DisplayItem::SetPoints);
|
||||
self.push_iter(points);
|
||||
}
|
||||
self.push_item(&item);
|
||||
id
|
||||
}
|
||||
|
@ -1928,7 +2016,7 @@ impl DisplayListBuilder {
|
|||
self.cache_size = cache_size;
|
||||
}
|
||||
|
||||
pub fn finalize(mut self) -> (PipelineId, LayoutSize, BuiltDisplayList) {
|
||||
pub fn finalize(mut self) -> (PipelineId, BuiltDisplayList) {
|
||||
assert!(self.save_state.is_none(), "Finalized DisplayListBuilder with a pending save");
|
||||
|
||||
if let Some(content) = self.serialized_content_buffer.take() {
|
||||
|
@ -1951,9 +2039,9 @@ impl DisplayListBuilder {
|
|||
let end_time = precise_time_ns();
|
||||
(
|
||||
self.pipeline_id,
|
||||
self.content_size,
|
||||
BuiltDisplayList {
|
||||
descriptor: BuiltDisplayListDescriptor {
|
||||
gecko_display_list_type: GeckoDisplayListType::None,
|
||||
builder_start_time: self.builder_start_time,
|
||||
builder_finish_time: end_time,
|
||||
send_start_time: end_time,
|
||||
|
|
|
@ -15,10 +15,11 @@ use std::cmp::Ordering;
|
|||
use std::hash::{Hash, Hasher};
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
use std::path::PathBuf;
|
||||
use std::sync::{Arc, RwLock, RwLockReadGuard, mpsc::Sender};
|
||||
use std::sync::{Arc, RwLock, RwLockReadGuard};
|
||||
use std::collections::HashMap;
|
||||
// local imports
|
||||
use crate::api::IdNamespace;
|
||||
use crate::IdNamespace;
|
||||
use crate::channel::Sender;
|
||||
use crate::color::ColorU;
|
||||
use crate::units::LayoutPoint;
|
||||
|
||||
|
|
|
@ -132,7 +132,9 @@ impl GradientBuilder {
|
|||
let first = *stops.first().unwrap();
|
||||
let last = *stops.last().unwrap();
|
||||
|
||||
assert!(first.offset <= last.offset);
|
||||
// Express the assertion so that if one of the offsets is NaN, we don't panic
|
||||
// and instead take the branch that handles degenerate gradients.
|
||||
assert!(!(first.offset > last.offset));
|
||||
|
||||
let stops_delta = last.offset - first.offset;
|
||||
|
||||
|
|
33
third_party/webrender/webrender_api/src/image.rs
vendored
33
third_party/webrender/webrender_api/src/image.rs
vendored
|
@ -9,7 +9,7 @@ use peek_poke::PeekPoke;
|
|||
use std::ops::{Add, Sub};
|
||||
use std::sync::Arc;
|
||||
// local imports
|
||||
use crate::api::{IdNamespace, PipelineId, TileSize};
|
||||
use crate::{IdNamespace, TileSize};
|
||||
use crate::display_item::ImageRendering;
|
||||
use crate::font::{FontInstanceKey, FontInstanceData, FontKey, FontTemplate};
|
||||
use crate::units::*;
|
||||
|
@ -100,38 +100,24 @@ pub trait ExternalImageHandler {
|
|||
fn unlock(&mut self, key: ExternalImageId, channel_index: u8);
|
||||
}
|
||||
|
||||
/// Allows callers to receive a texture with the contents of a specific
|
||||
/// pipeline copied to it.
|
||||
pub trait OutputImageHandler {
|
||||
/// Return the native texture handle and the size of the texture.
|
||||
fn lock(&mut self, pipeline_id: PipelineId) -> Option<(u32, FramebufferIntSize)>;
|
||||
/// Unlock will only be called if the lock() call succeeds, when WR has issued
|
||||
/// the GL commands to copy the output to the texture handle.
|
||||
fn unlock(&mut self, pipeline_id: PipelineId);
|
||||
}
|
||||
|
||||
/// Specifies the type of texture target in driver terms.
|
||||
#[repr(u8)]
|
||||
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
|
||||
pub enum TextureTarget {
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
|
||||
pub enum ImageBufferKind {
|
||||
/// Standard texture. This maps to GL_TEXTURE_2D in OpenGL.
|
||||
Default = 0,
|
||||
/// Array texture. This maps to GL_TEXTURE_2D_ARRAY in OpenGL. See
|
||||
/// https://www.khronos.org/opengl/wiki/Array_Texture for background
|
||||
/// on Array textures.
|
||||
Array = 1,
|
||||
Texture2D = 0,
|
||||
/// Rectangle texture. This maps to GL_TEXTURE_RECTANGLE in OpenGL. This
|
||||
/// is similar to a standard texture, with a few subtle differences
|
||||
/// (no mipmaps, non-power-of-two dimensions, different coordinate space)
|
||||
/// that make it useful for representing the kinds of textures we use
|
||||
/// in WebRender. See https://www.khronos.org/opengl/wiki/Rectangle_Texture
|
||||
/// for background on Rectangle textures.
|
||||
Rect = 2,
|
||||
TextureRect = 1,
|
||||
/// External texture. This maps to GL_TEXTURE_EXTERNAL_OES in OpenGL, which
|
||||
/// is an extension. This is used for image formats that OpenGL doesn't
|
||||
/// understand, particularly YUV. See
|
||||
/// https://www.khronos.org/registry/OpenGL/extensions/OES/OES_EGL_image_external.txt
|
||||
External = 3,
|
||||
TextureExternal = 2,
|
||||
}
|
||||
|
||||
/// Storage format identifier for externally-managed images.
|
||||
|
@ -139,7 +125,7 @@ pub enum TextureTarget {
|
|||
#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq, Serialize, Deserialize)]
|
||||
pub enum ExternalImageType {
|
||||
/// The image is texture-backed.
|
||||
TextureHandle(TextureTarget),
|
||||
TextureHandle(ImageBufferKind),
|
||||
/// The image is heap-allocated by the embedding.
|
||||
Buffer,
|
||||
}
|
||||
|
@ -340,10 +326,9 @@ pub enum ImageData {
|
|||
}
|
||||
|
||||
mod serde_image_data_raw {
|
||||
use serde_bytes;
|
||||
|
||||
use std::sync::Arc;
|
||||
use serde::{Deserializer, Serializer};
|
||||
use serde_bytes;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub fn serialize<S: Serializer>(bytes: &Arc<Vec<u8>>, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
serde_bytes::serialize(bytes.as_slice(), serializer)
|
||||
|
|
|
@ -1,815 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use crate::{TileSize, EdgeAaSegmentMask};
|
||||
use crate::units::*;
|
||||
use euclid::{point2, size2};
|
||||
use std::i32;
|
||||
use std::ops::Range;
|
||||
|
||||
/// If repetitions are far enough apart that only one is within
|
||||
/// the primitive rect, then we can simplify the parameters and
|
||||
/// treat the primitive as not repeated.
|
||||
/// This can let us avoid unnecessary work later to handle some
|
||||
/// of the parameters.
|
||||
pub fn simplify_repeated_primitive(
|
||||
stretch_size: &LayoutSize,
|
||||
tile_spacing: &mut LayoutSize,
|
||||
prim_rect: &mut LayoutRect,
|
||||
) {
|
||||
let stride = *stretch_size + *tile_spacing;
|
||||
|
||||
if stride.width >= prim_rect.size.width {
|
||||
tile_spacing.width = 0.0;
|
||||
prim_rect.size.width = f32::min(prim_rect.size.width, stretch_size.width);
|
||||
}
|
||||
if stride.height >= prim_rect.size.height {
|
||||
tile_spacing.height = 0.0;
|
||||
prim_rect.size.height = f32::min(prim_rect.size.height, stretch_size.height);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Repetition {
|
||||
pub origin: LayoutPoint,
|
||||
pub edge_flags: EdgeAaSegmentMask,
|
||||
}
|
||||
|
||||
pub struct RepetitionIterator {
|
||||
current_x: i32,
|
||||
x_count: i32,
|
||||
current_y: i32,
|
||||
y_count: i32,
|
||||
row_flags: EdgeAaSegmentMask,
|
||||
current_origin: LayoutPoint,
|
||||
initial_origin: LayoutPoint,
|
||||
stride: LayoutSize,
|
||||
}
|
||||
|
||||
impl Iterator for RepetitionIterator {
|
||||
type Item = Repetition;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.current_x == self.x_count {
|
||||
self.current_y += 1;
|
||||
if self.current_y >= self.y_count {
|
||||
return None;
|
||||
}
|
||||
self.current_x = 0;
|
||||
|
||||
self.row_flags = EdgeAaSegmentMask::empty();
|
||||
if self.current_y == self.y_count - 1 {
|
||||
self.row_flags |= EdgeAaSegmentMask::BOTTOM;
|
||||
}
|
||||
|
||||
self.current_origin.x = self.initial_origin.x;
|
||||
self.current_origin.y += self.stride.height;
|
||||
}
|
||||
|
||||
let mut edge_flags = self.row_flags;
|
||||
if self.current_x == 0 {
|
||||
edge_flags |= EdgeAaSegmentMask::LEFT;
|
||||
}
|
||||
|
||||
if self.current_x == self.x_count - 1 {
|
||||
edge_flags |= EdgeAaSegmentMask::RIGHT;
|
||||
}
|
||||
|
||||
let repetition = Repetition {
|
||||
origin: self.current_origin,
|
||||
edge_flags,
|
||||
};
|
||||
|
||||
self.current_origin.x += self.stride.width;
|
||||
self.current_x += 1;
|
||||
|
||||
Some(repetition)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn repetitions(
|
||||
prim_rect: &LayoutRect,
|
||||
visible_rect: &LayoutRect,
|
||||
stride: LayoutSize,
|
||||
) -> RepetitionIterator {
|
||||
assert!(stride.width > 0.0);
|
||||
assert!(stride.height > 0.0);
|
||||
|
||||
let visible_rect = match prim_rect.intersection(&visible_rect) {
|
||||
Some(rect) => rect,
|
||||
None => {
|
||||
return RepetitionIterator {
|
||||
current_origin: LayoutPoint::zero(),
|
||||
initial_origin: LayoutPoint::zero(),
|
||||
current_x: 0,
|
||||
current_y: 0,
|
||||
x_count: 0,
|
||||
y_count: 0,
|
||||
stride,
|
||||
row_flags: EdgeAaSegmentMask::empty(),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let nx = if visible_rect.origin.x > prim_rect.origin.x {
|
||||
f32::floor((visible_rect.origin.x - prim_rect.origin.x) / stride.width)
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
|
||||
let ny = if visible_rect.origin.y > prim_rect.origin.y {
|
||||
f32::floor((visible_rect.origin.y - prim_rect.origin.y) / stride.height)
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
|
||||
let x0 = prim_rect.origin.x + nx * stride.width;
|
||||
let y0 = prim_rect.origin.y + ny * stride.height;
|
||||
|
||||
let x_most = visible_rect.max_x();
|
||||
let y_most = visible_rect.max_y();
|
||||
|
||||
let x_count = f32::ceil((x_most - x0) / stride.width) as i32;
|
||||
let y_count = f32::ceil((y_most - y0) / stride.height) as i32;
|
||||
|
||||
let mut row_flags = EdgeAaSegmentMask::TOP;
|
||||
if y_count == 1 {
|
||||
row_flags |= EdgeAaSegmentMask::BOTTOM;
|
||||
}
|
||||
|
||||
RepetitionIterator {
|
||||
current_origin: LayoutPoint::new(x0, y0),
|
||||
initial_origin: LayoutPoint::new(x0, y0),
|
||||
current_x: 0,
|
||||
current_y: 0,
|
||||
x_count,
|
||||
y_count,
|
||||
row_flags,
|
||||
stride,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Tile {
|
||||
pub rect: LayoutRect,
|
||||
pub offset: TileOffset,
|
||||
pub edge_flags: EdgeAaSegmentMask,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TileIteratorExtent {
|
||||
/// Range of visible tiles to iterate over in number of tiles.
|
||||
tile_range: Range<i32>,
|
||||
/// Range of tiles of the full image including tiles that are culled out.
|
||||
image_tiles: Range<i32>,
|
||||
/// Size of the first tile in layout space.
|
||||
first_tile_layout_size: f32,
|
||||
/// Size of the last tile in layout space.
|
||||
last_tile_layout_size: f32,
|
||||
/// Position of blob point (0, 0) in layout space.
|
||||
layout_tiling_origin: f32,
|
||||
/// Position of the top-left corner of the primitive rect in layout space.
|
||||
layout_prim_start: f32,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TileIterator {
|
||||
current_tile: TileOffset,
|
||||
x: TileIteratorExtent,
|
||||
y: TileIteratorExtent,
|
||||
regular_tile_size: LayoutSize,
|
||||
}
|
||||
|
||||
impl Iterator for TileIterator {
|
||||
type Item = Tile;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
// If we reach the end of a row, reset to the beginning of the next row.
|
||||
if self.current_tile.x >= self.x.tile_range.end {
|
||||
self.current_tile.y += 1;
|
||||
self.current_tile.x = self.x.tile_range.start;
|
||||
}
|
||||
|
||||
// Stop iterating if we reach the last tile. We may start here if there
|
||||
// were no tiles to iterate over.
|
||||
if self.current_tile.x >= self.x.tile_range.end || self.current_tile.y >= self.y.tile_range.end {
|
||||
return None;
|
||||
}
|
||||
|
||||
let tile_offset = self.current_tile;
|
||||
|
||||
let mut segment_rect = LayoutRect {
|
||||
origin: LayoutPoint::new(
|
||||
self.x.layout_tiling_origin + tile_offset.x as f32 * self.regular_tile_size.width,
|
||||
self.y.layout_tiling_origin + tile_offset.y as f32 * self.regular_tile_size.height,
|
||||
),
|
||||
size: self.regular_tile_size,
|
||||
};
|
||||
|
||||
let mut edge_flags = EdgeAaSegmentMask::empty();
|
||||
|
||||
if tile_offset.x == self.x.image_tiles.start {
|
||||
edge_flags |= EdgeAaSegmentMask::LEFT;
|
||||
segment_rect.size.width = self.x.first_tile_layout_size;
|
||||
segment_rect.origin.x = self.x.layout_prim_start;
|
||||
}
|
||||
if tile_offset.x == self.x.image_tiles.end - 1 {
|
||||
edge_flags |= EdgeAaSegmentMask::RIGHT;
|
||||
segment_rect.size.width = self.x.last_tile_layout_size;
|
||||
}
|
||||
|
||||
if tile_offset.y == self.y.image_tiles.start {
|
||||
segment_rect.size.height = self.y.first_tile_layout_size;
|
||||
segment_rect.origin.y = self.y.layout_prim_start;
|
||||
edge_flags |= EdgeAaSegmentMask::TOP;
|
||||
}
|
||||
if tile_offset.y == self.y.image_tiles.end - 1 {
|
||||
segment_rect.size.height = self.y.last_tile_layout_size;
|
||||
edge_flags |= EdgeAaSegmentMask::BOTTOM;
|
||||
}
|
||||
|
||||
assert!(tile_offset.y < self.y.tile_range.end);
|
||||
let tile = Tile {
|
||||
rect: segment_rect,
|
||||
offset: tile_offset,
|
||||
edge_flags,
|
||||
};
|
||||
|
||||
self.current_tile.x += 1;
|
||||
|
||||
Some(tile)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tiles(
|
||||
prim_rect: &LayoutRect,
|
||||
visible_rect: &LayoutRect,
|
||||
image_rect: &DeviceIntRect,
|
||||
device_tile_size: i32,
|
||||
) -> TileIterator {
|
||||
// The image resource is tiled. We have to generate an image primitive
|
||||
// for each tile.
|
||||
// We need to do this because the image is broken up into smaller tiles in the texture
|
||||
// cache and the image shader is not able to work with this type of sparse representation.
|
||||
|
||||
// The tiling logic works as follows:
|
||||
//
|
||||
// +-#################-+ -+
|
||||
// | #//| | |//# | | image size
|
||||
// | #//| | |//# | |
|
||||
// +-#--+----+----+--#-+ | -+
|
||||
// | #//| | |//# | | | regular tile size
|
||||
// | #//| | |//# | | |
|
||||
// +-#--+----+----+--#-+ | -+-+
|
||||
// | #//|////|////|//# | | | "leftover" height
|
||||
// | ################# | -+ ---+
|
||||
// +----+----+----+----+
|
||||
//
|
||||
// In the ascii diagram above, a large image is split into tiles of almost regular size.
|
||||
// The tiles on the edges (hatched in the diagram) can be smaller than the regular tiles
|
||||
// and are handled separately in the code (we'll call them boundary tiles).
|
||||
//
|
||||
// Each generated segment corresponds to a tile in the texture cache, with the
|
||||
// assumption that the boundary tiles are sized to fit their own irregular size in the
|
||||
// texture cache.
|
||||
//
|
||||
// Because we can have very large virtual images we iterate over the visible portion of
|
||||
// the image in layer space instead of iterating over all device tiles.
|
||||
|
||||
let visible_rect = match prim_rect.intersection(&visible_rect) {
|
||||
Some(rect) => rect,
|
||||
None => {
|
||||
return TileIterator {
|
||||
current_tile: TileOffset::zero(),
|
||||
x: TileIteratorExtent {
|
||||
tile_range: 0..0,
|
||||
image_tiles: 0..0,
|
||||
first_tile_layout_size: 0.0,
|
||||
last_tile_layout_size: 0.0,
|
||||
layout_tiling_origin: 0.0,
|
||||
layout_prim_start: prim_rect.origin.x,
|
||||
},
|
||||
y: TileIteratorExtent {
|
||||
tile_range: 0..0,
|
||||
image_tiles: 0..0,
|
||||
first_tile_layout_size: 0.0,
|
||||
last_tile_layout_size: 0.0,
|
||||
layout_tiling_origin: 0.0,
|
||||
layout_prim_start: prim_rect.origin.y,
|
||||
},
|
||||
regular_tile_size: LayoutSize::zero(),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Size of regular tiles in layout space.
|
||||
let layout_tile_size = LayoutSize::new(
|
||||
device_tile_size as f32 / image_rect.size.width as f32 * prim_rect.size.width,
|
||||
device_tile_size as f32 / image_rect.size.height as f32 * prim_rect.size.height,
|
||||
);
|
||||
|
||||
// The decomposition logic is exactly the same on each axis so we reduce
|
||||
// this to a 1-dimensional problem in an attempt to make the code simpler.
|
||||
|
||||
let x_extent = tiles_1d(
|
||||
layout_tile_size.width,
|
||||
visible_rect.x_range(),
|
||||
prim_rect.min_x(),
|
||||
image_rect.x_range(),
|
||||
device_tile_size,
|
||||
);
|
||||
|
||||
let y_extent = tiles_1d(
|
||||
layout_tile_size.height,
|
||||
visible_rect.y_range(),
|
||||
prim_rect.min_y(),
|
||||
image_rect.y_range(),
|
||||
device_tile_size,
|
||||
);
|
||||
|
||||
TileIterator {
|
||||
current_tile: point2(
|
||||
x_extent.tile_range.start,
|
||||
y_extent.tile_range.start,
|
||||
),
|
||||
x: x_extent,
|
||||
y: y_extent,
|
||||
regular_tile_size: layout_tile_size,
|
||||
}
|
||||
}
|
||||
|
||||
/// Decompose tiles along an arbitrary axis.
|
||||
///
|
||||
/// This does most of the heavy lifting needed for `tiles` but in a single dimension for
|
||||
/// the sake of simplicity since the problem is independent on the x and y axes.
|
||||
fn tiles_1d(
|
||||
layout_tile_size: f32,
|
||||
layout_visible_range: Range<f32>,
|
||||
layout_prim_start: f32,
|
||||
device_image_range: Range<i32>,
|
||||
device_tile_size: i32,
|
||||
) -> TileIteratorExtent {
|
||||
// A few sanity checks.
|
||||
debug_assert!(layout_tile_size > 0.0);
|
||||
debug_assert!(layout_visible_range.end >= layout_visible_range.start);
|
||||
debug_assert!(device_image_range.end > device_image_range.start);
|
||||
debug_assert!(device_tile_size > 0);
|
||||
|
||||
// Sizes of the boundary tiles in pixels.
|
||||
let first_tile_device_size = first_tile_size_1d(&device_image_range, device_tile_size);
|
||||
let last_tile_device_size = last_tile_size_1d(&device_image_range, device_tile_size);
|
||||
|
||||
// [start..end[ Range of tiles of this row/column (in number of tiles) without
|
||||
// taking culling into account.
|
||||
let image_tiles = tile_range_1d(&device_image_range, device_tile_size);
|
||||
|
||||
// Layout offset of tile (0, 0) with respect to the top-left corner of the display item.
|
||||
let layout_offset = device_image_range.start as f32 * layout_tile_size / device_tile_size as f32;
|
||||
// Position in layout space of tile (0, 0).
|
||||
let layout_tiling_origin = layout_prim_start - layout_offset;
|
||||
|
||||
// [start..end[ Range of the visible tiles (because of culling).
|
||||
let visible_tiles_start = f32::floor((layout_visible_range.start - layout_tiling_origin) / layout_tile_size) as i32;
|
||||
let visible_tiles_end = f32::ceil((layout_visible_range.end - layout_tiling_origin) / layout_tile_size) as i32;
|
||||
|
||||
// Combine the above two to get the tiles in the image that are visible this frame.
|
||||
let mut tiles_start = i32::max(image_tiles.start, visible_tiles_start);
|
||||
let tiles_end = i32::min(image_tiles.end, visible_tiles_end);
|
||||
if tiles_start > tiles_end {
|
||||
tiles_start = tiles_end;
|
||||
}
|
||||
|
||||
// The size in layout space of the boundary tiles.
|
||||
let first_tile_layout_size = if tiles_start == image_tiles.start {
|
||||
first_tile_device_size as f32 * layout_tile_size / device_tile_size as f32
|
||||
} else {
|
||||
// boundary tile was culled out, so the new first tile is a regularly sized tile.
|
||||
layout_tile_size
|
||||
};
|
||||
|
||||
// Same here.
|
||||
let last_tile_layout_size = if tiles_end == image_tiles.end {
|
||||
last_tile_device_size as f32 * layout_tile_size / device_tile_size as f32
|
||||
} else {
|
||||
layout_tile_size
|
||||
};
|
||||
|
||||
TileIteratorExtent {
|
||||
tile_range: tiles_start..tiles_end,
|
||||
image_tiles,
|
||||
first_tile_layout_size,
|
||||
last_tile_layout_size,
|
||||
layout_tiling_origin,
|
||||
layout_prim_start,
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute the range of tiles (in number of tiles) that intersect the provided
|
||||
/// image range (in pixels) in an arbitrary dimension.
|
||||
///
|
||||
/// ```ignore
|
||||
///
|
||||
/// 0
|
||||
/// :
|
||||
/// #-+---+---+---+---+---+--#
|
||||
/// # | | | | | | #
|
||||
/// #-+---+---+---+---+---+--#
|
||||
/// ^ : ^
|
||||
///
|
||||
/// +------------------------+ image_range
|
||||
/// +---+ regular_tile_size
|
||||
///
|
||||
/// ```
|
||||
fn tile_range_1d(
|
||||
image_range: &Range<i32>,
|
||||
regular_tile_size: i32,
|
||||
) -> Range<i32> {
|
||||
// Integer division truncates towards zero so with negative values if the first/last
|
||||
// tile isn't a full tile we can get offset by one which we account for here.
|
||||
|
||||
let mut start = image_range.start / regular_tile_size;
|
||||
if image_range.start % regular_tile_size < 0 {
|
||||
start -= 1;
|
||||
}
|
||||
|
||||
let mut end = image_range.end / regular_tile_size;
|
||||
if image_range.end % regular_tile_size > 0 {
|
||||
end += 1;
|
||||
}
|
||||
|
||||
start..end
|
||||
}
|
||||
|
||||
// Sizes of the first boundary tile in pixels.
|
||||
//
|
||||
// It can be smaller than the regular tile size if the image is not a multiple
|
||||
// of the regular tile size.
|
||||
fn first_tile_size_1d(
|
||||
image_range: &Range<i32>,
|
||||
regular_tile_size: i32,
|
||||
) -> i32 {
|
||||
// We have to account for how the % operation behaves for negative values.
|
||||
let image_size = image_range.end - image_range.start;
|
||||
i32::min(
|
||||
match image_range.start % regular_tile_size {
|
||||
// . #------+------+ .
|
||||
// . #//////| | .
|
||||
0 => regular_tile_size,
|
||||
// (zero) -> 0 . #--+------+ .
|
||||
// . . #//| | .
|
||||
// %(m): ~~>
|
||||
m if m > 0 => regular_tile_size - m,
|
||||
// . . #--+------+ 0 <- (zero)
|
||||
// . . #//| | .
|
||||
// %(m): <~~
|
||||
m => -m,
|
||||
},
|
||||
image_size
|
||||
)
|
||||
}
|
||||
|
||||
// Sizes of the last boundary tile in pixels.
|
||||
//
|
||||
// It can be smaller than the regular tile size if the image is not a multiple
|
||||
// of the regular tile size.
|
||||
fn last_tile_size_1d(
|
||||
image_range: &Range<i32>,
|
||||
regular_tile_size: i32,
|
||||
) -> i32 {
|
||||
// We have to account for how the modulo operation behaves for negative values.
|
||||
let image_size = image_range.end - image_range.start;
|
||||
i32::min(
|
||||
match image_range.end % regular_tile_size {
|
||||
// +------+------# .
|
||||
// tiles: . | |//////# .
|
||||
0 => regular_tile_size,
|
||||
// . +------+--# . 0 <- (zero)
|
||||
// . | |//# . .
|
||||
// modulo (m): <~~
|
||||
m if m < 0 => regular_tile_size + m,
|
||||
// (zero) -> 0 +------+--# . .
|
||||
// . | |//# . .
|
||||
// modulo (m): ~~>
|
||||
m => m,
|
||||
},
|
||||
image_size,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn compute_tile_rect(
|
||||
image_rect: &DeviceIntRect,
|
||||
regular_tile_size: TileSize,
|
||||
tile: TileOffset,
|
||||
) -> DeviceIntRect {
|
||||
let regular_tile_size = regular_tile_size as i32;
|
||||
DeviceIntRect {
|
||||
origin: point2(
|
||||
compute_tile_origin_1d(image_rect.x_range(), regular_tile_size, tile.x as i32),
|
||||
compute_tile_origin_1d(image_rect.y_range(), regular_tile_size, tile.y as i32),
|
||||
),
|
||||
size: size2(
|
||||
compute_tile_size_1d(image_rect.x_range(), regular_tile_size, tile.x as i32),
|
||||
compute_tile_size_1d(image_rect.y_range(), regular_tile_size, tile.y as i32),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn compute_tile_origin_1d(
|
||||
img_range: Range<i32>,
|
||||
regular_tile_size: i32,
|
||||
tile_offset: i32,
|
||||
) -> i32 {
|
||||
let tile_range = tile_range_1d(&img_range, regular_tile_size);
|
||||
if tile_offset == tile_range.start {
|
||||
img_range.start
|
||||
} else {
|
||||
tile_offset * regular_tile_size
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the width and height in pixels of a tile depending on its position in the image.
|
||||
pub fn compute_tile_size(
|
||||
image_rect: &DeviceIntRect,
|
||||
regular_tile_size: TileSize,
|
||||
tile: TileOffset,
|
||||
) -> DeviceIntSize {
|
||||
let regular_tile_size = regular_tile_size as i32;
|
||||
size2(
|
||||
compute_tile_size_1d(image_rect.x_range(), regular_tile_size, tile.x as i32),
|
||||
compute_tile_size_1d(image_rect.y_range(), regular_tile_size, tile.y as i32),
|
||||
)
|
||||
}
|
||||
|
||||
fn compute_tile_size_1d(
|
||||
img_range: Range<i32>,
|
||||
regular_tile_size: i32,
|
||||
tile_offset: i32,
|
||||
) -> i32 {
|
||||
let tile_range = tile_range_1d(&img_range, regular_tile_size);
|
||||
|
||||
// Most tiles are going to have base_size as width and height,
|
||||
// except for tiles around the edges that are shrunk to fit the image data.
|
||||
let actual_size = if tile_offset == tile_range.start {
|
||||
first_tile_size_1d(&img_range, regular_tile_size)
|
||||
} else if tile_offset == tile_range.end - 1 {
|
||||
last_tile_size_1d(&img_range, regular_tile_size)
|
||||
} else {
|
||||
regular_tile_size
|
||||
};
|
||||
|
||||
assert!(actual_size > 0);
|
||||
|
||||
actual_size
|
||||
}
|
||||
|
||||
pub fn compute_tile_range(
|
||||
visible_area: &DeviceIntRect,
|
||||
tile_size: u16,
|
||||
) -> TileRange {
|
||||
let tile_size = tile_size as i32;
|
||||
let x_range = tile_range_1d(&visible_area.x_range(), tile_size);
|
||||
let y_range = tile_range_1d(&visible_area.y_range(), tile_size);
|
||||
|
||||
TileRange {
|
||||
origin: point2(x_range.start, y_range.start),
|
||||
size: size2(x_range.end - x_range.start, y_range.end - y_range.start),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn for_each_tile_in_range(
|
||||
range: &TileRange,
|
||||
mut callback: impl FnMut(TileOffset),
|
||||
) {
|
||||
for y in range.y_range() {
|
||||
for x in range.x_range() {
|
||||
callback(point2(x, y));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compute_valid_tiles_if_bounds_change(
|
||||
prev_rect: &DeviceIntRect,
|
||||
new_rect: &DeviceIntRect,
|
||||
tile_size: u16,
|
||||
) -> Option<TileRange> {
|
||||
let intersection = match prev_rect.intersection(new_rect) {
|
||||
Some(rect) => rect,
|
||||
None => {
|
||||
return Some(TileRange::zero());
|
||||
}
|
||||
};
|
||||
|
||||
let left = prev_rect.min_x() != new_rect.min_x();
|
||||
let right = prev_rect.max_x() != new_rect.max_x();
|
||||
let top = prev_rect.min_y() != new_rect.min_y();
|
||||
let bottom = prev_rect.max_y() != new_rect.max_y();
|
||||
|
||||
if !left && !right && !top && !bottom {
|
||||
// Bounds have not changed.
|
||||
return None;
|
||||
}
|
||||
|
||||
let tw = 1.0 / (tile_size as f32);
|
||||
let th = 1.0 / (tile_size as f32);
|
||||
|
||||
let tiles = intersection
|
||||
.cast::<f32>()
|
||||
.scale(tw, th);
|
||||
|
||||
let min_x = if left { f32::ceil(tiles.min_x()) } else { f32::floor(tiles.min_x()) };
|
||||
let min_y = if top { f32::ceil(tiles.min_y()) } else { f32::floor(tiles.min_y()) };
|
||||
let max_x = if right { f32::floor(tiles.max_x()) } else { f32::ceil(tiles.max_x()) };
|
||||
let max_y = if bottom { f32::floor(tiles.max_y()) } else { f32::ceil(tiles.max_y()) };
|
||||
|
||||
Some(TileRange {
|
||||
origin: point2(min_x as i32, min_y as i32),
|
||||
size: size2((max_x - min_x) as i32, (max_y - min_y) as i32),
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::collections::HashSet;
|
||||
use euclid::rect;
|
||||
|
||||
// this checks some additional invariants
|
||||
fn checked_for_each_tile(
|
||||
prim_rect: &LayoutRect,
|
||||
visible_rect: &LayoutRect,
|
||||
device_image_rect: &DeviceIntRect,
|
||||
device_tile_size: i32,
|
||||
callback: &mut dyn FnMut(&LayoutRect, TileOffset, EdgeAaSegmentMask),
|
||||
) {
|
||||
let mut coverage = LayoutRect::zero();
|
||||
let mut seen_tiles = HashSet::new();
|
||||
for tile in tiles(
|
||||
prim_rect,
|
||||
visible_rect,
|
||||
device_image_rect,
|
||||
device_tile_size,
|
||||
) {
|
||||
// make sure we don't get sent duplicate tiles
|
||||
assert!(!seen_tiles.contains(&tile.offset));
|
||||
seen_tiles.insert(tile.offset);
|
||||
coverage = coverage.union(&tile.rect);
|
||||
assert!(prim_rect.contains_rect(&tile.rect));
|
||||
callback(&tile.rect, tile.offset, tile.edge_flags);
|
||||
}
|
||||
assert!(prim_rect.contains_rect(&coverage));
|
||||
assert!(coverage.contains_rect(&visible_rect.intersection(&prim_rect).unwrap_or(LayoutRect::zero())));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn basic() {
|
||||
let mut count = 0;
|
||||
checked_for_each_tile(&rect(0., 0., 1000., 1000.),
|
||||
&rect(75., 75., 400., 400.),
|
||||
&rect(0, 0, 400, 400),
|
||||
36,
|
||||
&mut |_tile_rect, _tile_offset, _tile_flags| {
|
||||
count += 1;
|
||||
},
|
||||
);
|
||||
assert_eq!(count, 36);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty() {
|
||||
let mut count = 0;
|
||||
checked_for_each_tile(&rect(0., 0., 74., 74.),
|
||||
&rect(75., 75., 400., 400.),
|
||||
&rect(0, 0, 400, 400),
|
||||
36,
|
||||
&mut |_tile_rect, _tile_offset, _tile_flags| {
|
||||
count += 1;
|
||||
},
|
||||
);
|
||||
assert_eq!(count, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tiles_1d() {
|
||||
// Exactly one full tile at positive offset.
|
||||
let result = tiles_1d(64.0, -10000.0..10000.0, 0.0, 0..64, 64);
|
||||
assert_eq!(result.tile_range.start, 0);
|
||||
assert_eq!(result.tile_range.end, 1);
|
||||
assert_eq!(result.first_tile_layout_size, 64.0);
|
||||
assert_eq!(result.last_tile_layout_size, 64.0);
|
||||
|
||||
// Exactly one full tile at negative offset.
|
||||
let result = tiles_1d(64.0, -10000.0..10000.0, -64.0, -64..0, 64);
|
||||
assert_eq!(result.tile_range.start, -1);
|
||||
assert_eq!(result.tile_range.end, 0);
|
||||
assert_eq!(result.first_tile_layout_size, 64.0);
|
||||
assert_eq!(result.last_tile_layout_size, 64.0);
|
||||
|
||||
// Two full tiles at negative and positive offsets.
|
||||
let result = tiles_1d(64.0, -10000.0..10000.0, -64.0, -64..64, 64);
|
||||
assert_eq!(result.tile_range.start, -1);
|
||||
assert_eq!(result.tile_range.end, 1);
|
||||
assert_eq!(result.first_tile_layout_size, 64.0);
|
||||
assert_eq!(result.last_tile_layout_size, 64.0);
|
||||
|
||||
// One partial tile at positive offset, non-zero origin, culled out.
|
||||
let result = tiles_1d(64.0, -100.0..10.0, 64.0, 64..310, 64);
|
||||
assert_eq!(result.tile_range.start, result.tile_range.end);
|
||||
|
||||
// Two tiles at negative and positive offsets, one of which is culled out.
|
||||
// The remaining tile is partially culled but it should still generate a full tile.
|
||||
let result = tiles_1d(64.0, 10.0..10000.0, -64.0, -64..64, 64);
|
||||
assert_eq!(result.tile_range.start, 0);
|
||||
assert_eq!(result.tile_range.end, 1);
|
||||
assert_eq!(result.first_tile_layout_size, 64.0);
|
||||
assert_eq!(result.last_tile_layout_size, 64.0);
|
||||
let result = tiles_1d(64.0, -10000.0..-10.0, -64.0, -64..64, 64);
|
||||
assert_eq!(result.tile_range.start, -1);
|
||||
assert_eq!(result.tile_range.end, 0);
|
||||
assert_eq!(result.first_tile_layout_size, 64.0);
|
||||
assert_eq!(result.last_tile_layout_size, 64.0);
|
||||
|
||||
// Stretched tile in layout space device tile size is 64 and layout tile size is 128.
|
||||
// So the resulting tile sizes in layout space should be multiplied by two.
|
||||
let result = tiles_1d(128.0, -10000.0..10000.0, -64.0, -64..32, 64);
|
||||
assert_eq!(result.tile_range.start, -1);
|
||||
assert_eq!(result.tile_range.end, 1);
|
||||
assert_eq!(result.first_tile_layout_size, 128.0);
|
||||
assert_eq!(result.last_tile_layout_size, 64.0);
|
||||
|
||||
// Two visible tiles (the rest is culled out).
|
||||
let result = tiles_1d(10.0, 0.0..20.0, 0.0, 0..64, 64);
|
||||
assert_eq!(result.tile_range.start, 0);
|
||||
assert_eq!(result.tile_range.end, 1);
|
||||
assert_eq!(result.first_tile_layout_size, 10.0);
|
||||
assert_eq!(result.last_tile_layout_size, 10.0);
|
||||
|
||||
// Two visible tiles at negative layout offsets (the rest is culled out).
|
||||
let result = tiles_1d(10.0, -20.0..0.0, -20.0, 0..64, 64);
|
||||
assert_eq!(result.tile_range.start, 0);
|
||||
assert_eq!(result.tile_range.end, 1);
|
||||
assert_eq!(result.first_tile_layout_size, 10.0);
|
||||
assert_eq!(result.last_tile_layout_size, 10.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tile_range_1d() {
|
||||
assert_eq!(tile_range_1d(&(0..256), 256), 0..1);
|
||||
assert_eq!(tile_range_1d(&(0..257), 256), 0..2);
|
||||
assert_eq!(tile_range_1d(&(-1..257), 256), -1..2);
|
||||
assert_eq!(tile_range_1d(&(-256..256), 256), -1..1);
|
||||
assert_eq!(tile_range_1d(&(-20..-10), 6), -4..-1);
|
||||
assert_eq!(tile_range_1d(&(20..100), 256), 0..1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_first_last_tile_size_1d() {
|
||||
assert_eq!(first_tile_size_1d(&(0..10), 64), 10);
|
||||
assert_eq!(first_tile_size_1d(&(-20..0), 64), 20);
|
||||
|
||||
assert_eq!(last_tile_size_1d(&(0..10), 64), 10);
|
||||
assert_eq!(last_tile_size_1d(&(-20..0), 64), 20);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doubly_partial_tiles() {
|
||||
// In the following tests the image is a single tile and none of the sides of the tile
|
||||
// align with the tile grid.
|
||||
// This can only happen when we have a single non-aligned partial tile and no regular
|
||||
// tiles.
|
||||
assert_eq!(first_tile_size_1d(&(300..310), 64), 10);
|
||||
assert_eq!(first_tile_size_1d(&(-20..-10), 64), 10);
|
||||
|
||||
assert_eq!(last_tile_size_1d(&(300..310), 64), 10);
|
||||
assert_eq!(last_tile_size_1d(&(-20..-10), 64), 10);
|
||||
|
||||
|
||||
// One partial tile at positve offset, non-zero origin.
|
||||
let result = tiles_1d(64.0, -10000.0..10000.0, 0.0, 300..310, 64);
|
||||
assert_eq!(result.tile_range.start, 4);
|
||||
assert_eq!(result.tile_range.end, 5);
|
||||
assert_eq!(result.first_tile_layout_size, 10.0);
|
||||
assert_eq!(result.last_tile_layout_size, 10.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn smaller_than_tile_size_at_origin() {
|
||||
let r = compute_tile_rect(
|
||||
&rect(0, 0, 80, 80),
|
||||
256,
|
||||
point2(0, 0),
|
||||
);
|
||||
|
||||
assert_eq!(r, rect(0, 0, 80, 80));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn smaller_than_tile_size_with_offset() {
|
||||
let r = compute_tile_rect(
|
||||
&rect(20, 20, 80, 80),
|
||||
256,
|
||||
point2(0, 0),
|
||||
);
|
||||
|
||||
assert_eq!(r, rect(20, 20, 80, 80));
|
||||
}
|
||||
}
|
636
third_party/webrender/webrender_api/src/lib.rs
vendored
636
third_party/webrender/webrender_api/src/lib.rs
vendored
|
@ -15,25 +15,21 @@
|
|||
#![cfg_attr(feature = "cargo-clippy", allow(clippy::float_cmp, clippy::too_many_arguments))]
|
||||
#![cfg_attr(feature = "cargo-clippy", allow(clippy::unreadable_literal, clippy::new_without_default))]
|
||||
|
||||
pub use crossbeam_channel;
|
||||
pub use euclid;
|
||||
|
||||
#[macro_use]
|
||||
extern crate bitflags;
|
||||
#[cfg(feature = "nightly")]
|
||||
use core;
|
||||
#[cfg(target_os = "macos")]
|
||||
use core_foundation;
|
||||
#[cfg(target_os = "macos")]
|
||||
use core_graphics;
|
||||
#[macro_use]
|
||||
extern crate derive_more;
|
||||
pub use euclid;
|
||||
extern crate core;
|
||||
#[macro_use]
|
||||
extern crate malloc_size_of_derive;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
use malloc_size_of;
|
||||
use peek_poke;
|
||||
|
||||
mod api;
|
||||
pub mod channel;
|
||||
mod color;
|
||||
mod display_item;
|
||||
|
@ -42,13 +38,8 @@ mod display_list;
|
|||
mod font;
|
||||
mod gradient_builder;
|
||||
mod image;
|
||||
mod resources;
|
||||
pub mod units;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub mod image_tiling;
|
||||
|
||||
pub use crate::api::*;
|
||||
pub use crate::color::*;
|
||||
pub use crate::display_item::*;
|
||||
pub use crate::display_item_cache::DisplayItemCache;
|
||||
|
@ -56,4 +47,619 @@ pub use crate::display_list::*;
|
|||
pub use crate::font::*;
|
||||
pub use crate::gradient_builder::*;
|
||||
pub use crate::image::*;
|
||||
pub use crate::resources::DEFAULT_TILE_SIZE;
|
||||
|
||||
use crate::units::*;
|
||||
use crate::channel::Receiver;
|
||||
use std::marker::PhantomData;
|
||||
use std::sync::Arc;
|
||||
use std::os::raw::c_void;
|
||||
use peek_poke::PeekPoke;
|
||||
|
||||
/// Width and height in device pixels of image tiles.
|
||||
pub type TileSize = u16;
|
||||
|
||||
/// Various settings that the caller can select based on desired tradeoffs
|
||||
/// between rendering quality and performance / power usage.
|
||||
#[derive(Copy, Clone, Deserialize, Serialize)]
|
||||
pub struct QualitySettings {
|
||||
/// If true, disable creating separate picture cache slices when the
|
||||
/// scroll root changes. This gives maximum opportunity to find an
|
||||
/// opaque background, which enables subpixel AA. However, it is
|
||||
/// usually significantly more expensive to render when scrolling.
|
||||
pub force_subpixel_aa_where_possible: bool,
|
||||
}
|
||||
|
||||
impl Default for QualitySettings {
|
||||
fn default() -> Self {
|
||||
QualitySettings {
|
||||
// Prefer performance over maximum subpixel AA quality, since WR
|
||||
// already enables subpixel AA in more situations than other browsers.
|
||||
force_subpixel_aa_where_possible: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An epoch identifies the state of a pipeline in time.
|
||||
///
|
||||
/// This is mostly used as a synchronization mechanism to observe how/when particular pipeline
|
||||
/// updates propagate through WebRender and are applied at various stages.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
|
||||
pub struct Epoch(pub u32);
|
||||
|
||||
impl Epoch {
|
||||
/// Magic invalid epoch value.
|
||||
pub fn invalid() -> Epoch {
|
||||
Epoch(u32::MAX)
|
||||
}
|
||||
}
|
||||
|
||||
/// ID namespaces uniquely identify different users of WebRender's API.
|
||||
///
|
||||
/// For example in Gecko each content process uses a separate id namespace.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, MallocSizeOf, PartialEq, Hash, Ord, PartialOrd, PeekPoke)]
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct IdNamespace(pub u32);
|
||||
|
||||
/// A key uniquely identifying a WebRender document.
|
||||
///
|
||||
/// Instances can manage one or several documents (using the same render backend thread).
|
||||
/// Each document will internally correspond to a single scene, and scenes are made of
|
||||
/// one or several pipelines.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
|
||||
pub struct DocumentId {
|
||||
///
|
||||
pub namespace_id: IdNamespace,
|
||||
///
|
||||
pub id: u32,
|
||||
}
|
||||
|
||||
impl DocumentId {
|
||||
///
|
||||
pub fn new(namespace_id: IdNamespace, id: u32) -> Self {
|
||||
DocumentId {
|
||||
namespace_id,
|
||||
id,
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
pub const INVALID: DocumentId = DocumentId { namespace_id: IdNamespace(0), id: 0 };
|
||||
}
|
||||
|
||||
/// This type carries no valuable semantics for WR. However, it reflects the fact that
|
||||
/// clients (Servo) may generate pipelines by different semi-independent sources.
|
||||
/// These pipelines still belong to the same `IdNamespace` and the same `DocumentId`.
|
||||
/// Having this extra Id field enables them to generate `PipelineId` without collision.
|
||||
pub type PipelineSourceId = u32;
|
||||
|
||||
/// From the point of view of WR, `PipelineId` is completely opaque and generic as long as
|
||||
/// it's clonable, serializable, comparable, and hashable.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
|
||||
pub struct PipelineId(pub PipelineSourceId, pub u32);
|
||||
|
||||
impl Default for PipelineId {
|
||||
fn default() -> Self {
|
||||
PipelineId::dummy()
|
||||
}
|
||||
}
|
||||
|
||||
impl PipelineId {
|
||||
///
|
||||
pub fn dummy() -> Self {
|
||||
PipelineId(!0, !0)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// An opaque pointer-sized value.
|
||||
#[repr(C)]
|
||||
#[derive(Clone)]
|
||||
pub struct ExternalEvent {
|
||||
raw: usize,
|
||||
}
|
||||
|
||||
unsafe impl Send for ExternalEvent {}
|
||||
|
||||
impl ExternalEvent {
|
||||
/// Creates the event from an opaque pointer-sized value.
|
||||
pub fn from_raw(raw: usize) -> Self {
|
||||
ExternalEvent { raw }
|
||||
}
|
||||
/// Consumes self to make it obvious that the event should be forwarded only once.
|
||||
pub fn unwrap(self) -> usize {
|
||||
self.raw
|
||||
}
|
||||
}
|
||||
|
||||
/// Describe whether or not scrolling should be clamped by the content bounds.
|
||||
#[derive(Clone, Deserialize, Serialize)]
|
||||
pub enum ScrollClamping {
|
||||
///
|
||||
ToContentBounds,
|
||||
///
|
||||
NoClamping,
|
||||
}
|
||||
|
||||
/// A handler to integrate WebRender with the thread that contains the `Renderer`.
|
||||
pub trait RenderNotifier: Send {
|
||||
///
|
||||
fn clone(&self) -> Box<dyn RenderNotifier>;
|
||||
/// Wake the thread containing the `Renderer` up (after updates have been put
|
||||
/// in the renderer's queue).
|
||||
fn wake_up(
|
||||
&self,
|
||||
composite_needed: bool,
|
||||
);
|
||||
/// Notify the thread containing the `Renderer` that a new frame is ready.
|
||||
fn new_frame_ready(&self, _: DocumentId, scrolled: bool, composite_needed: bool, render_time_ns: Option<u64>);
|
||||
/// A Gecko-specific notification mechanism to get some code executed on the
|
||||
/// `Renderer`'s thread, mostly replaced by `NotificationHandler`. You should
|
||||
/// probably use the latter instead.
|
||||
fn external_event(&self, _evt: ExternalEvent) {
|
||||
unimplemented!()
|
||||
}
|
||||
/// Notify the thread containing the `Renderer` that the render backend has been
|
||||
/// shut down.
|
||||
fn shut_down(&self) {}
|
||||
}
|
||||
|
||||
/// A stage of the rendering pipeline.
|
||||
#[repr(u32)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Checkpoint {
|
||||
///
|
||||
SceneBuilt,
|
||||
///
|
||||
FrameBuilt,
|
||||
///
|
||||
FrameTexturesUpdated,
|
||||
///
|
||||
FrameRendered,
|
||||
/// NotificationRequests get notified with this if they get dropped without having been
|
||||
/// notified. This provides the guarantee that if a request is created it will get notified.
|
||||
TransactionDropped,
|
||||
}
|
||||
|
||||
/// A handler to notify when a transaction reaches certain stages of the rendering
|
||||
/// pipeline.
|
||||
pub trait NotificationHandler : Send + Sync {
|
||||
/// Entry point of the handler to implement. Invoked by WebRender.
|
||||
fn notify(&self, when: Checkpoint);
|
||||
}
|
||||
|
||||
/// A request to notify a handler when the transaction reaches certain stages of the
|
||||
/// rendering pipeline.
|
||||
///
|
||||
/// The request is guaranteed to be notified once and only once, even if the transaction
|
||||
/// is dropped before the requested check-point.
|
||||
pub struct NotificationRequest {
|
||||
handler: Option<Box<dyn NotificationHandler>>,
|
||||
when: Checkpoint,
|
||||
}
|
||||
|
||||
impl NotificationRequest {
|
||||
/// Constructor.
|
||||
pub fn new(when: Checkpoint, handler: Box<dyn NotificationHandler>) -> Self {
|
||||
NotificationRequest {
|
||||
handler: Some(handler),
|
||||
when,
|
||||
}
|
||||
}
|
||||
|
||||
/// The specified stage at which point the handler should be notified.
|
||||
pub fn when(&self) -> Checkpoint { self.when }
|
||||
|
||||
/// Called by WebRender at specified stages to notify the registered handler.
|
||||
pub fn notify(mut self) {
|
||||
if let Some(handler) = self.handler.take() {
|
||||
handler.notify(self.when);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An object that can perform hit-testing without doing synchronous queries to
|
||||
/// the RenderBackendThread.
|
||||
pub trait ApiHitTester: Send + Sync {
|
||||
/// Does a hit test on display items in the specified document, at the given
|
||||
/// point. If a pipeline_id is specified, it is used to further restrict the
|
||||
/// hit results so that only items inside that pipeline are matched. The vector
|
||||
/// of hit results will contain all display items that match, ordered from
|
||||
/// front to back.
|
||||
fn hit_test(&self, pipeline_id: Option<PipelineId>, point: WorldPoint, flags: HitTestFlags) -> HitTestResult;
|
||||
}
|
||||
|
||||
/// A hit tester requested to the render backend thread but not necessarily ready yet.
|
||||
///
|
||||
/// The request should be resolved as late as possible to reduce the likelihood of blocking.
|
||||
pub struct HitTesterRequest {
|
||||
#[doc(hidden)]
|
||||
pub rx: Receiver<Arc<dyn ApiHitTester>>,
|
||||
}
|
||||
|
||||
impl HitTesterRequest {
|
||||
/// Block until the hit tester is available and return it, consuming teh request.
|
||||
pub fn resolve(self) -> Arc<dyn ApiHitTester> {
|
||||
self.rx.recv().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// Describe an item that matched a hit-test query.
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
|
||||
pub struct HitTestItem {
|
||||
/// The pipeline that the display item that was hit belongs to.
|
||||
pub pipeline: PipelineId,
|
||||
|
||||
/// The tag of the hit display item.
|
||||
pub tag: ItemTag,
|
||||
|
||||
/// The hit point in the coordinate space of the "viewport" of the display item. The
|
||||
/// viewport is the scroll node formed by the root reference frame of the display item's
|
||||
/// pipeline.
|
||||
pub point_in_viewport: LayoutPoint,
|
||||
|
||||
/// The coordinates of the original hit test point relative to the origin of this item.
|
||||
/// This is useful for calculating things like text offsets in the client.
|
||||
pub point_relative_to_item: LayoutPoint,
|
||||
}
|
||||
|
||||
/// Returned by `RenderApi::hit_test`.
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
pub struct HitTestResult {
|
||||
/// List of items that are match the hit-test query.
|
||||
pub items: Vec<HitTestItem>,
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[derive(Deserialize, MallocSizeOf, Serialize)]
|
||||
///
|
||||
pub struct HitTestFlags: u8 {
|
||||
///
|
||||
const FIND_ALL = 0b00000001;
|
||||
///
|
||||
const POINT_RELATIVE_TO_PIPELINE_VIEWPORT = 0b00000010;
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for NotificationRequest {
|
||||
fn drop(&mut self) {
|
||||
if let Some(ref mut handler) = self.handler {
|
||||
handler.notify(Checkpoint::TransactionDropped);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This Clone impl yields an "empty" request because we don't want the requests
|
||||
// to be notified twice so the request is owned by only one of the API messages
|
||||
// (the original one) after the clone.
|
||||
// This works in practice because the notifications requests are used for
|
||||
// synchronization so we don't need to include them in the recording mechanism
|
||||
// in wrench that clones the messages.
|
||||
impl Clone for NotificationRequest {
|
||||
fn clone(&self) -> Self {
|
||||
NotificationRequest {
|
||||
when: self.when,
|
||||
handler: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// A key to identify an animated property binding.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Default, Deserialize, MallocSizeOf, PartialEq, Serialize, Eq, Hash, PeekPoke)]
|
||||
pub struct PropertyBindingId {
|
||||
pub namespace: IdNamespace,
|
||||
pub uid: u32,
|
||||
}
|
||||
|
||||
impl PropertyBindingId {
|
||||
/// Constructor.
|
||||
pub fn new(value: u64) -> Self {
|
||||
PropertyBindingId {
|
||||
namespace: IdNamespace((value >> 32) as u32),
|
||||
uid: value as u32,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A unique key that is used for connecting animated property
|
||||
/// values to bindings in the display list.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
|
||||
pub struct PropertyBindingKey<T> {
|
||||
///
|
||||
pub id: PropertyBindingId,
|
||||
#[doc(hidden)]
|
||||
pub _phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
/// Construct a property value from a given key and value.
|
||||
impl<T: Copy> PropertyBindingKey<T> {
|
||||
///
|
||||
pub fn with(self, value: T) -> PropertyValue<T> {
|
||||
PropertyValue { key: self, value }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PropertyBindingKey<T> {
|
||||
/// Constructor.
|
||||
pub fn new(value: u64) -> Self {
|
||||
PropertyBindingKey {
|
||||
id: PropertyBindingId::new(value),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A binding property can either be a specific value
|
||||
/// (the normal, non-animated case) or point to a binding location
|
||||
/// to fetch the current value from.
|
||||
/// Note that Binding has also a non-animated value, the value is
|
||||
/// used for the case where the animation is still in-delay phase
|
||||
/// (i.e. the animation doesn't produce any animation values).
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
|
||||
pub enum PropertyBinding<T> {
|
||||
/// Non-animated value.
|
||||
Value(T),
|
||||
/// Animated binding.
|
||||
Binding(PropertyBindingKey<T>, T),
|
||||
}
|
||||
|
||||
impl<T: Default> Default for PropertyBinding<T> {
|
||||
fn default() -> Self {
|
||||
PropertyBinding::Value(Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<T> for PropertyBinding<T> {
|
||||
fn from(value: T) -> PropertyBinding<T> {
|
||||
PropertyBinding::Value(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PropertyBindingKey<ColorF>> for PropertyBindingKey<ColorU> {
|
||||
fn from(key: PropertyBindingKey<ColorF>) -> PropertyBindingKey<ColorU> {
|
||||
PropertyBindingKey {
|
||||
id: key.id.clone(),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PropertyBindingKey<ColorU>> for PropertyBindingKey<ColorF> {
|
||||
fn from(key: PropertyBindingKey<ColorU>) -> PropertyBindingKey<ColorF> {
|
||||
PropertyBindingKey {
|
||||
id: key.id.clone(),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PropertyBinding<ColorF>> for PropertyBinding<ColorU> {
|
||||
fn from(value: PropertyBinding<ColorF>) -> PropertyBinding<ColorU> {
|
||||
match value {
|
||||
PropertyBinding::Value(value) => PropertyBinding::Value(value.into()),
|
||||
PropertyBinding::Binding(k, v) => {
|
||||
PropertyBinding::Binding(k.into(), v.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PropertyBinding<ColorU>> for PropertyBinding<ColorF> {
|
||||
fn from(value: PropertyBinding<ColorU>) -> PropertyBinding<ColorF> {
|
||||
match value {
|
||||
PropertyBinding::Value(value) => PropertyBinding::Value(value.into()),
|
||||
PropertyBinding::Binding(k, v) => {
|
||||
PropertyBinding::Binding(k.into(), v.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The current value of an animated property. This is
|
||||
/// supplied by the calling code.
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Serialize, PartialEq)]
|
||||
pub struct PropertyValue<T> {
|
||||
///
|
||||
pub key: PropertyBindingKey<T>,
|
||||
///
|
||||
pub value: T,
|
||||
}
|
||||
|
||||
/// When using `generate_frame()`, a list of `PropertyValue` structures
|
||||
/// can optionally be supplied to provide the current value of any
|
||||
/// animated properties.
|
||||
#[derive(Clone, Deserialize, Serialize, Debug, PartialEq, Default)]
|
||||
pub struct DynamicProperties {
|
||||
///
|
||||
pub transforms: Vec<PropertyValue<LayoutTransform>>,
|
||||
/// opacity
|
||||
pub floats: Vec<PropertyValue<f32>>,
|
||||
/// background color
|
||||
pub colors: Vec<PropertyValue<ColorF>>,
|
||||
}
|
||||
|
||||
/// A C function that takes a pointer to a heap allocation and returns its size.
|
||||
///
|
||||
/// This is borrowed from the malloc_size_of crate, upon which we want to avoid
|
||||
/// a dependency from WebRender.
|
||||
pub type VoidPtrToSizeFn = unsafe extern "C" fn(ptr: *const c_void) -> usize;
|
||||
|
||||
bitflags! {
|
||||
/// Flags to enable/disable various builtin debugging tools.
|
||||
#[repr(C)]
|
||||
#[derive(Default, Deserialize, MallocSizeOf, Serialize)]
|
||||
pub struct DebugFlags: u32 {
|
||||
/// Display the frame profiler on screen.
|
||||
const PROFILER_DBG = 1 << 0;
|
||||
/// Display intermediate render targets on screen.
|
||||
const RENDER_TARGET_DBG = 1 << 1;
|
||||
/// Display all texture cache pages on screen.
|
||||
const TEXTURE_CACHE_DBG = 1 << 2;
|
||||
/// Display GPU timing results.
|
||||
const GPU_TIME_QUERIES = 1 << 3;
|
||||
/// Query the number of pixels that pass the depth test divided and show it
|
||||
/// in the profiler as a percentage of the number of pixels in the screen
|
||||
/// (window width times height).
|
||||
const GPU_SAMPLE_QUERIES = 1 << 4;
|
||||
/// Render each quad with their own draw call.
|
||||
///
|
||||
/// Terrible for performance but can help with understanding the drawing
|
||||
/// order when inspecting renderdoc or apitrace recordings.
|
||||
const DISABLE_BATCHING = 1 << 5;
|
||||
/// Display the pipeline epochs.
|
||||
const EPOCHS = 1 << 6;
|
||||
/// Print driver messages to stdout.
|
||||
const ECHO_DRIVER_MESSAGES = 1 << 7;
|
||||
/// Show an overlay displaying overdraw amount.
|
||||
const SHOW_OVERDRAW = 1 << 8;
|
||||
/// Display the contents of GPU cache.
|
||||
const GPU_CACHE_DBG = 1 << 9;
|
||||
/// Clear evicted parts of the texture cache for debugging purposes.
|
||||
const TEXTURE_CACHE_DBG_CLEAR_EVICTED = 1 << 10;
|
||||
/// Show picture caching debug overlay
|
||||
const PICTURE_CACHING_DBG = 1 << 11;
|
||||
/// Highlight all primitives with colors based on kind.
|
||||
const PRIMITIVE_DBG = 1 << 12;
|
||||
/// Draw a zoom widget showing part of the framebuffer zoomed in.
|
||||
const ZOOM_DBG = 1 << 13;
|
||||
/// Scale the debug renderer down for a smaller screen. This will disrupt
|
||||
/// any mapping between debug display items and page content, so shouldn't
|
||||
/// be used with overlays like the picture caching or primitive display.
|
||||
const SMALL_SCREEN = 1 << 14;
|
||||
/// Disable various bits of the WebRender pipeline, to help narrow
|
||||
/// down where slowness might be coming from.
|
||||
const DISABLE_OPAQUE_PASS = 1 << 15;
|
||||
///
|
||||
const DISABLE_ALPHA_PASS = 1 << 16;
|
||||
///
|
||||
const DISABLE_CLIP_MASKS = 1 << 17;
|
||||
///
|
||||
const DISABLE_TEXT_PRIMS = 1 << 18;
|
||||
///
|
||||
const DISABLE_GRADIENT_PRIMS = 1 << 19;
|
||||
///
|
||||
const OBSCURE_IMAGES = 1 << 20;
|
||||
/// Taint the transparent area of the glyphs with a random opacity to easily
|
||||
/// see when glyphs are re-rasterized.
|
||||
const GLYPH_FLASHING = 1 << 21;
|
||||
/// The profiler only displays information that is out of the ordinary.
|
||||
const SMART_PROFILER = 1 << 22;
|
||||
/// If set, dump picture cache invalidation debug to console.
|
||||
const INVALIDATION_DBG = 1 << 23;
|
||||
/// Log tile cache to memory for later saving as part of wr-capture
|
||||
const TILE_CACHE_LOGGING_DBG = 1 << 24;
|
||||
/// Collect and dump profiler statistics to captures.
|
||||
const PROFILER_CAPTURE = (1 as u32) << 25; // need "as u32" until we have cbindgen#556
|
||||
/// Invalidate picture tiles every frames (useful when inspecting GPU work in external tools).
|
||||
const FORCE_PICTURE_INVALIDATION = (1 as u32) << 26;
|
||||
const USE_BATCHED_TEXTURE_UPLOADS = (1 as u32) << 27;
|
||||
const USE_DRAW_CALLS_FOR_TEXTURE_COPY = (1 as u32) << 28;
|
||||
}
|
||||
}
|
||||
|
||||
/// Information specific to a primitive type that
|
||||
/// uniquely identifies a primitive template by key.
|
||||
#[derive(Debug, Clone, Eq, MallocSizeOf, PartialEq, Hash, Serialize, Deserialize)]
|
||||
pub enum PrimitiveKeyKind {
|
||||
/// Clear an existing rect, used for special effects on some platforms.
|
||||
Clear,
|
||||
///
|
||||
Rectangle {
|
||||
///
|
||||
color: PropertyBinding<ColorU>,
|
||||
},
|
||||
}
|
||||
|
||||
///
|
||||
#[derive(Clone)]
|
||||
pub struct ScrollNodeState {
|
||||
///
|
||||
pub id: ExternalScrollId,
|
||||
///
|
||||
pub scroll_offset: LayoutVector2D,
|
||||
}
|
||||
|
||||
///
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum ScrollLocation {
|
||||
/// Scroll by a certain amount.
|
||||
Delta(LayoutVector2D),
|
||||
/// Scroll to very top of element.
|
||||
Start,
|
||||
/// Scroll to very bottom of element.
|
||||
End,
|
||||
}
|
||||
|
||||
/// Represents a zoom factor.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct ZoomFactor(f32);
|
||||
|
||||
impl ZoomFactor {
|
||||
/// Construct a new zoom factor.
|
||||
pub fn new(scale: f32) -> Self {
|
||||
ZoomFactor(scale)
|
||||
}
|
||||
|
||||
/// Get the zoom factor as an untyped float.
|
||||
pub fn get(self) -> f32 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// Crash annotations included in crash reports.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum CrashAnnotation {
|
||||
CompileShader = 0,
|
||||
DrawShader = 1,
|
||||
}
|
||||
|
||||
/// Handler to expose support for annotating crash reports.
|
||||
pub trait CrashAnnotator : Send {
|
||||
fn set(&self, annotation: CrashAnnotation, value: &std::ffi::CStr);
|
||||
fn clear(&self, annotation: CrashAnnotation);
|
||||
fn box_clone(&self) -> Box<dyn CrashAnnotator>;
|
||||
}
|
||||
|
||||
impl Clone for Box<dyn CrashAnnotator> {
|
||||
fn clone(&self) -> Box<dyn CrashAnnotator> {
|
||||
self.box_clone()
|
||||
}
|
||||
}
|
||||
|
||||
/// Guard to add a crash annotation at creation, and clear it at destruction.
|
||||
pub struct CrashAnnotatorGuard<'a> {
|
||||
annotator: &'a Option<Box<dyn CrashAnnotator>>,
|
||||
annotation: CrashAnnotation,
|
||||
}
|
||||
|
||||
impl<'a> CrashAnnotatorGuard<'a> {
|
||||
pub fn new(
|
||||
annotator: &'a Option<Box<dyn CrashAnnotator>>,
|
||||
annotation: CrashAnnotation,
|
||||
value: &std::ffi::CStr,
|
||||
) -> Self {
|
||||
if let Some(ref annotator) = annotator {
|
||||
annotator.set(annotation, value);
|
||||
}
|
||||
Self {
|
||||
annotator,
|
||||
annotation,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Drop for CrashAnnotatorGuard<'a> {
|
||||
fn drop(&mut self) {
|
||||
if let Some(ref annotator) = self.annotator {
|
||||
annotator.clear(self.annotation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
327
third_party/webrender/webrender_api/src/resources.rs
vendored
327
third_party/webrender/webrender_api/src/resources.rs
vendored
|
@ -1,327 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use crate::{BlobImageKey, ImageDescriptor, DirtyRect, TileSize, ResourceUpdate};
|
||||
use crate::{BlobImageHandler, AsyncBlobImageRasterizer, BlobImageData, BlobImageParams};
|
||||
use crate::{BlobImageRequest, BlobImageDescriptor, BlobImageResources, TransactionMsg};
|
||||
use crate::{FontKey, FontTemplate, FontInstanceData, FontInstanceKey, AddFont};
|
||||
use crate::image_tiling::*;
|
||||
use crate::units::*;
|
||||
use crate::font::SharedFontInstanceMap;
|
||||
use crate::euclid::{point2, size2};
|
||||
|
||||
pub const DEFAULT_TILE_SIZE: TileSize = 512;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// We use this to generate the async blob rendering requests.
|
||||
struct BlobImageTemplate {
|
||||
descriptor: ImageDescriptor,
|
||||
tile_size: TileSize,
|
||||
dirty_rect: BlobDirtyRect,
|
||||
/// See ImageResource::visible_rect.
|
||||
visible_rect: DeviceIntRect,
|
||||
// If the active rect of the blob changes, this represents the
|
||||
// range of tiles that remain valid. This must be taken into
|
||||
// account in addition to the valid rect when submitting blob
|
||||
// rasterization requests.
|
||||
// `None` means the bounds have not changed (tiles are still valid).
|
||||
// `Some(TileRange::zero())` means all of the tiles are invalid.
|
||||
valid_tiles_after_bounds_change: Option<TileRange>,
|
||||
}
|
||||
|
||||
struct FontResources {
|
||||
templates: HashMap<FontKey, FontTemplate>,
|
||||
instances: SharedFontInstanceMap,
|
||||
}
|
||||
|
||||
pub struct ApiResources {
|
||||
blob_image_templates: HashMap<BlobImageKey, BlobImageTemplate>,
|
||||
pub blob_image_handler: Option<Box<dyn BlobImageHandler>>,
|
||||
fonts: FontResources,
|
||||
}
|
||||
|
||||
impl BlobImageResources for FontResources {
|
||||
fn get_font_data(&self, key: FontKey) -> &FontTemplate {
|
||||
self.templates.get(&key).unwrap()
|
||||
}
|
||||
fn get_font_instance_data(&self, key: FontInstanceKey) -> Option<FontInstanceData> {
|
||||
self.instances.get_font_instance_data(key)
|
||||
}
|
||||
}
|
||||
|
||||
impl ApiResources {
|
||||
pub fn new(
|
||||
blob_image_handler: Option<Box<dyn BlobImageHandler>>,
|
||||
instances: SharedFontInstanceMap,
|
||||
) -> Self {
|
||||
ApiResources {
|
||||
blob_image_templates: HashMap::new(),
|
||||
blob_image_handler,
|
||||
fonts: FontResources {
|
||||
templates: HashMap::new(),
|
||||
instances,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_shared_font_instances(&self) -> SharedFontInstanceMap {
|
||||
self.fonts.instances.clone()
|
||||
}
|
||||
|
||||
pub fn update(&mut self, transaction: &mut TransactionMsg) {
|
||||
let mut blobs_to_rasterize = Vec::new();
|
||||
for update in &transaction.resource_updates {
|
||||
match *update {
|
||||
ResourceUpdate::AddBlobImage(ref img) => {
|
||||
self.blob_image_handler
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.add(img.key, Arc::clone(&img.data), &img.visible_rect, img.tile_size);
|
||||
|
||||
self.blob_image_templates.insert(
|
||||
img.key,
|
||||
BlobImageTemplate {
|
||||
descriptor: img.descriptor,
|
||||
tile_size: img.tile_size,
|
||||
dirty_rect: DirtyRect::All,
|
||||
valid_tiles_after_bounds_change: None,
|
||||
visible_rect: img.visible_rect,
|
||||
},
|
||||
);
|
||||
blobs_to_rasterize.push(img.key);
|
||||
}
|
||||
ResourceUpdate::UpdateBlobImage(ref img) => {
|
||||
debug_assert_eq!(img.visible_rect.size, img.descriptor.size);
|
||||
self.update_blob_image(
|
||||
img.key,
|
||||
Some(&img.descriptor),
|
||||
Some(&img.dirty_rect),
|
||||
Some(Arc::clone(&img.data)),
|
||||
&img.visible_rect,
|
||||
);
|
||||
blobs_to_rasterize.push(img.key);
|
||||
}
|
||||
ResourceUpdate::DeleteBlobImage(key) => {
|
||||
self.blob_image_templates.remove(&key);
|
||||
}
|
||||
ResourceUpdate::SetBlobImageVisibleArea(ref key, ref area) => {
|
||||
self.update_blob_image(*key, None, None, None, area);
|
||||
blobs_to_rasterize.push(*key);
|
||||
}
|
||||
ResourceUpdate::AddFont(ref font) => {
|
||||
match font {
|
||||
AddFont::Raw(key, bytes, index) => {
|
||||
self.fonts.templates.insert(
|
||||
*key,
|
||||
FontTemplate::Raw(Arc::clone(bytes), *index),
|
||||
);
|
||||
}
|
||||
AddFont::Native(key, native_font_handle) => {
|
||||
self.fonts.templates.insert(
|
||||
*key,
|
||||
FontTemplate::Native(native_font_handle.clone()),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
ResourceUpdate::AddFontInstance(ref instance) => {
|
||||
// TODO(nical): Don't clone these.
|
||||
self.fonts.instances.add_font_instance(
|
||||
instance.key,
|
||||
instance.font_key,
|
||||
instance.glyph_size,
|
||||
instance.options.clone(),
|
||||
instance.platform_options.clone(),
|
||||
instance.variations.clone(),
|
||||
);
|
||||
}
|
||||
ResourceUpdate::DeleteFont(key) => {
|
||||
self.fonts.templates.remove(&key);
|
||||
if let Some(ref mut handler) = self.blob_image_handler {
|
||||
handler.delete_font(key);
|
||||
}
|
||||
}
|
||||
ResourceUpdate::DeleteFontInstance(key) => {
|
||||
// We will delete from the shared font instance map in the resource cache
|
||||
// after scene swap.
|
||||
|
||||
if let Some(ref mut r) = self.blob_image_handler {
|
||||
r.delete_font_instance(key);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let (rasterizer, requests) = self.create_blob_scene_builder_requests(&blobs_to_rasterize);
|
||||
transaction.blob_rasterizer = rasterizer;
|
||||
transaction.blob_requests = requests;
|
||||
}
|
||||
|
||||
pub fn enable_multithreading(&mut self, enable: bool) {
|
||||
if let Some(ref mut handler) = self.blob_image_handler {
|
||||
handler.enable_multithreading(enable);
|
||||
}
|
||||
}
|
||||
|
||||
fn update_blob_image(
|
||||
&mut self,
|
||||
key: BlobImageKey,
|
||||
descriptor: Option<&ImageDescriptor>,
|
||||
dirty_rect: Option<&BlobDirtyRect>,
|
||||
data: Option<Arc<BlobImageData>>,
|
||||
visible_rect: &DeviceIntRect,
|
||||
) {
|
||||
if let Some(data) = data {
|
||||
let dirty_rect = dirty_rect.unwrap();
|
||||
self.blob_image_handler.as_mut().unwrap().update(key, data, visible_rect, dirty_rect);
|
||||
}
|
||||
|
||||
let image = self.blob_image_templates
|
||||
.get_mut(&key)
|
||||
.expect("Attempt to update non-existent blob image");
|
||||
|
||||
let mut valid_tiles_after_bounds_change = compute_valid_tiles_if_bounds_change(
|
||||
&image.visible_rect,
|
||||
visible_rect,
|
||||
image.tile_size,
|
||||
);
|
||||
|
||||
match (image.valid_tiles_after_bounds_change, valid_tiles_after_bounds_change) {
|
||||
(Some(old), Some(ref mut new)) => {
|
||||
*new = new.intersection(&old).unwrap_or_else(TileRange::zero);
|
||||
}
|
||||
(Some(old), None) => {
|
||||
valid_tiles_after_bounds_change = Some(old);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let blob_size = visible_rect.size;
|
||||
|
||||
if let Some(descriptor) = descriptor {
|
||||
image.descriptor = *descriptor;
|
||||
} else {
|
||||
// make sure the descriptor size matches the visible rect.
|
||||
// This might not be necessary but let's stay on the safe side.
|
||||
image.descriptor.size = blob_size;
|
||||
}
|
||||
|
||||
if let Some(dirty_rect) = dirty_rect {
|
||||
image.dirty_rect = image.dirty_rect.union(dirty_rect);
|
||||
}
|
||||
|
||||
image.valid_tiles_after_bounds_change = valid_tiles_after_bounds_change;
|
||||
image.visible_rect = *visible_rect;
|
||||
}
|
||||
|
||||
pub fn create_blob_scene_builder_requests(
|
||||
&mut self,
|
||||
keys: &[BlobImageKey]
|
||||
) -> (Option<Box<dyn AsyncBlobImageRasterizer>>, Vec<BlobImageParams>) {
|
||||
if self.blob_image_handler.is_none() || keys.is_empty() {
|
||||
return (None, Vec::new());
|
||||
}
|
||||
|
||||
let mut blob_request_params = Vec::new();
|
||||
for key in keys {
|
||||
let template = self.blob_image_templates.get_mut(key).unwrap();
|
||||
|
||||
// If we know that only a portion of the blob image is in the viewport,
|
||||
// only request these visible tiles since blob images can be huge.
|
||||
let tiles = compute_tile_range(
|
||||
&template.visible_rect,
|
||||
template.tile_size,
|
||||
);
|
||||
|
||||
// Don't request tiles that weren't invalidated.
|
||||
let dirty_tiles = match template.dirty_rect {
|
||||
DirtyRect::Partial(dirty_rect) => {
|
||||
compute_tile_range(
|
||||
&dirty_rect.cast_unit(),
|
||||
template.tile_size,
|
||||
)
|
||||
}
|
||||
DirtyRect::All => tiles,
|
||||
};
|
||||
|
||||
for_each_tile_in_range(&tiles, |tile| {
|
||||
let still_valid = template.valid_tiles_after_bounds_change
|
||||
.map(|valid_tiles| valid_tiles.contains(tile))
|
||||
.unwrap_or(true);
|
||||
|
||||
if still_valid && !dirty_tiles.contains(tile) {
|
||||
return;
|
||||
}
|
||||
|
||||
let descriptor = BlobImageDescriptor {
|
||||
rect: compute_tile_rect(
|
||||
&template.visible_rect,
|
||||
template.tile_size,
|
||||
tile,
|
||||
).cast_unit(),
|
||||
format: template.descriptor.format,
|
||||
};
|
||||
|
||||
assert!(descriptor.rect.size.width > 0 && descriptor.rect.size.height > 0);
|
||||
blob_request_params.push(
|
||||
BlobImageParams {
|
||||
request: BlobImageRequest { key: *key, tile },
|
||||
descriptor,
|
||||
dirty_rect: DirtyRect::All,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
template.dirty_rect = DirtyRect::empty();
|
||||
template.valid_tiles_after_bounds_change = None;
|
||||
}
|
||||
|
||||
let handler = self.blob_image_handler.as_mut().unwrap();
|
||||
handler.prepare_resources(&self.fonts, &blob_request_params);
|
||||
(Some(handler.create_blob_rasterizer()), blob_request_params)
|
||||
}
|
||||
}
|
||||
|
||||
fn compute_valid_tiles_if_bounds_change(
|
||||
prev_rect: &DeviceIntRect,
|
||||
new_rect: &DeviceIntRect,
|
||||
tile_size: u16,
|
||||
) -> Option<TileRange> {
|
||||
let intersection = match prev_rect.intersection(new_rect) {
|
||||
Some(rect) => rect,
|
||||
None => {
|
||||
return Some(TileRange::zero());
|
||||
}
|
||||
};
|
||||
|
||||
let left = prev_rect.min_x() != new_rect.min_x();
|
||||
let right = prev_rect.max_x() != new_rect.max_x();
|
||||
let top = prev_rect.min_y() != new_rect.min_y();
|
||||
let bottom = prev_rect.max_y() != new_rect.max_y();
|
||||
|
||||
if !left && !right && !top && !bottom {
|
||||
// Bounds have not changed.
|
||||
return None;
|
||||
}
|
||||
|
||||
let tw = 1.0 / (tile_size as f32);
|
||||
let th = 1.0 / (tile_size as f32);
|
||||
|
||||
let tiles = intersection
|
||||
.cast::<f32>()
|
||||
.scale(tw, th);
|
||||
|
||||
let min_x = if left { f32::ceil(tiles.min_x()) } else { f32::floor(tiles.min_x()) };
|
||||
let min_y = if top { f32::ceil(tiles.min_y()) } else { f32::floor(tiles.min_y()) };
|
||||
let max_x = if right { f32::floor(tiles.max_x()) } else { f32::ceil(tiles.max_x()) };
|
||||
let max_y = if bottom { f32::floor(tiles.max_y()) } else { f32::ceil(tiles.max_y()) };
|
||||
|
||||
Some(TileRange {
|
||||
origin: point2(min_x as i32, min_y as i32),
|
||||
size: size2((max_x - min_x) as i32, (max_y - min_y) as i32),
|
||||
})
|
||||
}
|
|
@ -32,6 +32,7 @@ pub type DeviceIntLength = Length<i32, DevicePixel>;
|
|||
pub type DeviceIntSideOffsets = SideOffsets2D<i32, DevicePixel>;
|
||||
|
||||
pub type DeviceRect = Rect<f32, DevicePixel>;
|
||||
pub type DeviceBox2D = Box2D<f32, DevicePixel>;
|
||||
pub type DevicePoint = Point2D<f32, DevicePixel>;
|
||||
pub type DeviceVector2D = Vector2D<f32, DevicePixel>;
|
||||
pub type DeviceSize = Size2D<f32, DevicePixel>;
|
||||
|
@ -149,7 +150,7 @@ pub type BlobToDeviceTranslation = Translation2D<i32, LayoutPixel, DevicePixel>;
|
|||
/// may grow. Storing them as texel coords and normalizing
|
||||
/// the UVs in the vertex shader means nothing needs to be
|
||||
/// updated on the CPU when the texture size changes.
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct TexelRect {
|
||||
pub uv0: DevicePoint,
|
||||
pub uv1: DevicePoint,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue