Skip to main content

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};
6use api::{ImageRendering, RepeatMode, PrimitiveFlags};
7use api::{FillRule, POLYGON_CLIP_VERTEX_MAX};
8use api::units::*;
9use euclid::{SideOffsets2D, Size2D};
10use malloc_size_of::MallocSizeOf;
11use crate::clip::ClipLeafId;
12use crate::quad::QuadTileClassifier;
13use crate::renderer::{GpuBufferAddress, GpuBufferHandle, GpuBufferWriterF};
14use crate::segment::EdgeMask;
15use crate::border::BorderSegmentCacheKey;
16use crate::debug_item::{DebugItem, DebugMessage};
17use crate::debug_colors;
18use glyph_rasterizer::GlyphKey;
19use crate::gpu_types::{BrushFlags, BrushSegmentGpuData, QuadSegment};
20use crate::intern;
21use crate::picture::{PictureInstance, PictureScratch};
22use crate::render_task_graph::RenderTaskId;
23use crate::resource_cache::ImageProperties;
24use std::{hash, u32, usize};
25use crate::util::Recycler;
26use crate::internal_types::{FastHashSet, LayoutPrimitiveInfo};
27use crate::visibility::PrimitiveDrawHeader;
28
29pub mod backdrop;
30pub mod borders;
31pub mod gradient;
32pub mod image;
33pub mod line_dec;
34pub mod picture;
35pub mod rectangle;
36pub mod text_run;
37pub mod interned;
38
39pub mod storage;
40
41use backdrop::{BackdropCaptureDataHandle, BackdropRenderDataHandle, BackdropRenderScratch};
42use borders::{ImageBorderDataHandle, ImageBorderScratch, NormalBorderDataHandle, NormalBorderScratch};
43use gradient::{LinearGradientDataHandle, RadialGradientDataHandle, ConicGradientDataHandle};
44use image::{ImageDataHandle, ImageScratch, VisibleImageTile, YuvImageDataHandle};
45use line_dec::{LineDecorationDataHandle, LineDecorationScratch};
46use picture::PictureDataHandle;
47use rectangle::RectangleDataHandle;
48use text_run::{TextRunDataHandle, TextRunScratch};
49use crate::box_shadow::BoxShadowDataHandle;
50
51pub const VECS_PER_SEGMENT: usize = 2;
52
53#[cfg_attr(feature = "capture", derive(Serialize))]
54#[cfg_attr(feature = "replay", derive(Deserialize))]
55#[derive(Debug, Copy, Clone, MallocSizeOf)]
56pub struct PrimitiveOpacity {
57    pub is_opaque: bool,
58}
59
60impl PrimitiveOpacity {
61    pub fn opaque() -> PrimitiveOpacity {
62        PrimitiveOpacity { is_opaque: true }
63    }
64
65    pub fn translucent() -> PrimitiveOpacity {
66        PrimitiveOpacity { is_opaque: false }
67    }
68
69    pub fn from_alpha(alpha: f32) -> PrimitiveOpacity {
70        PrimitiveOpacity {
71            is_opaque: alpha >= 1.0,
72        }
73    }
74}
75
76/// For external images, it's not possible to know the
77/// UV coords of the image (or the image data itself)
78/// until the render thread receives the frame and issues
79/// callbacks to the client application. For external
80/// images that are visible, a DeferredResolve is created
81/// that is stored in the frame. This allows the render
82/// thread to iterate this list and update any changed
83/// texture data and update the UV rect. Any filtering
84/// is handled externally for NativeTexture external
85/// images.
86#[cfg_attr(feature = "capture", derive(Serialize))]
87#[cfg_attr(feature = "replay", derive(Deserialize))]
88pub struct DeferredResolve {
89    pub handle: GpuBufferHandle,
90    pub image_properties: ImageProperties,
91    pub rendering: ImageRendering,
92    pub is_composited: bool,
93}
94
95#[derive(Debug, Copy, Clone, PartialEq)]
96#[cfg_attr(feature = "capture", derive(Serialize))]
97pub struct ClipTaskIndex(pub u32);
98
99impl ClipTaskIndex {
100    pub const INVALID: ClipTaskIndex = ClipTaskIndex(0);
101}
102
103#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, MallocSizeOf, Ord, PartialOrd)]
104#[cfg_attr(feature = "capture", derive(Serialize))]
105#[cfg_attr(feature = "replay", derive(Deserialize))]
106pub struct PictureIndex(pub usize);
107
108impl PictureIndex {
109    pub const INVALID: PictureIndex = PictureIndex(!0);
110}
111
112#[cfg_attr(feature = "capture", derive(Serialize))]
113#[cfg_attr(feature = "replay", derive(Deserialize))]
114#[derive(Copy, Debug, Clone, MallocSizeOf, PartialEq)]
115pub struct RectKey {
116    pub x0: f32,
117    pub y0: f32,
118    pub x1: f32,
119    pub y1: f32,
120}
121
122impl RectKey {
123    pub fn intersects(&self, other: &Self) -> bool {
124        self.x0 < other.x1
125            && other.x0 < self.x1
126            && self.y0 < other.y1
127            && other.y0 < self.y1
128    }
129}
130
131impl Eq for RectKey {}
132
133impl hash::Hash for RectKey {
134    fn hash<H: hash::Hasher>(&self, state: &mut H) {
135        self.x0.to_bits().hash(state);
136        self.y0.to_bits().hash(state);
137        self.x1.to_bits().hash(state);
138        self.y1.to_bits().hash(state);
139    }
140}
141
142impl From<RectKey> for LayoutRect {
143    fn from(key: RectKey) -> LayoutRect {
144        LayoutRect {
145            min: LayoutPoint::new(key.x0, key.y0),
146            max: LayoutPoint::new(key.x1, key.y1),
147        }
148    }
149}
150
151impl From<RectKey> for WorldRect {
152    fn from(key: RectKey) -> WorldRect {
153        WorldRect {
154            min: WorldPoint::new(key.x0, key.y0),
155            max: WorldPoint::new(key.x1, key.y1),
156        }
157    }
158}
159
160impl From<LayoutRect> for RectKey {
161    fn from(rect: LayoutRect) -> RectKey {
162        RectKey {
163            x0: rect.min.x,
164            y0: rect.min.y,
165            x1: rect.max.x,
166            y1: rect.max.y,
167        }
168    }
169}
170
171impl From<PictureRect> for RectKey {
172    fn from(rect: PictureRect) -> RectKey {
173        RectKey {
174            x0: rect.min.x,
175            y0: rect.min.y,
176            x1: rect.max.x,
177            y1: rect.max.y,
178        }
179    }
180}
181
182impl From<WorldRect> for RectKey {
183    fn from(rect: WorldRect) -> RectKey {
184        RectKey {
185            x0: rect.min.x,
186            y0: rect.min.y,
187            x1: rect.max.x,
188            y1: rect.max.y,
189        }
190    }
191}
192
193/// To create a fixed-size representation of a polygon, we use a fixed
194/// number of points. Our initialization method restricts us to values
195/// <= 32. If our constant POLYGON_CLIP_VERTEX_MAX is > 32, the Rust
196/// compiler will complain.
197#[cfg_attr(feature = "capture", derive(Serialize))]
198#[cfg_attr(feature = "replay", derive(Deserialize))]
199#[derive(Copy, Debug, Clone, Hash, MallocSizeOf, PartialEq)]
200pub struct PolygonKey {
201    pub point_count: u8,
202    pub points: [PointKey; POLYGON_CLIP_VERTEX_MAX],
203    pub fill_rule: FillRule,
204}
205
206impl PolygonKey {
207    pub fn new(
208        points_layout: &Vec<LayoutPoint>,
209        fill_rule: FillRule,
210    ) -> Self {
211        // We have to fill fixed-size arrays with data from a Vec.
212        // We'll do this by initializing the arrays to known-good
213        // values then overwriting those values as long as our
214        // iterator provides values.
215        let mut points: [PointKey; POLYGON_CLIP_VERTEX_MAX] = [PointKey { x: 0.0, y: 0.0}; POLYGON_CLIP_VERTEX_MAX];
216
217        let mut point_count: u8 = 0;
218        for (src, dest) in points_layout.iter().zip(points.iter_mut()) {
219            *dest = (*src as LayoutPoint).into();
220            point_count = point_count + 1;
221        }
222
223        PolygonKey {
224            point_count,
225            points,
226            fill_rule,
227        }
228    }
229}
230
231impl Eq for PolygonKey {}
232
233/// A hashable SideOffset2D that can be used in primitive keys.
234#[cfg_attr(feature = "capture", derive(Serialize))]
235#[cfg_attr(feature = "replay", derive(Deserialize))]
236#[derive(Debug, Clone, MallocSizeOf, PartialEq)]
237pub struct SideOffsetsKey {
238    pub top: f32,
239    pub right: f32,
240    pub bottom: f32,
241    pub left: f32,
242}
243
244impl Eq for SideOffsetsKey {}
245
246impl hash::Hash for SideOffsetsKey {
247    fn hash<H: hash::Hasher>(&self, state: &mut H) {
248        self.top.to_bits().hash(state);
249        self.right.to_bits().hash(state);
250        self.bottom.to_bits().hash(state);
251        self.left.to_bits().hash(state);
252    }
253}
254
255impl From<SideOffsetsKey> for LayoutSideOffsets {
256    fn from(key: SideOffsetsKey) -> LayoutSideOffsets {
257        LayoutSideOffsets::new(
258            key.top,
259            key.right,
260            key.bottom,
261            key.left,
262        )
263    }
264}
265
266impl<U> From<SideOffsets2D<f32, U>> for SideOffsetsKey {
267    fn from(offsets: SideOffsets2D<f32, U>) -> SideOffsetsKey {
268        SideOffsetsKey {
269            top: offsets.top,
270            right: offsets.right,
271            bottom: offsets.bottom,
272            left: offsets.left,
273        }
274    }
275}
276
277/// A hashable size for using as a key during primitive interning.
278#[cfg_attr(feature = "capture", derive(Serialize))]
279#[cfg_attr(feature = "replay", derive(Deserialize))]
280#[derive(Copy, Debug, Clone, MallocSizeOf, PartialEq)]
281pub struct SizeKey {
282    w: f32,
283    h: f32,
284}
285
286impl Eq for SizeKey {}
287
288impl hash::Hash for SizeKey {
289    fn hash<H: hash::Hasher>(&self, state: &mut H) {
290        self.w.to_bits().hash(state);
291        self.h.to_bits().hash(state);
292    }
293}
294
295impl From<SizeKey> for LayoutSize {
296    fn from(key: SizeKey) -> LayoutSize {
297        LayoutSize::new(key.w, key.h)
298    }
299}
300
301impl<U> From<Size2D<f32, U>> for SizeKey {
302    fn from(size: Size2D<f32, U>) -> SizeKey {
303        SizeKey {
304            w: size.width,
305            h: size.height,
306        }
307    }
308}
309
310/// A hashable vec for using as a key during primitive interning.
311#[cfg_attr(feature = "capture", derive(Serialize))]
312#[cfg_attr(feature = "replay", derive(Deserialize))]
313#[derive(Copy, Debug, Clone, MallocSizeOf, PartialEq)]
314pub struct VectorKey {
315    pub x: f32,
316    pub y: f32,
317}
318
319impl Eq for VectorKey {}
320
321impl hash::Hash for VectorKey {
322    fn hash<H: hash::Hasher>(&self, state: &mut H) {
323        self.x.to_bits().hash(state);
324        self.y.to_bits().hash(state);
325    }
326}
327
328impl From<VectorKey> for LayoutVector2D {
329    fn from(key: VectorKey) -> LayoutVector2D {
330        LayoutVector2D::new(key.x, key.y)
331    }
332}
333
334impl From<VectorKey> for WorldVector2D {
335    fn from(key: VectorKey) -> WorldVector2D {
336        WorldVector2D::new(key.x, key.y)
337    }
338}
339
340impl From<LayoutVector2D> for VectorKey {
341    fn from(vec: LayoutVector2D) -> VectorKey {
342        VectorKey {
343            x: vec.x,
344            y: vec.y,
345        }
346    }
347}
348
349impl From<WorldVector2D> for VectorKey {
350    fn from(vec: WorldVector2D) -> VectorKey {
351        VectorKey {
352            x: vec.x,
353            y: vec.y,
354        }
355    }
356}
357
358/// A hashable point for using as a key during primitive interning.
359#[cfg_attr(feature = "capture", derive(Serialize))]
360#[cfg_attr(feature = "replay", derive(Deserialize))]
361#[derive(Debug, Copy, Clone, MallocSizeOf, PartialEq)]
362pub struct PointKey {
363    pub x: f32,
364    pub y: f32,
365}
366
367impl Eq for PointKey {}
368
369impl hash::Hash for PointKey {
370    fn hash<H: hash::Hasher>(&self, state: &mut H) {
371        self.x.to_bits().hash(state);
372        self.y.to_bits().hash(state);
373    }
374}
375
376impl From<PointKey> for LayoutPoint {
377    fn from(key: PointKey) -> LayoutPoint {
378        LayoutPoint::new(key.x, key.y)
379    }
380}
381
382impl From<LayoutPoint> for PointKey {
383    fn from(p: LayoutPoint) -> PointKey {
384        PointKey {
385            x: p.x,
386            y: p.y,
387        }
388    }
389}
390
391impl From<PicturePoint> for PointKey {
392    fn from(p: PicturePoint) -> PointKey {
393        PointKey {
394            x: p.x,
395            y: p.y,
396        }
397    }
398}
399
400impl From<WorldPoint> for PointKey {
401    fn from(p: WorldPoint) -> PointKey {
402        PointKey {
403            x: p.x,
404            y: p.y,
405        }
406    }
407}
408
409/// A hashable float for using as a key during primitive interning.
410
411#[cfg_attr(feature = "capture", derive(Serialize))]
412#[cfg_attr(feature = "replay", derive(Deserialize))]
413#[derive(Debug, Clone, Eq, MallocSizeOf, PartialEq, Hash)]
414pub struct PrimKeyCommonData {
415    pub flags: PrimitiveFlags,
416    pub aligned_aa_edges: EdgeMask,
417    pub transformed_aa_edges: EdgeMask,
418}
419
420impl From<&LayoutPrimitiveInfo> for PrimKeyCommonData {
421    fn from(info: &LayoutPrimitiveInfo) -> Self {
422        PrimKeyCommonData {
423            flags: info.flags,
424            aligned_aa_edges: info.aligned_aa_edges,
425            transformed_aa_edges: info.transformed_aa_edges,
426        }
427    }
428}
429
430#[cfg_attr(feature = "capture", derive(Serialize))]
431#[cfg_attr(feature = "replay", derive(Deserialize))]
432#[derive(Debug, Clone, Eq, MallocSizeOf, PartialEq, Hash)]
433pub struct PrimKey<T: MallocSizeOf> {
434    pub common: PrimKeyCommonData,
435    pub kind: T,
436}
437
438#[cfg_attr(feature = "capture", derive(Serialize))]
439#[cfg_attr(feature = "replay", derive(Deserialize))]
440#[derive(MallocSizeOf)]
441#[derive(Debug)]
442pub struct PrimTemplateCommonData {
443    pub flags: PrimitiveFlags,
444    pub opacity: PrimitiveOpacity,
445    /// Address of the per-primitive data in the GPU cache.
446    ///
447    /// TODO: This is only valid during the current frame and must
448    /// be overwritten each frame. We should move this out of the
449    /// common data to avoid accidental reuse.
450    pub gpu_buffer_address: GpuBufferAddress,
451    pub aligned_aa_edges: EdgeMask,
452    pub transformed_aa_edges: EdgeMask,
453}
454
455impl PrimTemplateCommonData {
456    pub fn with_key_common(common: PrimKeyCommonData) -> Self {
457        PrimTemplateCommonData {
458            flags: common.flags,
459            gpu_buffer_address: GpuBufferAddress::INVALID,
460            opacity: PrimitiveOpacity::translucent(),
461            aligned_aa_edges: common.aligned_aa_edges,
462            transformed_aa_edges: common.transformed_aa_edges,
463        }
464    }
465}
466
467#[cfg_attr(feature = "capture", derive(Serialize))]
468#[cfg_attr(feature = "replay", derive(Deserialize))]
469#[derive(MallocSizeOf)]
470pub struct PrimTemplate<T> {
471    pub common: PrimTemplateCommonData,
472    pub kind: T,
473}
474
475#[derive(Debug, MallocSizeOf)]
476#[cfg_attr(feature = "capture", derive(Serialize))]
477#[cfg_attr(feature = "replay", derive(Deserialize))]
478pub struct VisibleMaskImageTile {
479    pub tile_offset: TileOffset,
480    pub tile_rect: LayoutRect,
481    pub task_id: RenderTaskId,
482}
483
484/// Information about how to cache a border segment,
485/// along with the current render task cache entry.
486#[cfg_attr(feature = "capture", derive(Serialize))]
487#[cfg_attr(feature = "replay", derive(Deserialize))]
488#[derive(Debug, MallocSizeOf)]
489pub struct BorderSegmentInfo {
490    pub local_task_size: LayoutSize,
491    pub cache_key: BorderSegmentCacheKey,
492}
493
494/// Represents the visibility state of a segment (wrt clip masks).
495#[cfg_attr(feature = "capture", derive(Serialize))]
496#[derive(Debug, Clone)]
497pub enum ClipMaskKind {
498    /// The segment has a clip mask, specified by the render task.
499    Mask(RenderTaskId),
500    /// The segment has no clip mask.
501    None,
502    /// The segment is made invisible / clipped completely.
503    Clipped,
504}
505
506#[cfg_attr(feature = "capture", derive(Serialize))]
507#[cfg_attr(feature = "replay", derive(Deserialize))]
508#[derive(Debug, Clone, MallocSizeOf)]
509pub struct BrushSegment {
510    pub local_rect: LayoutRect,
511    pub may_need_clip_mask: bool,
512    pub edge_flags: EdgeMask,
513    pub extra_data: [f32; 4],
514    pub brush_flags: BrushFlags,
515}
516
517impl BrushSegment {
518    pub fn new(
519        local_rect: LayoutRect,
520        may_need_clip_mask: bool,
521        edge_flags: EdgeMask,
522        extra_data: [f32; 4],
523        brush_flags: BrushFlags,
524    ) -> Self {
525        Self {
526            local_rect,
527            may_need_clip_mask,
528            edge_flags,
529            extra_data,
530            brush_flags,
531        }
532    }
533
534    pub fn gpu_data(&self) -> BrushSegmentGpuData {
535        BrushSegmentGpuData {
536            local_rect: self.local_rect,
537            extra_data: self.extra_data,
538        }
539    }
540
541    pub fn write_gpu_blocks(&self, writer: &mut GpuBufferWriterF) {
542        writer.push(&self.gpu_data());
543    }
544}
545
546#[derive(Debug, Clone)]
547#[repr(C)]
548#[cfg_attr(feature = "capture", derive(Serialize))]
549#[cfg_attr(feature = "replay", derive(Deserialize))]
550struct ClipRect {
551    rect: LayoutRect,
552    mode: f32,
553}
554
555#[derive(Debug, Clone)]
556#[repr(C)]
557#[cfg_attr(feature = "capture", derive(Serialize))]
558#[cfg_attr(feature = "replay", derive(Deserialize))]
559struct ClipCorner {
560    rect: LayoutRect,
561    outer_radius_x: f32,
562    outer_radius_y: f32,
563    inner_radius_x: f32,
564    inner_radius_y: f32,
565}
566
567impl ClipCorner {
568    fn uniform(rect: LayoutRect, outer_radius: f32, inner_radius: f32) -> ClipCorner {
569        ClipCorner {
570            rect,
571            outer_radius_x: outer_radius,
572            outer_radius_y: outer_radius,
573            inner_radius_x: inner_radius,
574            inner_radius_y: inner_radius,
575        }
576    }
577}
578
579#[derive(Debug, Clone)]
580#[repr(C)]
581#[cfg_attr(feature = "capture", derive(Serialize))]
582#[cfg_attr(feature = "replay", derive(Deserialize))]
583pub struct ClipData {
584    rect: ClipRect,
585    top_left: ClipCorner,
586    top_right: ClipCorner,
587    bottom_left: ClipCorner,
588    bottom_right: ClipCorner,
589}
590
591impl ClipData {
592    pub fn rounded_rect(size: LayoutSize, radii: &BorderRadius, mode: ClipMode) -> ClipData {
593        // TODO(gw): For simplicity, keep most of the clip GPU structs the
594        //           same as they were, even though the origin is now always
595        //           zero, since they are in the clip's local space. In future,
596        //           we could reduce the GPU cache size of ClipData.
597        let rect = LayoutRect::from_size(size);
598
599        ClipData {
600            rect: ClipRect {
601                rect,
602                mode: mode as u32 as f32,
603            },
604            top_left: ClipCorner {
605                rect: LayoutRect::from_origin_and_size(
606                    LayoutPoint::new(rect.min.x, rect.min.y),
607                    LayoutSize::new(radii.top_left.width, radii.top_left.height),
608                ),
609                outer_radius_x: radii.top_left.width,
610                outer_radius_y: radii.top_left.height,
611                inner_radius_x: 0.0,
612                inner_radius_y: 0.0,
613            },
614            top_right: ClipCorner {
615                rect: LayoutRect::from_origin_and_size(
616                    LayoutPoint::new(
617                        rect.max.x - radii.top_right.width,
618                        rect.min.y,
619                    ),
620                    LayoutSize::new(radii.top_right.width, radii.top_right.height),
621                ),
622                outer_radius_x: radii.top_right.width,
623                outer_radius_y: radii.top_right.height,
624                inner_radius_x: 0.0,
625                inner_radius_y: 0.0,
626            },
627            bottom_left: ClipCorner {
628                rect: LayoutRect::from_origin_and_size(
629                    LayoutPoint::new(
630                        rect.min.x,
631                        rect.max.y - radii.bottom_left.height,
632                    ),
633                    LayoutSize::new(radii.bottom_left.width, radii.bottom_left.height),
634                ),
635                outer_radius_x: radii.bottom_left.width,
636                outer_radius_y: radii.bottom_left.height,
637                inner_radius_x: 0.0,
638                inner_radius_y: 0.0,
639            },
640            bottom_right: ClipCorner {
641                rect: LayoutRect::from_origin_and_size(
642                    LayoutPoint::new(
643                        rect.max.x - radii.bottom_right.width,
644                        rect.max.y - radii.bottom_right.height,
645                    ),
646                    LayoutSize::new(radii.bottom_right.width, radii.bottom_right.height),
647                ),
648                outer_radius_x: radii.bottom_right.width,
649                outer_radius_y: radii.bottom_right.height,
650                inner_radius_x: 0.0,
651                inner_radius_y: 0.0,
652            },
653        }
654    }
655
656    pub fn uniform(size: LayoutSize, radius: f32, mode: ClipMode) -> ClipData {
657        // TODO(gw): For simplicity, keep most of the clip GPU structs the
658        //           same as they were, even though the origin is now always
659        //           zero, since they are in the clip's local space. In future,
660        //           we could reduce the GPU cache size of ClipData.
661        let rect = LayoutRect::from_size(size);
662
663        ClipData {
664            rect: ClipRect {
665                rect,
666                mode: mode as u32 as f32,
667            },
668            top_left: ClipCorner::uniform(
669                LayoutRect::from_origin_and_size(
670                    LayoutPoint::new(rect.min.x, rect.min.y),
671                    LayoutSize::new(radius, radius),
672                ),
673                radius,
674                0.0,
675            ),
676            top_right: ClipCorner::uniform(
677                LayoutRect::from_origin_and_size(
678                    LayoutPoint::new(rect.max.x - radius, rect.min.y),
679                    LayoutSize::new(radius, radius),
680                ),
681                radius,
682                0.0,
683            ),
684            bottom_left: ClipCorner::uniform(
685                LayoutRect::from_origin_and_size(
686                    LayoutPoint::new(rect.min.x, rect.max.y - radius),
687                    LayoutSize::new(radius, radius),
688                ),
689                radius,
690                0.0,
691            ),
692            bottom_right: ClipCorner::uniform(
693                LayoutRect::from_origin_and_size(
694                    LayoutPoint::new(
695                        rect.max.x - radius,
696                        rect.max.y - radius,
697                    ),
698                    LayoutSize::new(radius, radius),
699                ),
700                radius,
701                0.0,
702            ),
703        }
704    }
705}
706
707/// A hashable descriptor for nine-patches, used by image and
708/// gradient borders.
709#[derive(Debug, Clone, PartialEq, Eq, Hash, MallocSizeOf)]
710#[cfg_attr(feature = "capture", derive(Serialize))]
711#[cfg_attr(feature = "replay", derive(Deserialize))]
712pub struct NinePatchDescriptor {
713    pub width: i32,
714    pub height: i32,
715    pub slice: DeviceIntSideOffsets,
716    pub fill: bool,
717    pub repeat_horizontal: RepeatMode,
718    pub repeat_vertical: RepeatMode,
719    pub widths: SideOffsetsKey,
720}
721
722#[derive(Debug)]
723#[cfg_attr(feature = "capture", derive(Serialize))]
724pub enum PrimitiveKind {
725    /// Direct reference to a Picture
726    Picture {
727        /// Handle to the common interned data for this primitive.
728        data_handle: PictureDataHandle,
729        pic_index: PictureIndex,
730    },
731    /// A run of glyphs, with associated font parameters.
732    TextRun {
733        /// Handle to the common interned data for this primitive.
734        data_handle: TextRunDataHandle,
735    },
736    /// A line decoration. cache_handle refers to a cached render
737    /// task handle, if this line decoration is not a simple solid.
738    LineDecoration {
739        /// Handle to the common interned data for this primitive.
740        data_handle: LineDecorationDataHandle,
741    },
742    NormalBorder {
743        /// Handle to the common interned data for this primitive.
744        data_handle: NormalBorderDataHandle,
745    },
746    ImageBorder {
747        /// Handle to the common interned data for this primitive.
748        data_handle: ImageBorderDataHandle,
749    },
750    Rectangle {
751        /// Handle to the common interned data for this primitive.
752        data_handle: RectangleDataHandle,
753    },
754    YuvImage {
755        /// Handle to the common interned data for this primitive.
756        data_handle: YuvImageDataHandle,
757    },
758    Image {
759        /// Handle to the common interned data for this primitive.
760        data_handle: ImageDataHandle,
761    },
762    LinearGradient {
763        /// Handle to the common interned data for this primitive.
764        data_handle: LinearGradientDataHandle,
765    },
766    RadialGradient {
767        /// Handle to the common interned data for this primitive.
768        data_handle: RadialGradientDataHandle,
769    },
770    ConicGradient {
771        /// Handle to the common interned data for this primitive.
772        data_handle: ConicGradientDataHandle,
773    },
774    /// Render a portion of a specified backdrop.
775    BackdropCapture {
776        data_handle: BackdropCaptureDataHandle,
777    },
778    BackdropRender {
779        data_handle: BackdropRenderDataHandle,
780        pic_index: PictureIndex,
781    },
782    BoxShadow {
783        data_handle: BoxShadowDataHandle,
784    },
785}
786
787impl PrimitiveKind {
788    pub fn as_pic(&self) -> PictureIndex {
789        match self {
790            PrimitiveKind::Picture { pic_index, .. } => *pic_index,
791            _ => panic!("bug: as_pic called on a prim that is not a picture"),
792        }
793    }
794}
795
796#[derive(Debug, Copy, Clone)]
797#[cfg_attr(feature = "capture", derive(Serialize))]
798#[cfg_attr(feature = "replay", derive(Deserialize))]
799pub struct PrimitiveInstanceIndex(pub u32);
800
801impl PrimitiveInstanceIndex {
802    pub const INVALID: PrimitiveInstanceIndex = PrimitiveInstanceIndex(!0);
803}
804
805#[derive(Debug)]
806#[cfg_attr(feature = "capture", derive(Serialize))]
807pub struct PrimitiveInstance {
808    /// Identifies the kind of primitive this
809    /// instance is, and references to where
810    /// the relevant information for the primitive
811    /// can be found.
812    pub kind: PrimitiveKind,
813
814    /// All information and state related to clip(s) for this primitive
815    pub clip_leaf_id: ClipLeafId,
816
817    /// Local-space rect of the primitive (origin + size), as authored by the
818    /// display list (not snapped to the device pixel grid). Carries both the
819    /// position and the per-instance size; the latter used to live on
820    /// `PrimTemplateCommonData.prim_size` but is per-instance now so that the
821    /// intern key can deduplicate across differently-sized instances of the
822    /// same prim shape.
823    pub unsnapped_prim_rect: LayoutRect,
824}
825
826impl PrimitiveInstance {
827    pub fn new(
828        kind: PrimitiveKind,
829        clip_leaf_id: ClipLeafId,
830        unsnapped_prim_rect: LayoutRect,
831    ) -> Self {
832        PrimitiveInstance {
833            kind,
834            clip_leaf_id,
835            unsnapped_prim_rect,
836        }
837    }
838
839    pub fn uid(&self) -> intern::ItemUid {
840        match &self.kind {
841            PrimitiveKind::Rectangle { data_handle, .. } => {
842                data_handle.uid()
843            }
844            PrimitiveKind::Image { data_handle, .. } => {
845                data_handle.uid()
846            }
847            PrimitiveKind::ImageBorder { data_handle, .. } => {
848                data_handle.uid()
849            }
850            PrimitiveKind::LineDecoration { data_handle, .. } => {
851                data_handle.uid()
852            }
853            PrimitiveKind::LinearGradient { data_handle, .. } => {
854                data_handle.uid()
855            }
856            PrimitiveKind::NormalBorder { data_handle, .. } => {
857                data_handle.uid()
858            }
859            PrimitiveKind::Picture { data_handle, .. } => {
860                data_handle.uid()
861            }
862            PrimitiveKind::RadialGradient { data_handle, .. } => {
863                data_handle.uid()
864            }
865            PrimitiveKind::ConicGradient { data_handle, .. } => {
866                data_handle.uid()
867            }
868            PrimitiveKind::TextRun { data_handle, .. } => {
869                data_handle.uid()
870            }
871            PrimitiveKind::YuvImage { data_handle, .. } => {
872                data_handle.uid()
873            }
874            PrimitiveKind::BackdropCapture { data_handle, .. } => {
875                data_handle.uid()
876            }
877            PrimitiveKind::BackdropRender { data_handle, .. } => {
878                data_handle.uid()
879            }
880            PrimitiveKind::BoxShadow { data_handle, .. } => {
881                data_handle.uid()
882            }
883
884        }
885    }
886}
887
888#[cfg_attr(feature = "capture", derive(Serialize))]
889#[derive(Debug)]
890pub struct BrushSegmentation {
891    pub gpu_data: GpuBufferAddress,
892    pub segments_range: SegmentsRange,
893}
894
895pub type GlyphKeyStorage = storage::Storage<GlyphKey>;
896pub type SegmentStorage = storage::Storage<BrushSegment>;
897pub type SegmentsRange = storage::Range<BrushSegment>;
898pub type SegmentInstanceStorage = storage::Storage<BrushSegmentation>;
899pub type SegmentInstanceIndex = storage::Index<BrushSegmentation>;
900/// Per-frame scratch storage. All fields are cleared every frame in
901/// `begin_frame`. Anything written here lives only for the current frame.
902#[cfg_attr(feature = "capture", derive(Serialize))]
903pub struct PrimitiveFrameScratch {
904    /// Per-frame draw headers, one entry per `PrimitiveInstance`.
905    /// Resized to `prim_instances.len()` at frame start and identity-
906    /// indexed by `PrimitiveInstanceIndex.0` (a follow-up will switch
907    /// this to push-per-draw with `Index<PrimitiveDrawHeader>`). Holds
908    /// visibility state, clip chain and clip-task index for each
909    /// visible primitive.
910    pub draws: Vec<PrimitiveDrawHeader>,
911
912    /// Per-frame scratch for LineDecoration primitives.
913    pub line_decoration: storage::Storage<LineDecorationScratch>,
914
915    /// Per-frame scratch for NormalBorder primitives.
916    pub normal_border: storage::Storage<NormalBorderScratch>,
917
918    /// Per-frame scratch for BackdropRender primitives. Captures the
919    /// source sub-graph render task id at prepare time so batch reads
920    /// don't reach into the source Picture's per-frame state.
921    pub backdrop_render: storage::Storage<BackdropRenderScratch>,
922
923    /// Per-frame scratch for Picture primitives. Holds the picture's
924    /// primary/secondary render task ids and any per-composite-mode
925    /// extra GPU buffer addresses. Indexed by `scratch_handle` on
926    /// `PrimitiveKind::Picture`.
927    pub pictures: storage::Storage<PictureScratch>,
928
929    /// Per-frame scratch for Image primitives. Holds the source render
930    /// task (or a Range of per-tile tasks for tiled images), normalized-
931    /// uvs flag, and image adjustment.
932    pub images: storage::Storage<ImageScratch>,
933
934    /// Per-tile entries for tiled Image primitives. Each `ImageScratch`
935    /// holds a `Range` into this storage.
936    pub visible_image_tiles: storage::Storage<VisibleImageTile>,
937
938    /// Per-frame scratch for TextRun primitives. Holds the per-frame
939    /// font snapshot, glyph-key range, snapping offset, and raster
940    /// scale for each visible text run.
941    pub text_runs: storage::Storage<TextRunScratch>,
942
943    /// Per-frame storage for glyph keys allocated by visible text
944    /// runs. Each `TextRunScratch` holds a `Range` into this storage.
945    /// Used to be on `PrimitiveSceneCache` (memoized across frames);
946    /// graduated to per-frame here so the scene buffer cannot grow
947    /// unbounded between scene rebuilds.
948    pub glyph_keys: GlyphKeyStorage,
949
950    /// A list of brush segments built each frame for the segmented
951    /// brush primitives (Rectangle, YuvImage, non-tiled Image). The
952    /// segment builder runs every frame for every visible segmented
953    /// prim.
954    pub segments: SegmentStorage,
955
956    /// A list of per-prim brush segmentation records (segments range
957    /// + GPU buffer address). Each PrimitiveDrawHeader.segment_instance_index
958    /// holds an index into this storage, or UNUSED for non-segmented
959    /// prims.
960    pub segment_instances: SegmentInstanceStorage,
961
962    /// Trailing-array store for per-segment cached render-task ids
963    /// referenced by NormalBorderScratch entries.
964    pub border_task_ids: storage::Storage<RenderTaskId>,
965
966    /// Per-frame BorderSegmentInfo arena. NormalBorder builds its
967    /// edge/corner segment list each frame against the prim's size and
968    /// stores the resulting range on `NormalBorderScratch`.
969    pub border_segments: storage::Storage<BorderSegmentInfo>,
970
971    /// Per-frame scratch for ImageBorder primitives. Holds the range
972    /// into `segments` for the nine-patch brush segments built each
973    /// frame against the prim's size.
974    pub image_border: storage::Storage<ImageBorderScratch>,
975
976    /// Contains a list of clip mask instance parameters
977    /// per segment generated.
978    pub clip_mask_instances: Vec<ClipMaskKind>,
979
980    /// List of debug display items for rendering. Cleared in `begin_frame`
981    /// and refilled in `end_frame` (where retained `messages` are flushed
982    /// into it for on-screen display).
983    pub debug_items: Vec<DebugItem>,
984
985    /// Set of sub-graphs that are required, determined during visibility pass
986    pub required_sub_graphs: FastHashSet<PictureIndex>,
987
988    /// Temporary buffers for building segments in to during prepare pass
989    pub quad_direct_segments: Vec<QuadSegment>,
990    pub quad_indirect_segments: Vec<QuadSegment>,
991}
992
993impl Default for PrimitiveFrameScratch {
994    fn default() -> Self {
995        PrimitiveFrameScratch {
996            draws: Vec::new(),
997            line_decoration: storage::Storage::new(0),
998            normal_border: storage::Storage::new(0),
999            backdrop_render: storage::Storage::new(0),
1000            pictures: storage::Storage::new(0),
1001            images: storage::Storage::new(0),
1002            visible_image_tiles: storage::Storage::new(0),
1003            text_runs: storage::Storage::new(0),
1004            glyph_keys: GlyphKeyStorage::new(0),
1005            segments: SegmentStorage::new(0),
1006            segment_instances: SegmentInstanceStorage::new(0),
1007            border_task_ids: storage::Storage::new(0),
1008            border_segments: storage::Storage::new(0),
1009            image_border: storage::Storage::new(0),
1010            clip_mask_instances: Vec::new(),
1011            debug_items: Vec::new(),
1012            required_sub_graphs: FastHashSet::default(),
1013            quad_direct_segments: Vec::new(),
1014            quad_indirect_segments: Vec::new(),
1015        }
1016    }
1017}
1018
1019impl PrimitiveFrameScratch {
1020    pub fn recycle(&mut self, recycler: &mut Recycler) {
1021        recycler.recycle_vec(&mut self.draws);
1022        self.line_decoration.recycle(recycler);
1023        self.normal_border.recycle(recycler);
1024        self.backdrop_render.recycle(recycler);
1025        self.pictures.recycle(recycler);
1026        self.images.recycle(recycler);
1027        self.visible_image_tiles.recycle(recycler);
1028        self.text_runs.recycle(recycler);
1029        self.glyph_keys.recycle(recycler);
1030        self.segments.recycle(recycler);
1031        self.segment_instances.recycle(recycler);
1032        self.border_task_ids.recycle(recycler);
1033        self.border_segments.recycle(recycler);
1034        self.image_border.recycle(recycler);
1035        recycler.recycle_vec(&mut self.clip_mask_instances);
1036        recycler.recycle_vec(&mut self.debug_items);
1037        recycler.recycle_vec(&mut self.quad_direct_segments);
1038        recycler.recycle_vec(&mut self.quad_indirect_segments);
1039    }
1040
1041    pub fn begin_frame(&mut self) {
1042        self.line_decoration.clear();
1043        self.normal_border.clear();
1044        self.backdrop_render.clear();
1045        self.pictures.clear();
1046        self.images.clear();
1047        self.visible_image_tiles.clear();
1048        self.text_runs.clear();
1049        self.glyph_keys.clear();
1050        self.segments.clear();
1051        self.segment_instances.clear();
1052        self.border_task_ids.clear();
1053        self.border_segments.clear();
1054        self.image_border.clear();
1055
1056        // Clear the clip mask tasks for the beginning of the frame. Append
1057        // a single kind representing no clip mask, at the ClipTaskIndex::INVALID
1058        // location.
1059        self.clip_mask_instances.clear();
1060        self.clip_mask_instances.push(ClipMaskKind::None);
1061        self.quad_direct_segments.clear();
1062        self.quad_indirect_segments.clear();
1063
1064        self.required_sub_graphs.clear();
1065
1066        self.debug_items.clear();
1067    }
1068}
1069
1070/// Per-scene cache. Now empty — the originally memoized fields have
1071/// migrated to per-frame storage. Kept as a placeholder for any future
1072/// scene-stable state and so the lifetime invariant on
1073/// PrimitiveScratchBuffer (frame / scene / retained) remains visible
1074/// at the type level; a follow-up may drop it entirely.
1075#[cfg_attr(feature = "capture", derive(Serialize))]
1076#[derive(Default)]
1077pub struct PrimitiveSceneCache {}
1078
1079impl PrimitiveSceneCache {
1080    pub fn recycle(&mut self, _recycler: &mut Recycler) {}
1081}
1082
1083/// State that lives strictly longer than a single frame *and* is not tied
1084/// to scene lifetime. These fields manage their own trim/eviction policy
1085/// rather than being cleared by `begin_frame` or `recycle`.
1086#[cfg_attr(feature = "capture", derive(Serialize))]
1087pub struct PrimitiveRetained {
1088    /// Debug log of recent messages. Trimmed by time/count in
1089    /// `PrimitiveScratchBuffer::end_frame` and flushed into
1090    /// `PrimitiveFrameScratch::debug_items` for display.
1091    messages: Vec<DebugMessage>,
1092
1093    /// A retained classifier for checking which segments of a tiled
1094    /// primitive need a mask / are clipped / can be rendered directly.
1095    pub quad_tile_classifier: QuadTileClassifier,
1096}
1097
1098impl Default for PrimitiveRetained {
1099    fn default() -> Self {
1100        PrimitiveRetained {
1101            messages: Vec::new(),
1102            quad_tile_classifier: QuadTileClassifier::new(),
1103        }
1104    }
1105}
1106
1107/// Contains various vecs of data that is used only during frame building,
1108/// where we want to recycle the memory each new display list, to avoid
1109/// constantly re-allocating and moving memory around. Written during
1110/// primitive preparation, and read during batching.
1111///
1112/// Storage is partitioned by lifetime: `frame` is per-frame (cleared in
1113/// `begin_frame`), `scene` is per-scene (recycled on scene rebuild), and
1114/// `retained` lives across both with its own trim policy.
1115#[cfg_attr(feature = "capture", derive(Serialize))]
1116#[derive(Default)]
1117pub struct PrimitiveScratchBuffer {
1118    pub frame: PrimitiveFrameScratch,
1119    pub scene: PrimitiveSceneCache,
1120    pub retained: PrimitiveRetained,
1121}
1122
1123impl PrimitiveScratchBuffer {
1124    pub fn recycle(&mut self, recycler: &mut Recycler) {
1125        self.frame.recycle(recycler);
1126        self.scene.recycle(recycler);
1127    }
1128
1129    pub fn begin_frame(&mut self) {
1130        self.frame.begin_frame();
1131    }
1132
1133    pub fn end_frame(&mut self) {
1134        const MSGS_TO_RETAIN: usize = 32;
1135        const TIME_TO_RETAIN: u64 = 2000000000;
1136        const LINE_HEIGHT: f32 = 20.0;
1137        const X0: f32 = 32.0;
1138        const Y0: f32 = 32.0;
1139        let now = zeitstempel::now();
1140
1141        let messages = &mut self.retained.messages;
1142        let msgs_to_remove = messages.len().max(MSGS_TO_RETAIN) - MSGS_TO_RETAIN;
1143        let mut msgs_removed = 0;
1144
1145        messages.retain(|msg| {
1146            if msgs_removed < msgs_to_remove {
1147                msgs_removed += 1;
1148                return false;
1149            }
1150
1151            if msg.timestamp + TIME_TO_RETAIN < now {
1152                return false;
1153            }
1154
1155            true
1156        });
1157
1158        let mut y = Y0 + messages.len() as f32 * LINE_HEIGHT;
1159        let shadow_offset = 1.0;
1160        let debug_items = &mut self.frame.debug_items;
1161
1162        for msg in messages.iter() {
1163            debug_items.push(DebugItem::Text {
1164                position: DevicePoint::new(X0 + shadow_offset, y + shadow_offset),
1165                color: debug_colors::BLACK,
1166                msg: msg.msg.clone(),
1167            });
1168
1169            debug_items.push(DebugItem::Text {
1170                position: DevicePoint::new(X0, y),
1171                color: debug_colors::RED,
1172                msg: msg.msg.clone(),
1173            });
1174
1175            y -= LINE_HEIGHT;
1176        }
1177    }
1178
1179    pub fn push_debug_rect_with_stroke_width(
1180        &mut self,
1181        rect: WorldRect,
1182        border: ColorF,
1183        stroke_width: f32
1184    ) {
1185        let top_edge = WorldRect::new(
1186            WorldPoint::new(rect.min.x + stroke_width, rect.min.y),
1187            WorldPoint::new(rect.max.x - stroke_width, rect.min.y + stroke_width)
1188        );
1189        self.push_debug_rect(top_edge * DevicePixelScale::new(1.0), 1, border, border);
1190
1191        let bottom_edge = WorldRect::new(
1192            WorldPoint::new(rect.min.x + stroke_width, rect.max.y - stroke_width),
1193            WorldPoint::new(rect.max.x - stroke_width, rect.max.y)
1194        );
1195        self.push_debug_rect(bottom_edge * DevicePixelScale::new(1.0), 1, border, border);
1196
1197        let right_edge = WorldRect::new(
1198            WorldPoint::new(rect.max.x - stroke_width, rect.min.y),
1199            rect.max
1200        );
1201        self.push_debug_rect(right_edge * DevicePixelScale::new(1.0), 1, border, border);
1202
1203        let left_edge = WorldRect::new(
1204            rect.min,
1205            WorldPoint::new(rect.min.x + stroke_width, rect.max.y)
1206        );
1207        self.push_debug_rect(left_edge * DevicePixelScale::new(1.0), 1, border, border);
1208    }
1209
1210    #[allow(dead_code)]
1211    pub fn push_debug_rect(
1212        &mut self,
1213        rect: DeviceRect,
1214        thickness: i32,
1215        outer_color: ColorF,
1216        inner_color: ColorF,
1217    ) {
1218        self.frame.debug_items.push(DebugItem::Rect {
1219            rect,
1220            outer_color,
1221            inner_color,
1222            thickness,
1223        });
1224    }
1225
1226    #[allow(dead_code)]
1227    pub fn push_debug_string(
1228        &mut self,
1229        position: DevicePoint,
1230        color: ColorF,
1231        msg: String,
1232    ) {
1233        self.frame.debug_items.push(DebugItem::Text {
1234            position,
1235            color,
1236            msg,
1237        });
1238    }
1239
1240    #[allow(dead_code)]
1241    pub fn log(
1242        &mut self,
1243        msg: String,
1244    ) {
1245        self.retained.messages.push(DebugMessage {
1246            msg,
1247            timestamp: zeitstempel::now(),
1248        })
1249    }
1250}
1251
1252#[cfg_attr(feature = "capture", derive(Serialize))]
1253#[cfg_attr(feature = "replay", derive(Deserialize))]
1254#[derive(Clone, Debug)]
1255pub struct PrimitiveStoreStats {
1256    picture_count: usize,
1257}
1258
1259impl PrimitiveStoreStats {
1260    pub fn empty() -> Self {
1261        PrimitiveStoreStats {
1262            picture_count: 0,
1263        }
1264    }
1265}
1266
1267#[cfg_attr(feature = "capture", derive(Serialize))]
1268pub struct PrimitiveStore {
1269    pub pictures: Vec<PictureInstance>,
1270}
1271
1272impl PrimitiveStore {
1273    pub fn new(stats: &PrimitiveStoreStats) -> PrimitiveStore {
1274        PrimitiveStore {
1275            pictures: Vec::with_capacity(stats.picture_count),
1276        }
1277    }
1278
1279    pub fn reset(&mut self) {
1280        self.pictures.clear();
1281    }
1282
1283    pub fn get_stats(&self) -> PrimitiveStoreStats {
1284        PrimitiveStoreStats {
1285            picture_count: self.pictures.len(),
1286        }
1287    }
1288
1289    #[allow(unused)]
1290    pub fn print_picture_tree(&self, root: PictureIndex) {
1291        use crate::print_tree::PrintTree;
1292        let mut pt = PrintTree::new("picture tree");
1293        self.pictures[root.0].print(&self.pictures, root, &mut pt);
1294    }
1295}
1296
1297impl Default for PrimitiveStore {
1298    fn default() -> Self {
1299        PrimitiveStore::new(&PrimitiveStoreStats::empty())
1300    }
1301}
1302
1303/// Trait for primitives that are directly internable.
1304/// see SceneBuilder::add_primitive<P>
1305pub trait InternablePrimitive: intern::Internable<InternData = ()> + Sized {
1306    /// Build a new key from self with `info`.
1307    fn into_key(
1308        self,
1309        info: &LayoutPrimitiveInfo,
1310    ) -> Self::Key;
1311
1312    fn make_instance_kind(
1313        key: Self::Key,
1314        data_handle: intern::Handle<Self>,
1315        prim_store: &mut PrimitiveStore,
1316    ) -> PrimitiveKind;
1317}
1318
1319
1320#[test]
1321#[cfg(target_pointer_width = "64")]
1322fn test_struct_sizes() {
1323    use std::mem;
1324    // The sizes of these structures are critical for performance on a number of
1325    // talos stress tests. If you get a failure here on CI, there's two possibilities:
1326    // (a) You made a structure smaller than it currently is. Great work! Update the
1327    //     test expectations and move on.
1328    // (b) You made a structure larger. This is not necessarily a problem, but should only
1329    //     be done with care, and after checking if talos performance regresses badly.
1330    assert_eq!(mem::size_of::<PrimitiveInstance>(), 48, "PrimitiveInstance size changed");
1331    assert_eq!(mem::size_of::<PrimitiveKind>(), 24, "PrimitiveKind size changed");
1332}