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