webrender/prim_store/
mod.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5use api::{BorderRadius, ClipMode, ColorF, ColorU, RasterSpace};
6use api::{ImageRendering, RepeatMode, PrimitiveFlags};
7use api::{PremultipliedColorF, PropertyBinding, Shadow};
8use api::{PrimitiveKeyKind, FillRule, POLYGON_CLIP_VERTEX_MAX};
9use api::units::*;
10use euclid::{SideOffsets2D, Size2D};
11use malloc_size_of::MallocSizeOf;
12use crate::composite::CompositorSurfaceKind;
13use crate::clip::ClipLeafId;
14use crate::pattern::{Pattern, PatternBuilder, PatternBuilderContext, PatternBuilderState};
15use crate::quad::QuadTileClassifier;
16use crate::segment::EdgeAaSegmentMask;
17use crate::border::BorderSegmentCacheKey;
18use crate::debug_item::{DebugItem, DebugMessage};
19use crate::debug_colors;
20use crate::scene_building::{CreateShadow, IsVisible};
21use crate::frame_builder::FrameBuildingState;
22use glyph_rasterizer::GlyphKey;
23use crate::gpu_cache::{GpuCacheAddress, GpuCacheHandle, GpuDataRequest};
24use crate::gpu_types::{BrushFlags, QuadSegment};
25use crate::intern;
26use crate::picture::PicturePrimitive;
27use crate::render_task_graph::RenderTaskId;
28use crate::resource_cache::ImageProperties;
29use crate::scene::SceneProperties;
30use std::{hash, ops, u32, usize};
31use crate::util::Recycler;
32use crate::internal_types::{FastHashSet, LayoutPrimitiveInfo};
33use crate::visibility::PrimitiveVisibility;
34
35pub mod backdrop;
36pub mod borders;
37pub mod gradient;
38pub mod image;
39pub mod line_dec;
40pub mod picture;
41pub mod text_run;
42pub mod interned;
43
44mod storage;
45
46use backdrop::{BackdropCaptureDataHandle, BackdropRenderDataHandle};
47use borders::{ImageBorderDataHandle, NormalBorderDataHandle};
48use gradient::{LinearGradientPrimitive, LinearGradientDataHandle, RadialGradientDataHandle, ConicGradientDataHandle};
49use image::{ImageDataHandle, ImageInstance, YuvImageDataHandle};
50use line_dec::LineDecorationDataHandle;
51use picture::PictureDataHandle;
52use text_run::{TextRunDataHandle, TextRunPrimitive};
53use crate::box_shadow::BoxShadowDataHandle;
54
55pub const VECS_PER_SEGMENT: usize = 2;
56
57#[cfg_attr(feature = "capture", derive(Serialize))]
58#[cfg_attr(feature = "replay", derive(Deserialize))]
59#[derive(Debug, Copy, Clone, MallocSizeOf)]
60pub struct PrimitiveOpacity {
61    pub is_opaque: bool,
62}
63
64impl PrimitiveOpacity {
65    pub fn opaque() -> PrimitiveOpacity {
66        PrimitiveOpacity { is_opaque: true }
67    }
68
69    pub fn translucent() -> PrimitiveOpacity {
70        PrimitiveOpacity { is_opaque: false }
71    }
72
73    pub fn from_alpha(alpha: f32) -> PrimitiveOpacity {
74        PrimitiveOpacity {
75            is_opaque: alpha >= 1.0,
76        }
77    }
78}
79
80/// For external images, it's not possible to know the
81/// UV coords of the image (or the image data itself)
82/// until the render thread receives the frame and issues
83/// callbacks to the client application. For external
84/// images that are visible, a DeferredResolve is created
85/// that is stored in the frame. This allows the render
86/// thread to iterate this list and update any changed
87/// texture data and update the UV rect. Any filtering
88/// is handled externally for NativeTexture external
89/// images.
90#[cfg_attr(feature = "capture", derive(Serialize))]
91#[cfg_attr(feature = "replay", derive(Deserialize))]
92pub struct DeferredResolve {
93    pub address: GpuCacheAddress,
94    pub image_properties: ImageProperties,
95    pub rendering: ImageRendering,
96}
97
98#[derive(Debug, Copy, Clone, PartialEq)]
99#[cfg_attr(feature = "capture", derive(Serialize))]
100pub struct ClipTaskIndex(pub u32);
101
102impl ClipTaskIndex {
103    pub const INVALID: ClipTaskIndex = ClipTaskIndex(0);
104}
105
106#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, MallocSizeOf, Ord, PartialOrd)]
107#[cfg_attr(feature = "capture", derive(Serialize))]
108#[cfg_attr(feature = "replay", derive(Deserialize))]
109pub struct PictureIndex(pub usize);
110
111impl PictureIndex {
112    pub const INVALID: PictureIndex = PictureIndex(!0);
113}
114
115#[cfg_attr(feature = "capture", derive(Serialize))]
116#[cfg_attr(feature = "replay", derive(Deserialize))]
117#[derive(Copy, Debug, Clone, MallocSizeOf, PartialEq)]
118pub struct RectangleKey {
119    pub x0: f32,
120    pub y0: f32,
121    pub x1: f32,
122    pub y1: f32,
123}
124
125impl RectangleKey {
126    pub fn intersects(&self, other: &Self) -> bool {
127        self.x0 < other.x1
128            && other.x0 < self.x1
129            && self.y0 < other.y1
130            && other.y0 < self.y1
131    }
132}
133
134impl Eq for RectangleKey {}
135
136impl hash::Hash for RectangleKey {
137    fn hash<H: hash::Hasher>(&self, state: &mut H) {
138        self.x0.to_bits().hash(state);
139        self.y0.to_bits().hash(state);
140        self.x1.to_bits().hash(state);
141        self.y1.to_bits().hash(state);
142    }
143}
144
145impl From<RectangleKey> for LayoutRect {
146    fn from(key: RectangleKey) -> LayoutRect {
147        LayoutRect {
148            min: LayoutPoint::new(key.x0, key.y0),
149            max: LayoutPoint::new(key.x1, key.y1),
150        }
151    }
152}
153
154impl From<RectangleKey> for WorldRect {
155    fn from(key: RectangleKey) -> WorldRect {
156        WorldRect {
157            min: WorldPoint::new(key.x0, key.y0),
158            max: WorldPoint::new(key.x1, key.y1),
159        }
160    }
161}
162
163impl From<LayoutRect> for RectangleKey {
164    fn from(rect: LayoutRect) -> RectangleKey {
165        RectangleKey {
166            x0: rect.min.x,
167            y0: rect.min.y,
168            x1: rect.max.x,
169            y1: rect.max.y,
170        }
171    }
172}
173
174impl From<PictureRect> for RectangleKey {
175    fn from(rect: PictureRect) -> RectangleKey {
176        RectangleKey {
177            x0: rect.min.x,
178            y0: rect.min.y,
179            x1: rect.max.x,
180            y1: rect.max.y,
181        }
182    }
183}
184
185impl From<WorldRect> for RectangleKey {
186    fn from(rect: WorldRect) -> RectangleKey {
187        RectangleKey {
188            x0: rect.min.x,
189            y0: rect.min.y,
190            x1: rect.max.x,
191            y1: rect.max.y,
192        }
193    }
194}
195
196/// To create a fixed-size representation of a polygon, we use a fixed
197/// number of points. Our initialization method restricts us to values
198/// <= 32. If our constant POLYGON_CLIP_VERTEX_MAX is > 32, the Rust
199/// compiler will complain.
200#[cfg_attr(feature = "capture", derive(Serialize))]
201#[cfg_attr(feature = "replay", derive(Deserialize))]
202#[derive(Copy, Debug, Clone, Hash, MallocSizeOf, PartialEq)]
203pub struct PolygonKey {
204    pub point_count: u8,
205    pub points: [PointKey; POLYGON_CLIP_VERTEX_MAX],
206    pub fill_rule: FillRule,
207}
208
209impl PolygonKey {
210    pub fn new(
211        points_layout: &Vec<LayoutPoint>,
212        fill_rule: FillRule,
213    ) -> Self {
214        // We have to fill fixed-size arrays with data from a Vec.
215        // We'll do this by initializing the arrays to known-good
216        // values then overwriting those values as long as our
217        // iterator provides values.
218        let mut points: [PointKey; POLYGON_CLIP_VERTEX_MAX] = [PointKey { x: 0.0, y: 0.0}; POLYGON_CLIP_VERTEX_MAX];
219
220        let mut point_count: u8 = 0;
221        for (src, dest) in points_layout.iter().zip(points.iter_mut()) {
222            *dest = (*src as LayoutPoint).into();
223            point_count = point_count + 1;
224        }
225
226        PolygonKey {
227            point_count,
228            points,
229            fill_rule,
230        }
231    }
232}
233
234impl Eq for PolygonKey {}
235
236/// A hashable SideOffset2D that can be used in primitive keys.
237#[cfg_attr(feature = "capture", derive(Serialize))]
238#[cfg_attr(feature = "replay", derive(Deserialize))]
239#[derive(Debug, Clone, MallocSizeOf, PartialEq)]
240pub struct SideOffsetsKey {
241    pub top: f32,
242    pub right: f32,
243    pub bottom: f32,
244    pub left: f32,
245}
246
247impl Eq for SideOffsetsKey {}
248
249impl hash::Hash for SideOffsetsKey {
250    fn hash<H: hash::Hasher>(&self, state: &mut H) {
251        self.top.to_bits().hash(state);
252        self.right.to_bits().hash(state);
253        self.bottom.to_bits().hash(state);
254        self.left.to_bits().hash(state);
255    }
256}
257
258impl From<SideOffsetsKey> for LayoutSideOffsets {
259    fn from(key: SideOffsetsKey) -> LayoutSideOffsets {
260        LayoutSideOffsets::new(
261            key.top,
262            key.right,
263            key.bottom,
264            key.left,
265        )
266    }
267}
268
269impl<U> From<SideOffsets2D<f32, U>> for SideOffsetsKey {
270    fn from(offsets: SideOffsets2D<f32, U>) -> SideOffsetsKey {
271        SideOffsetsKey {
272            top: offsets.top,
273            right: offsets.right,
274            bottom: offsets.bottom,
275            left: offsets.left,
276        }
277    }
278}
279
280/// A hashable size for using as a key during primitive interning.
281#[cfg_attr(feature = "capture", derive(Serialize))]
282#[cfg_attr(feature = "replay", derive(Deserialize))]
283#[derive(Copy, Debug, Clone, MallocSizeOf, PartialEq)]
284pub struct SizeKey {
285    w: f32,
286    h: f32,
287}
288
289impl Eq for SizeKey {}
290
291impl hash::Hash for SizeKey {
292    fn hash<H: hash::Hasher>(&self, state: &mut H) {
293        self.w.to_bits().hash(state);
294        self.h.to_bits().hash(state);
295    }
296}
297
298impl From<SizeKey> for LayoutSize {
299    fn from(key: SizeKey) -> LayoutSize {
300        LayoutSize::new(key.w, key.h)
301    }
302}
303
304impl<U> From<Size2D<f32, U>> for SizeKey {
305    fn from(size: Size2D<f32, U>) -> SizeKey {
306        SizeKey {
307            w: size.width,
308            h: size.height,
309        }
310    }
311}
312
313/// A hashable vec for using as a key during primitive interning.
314#[cfg_attr(feature = "capture", derive(Serialize))]
315#[cfg_attr(feature = "replay", derive(Deserialize))]
316#[derive(Copy, Debug, Clone, MallocSizeOf, PartialEq)]
317pub struct VectorKey {
318    pub x: f32,
319    pub y: f32,
320}
321
322impl Eq for VectorKey {}
323
324impl hash::Hash for VectorKey {
325    fn hash<H: hash::Hasher>(&self, state: &mut H) {
326        self.x.to_bits().hash(state);
327        self.y.to_bits().hash(state);
328    }
329}
330
331impl From<VectorKey> for LayoutVector2D {
332    fn from(key: VectorKey) -> LayoutVector2D {
333        LayoutVector2D::new(key.x, key.y)
334    }
335}
336
337impl From<VectorKey> for WorldVector2D {
338    fn from(key: VectorKey) -> WorldVector2D {
339        WorldVector2D::new(key.x, key.y)
340    }
341}
342
343impl From<LayoutVector2D> for VectorKey {
344    fn from(vec: LayoutVector2D) -> VectorKey {
345        VectorKey {
346            x: vec.x,
347            y: vec.y,
348        }
349    }
350}
351
352impl From<WorldVector2D> for VectorKey {
353    fn from(vec: WorldVector2D) -> VectorKey {
354        VectorKey {
355            x: vec.x,
356            y: vec.y,
357        }
358    }
359}
360
361/// A hashable point for using as a key during primitive interning.
362#[cfg_attr(feature = "capture", derive(Serialize))]
363#[cfg_attr(feature = "replay", derive(Deserialize))]
364#[derive(Debug, Copy, Clone, MallocSizeOf, PartialEq)]
365pub struct PointKey {
366    pub x: f32,
367    pub y: f32,
368}
369
370impl Eq for PointKey {}
371
372impl hash::Hash for PointKey {
373    fn hash<H: hash::Hasher>(&self, state: &mut H) {
374        self.x.to_bits().hash(state);
375        self.y.to_bits().hash(state);
376    }
377}
378
379impl From<PointKey> for LayoutPoint {
380    fn from(key: PointKey) -> LayoutPoint {
381        LayoutPoint::new(key.x, key.y)
382    }
383}
384
385impl From<LayoutPoint> for PointKey {
386    fn from(p: LayoutPoint) -> PointKey {
387        PointKey {
388            x: p.x,
389            y: p.y,
390        }
391    }
392}
393
394impl From<PicturePoint> for PointKey {
395    fn from(p: PicturePoint) -> PointKey {
396        PointKey {
397            x: p.x,
398            y: p.y,
399        }
400    }
401}
402
403impl From<WorldPoint> for PointKey {
404    fn from(p: WorldPoint) -> PointKey {
405        PointKey {
406            x: p.x,
407            y: p.y,
408        }
409    }
410}
411
412/// A hashable float for using as a key during primitive interning.
413#[cfg_attr(feature = "capture", derive(Serialize))]
414#[cfg_attr(feature = "replay", derive(Deserialize))]
415#[derive(Debug, Copy, Clone, MallocSizeOf, PartialEq)]
416pub struct FloatKey(f32);
417
418impl Eq for FloatKey {}
419
420impl hash::Hash for FloatKey {
421    fn hash<H: hash::Hasher>(&self, state: &mut H) {
422        self.0.to_bits().hash(state);
423    }
424}
425
426
427#[cfg_attr(feature = "capture", derive(Serialize))]
428#[cfg_attr(feature = "replay", derive(Deserialize))]
429#[derive(Debug, Clone, Eq, MallocSizeOf, PartialEq, Hash)]
430pub struct PrimKeyCommonData {
431    pub flags: PrimitiveFlags,
432    pub prim_rect: RectangleKey,
433}
434
435impl From<&LayoutPrimitiveInfo> for PrimKeyCommonData {
436    fn from(info: &LayoutPrimitiveInfo) -> Self {
437        PrimKeyCommonData {
438            flags: info.flags,
439            prim_rect: info.rect.into(),
440        }
441    }
442}
443
444#[cfg_attr(feature = "capture", derive(Serialize))]
445#[cfg_attr(feature = "replay", derive(Deserialize))]
446#[derive(Debug, Clone, Eq, MallocSizeOf, PartialEq, Hash)]
447pub struct PrimKey<T: MallocSizeOf> {
448    pub common: PrimKeyCommonData,
449    pub kind: T,
450}
451
452#[cfg_attr(feature = "capture", derive(Serialize))]
453#[cfg_attr(feature = "replay", derive(Deserialize))]
454#[derive(Debug, Clone, Eq, MallocSizeOf, PartialEq, Hash)]
455pub struct PrimitiveKey {
456    pub common: PrimKeyCommonData,
457    pub kind: PrimitiveKeyKind,
458}
459
460impl PrimitiveKey {
461    pub fn new(
462        info: &LayoutPrimitiveInfo,
463        kind: PrimitiveKeyKind,
464    ) -> Self {
465        PrimitiveKey {
466            common: info.into(),
467            kind,
468        }
469    }
470}
471
472impl intern::InternDebug for PrimitiveKey {}
473
474/// The shared information for a given primitive. This is interned and retained
475/// both across frames and display lists, by comparing the matching PrimitiveKey.
476#[cfg_attr(feature = "capture", derive(Serialize))]
477#[cfg_attr(feature = "replay", derive(Deserialize))]
478#[derive(MallocSizeOf)]
479pub enum PrimitiveTemplateKind {
480    Rectangle {
481        color: PropertyBinding<ColorF>,
482    },
483    Clear,
484}
485
486impl PrimitiveTemplateKind {
487    /// Write any GPU blocks for the primitive template to the given request object.
488    pub fn write_prim_gpu_blocks(
489        &self,
490        request: &mut GpuDataRequest,
491        scene_properties: &SceneProperties,
492    ) {
493        match *self {
494            PrimitiveTemplateKind::Clear => {
495                // Opaque black with operator dest out
496                request.push(PremultipliedColorF::BLACK);
497            }
498            PrimitiveTemplateKind::Rectangle { ref color, .. } => {
499                request.push(scene_properties.resolve_color(color).premultiplied())
500            }
501        }
502    }
503}
504
505/// Construct the primitive template data from a primitive key. This
506/// is invoked when a primitive key is created and the interner
507/// doesn't currently contain a primitive with this key.
508impl From<PrimitiveKeyKind> for PrimitiveTemplateKind {
509    fn from(kind: PrimitiveKeyKind) -> Self {
510        match kind {
511            PrimitiveKeyKind::Clear => {
512                PrimitiveTemplateKind::Clear
513            }
514            PrimitiveKeyKind::Rectangle { color, .. } => {
515                PrimitiveTemplateKind::Rectangle {
516                    color: color.into(),
517                }
518            }
519        }
520    }
521}
522
523#[cfg_attr(feature = "capture", derive(Serialize))]
524#[cfg_attr(feature = "replay", derive(Deserialize))]
525#[derive(MallocSizeOf)]
526#[derive(Debug)]
527pub struct PrimTemplateCommonData {
528    pub flags: PrimitiveFlags,
529    pub may_need_repetition: bool,
530    pub prim_rect: LayoutRect,
531    pub opacity: PrimitiveOpacity,
532    /// The GPU cache handle for a primitive template. Since this structure
533    /// is retained across display lists by interning, this GPU cache handle
534    /// also remains valid, which reduces the number of updates to the GPU
535    /// cache when a new display list is processed.
536    pub gpu_cache_handle: GpuCacheHandle,
537    /// Specifies the edges that are *allowed* to have anti-aliasing.
538    /// In other words EdgeAaSegmentFlags::all() does not necessarily mean all edges will
539    /// be anti-aliased, only that they could be.
540    ///
541    /// Use this to force disable anti-alasing on edges of the primitives.
542    pub edge_aa_mask: EdgeAaSegmentMask,
543}
544
545impl PrimTemplateCommonData {
546    pub fn with_key_common(common: PrimKeyCommonData) -> Self {
547        PrimTemplateCommonData {
548            flags: common.flags,
549            may_need_repetition: true,
550            prim_rect: common.prim_rect.into(),
551            gpu_cache_handle: GpuCacheHandle::new(),
552            opacity: PrimitiveOpacity::translucent(),
553            edge_aa_mask: EdgeAaSegmentMask::all(),
554        }
555    }
556}
557
558#[cfg_attr(feature = "capture", derive(Serialize))]
559#[cfg_attr(feature = "replay", derive(Deserialize))]
560#[derive(MallocSizeOf)]
561pub struct PrimTemplate<T> {
562    pub common: PrimTemplateCommonData,
563    pub kind: T,
564}
565
566#[cfg_attr(feature = "capture", derive(Serialize))]
567#[cfg_attr(feature = "replay", derive(Deserialize))]
568#[derive(MallocSizeOf)]
569pub struct PrimitiveTemplate {
570    pub common: PrimTemplateCommonData,
571    pub kind: PrimitiveTemplateKind,
572}
573
574impl PatternBuilder for PrimitiveTemplate {
575    fn build(
576        &self,
577        _sub_rect: Option<DeviceRect>,
578        ctx: &PatternBuilderContext,
579        _state: &mut PatternBuilderState,
580    ) -> crate::pattern::Pattern {
581        match self.kind {
582            PrimitiveTemplateKind::Clear => Pattern::clear(),
583            PrimitiveTemplateKind::Rectangle { ref color, .. } => {
584                let color = ctx.scene_properties.resolve_color(color);
585                Pattern::color(color)
586            }
587        }
588    }
589
590    fn get_base_color(
591        &self,
592        ctx: &PatternBuilderContext,
593    ) -> ColorF {
594        match self.kind {
595            PrimitiveTemplateKind::Clear => ColorF::BLACK,
596            PrimitiveTemplateKind::Rectangle { ref color, .. } => {
597                ctx.scene_properties.resolve_color(color)
598            }
599        }
600    }
601
602    fn use_shared_pattern(
603        &self,
604    ) -> bool {
605        true
606    }
607}
608
609impl ops::Deref for PrimitiveTemplate {
610    type Target = PrimTemplateCommonData;
611    fn deref(&self) -> &Self::Target {
612        &self.common
613    }
614}
615
616impl ops::DerefMut for PrimitiveTemplate {
617    fn deref_mut(&mut self) -> &mut Self::Target {
618        &mut self.common
619    }
620}
621
622impl From<PrimitiveKey> for PrimitiveTemplate {
623    fn from(item: PrimitiveKey) -> Self {
624        PrimitiveTemplate {
625            common: PrimTemplateCommonData::with_key_common(item.common),
626            kind: item.kind.into(),
627        }
628    }
629}
630
631impl PrimitiveTemplate {
632    /// Update the GPU cache for a given primitive template. This may be called multiple
633    /// times per frame, by each primitive reference that refers to this interned
634    /// template. The initial request call to the GPU cache ensures that work is only
635    /// done if the cache entry is invalid (due to first use or eviction).
636    pub fn update(
637        &mut self,
638        frame_state: &mut FrameBuildingState,
639        scene_properties: &SceneProperties,
640    ) {
641        if let Some(mut request) = frame_state.gpu_cache.request(&mut self.common.gpu_cache_handle) {
642            self.kind.write_prim_gpu_blocks(&mut request, scene_properties);
643        }
644
645        self.opacity = match self.kind {
646            PrimitiveTemplateKind::Clear => {
647                PrimitiveOpacity::translucent()
648            }
649            PrimitiveTemplateKind::Rectangle { ref color, .. } => {
650                PrimitiveOpacity::from_alpha(scene_properties.resolve_color(color).a)
651            }
652        };
653    }
654}
655
656type PrimitiveDataHandle = intern::Handle<PrimitiveKeyKind>;
657
658impl intern::Internable for PrimitiveKeyKind {
659    type Key = PrimitiveKey;
660    type StoreData = PrimitiveTemplate;
661    type InternData = ();
662    const PROFILE_COUNTER: usize = crate::profiler::INTERNED_PRIMITIVES;
663}
664
665impl InternablePrimitive for PrimitiveKeyKind {
666    fn into_key(
667        self,
668        info: &LayoutPrimitiveInfo,
669    ) -> PrimitiveKey {
670        PrimitiveKey::new(info, self)
671    }
672
673    fn make_instance_kind(
674        key: PrimitiveKey,
675        data_handle: PrimitiveDataHandle,
676        prim_store: &mut PrimitiveStore,
677    ) -> PrimitiveInstanceKind {
678        match key.kind {
679            PrimitiveKeyKind::Clear => {
680                PrimitiveInstanceKind::Clear {
681                    data_handle
682                }
683            }
684            PrimitiveKeyKind::Rectangle { color, .. } => {
685                let color_binding_index = match color {
686                    PropertyBinding::Binding(..) => {
687                        prim_store.color_bindings.push(color)
688                    }
689                    PropertyBinding::Value(..) => ColorBindingIndex::INVALID,
690                };
691                PrimitiveInstanceKind::Rectangle {
692                    data_handle,
693                    segment_instance_index: SegmentInstanceIndex::INVALID,
694                    color_binding_index,
695                    use_legacy_path: false,
696                }
697            }
698        }
699    }
700}
701
702#[derive(Debug, MallocSizeOf)]
703#[cfg_attr(feature = "capture", derive(Serialize))]
704#[cfg_attr(feature = "replay", derive(Deserialize))]
705pub struct VisibleMaskImageTile {
706    pub tile_offset: TileOffset,
707    pub tile_rect: LayoutRect,
708    pub task_id: RenderTaskId,
709}
710
711#[derive(Debug)]
712#[cfg_attr(feature = "capture", derive(Serialize))]
713pub struct VisibleGradientTile {
714    pub handle: GpuCacheHandle,
715    pub local_rect: LayoutRect,
716    pub local_clip_rect: LayoutRect,
717}
718
719/// Information about how to cache a border segment,
720/// along with the current render task cache entry.
721#[cfg_attr(feature = "capture", derive(Serialize))]
722#[cfg_attr(feature = "replay", derive(Deserialize))]
723#[derive(Debug, MallocSizeOf)]
724pub struct BorderSegmentInfo {
725    pub local_task_size: LayoutSize,
726    pub cache_key: BorderSegmentCacheKey,
727}
728
729/// Represents the visibility state of a segment (wrt clip masks).
730#[cfg_attr(feature = "capture", derive(Serialize))]
731#[derive(Debug, Clone)]
732pub enum ClipMaskKind {
733    /// The segment has a clip mask, specified by the render task.
734    Mask(RenderTaskId),
735    /// The segment has no clip mask.
736    None,
737    /// The segment is made invisible / clipped completely.
738    Clipped,
739}
740
741#[cfg_attr(feature = "capture", derive(Serialize))]
742#[cfg_attr(feature = "replay", derive(Deserialize))]
743#[derive(Debug, Clone, MallocSizeOf)]
744pub struct BrushSegment {
745    pub local_rect: LayoutRect,
746    pub may_need_clip_mask: bool,
747    pub edge_flags: EdgeAaSegmentMask,
748    pub extra_data: [f32; 4],
749    pub brush_flags: BrushFlags,
750}
751
752impl BrushSegment {
753    pub fn new(
754        local_rect: LayoutRect,
755        may_need_clip_mask: bool,
756        edge_flags: EdgeAaSegmentMask,
757        extra_data: [f32; 4],
758        brush_flags: BrushFlags,
759    ) -> Self {
760        Self {
761            local_rect,
762            may_need_clip_mask,
763            edge_flags,
764            extra_data,
765            brush_flags,
766        }
767    }
768}
769
770#[derive(Debug, Clone)]
771#[repr(C)]
772#[cfg_attr(feature = "capture", derive(Serialize))]
773#[cfg_attr(feature = "replay", derive(Deserialize))]
774struct ClipRect {
775    rect: LayoutRect,
776    mode: f32,
777}
778
779#[derive(Debug, Clone)]
780#[repr(C)]
781#[cfg_attr(feature = "capture", derive(Serialize))]
782#[cfg_attr(feature = "replay", derive(Deserialize))]
783struct ClipCorner {
784    rect: LayoutRect,
785    outer_radius_x: f32,
786    outer_radius_y: f32,
787    inner_radius_x: f32,
788    inner_radius_y: f32,
789}
790
791impl ClipCorner {
792    fn uniform(rect: LayoutRect, outer_radius: f32, inner_radius: f32) -> ClipCorner {
793        ClipCorner {
794            rect,
795            outer_radius_x: outer_radius,
796            outer_radius_y: outer_radius,
797            inner_radius_x: inner_radius,
798            inner_radius_y: inner_radius,
799        }
800    }
801}
802
803#[derive(Debug, Clone)]
804#[repr(C)]
805#[cfg_attr(feature = "capture", derive(Serialize))]
806#[cfg_attr(feature = "replay", derive(Deserialize))]
807pub struct ClipData {
808    rect: ClipRect,
809    top_left: ClipCorner,
810    top_right: ClipCorner,
811    bottom_left: ClipCorner,
812    bottom_right: ClipCorner,
813}
814
815impl ClipData {
816    pub fn rounded_rect(size: LayoutSize, radii: &BorderRadius, mode: ClipMode) -> ClipData {
817        // TODO(gw): For simplicity, keep most of the clip GPU structs the
818        //           same as they were, even though the origin is now always
819        //           zero, since they are in the clip's local space. In future,
820        //           we could reduce the GPU cache size of ClipData.
821        let rect = LayoutRect::from_size(size);
822
823        ClipData {
824            rect: ClipRect {
825                rect,
826                mode: mode as u32 as f32,
827            },
828            top_left: ClipCorner {
829                rect: LayoutRect::from_origin_and_size(
830                    LayoutPoint::new(rect.min.x, rect.min.y),
831                    LayoutSize::new(radii.top_left.width, radii.top_left.height),
832                ),
833                outer_radius_x: radii.top_left.width,
834                outer_radius_y: radii.top_left.height,
835                inner_radius_x: 0.0,
836                inner_radius_y: 0.0,
837            },
838            top_right: ClipCorner {
839                rect: LayoutRect::from_origin_and_size(
840                    LayoutPoint::new(
841                        rect.max.x - radii.top_right.width,
842                        rect.min.y,
843                    ),
844                    LayoutSize::new(radii.top_right.width, radii.top_right.height),
845                ),
846                outer_radius_x: radii.top_right.width,
847                outer_radius_y: radii.top_right.height,
848                inner_radius_x: 0.0,
849                inner_radius_y: 0.0,
850            },
851            bottom_left: ClipCorner {
852                rect: LayoutRect::from_origin_and_size(
853                    LayoutPoint::new(
854                        rect.min.x,
855                        rect.max.y - radii.bottom_left.height,
856                    ),
857                    LayoutSize::new(radii.bottom_left.width, radii.bottom_left.height),
858                ),
859                outer_radius_x: radii.bottom_left.width,
860                outer_radius_y: radii.bottom_left.height,
861                inner_radius_x: 0.0,
862                inner_radius_y: 0.0,
863            },
864            bottom_right: ClipCorner {
865                rect: LayoutRect::from_origin_and_size(
866                    LayoutPoint::new(
867                        rect.max.x - radii.bottom_right.width,
868                        rect.max.y - radii.bottom_right.height,
869                    ),
870                    LayoutSize::new(radii.bottom_right.width, radii.bottom_right.height),
871                ),
872                outer_radius_x: radii.bottom_right.width,
873                outer_radius_y: radii.bottom_right.height,
874                inner_radius_x: 0.0,
875                inner_radius_y: 0.0,
876            },
877        }
878    }
879
880    pub fn uniform(size: LayoutSize, radius: f32, mode: ClipMode) -> ClipData {
881        // TODO(gw): For simplicity, keep most of the clip GPU structs the
882        //           same as they were, even though the origin is now always
883        //           zero, since they are in the clip's local space. In future,
884        //           we could reduce the GPU cache size of ClipData.
885        let rect = LayoutRect::from_size(size);
886
887        ClipData {
888            rect: ClipRect {
889                rect,
890                mode: mode as u32 as f32,
891            },
892            top_left: ClipCorner::uniform(
893                LayoutRect::from_origin_and_size(
894                    LayoutPoint::new(rect.min.x, rect.min.y),
895                    LayoutSize::new(radius, radius),
896                ),
897                radius,
898                0.0,
899            ),
900            top_right: ClipCorner::uniform(
901                LayoutRect::from_origin_and_size(
902                    LayoutPoint::new(rect.max.x - radius, rect.min.y),
903                    LayoutSize::new(radius, radius),
904                ),
905                radius,
906                0.0,
907            ),
908            bottom_left: ClipCorner::uniform(
909                LayoutRect::from_origin_and_size(
910                    LayoutPoint::new(rect.min.x, rect.max.y - radius),
911                    LayoutSize::new(radius, radius),
912                ),
913                radius,
914                0.0,
915            ),
916            bottom_right: ClipCorner::uniform(
917                LayoutRect::from_origin_and_size(
918                    LayoutPoint::new(
919                        rect.max.x - radius,
920                        rect.max.y - radius,
921                    ),
922                    LayoutSize::new(radius, radius),
923                ),
924                radius,
925                0.0,
926            ),
927        }
928    }
929}
930
931/// A hashable descriptor for nine-patches, used by image and
932/// gradient borders.
933#[derive(Debug, Clone, PartialEq, Eq, Hash, MallocSizeOf)]
934#[cfg_attr(feature = "capture", derive(Serialize))]
935#[cfg_attr(feature = "replay", derive(Deserialize))]
936pub struct NinePatchDescriptor {
937    pub width: i32,
938    pub height: i32,
939    pub slice: DeviceIntSideOffsets,
940    pub fill: bool,
941    pub repeat_horizontal: RepeatMode,
942    pub repeat_vertical: RepeatMode,
943    pub widths: SideOffsetsKey,
944}
945
946impl IsVisible for PrimitiveKeyKind {
947    // Return true if the primary primitive is visible.
948    // Used to trivially reject non-visible primitives.
949    // TODO(gw): Currently, primitives other than those
950    //           listed here are handled before the
951    //           add_primitive() call. In the future
952    //           we should move the logic for all other
953    //           primitive types to use this.
954    fn is_visible(&self) -> bool {
955        match *self {
956            PrimitiveKeyKind::Clear => {
957                true
958            }
959            PrimitiveKeyKind::Rectangle { ref color, .. } => {
960                match *color {
961                    PropertyBinding::Value(value) => value.a > 0,
962                    PropertyBinding::Binding(..) => true,
963                }
964            }
965        }
966    }
967}
968
969impl CreateShadow for PrimitiveKeyKind {
970    // Create a clone of this PrimitiveContainer, applying whatever
971    // changes are necessary to the primitive to support rendering
972    // it as part of the supplied shadow.
973    fn create_shadow(
974        &self,
975        shadow: &Shadow,
976        _: bool,
977        _: RasterSpace,
978    ) -> PrimitiveKeyKind {
979        match *self {
980            PrimitiveKeyKind::Rectangle { .. } => {
981                PrimitiveKeyKind::Rectangle {
982                    color: PropertyBinding::Value(shadow.color.into()),
983                }
984            }
985            PrimitiveKeyKind::Clear => {
986                panic!("bug: this prim is not supported in shadow contexts");
987            }
988        }
989    }
990}
991
992#[derive(Debug)]
993#[cfg_attr(feature = "capture", derive(Serialize))]
994pub enum PrimitiveInstanceKind {
995    /// Direct reference to a Picture
996    Picture {
997        /// Handle to the common interned data for this primitive.
998        data_handle: PictureDataHandle,
999        pic_index: PictureIndex,
1000    },
1001    /// A run of glyphs, with associated font parameters.
1002    TextRun {
1003        /// Handle to the common interned data for this primitive.
1004        data_handle: TextRunDataHandle,
1005        /// Index to the per instance scratch data for this primitive.
1006        run_index: TextRunIndex,
1007    },
1008    /// A line decoration. cache_handle refers to a cached render
1009    /// task handle, if this line decoration is not a simple solid.
1010    LineDecoration {
1011        /// Handle to the common interned data for this primitive.
1012        data_handle: LineDecorationDataHandle,
1013        // TODO(gw): For now, we need to store some information in
1014        //           the primitive instance that is created during
1015        //           prepare_prims and read during the batching pass.
1016        //           Once we unify the prepare_prims and batching to
1017        //           occur at the same time, we can remove most of
1018        //           the things we store here in the instance, and
1019        //           use them directly. This will remove cache_handle,
1020        //           but also the opacity, clip_task_id etc below.
1021        render_task: Option<RenderTaskId>,
1022    },
1023    NormalBorder {
1024        /// Handle to the common interned data for this primitive.
1025        data_handle: NormalBorderDataHandle,
1026        render_task_ids: storage::Range<RenderTaskId>,
1027    },
1028    ImageBorder {
1029        /// Handle to the common interned data for this primitive.
1030        data_handle: ImageBorderDataHandle,
1031    },
1032    Rectangle {
1033        /// Handle to the common interned data for this primitive.
1034        data_handle: PrimitiveDataHandle,
1035        segment_instance_index: SegmentInstanceIndex,
1036        color_binding_index: ColorBindingIndex,
1037        use_legacy_path: bool,
1038    },
1039    YuvImage {
1040        /// Handle to the common interned data for this primitive.
1041        data_handle: YuvImageDataHandle,
1042        segment_instance_index: SegmentInstanceIndex,
1043        compositor_surface_kind: CompositorSurfaceKind,
1044    },
1045    Image {
1046        /// Handle to the common interned data for this primitive.
1047        data_handle: ImageDataHandle,
1048        image_instance_index: ImageInstanceIndex,
1049        compositor_surface_kind: CompositorSurfaceKind,
1050    },
1051    /// Always rendered directly into the picture. This tends to be
1052    /// faster with SWGL.
1053    LinearGradient {
1054        /// Handle to the common interned data for this primitive.
1055        data_handle: LinearGradientDataHandle,
1056        visible_tiles_range: GradientTileRange,
1057    },
1058    /// Always rendered via a cached render task. Usually faster with
1059    /// a GPU.
1060    CachedLinearGradient {
1061        /// Handle to the common interned data for this primitive.
1062        data_handle: LinearGradientDataHandle,
1063        visible_tiles_range: GradientTileRange,
1064    },
1065    RadialGradient {
1066        /// Handle to the common interned data for this primitive.
1067        data_handle: RadialGradientDataHandle,
1068        visible_tiles_range: GradientTileRange,
1069        cached: bool,
1070    },
1071    ConicGradient {
1072        /// Handle to the common interned data for this primitive.
1073        data_handle: ConicGradientDataHandle,
1074        visible_tiles_range: GradientTileRange,
1075        cached: bool,
1076    },
1077    /// Clear out a rect, used for special effects.
1078    Clear {
1079        /// Handle to the common interned data for this primitive.
1080        data_handle: PrimitiveDataHandle,
1081    },
1082    /// Render a portion of a specified backdrop.
1083    BackdropCapture {
1084        data_handle: BackdropCaptureDataHandle,
1085    },
1086    BackdropRender {
1087        data_handle: BackdropRenderDataHandle,
1088        pic_index: PictureIndex,
1089    },
1090    BoxShadow {
1091        data_handle: BoxShadowDataHandle,
1092    },
1093}
1094
1095impl PrimitiveInstanceKind {
1096    pub fn as_pic(&self) -> PictureIndex {
1097        match self {
1098            PrimitiveInstanceKind::Picture { pic_index, .. } => *pic_index,
1099            _ => panic!("bug: as_pic called on a prim that is not a picture"),
1100        }
1101    }
1102}
1103
1104#[derive(Debug, Copy, Clone)]
1105#[cfg_attr(feature = "capture", derive(Serialize))]
1106#[cfg_attr(feature = "replay", derive(Deserialize))]
1107pub struct PrimitiveInstanceIndex(pub u32);
1108
1109#[derive(Debug)]
1110#[cfg_attr(feature = "capture", derive(Serialize))]
1111pub struct PrimitiveInstance {
1112    /// Identifies the kind of primitive this
1113    /// instance is, and references to where
1114    /// the relevant information for the primitive
1115    /// can be found.
1116    pub kind: PrimitiveInstanceKind,
1117
1118    /// All information and state related to clip(s) for this primitive
1119    pub clip_leaf_id: ClipLeafId,
1120
1121    /// Information related to the current visibility state of this
1122    /// primitive.
1123    // TODO(gw): Currently built each frame, but can be retained.
1124    pub vis: PrimitiveVisibility,
1125}
1126
1127impl PrimitiveInstance {
1128    pub fn new(
1129        kind: PrimitiveInstanceKind,
1130        clip_leaf_id: ClipLeafId,
1131    ) -> Self {
1132        PrimitiveInstance {
1133            kind,
1134            vis: PrimitiveVisibility::new(),
1135            clip_leaf_id,
1136        }
1137    }
1138
1139    // Reset any pre-frame state for this primitive.
1140    pub fn reset(&mut self) {
1141        self.vis.reset();
1142    }
1143
1144    pub fn clear_visibility(&mut self) {
1145        self.vis.reset();
1146    }
1147
1148    pub fn uid(&self) -> intern::ItemUid {
1149        match &self.kind {
1150            PrimitiveInstanceKind::Clear { data_handle, .. } |
1151            PrimitiveInstanceKind::Rectangle { data_handle, .. } => {
1152                data_handle.uid()
1153            }
1154            PrimitiveInstanceKind::Image { data_handle, .. } => {
1155                data_handle.uid()
1156            }
1157            PrimitiveInstanceKind::ImageBorder { data_handle, .. } => {
1158                data_handle.uid()
1159            }
1160            PrimitiveInstanceKind::LineDecoration { data_handle, .. } => {
1161                data_handle.uid()
1162            }
1163            PrimitiveInstanceKind::LinearGradient { data_handle, .. } => {
1164                data_handle.uid()
1165            }
1166            PrimitiveInstanceKind::CachedLinearGradient { data_handle, .. } => {
1167                data_handle.uid()
1168            }
1169            PrimitiveInstanceKind::NormalBorder { data_handle, .. } => {
1170                data_handle.uid()
1171            }
1172            PrimitiveInstanceKind::Picture { data_handle, .. } => {
1173                data_handle.uid()
1174            }
1175            PrimitiveInstanceKind::RadialGradient { data_handle, .. } => {
1176                data_handle.uid()
1177            }
1178            PrimitiveInstanceKind::ConicGradient { data_handle, .. } => {
1179                data_handle.uid()
1180            }
1181            PrimitiveInstanceKind::TextRun { data_handle, .. } => {
1182                data_handle.uid()
1183            }
1184            PrimitiveInstanceKind::YuvImage { data_handle, .. } => {
1185                data_handle.uid()
1186            }
1187            PrimitiveInstanceKind::BackdropCapture { data_handle, .. } => {
1188                data_handle.uid()
1189            }
1190            PrimitiveInstanceKind::BackdropRender { data_handle, .. } => {
1191                data_handle.uid()
1192            }
1193            PrimitiveInstanceKind::BoxShadow { data_handle, .. } => {
1194                data_handle.uid()
1195            }
1196        }
1197    }
1198}
1199
1200#[cfg_attr(feature = "capture", derive(Serialize))]
1201#[derive(Debug)]
1202pub struct SegmentedInstance {
1203    pub gpu_cache_handle: GpuCacheHandle,
1204    pub segments_range: SegmentsRange,
1205}
1206
1207pub type GlyphKeyStorage = storage::Storage<GlyphKey>;
1208pub type TextRunIndex = storage::Index<TextRunPrimitive>;
1209pub type TextRunStorage = storage::Storage<TextRunPrimitive>;
1210pub type ColorBindingIndex = storage::Index<PropertyBinding<ColorU>>;
1211pub type ColorBindingStorage = storage::Storage<PropertyBinding<ColorU>>;
1212pub type BorderHandleStorage = storage::Storage<RenderTaskId>;
1213pub type SegmentStorage = storage::Storage<BrushSegment>;
1214pub type SegmentsRange = storage::Range<BrushSegment>;
1215pub type SegmentInstanceStorage = storage::Storage<SegmentedInstance>;
1216pub type SegmentInstanceIndex = storage::Index<SegmentedInstance>;
1217pub type ImageInstanceStorage = storage::Storage<ImageInstance>;
1218pub type ImageInstanceIndex = storage::Index<ImageInstance>;
1219pub type GradientTileStorage = storage::Storage<VisibleGradientTile>;
1220pub type GradientTileRange = storage::Range<VisibleGradientTile>;
1221pub type LinearGradientStorage = storage::Storage<LinearGradientPrimitive>;
1222
1223/// Contains various vecs of data that is used only during frame building,
1224/// where we want to recycle the memory each new display list, to avoid constantly
1225/// re-allocating and moving memory around. Written during primitive preparation,
1226/// and read during batching.
1227#[cfg_attr(feature = "capture", derive(Serialize))]
1228pub struct PrimitiveScratchBuffer {
1229    /// Contains a list of clip mask instance parameters
1230    /// per segment generated.
1231    pub clip_mask_instances: Vec<ClipMaskKind>,
1232
1233    /// List of glyphs keys that are allocated by each
1234    /// text run instance.
1235    pub glyph_keys: GlyphKeyStorage,
1236
1237    /// List of render task handles for border segment instances
1238    /// that have been added this frame.
1239    pub border_cache_handles: BorderHandleStorage,
1240
1241    /// A list of brush segments that have been built for this scene.
1242    pub segments: SegmentStorage,
1243
1244    /// A list of segment ranges and GPU cache handles for prim instances
1245    /// that have opted into segment building. In future, this should be
1246    /// removed in favor of segment building during primitive interning.
1247    pub segment_instances: SegmentInstanceStorage,
1248
1249    /// A list of visible tiles that tiled gradients use to store
1250    /// per-tile information.
1251    pub gradient_tiles: GradientTileStorage,
1252
1253    /// List of debug display items for rendering.
1254    pub debug_items: Vec<DebugItem>,
1255
1256    /// List of current debug messages to log on screen
1257    messages: Vec<DebugMessage>,
1258
1259    /// Set of sub-graphs that are required, determined during visibility pass
1260    pub required_sub_graphs: FastHashSet<PictureIndex>,
1261
1262    /// Temporary buffers for building segments in to during prepare pass
1263    pub quad_direct_segments: Vec<QuadSegment>,
1264    pub quad_color_segments: Vec<QuadSegment>,
1265    pub quad_indirect_segments: Vec<QuadSegment>,
1266
1267    /// A retained classifier for checking which segments of a tiled primitive
1268    /// need a mask / are clipped / can be rendered directly
1269    pub quad_tile_classifier: QuadTileClassifier,
1270}
1271
1272impl Default for PrimitiveScratchBuffer {
1273    fn default() -> Self {
1274        PrimitiveScratchBuffer {
1275            clip_mask_instances: Vec::new(),
1276            glyph_keys: GlyphKeyStorage::new(0),
1277            border_cache_handles: BorderHandleStorage::new(0),
1278            segments: SegmentStorage::new(0),
1279            segment_instances: SegmentInstanceStorage::new(0),
1280            gradient_tiles: GradientTileStorage::new(0),
1281            debug_items: Vec::new(),
1282            messages: Vec::new(),
1283            required_sub_graphs: FastHashSet::default(),
1284            quad_direct_segments: Vec::new(),
1285            quad_color_segments: Vec::new(),
1286            quad_indirect_segments: Vec::new(),
1287            quad_tile_classifier: QuadTileClassifier::new(),
1288        }
1289    }
1290}
1291
1292impl PrimitiveScratchBuffer {
1293    pub fn recycle(&mut self, recycler: &mut Recycler) {
1294        recycler.recycle_vec(&mut self.clip_mask_instances);
1295        self.glyph_keys.recycle(recycler);
1296        self.border_cache_handles.recycle(recycler);
1297        self.segments.recycle(recycler);
1298        self.segment_instances.recycle(recycler);
1299        self.gradient_tiles.recycle(recycler);
1300        recycler.recycle_vec(&mut self.debug_items);
1301        recycler.recycle_vec(&mut self.quad_direct_segments);
1302        recycler.recycle_vec(&mut self.quad_color_segments);
1303        recycler.recycle_vec(&mut self.quad_indirect_segments);
1304    }
1305
1306    pub fn begin_frame(&mut self) {
1307        // Clear the clip mask tasks for the beginning of the frame. Append
1308        // a single kind representing no clip mask, at the ClipTaskIndex::INVALID
1309        // location.
1310        self.clip_mask_instances.clear();
1311        self.clip_mask_instances.push(ClipMaskKind::None);
1312        self.quad_direct_segments.clear();
1313        self.quad_color_segments.clear();
1314        self.quad_indirect_segments.clear();
1315
1316        self.border_cache_handles.clear();
1317
1318        // TODO(gw): As in the previous code, the gradient tiles store GPU cache
1319        //           handles that are cleared (and thus invalidated + re-uploaded)
1320        //           every frame. This maintains the existing behavior, but we
1321        //           should fix this in the future to retain handles.
1322        self.gradient_tiles.clear();
1323
1324        self.required_sub_graphs.clear();
1325
1326        self.debug_items.clear();
1327    }
1328
1329    pub fn end_frame(&mut self) {
1330        const MSGS_TO_RETAIN: usize = 32;
1331        const TIME_TO_RETAIN: u64 = 2000000000;
1332        const LINE_HEIGHT: f32 = 20.0;
1333        const X0: f32 = 32.0;
1334        const Y0: f32 = 32.0;
1335        let now = api::precise_time_ns();
1336
1337        let msgs_to_remove = self.messages.len().max(MSGS_TO_RETAIN) - MSGS_TO_RETAIN;
1338        let mut msgs_removed = 0;
1339
1340        self.messages.retain(|msg| {
1341            if msgs_removed < msgs_to_remove {
1342                msgs_removed += 1;
1343                return false;
1344            }
1345
1346            if msg.timestamp + TIME_TO_RETAIN < now {
1347                return false;
1348            }
1349
1350            true
1351        });
1352
1353        let mut y = Y0 + self.messages.len() as f32 * LINE_HEIGHT;
1354        let shadow_offset = 1.0;
1355
1356        for msg in &self.messages {
1357            self.debug_items.push(DebugItem::Text {
1358                position: DevicePoint::new(X0 + shadow_offset, y + shadow_offset),
1359                color: debug_colors::BLACK,
1360                msg: msg.msg.clone(),
1361            });
1362
1363            self.debug_items.push(DebugItem::Text {
1364                position: DevicePoint::new(X0, y),
1365                color: debug_colors::RED,
1366                msg: msg.msg.clone(),
1367            });
1368
1369            y -= LINE_HEIGHT;
1370        }
1371    }
1372
1373    pub fn push_debug_rect_with_stroke_width(
1374        &mut self,
1375        rect: WorldRect,
1376        border: ColorF,
1377        stroke_width: f32
1378    ) {
1379        let top_edge = WorldRect::new(
1380            WorldPoint::new(rect.min.x + stroke_width, rect.min.y),
1381            WorldPoint::new(rect.max.x - stroke_width, rect.min.y + stroke_width)
1382        );
1383        self.push_debug_rect(top_edge * DevicePixelScale::new(1.0), 1, border, border);
1384
1385        let bottom_edge = WorldRect::new(
1386            WorldPoint::new(rect.min.x + stroke_width, rect.max.y - stroke_width),
1387            WorldPoint::new(rect.max.x - stroke_width, rect.max.y)
1388        );
1389        self.push_debug_rect(bottom_edge * DevicePixelScale::new(1.0), 1, border, border);
1390
1391        let right_edge = WorldRect::new(
1392            WorldPoint::new(rect.max.x - stroke_width, rect.min.y),
1393            rect.max
1394        );
1395        self.push_debug_rect(right_edge * DevicePixelScale::new(1.0), 1, border, border);
1396
1397        let left_edge = WorldRect::new(
1398            rect.min,
1399            WorldPoint::new(rect.min.x + stroke_width, rect.max.y)
1400        );
1401        self.push_debug_rect(left_edge * DevicePixelScale::new(1.0), 1, border, border);
1402    }
1403
1404    #[allow(dead_code)]
1405    pub fn push_debug_rect(
1406        &mut self,
1407        rect: DeviceRect,
1408        thickness: i32,
1409        outer_color: ColorF,
1410        inner_color: ColorF,
1411    ) {
1412        self.debug_items.push(DebugItem::Rect {
1413            rect,
1414            outer_color,
1415            inner_color,
1416            thickness,
1417        });
1418    }
1419
1420    #[allow(dead_code)]
1421    pub fn push_debug_string(
1422        &mut self,
1423        position: DevicePoint,
1424        color: ColorF,
1425        msg: String,
1426    ) {
1427        self.debug_items.push(DebugItem::Text {
1428            position,
1429            color,
1430            msg,
1431        });
1432    }
1433
1434    #[allow(dead_code)]
1435    pub fn log(
1436        &mut self,
1437        msg: String,
1438    ) {
1439        self.messages.push(DebugMessage {
1440            msg,
1441            timestamp: api::precise_time_ns(),
1442        })
1443    }
1444}
1445
1446#[cfg_attr(feature = "capture", derive(Serialize))]
1447#[cfg_attr(feature = "replay", derive(Deserialize))]
1448#[derive(Clone, Debug)]
1449pub struct PrimitiveStoreStats {
1450    picture_count: usize,
1451    text_run_count: usize,
1452    image_count: usize,
1453    linear_gradient_count: usize,
1454    color_binding_count: usize,
1455}
1456
1457impl PrimitiveStoreStats {
1458    pub fn empty() -> Self {
1459        PrimitiveStoreStats {
1460            picture_count: 0,
1461            text_run_count: 0,
1462            image_count: 0,
1463            linear_gradient_count: 0,
1464            color_binding_count: 0,
1465        }
1466    }
1467}
1468
1469#[cfg_attr(feature = "capture", derive(Serialize))]
1470pub struct PrimitiveStore {
1471    pub pictures: Vec<PicturePrimitive>,
1472    pub text_runs: TextRunStorage,
1473    pub linear_gradients: LinearGradientStorage,
1474
1475    /// A list of image instances. These are stored separately as
1476    /// storing them inline in the instance makes the structure bigger
1477    /// for other types.
1478    pub images: ImageInstanceStorage,
1479
1480    /// animated color bindings for this primitive.
1481    pub color_bindings: ColorBindingStorage,
1482}
1483
1484impl PrimitiveStore {
1485    pub fn new(stats: &PrimitiveStoreStats) -> PrimitiveStore {
1486        PrimitiveStore {
1487            pictures: Vec::with_capacity(stats.picture_count),
1488            text_runs: TextRunStorage::new(stats.text_run_count),
1489            images: ImageInstanceStorage::new(stats.image_count),
1490            color_bindings: ColorBindingStorage::new(stats.color_binding_count),
1491            linear_gradients: LinearGradientStorage::new(stats.linear_gradient_count),
1492        }
1493    }
1494
1495    pub fn reset(&mut self) {
1496        self.pictures.clear();
1497        self.text_runs.clear();
1498        self.images.clear();
1499        self.color_bindings.clear();
1500        self.linear_gradients.clear();
1501    }
1502
1503    pub fn get_stats(&self) -> PrimitiveStoreStats {
1504        PrimitiveStoreStats {
1505            picture_count: self.pictures.len(),
1506            text_run_count: self.text_runs.len(),
1507            image_count: self.images.len(),
1508            linear_gradient_count: self.linear_gradients.len(),
1509            color_binding_count: self.color_bindings.len(),
1510        }
1511    }
1512
1513    #[allow(unused)]
1514    pub fn print_picture_tree(&self, root: PictureIndex) {
1515        use crate::print_tree::PrintTree;
1516        let mut pt = PrintTree::new("picture tree");
1517        self.pictures[root.0].print(&self.pictures, root, &mut pt);
1518    }
1519}
1520
1521impl Default for PrimitiveStore {
1522    fn default() -> Self {
1523        PrimitiveStore::new(&PrimitiveStoreStats::empty())
1524    }
1525}
1526
1527/// Trait for primitives that are directly internable.
1528/// see SceneBuilder::add_primitive<P>
1529pub trait InternablePrimitive: intern::Internable<InternData = ()> + Sized {
1530    /// Build a new key from self with `info`.
1531    fn into_key(
1532        self,
1533        info: &LayoutPrimitiveInfo,
1534    ) -> Self::Key;
1535
1536    fn make_instance_kind(
1537        key: Self::Key,
1538        data_handle: intern::Handle<Self>,
1539        prim_store: &mut PrimitiveStore,
1540    ) -> PrimitiveInstanceKind;
1541}
1542
1543
1544#[test]
1545#[cfg(target_pointer_width = "64")]
1546fn test_struct_sizes() {
1547    use std::mem;
1548    // The sizes of these structures are critical for performance on a number of
1549    // talos stress tests. If you get a failure here on CI, there's two possibilities:
1550    // (a) You made a structure smaller than it currently is. Great work! Update the
1551    //     test expectations and move on.
1552    // (b) You made a structure larger. This is not necessarily a problem, but should only
1553    //     be done with care, and after checking if talos performance regresses badly.
1554    assert_eq!(mem::size_of::<PrimitiveInstance>(), 88, "PrimitiveInstance size changed");
1555    assert_eq!(mem::size_of::<PrimitiveInstanceKind>(), 24, "PrimitiveInstanceKind size changed");
1556    assert_eq!(mem::size_of::<PrimitiveTemplate>(), 56, "PrimitiveTemplate size changed");
1557    assert_eq!(mem::size_of::<PrimitiveTemplateKind>(), 28, "PrimitiveTemplateKind size changed");
1558    assert_eq!(mem::size_of::<PrimitiveKey>(), 36, "PrimitiveKey size changed");
1559    assert_eq!(mem::size_of::<PrimitiveKeyKind>(), 16, "PrimitiveKeyKind size changed");
1560}