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