webrender/
gpu_types.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::{DocumentLayer, PremultipliedColorF};
6use api::units::*;
7use crate::clip_scroll_tree::{ClipScrollTree, ROOT_SPATIAL_NODE_INDEX, SpatialNodeIndex};
8use crate::gpu_cache::{GpuCacheAddress, GpuDataRequest};
9use crate::internal_types::FastHashMap;
10use crate::prim_store::EdgeAaSegmentMask;
11use crate::render_task::RenderTaskAddress;
12use std::i32;
13use crate::util::{TransformedRectKind, MatrixHelpers};
14
15// Contains type that must exactly match the same structures declared in GLSL.
16
17pub const VECS_PER_TRANSFORM: usize = 8;
18
19#[derive(Copy, Clone, Debug, PartialEq)]
20#[repr(C)]
21#[cfg_attr(feature = "capture", derive(Serialize))]
22#[cfg_attr(feature = "replay", derive(Deserialize))]
23pub struct ZBufferId(i32);
24
25// We get 24 bits of Z value - use up 22 bits of it to give us
26// 4 bits to account for GPU issues. This seems to manifest on
27// some GPUs under certain perspectives due to z interpolation
28// precision problems.
29const MAX_DOCUMENT_LAYERS : i8 = 1 << 3;
30const MAX_ITEMS_PER_DOCUMENT_LAYER : i32 = 1 << 19;
31const MAX_DOCUMENT_LAYER_VALUE : i8 = MAX_DOCUMENT_LAYERS / 2 - 1;
32const MIN_DOCUMENT_LAYER_VALUE : i8 = -MAX_DOCUMENT_LAYERS / 2;
33
34impl ZBufferId {
35    pub fn invalid() -> Self {
36        ZBufferId(i32::MAX)
37    }
38}
39
40#[derive(Debug)]
41#[cfg_attr(feature = "capture", derive(Serialize))]
42#[cfg_attr(feature = "replay", derive(Deserialize))]
43pub struct ZBufferIdGenerator {
44    base: i32,
45    next: i32,
46}
47
48impl ZBufferIdGenerator {
49    pub fn new(layer: DocumentLayer) -> Self {
50        debug_assert!(layer >= MIN_DOCUMENT_LAYER_VALUE);
51        debug_assert!(layer <= MAX_DOCUMENT_LAYER_VALUE);
52        ZBufferIdGenerator {
53            base: layer as i32 * MAX_ITEMS_PER_DOCUMENT_LAYER,
54            next: 0
55        }
56    }
57
58    pub fn next(&mut self) -> ZBufferId {
59        debug_assert!(self.next < MAX_ITEMS_PER_DOCUMENT_LAYER);
60        let id = ZBufferId(self.next + self.base);
61        self.next += 1;
62        id
63    }
64}
65
66/// A shader kind identifier that can be used by a generic-shader to select the behavior at runtime.
67///
68/// Not all brush kinds need to be present in this enum, only those we want to support in the generic
69/// brush shader.
70/// Do not use the 24 lowest bits. This will be packed with other information in the vertex attributes.
71/// The constants must match the corresponding defines in brush_multi.glsl.
72#[repr(i32)]
73#[derive(Copy, Clone, Debug, PartialEq)]
74pub enum BrushShaderKind {
75    None            = 0,
76    Solid           = 1,
77    Image           = 2,
78    Text            = 3,
79    LinearGradient  = 4,
80    RadialGradient  = 5,
81    Blend           = 6,
82    MixBlend        = 7,
83    Yuv             = 8,
84    Opacity         = 9,
85}
86
87#[derive(Debug, Copy, Clone)]
88#[cfg_attr(feature = "capture", derive(Serialize))]
89#[cfg_attr(feature = "replay", derive(Deserialize))]
90#[repr(C)]
91pub enum RasterizationSpace {
92    Local = 0,
93    Screen = 1,
94}
95
96#[derive(Debug, Copy, Clone, MallocSizeOf)]
97#[cfg_attr(feature = "capture", derive(Serialize))]
98#[cfg_attr(feature = "replay", derive(Deserialize))]
99#[repr(C)]
100pub enum BoxShadowStretchMode {
101    Stretch = 0,
102    Simple = 1,
103}
104
105#[repr(i32)]
106#[derive(Debug, Copy, Clone)]
107#[cfg_attr(feature = "capture", derive(Serialize))]
108#[cfg_attr(feature = "replay", derive(Deserialize))]
109pub enum BlurDirection {
110    Horizontal = 0,
111    Vertical,
112}
113
114#[derive(Debug)]
115#[repr(C)]
116#[cfg_attr(feature = "capture", derive(Serialize))]
117#[cfg_attr(feature = "replay", derive(Deserialize))]
118pub struct BlurInstance {
119    pub task_address: RenderTaskAddress,
120    pub src_task_address: RenderTaskAddress,
121    pub blur_direction: BlurDirection,
122}
123
124#[derive(Debug)]
125#[repr(C)]
126#[cfg_attr(feature = "capture", derive(Serialize))]
127#[cfg_attr(feature = "replay", derive(Deserialize))]
128pub struct ScalingInstance {
129    pub target_rect: DeviceRect,
130    pub source_rect: DeviceIntRect,
131    pub source_layer: i32,
132}
133
134#[derive(Debug)]
135#[repr(C)]
136#[cfg_attr(feature = "capture", derive(Serialize))]
137#[cfg_attr(feature = "replay", derive(Deserialize))]
138pub struct SvgFilterInstance {
139    pub task_address: RenderTaskAddress,
140    pub input_1_task_address: RenderTaskAddress,
141    pub input_2_task_address: RenderTaskAddress,
142    pub kind: u16,
143    pub input_count: u16,
144    pub generic_int: u16,
145    pub extra_data_address: GpuCacheAddress,
146}
147
148#[derive(Copy, Clone, Debug, Hash, MallocSizeOf, PartialEq, Eq)]
149#[repr(C)]
150#[cfg_attr(feature = "capture", derive(Serialize))]
151#[cfg_attr(feature = "replay", derive(Deserialize))]
152pub enum BorderSegment {
153    TopLeft,
154    TopRight,
155    BottomRight,
156    BottomLeft,
157    Left,
158    Top,
159    Right,
160    Bottom,
161}
162
163#[derive(Debug, Clone)]
164#[repr(C)]
165#[cfg_attr(feature = "capture", derive(Serialize))]
166#[cfg_attr(feature = "replay", derive(Deserialize))]
167pub struct BorderInstance {
168    pub task_origin: DevicePoint,
169    pub local_rect: DeviceRect,
170    pub color0: PremultipliedColorF,
171    pub color1: PremultipliedColorF,
172    pub flags: i32,
173    pub widths: DeviceSize,
174    pub radius: DeviceSize,
175    pub clip_params: [f32; 8],
176}
177
178/// A clipping primitive drawn into the clipping mask.
179/// Could be an image or a rectangle, which defines the
180/// way `address` is treated.
181#[derive(Debug, Copy, Clone)]
182#[cfg_attr(feature = "capture", derive(Serialize))]
183#[cfg_attr(feature = "replay", derive(Deserialize))]
184#[repr(C)]
185pub struct ClipMaskInstance {
186    pub clip_transform_id: TransformPaletteId,
187    pub prim_transform_id: TransformPaletteId,
188    pub clip_data_address: GpuCacheAddress,
189    pub resource_address: GpuCacheAddress,
190    pub local_pos: LayoutPoint,
191    pub tile_rect: LayoutRect,
192    pub sub_rect: DeviceRect,
193    pub task_origin: DevicePoint,
194    pub screen_origin: DevicePoint,
195    pub device_pixel_scale: f32,
196}
197
198/// A border corner dot or dash drawn into the clipping mask.
199#[derive(Debug, Copy, Clone)]
200#[cfg_attr(feature = "capture", derive(Serialize))]
201#[cfg_attr(feature = "replay", derive(Deserialize))]
202#[repr(C)]
203pub struct ClipMaskBorderCornerDotDash {
204    pub clip_mask_instance: ClipMaskInstance,
205    pub dot_dash_data: [f32; 8],
206}
207
208// 16 bytes per instance should be enough for anyone!
209#[derive(Debug, Clone)]
210#[cfg_attr(feature = "capture", derive(Serialize))]
211#[cfg_attr(feature = "replay", derive(Deserialize))]
212pub struct PrimitiveInstanceData {
213    data: [i32; 4],
214}
215
216/// Vertex format for resolve style operations with pixel local storage.
217#[derive(Debug, Clone)]
218#[repr(C)]
219pub struct ResolveInstanceData {
220    rect: [f32; 4],
221}
222
223impl ResolveInstanceData {
224    pub fn new(rect: DeviceIntRect) -> Self {
225        ResolveInstanceData {
226            rect: [
227                rect.origin.x as f32,
228                rect.origin.y as f32,
229                rect.size.width as f32,
230                rect.size.height as f32,
231            ],
232        }
233    }
234}
235
236/// Vertex format for picture cache composite shader.
237#[derive(Debug, Clone)]
238#[repr(C)]
239pub struct CompositeInstance {
240    rect: DeviceRect,
241    clip_rect: DeviceRect,
242    color: PremultipliedColorF,
243    layer: f32,
244    z_id: f32,
245}
246
247impl CompositeInstance {
248    pub fn new(
249        rect: DeviceRect,
250        clip_rect: DeviceRect,
251        color: PremultipliedColorF,
252        layer: f32,
253        z_id: ZBufferId,
254    ) -> Self {
255        CompositeInstance {
256            rect,
257            clip_rect,
258            color,
259            layer,
260            z_id: z_id.0 as f32,
261        }
262    }
263}
264
265#[derive(Debug, Copy, Clone)]
266#[cfg_attr(feature = "capture", derive(Serialize))]
267#[cfg_attr(feature = "replay", derive(Deserialize))]
268pub struct PrimitiveHeaderIndex(pub i32);
269
270#[derive(Debug)]
271#[repr(C)]
272#[cfg_attr(feature = "capture", derive(Serialize))]
273#[cfg_attr(feature = "replay", derive(Deserialize))]
274pub struct PrimitiveHeaders {
275    // The integer-type headers for a primitive.
276    pub headers_int: Vec<PrimitiveHeaderI>,
277    // The float-type headers for a primitive.
278    pub headers_float: Vec<PrimitiveHeaderF>,
279}
280
281impl PrimitiveHeaders {
282    pub fn new() -> PrimitiveHeaders {
283        PrimitiveHeaders {
284            headers_int: Vec::new(),
285            headers_float: Vec::new(),
286        }
287    }
288
289    // Add a new primitive header.
290    pub fn push(
291        &mut self,
292        prim_header: &PrimitiveHeader,
293        z: ZBufferId,
294        user_data: [i32; 4],
295    ) -> PrimitiveHeaderIndex {
296        debug_assert_eq!(self.headers_int.len(), self.headers_float.len());
297        let id = self.headers_float.len();
298
299        self.headers_float.push(PrimitiveHeaderF {
300            local_rect: prim_header.local_rect,
301            local_clip_rect: prim_header.local_clip_rect,
302        });
303
304        self.headers_int.push(PrimitiveHeaderI {
305            z,
306            unused: 0,
307            specific_prim_address: prim_header.specific_prim_address.as_int(),
308            transform_id: prim_header.transform_id,
309            user_data,
310        });
311
312        PrimitiveHeaderIndex(id as i32)
313    }
314}
315
316// This is a convenience type used to make it easier to pass
317// the common parts around during batching.
318#[derive(Debug)]
319pub struct PrimitiveHeader {
320    pub local_rect: LayoutRect,
321    pub local_clip_rect: LayoutRect,
322    pub specific_prim_address: GpuCacheAddress,
323    pub transform_id: TransformPaletteId,
324}
325
326// f32 parts of a primitive header
327#[derive(Debug)]
328#[repr(C)]
329#[cfg_attr(feature = "capture", derive(Serialize))]
330#[cfg_attr(feature = "replay", derive(Deserialize))]
331pub struct PrimitiveHeaderF {
332    pub local_rect: LayoutRect,
333    pub local_clip_rect: LayoutRect,
334}
335
336// i32 parts of a primitive header
337// TODO(gw): Compress parts of these down to u16
338#[derive(Debug)]
339#[repr(C)]
340#[cfg_attr(feature = "capture", derive(Serialize))]
341#[cfg_attr(feature = "replay", derive(Deserialize))]
342pub struct PrimitiveHeaderI {
343    pub z: ZBufferId,
344    pub specific_prim_address: i32,
345    pub transform_id: TransformPaletteId,
346    pub unused: i32,                    // To ensure required 16 byte alignment of vertex textures
347    pub user_data: [i32; 4],
348}
349
350pub struct GlyphInstance {
351    pub prim_header_index: PrimitiveHeaderIndex,
352}
353
354impl GlyphInstance {
355    pub fn new(
356        prim_header_index: PrimitiveHeaderIndex,
357    ) -> Self {
358        GlyphInstance {
359            prim_header_index,
360        }
361    }
362
363    // TODO(gw): Some of these fields can be moved to the primitive
364    //           header since they are constant, and some can be
365    //           compressed to a smaller size.
366    pub fn build(&self, data0: i32, data1: i32, resource_address: i32) -> PrimitiveInstanceData {
367        PrimitiveInstanceData {
368            data: [
369                self.prim_header_index.0 as i32,
370                data0,
371                data1,
372                resource_address | ((BrushShaderKind::Text as i32) << 24),
373            ],
374        }
375    }
376}
377
378pub struct SplitCompositeInstance {
379    pub prim_header_index: PrimitiveHeaderIndex,
380    pub polygons_address: GpuCacheAddress,
381    pub z: ZBufferId,
382    pub render_task_address: RenderTaskAddress,
383}
384
385impl From<SplitCompositeInstance> for PrimitiveInstanceData {
386    fn from(instance: SplitCompositeInstance) -> Self {
387        PrimitiveInstanceData {
388            data: [
389                instance.prim_header_index.0,
390                instance.polygons_address.as_int(),
391                instance.z.0,
392                instance.render_task_address.0 as i32,
393            ],
394        }
395    }
396}
397
398bitflags! {
399    /// Flags that define how the common brush shader
400    /// code should process this instance.
401    #[cfg_attr(feature = "capture", derive(Serialize))]
402    #[cfg_attr(feature = "replay", derive(Deserialize))]
403    #[derive(MallocSizeOf)]
404    pub struct BrushFlags: u8 {
405        /// Apply perspective interpolation to UVs
406        const PERSPECTIVE_INTERPOLATION = 0x1;
407        /// Do interpolation relative to segment rect,
408        /// rather than primitive rect.
409        const SEGMENT_RELATIVE = 0x2;
410        /// Repeat UVs horizontally.
411        const SEGMENT_REPEAT_X = 0x4;
412        /// Repeat UVs vertically.
413        const SEGMENT_REPEAT_Y = 0x8;
414        /// The extra segment data is a texel rect.
415        const SEGMENT_TEXEL_RECT = 0x10;
416    }
417}
418
419/// Convenience structure to encode into PrimitiveInstanceData.
420pub struct BrushInstance {
421    pub prim_header_index: PrimitiveHeaderIndex,
422    pub render_task_address: RenderTaskAddress,
423    pub clip_task_address: RenderTaskAddress,
424    pub segment_index: i32,
425    pub edge_flags: EdgeAaSegmentMask,
426    pub brush_flags: BrushFlags,
427    pub resource_address: i32,
428    pub brush_kind: BrushShaderKind,
429}
430
431impl From<BrushInstance> for PrimitiveInstanceData {
432    fn from(instance: BrushInstance) -> Self {
433        PrimitiveInstanceData {
434            data: [
435                instance.prim_header_index.0,
436                ((instance.render_task_address.0 as i32) << 16)
437                | instance.clip_task_address.0 as i32,
438                instance.segment_index
439                | ((instance.edge_flags.bits() as i32) << 16)
440                | ((instance.brush_flags.bits() as i32) << 24),
441                instance.resource_address
442                | ((instance.brush_kind as i32) << 24),
443            ]
444        }
445    }
446}
447
448// Represents the information about a transform palette
449// entry that is passed to shaders. It includes an index
450// into the transform palette, and a set of flags. The
451// only flag currently used determines whether the
452// transform is axis-aligned (and this should have
453// pixel snapping applied).
454#[derive(Copy, Debug, Clone, PartialEq)]
455#[cfg_attr(feature = "capture", derive(Serialize))]
456#[cfg_attr(feature = "replay", derive(Deserialize))]
457#[repr(C)]
458pub struct TransformPaletteId(pub u32);
459
460impl TransformPaletteId {
461    /// Identity transform ID.
462    pub const IDENTITY: Self = TransformPaletteId(0);
463
464    /// Extract the transform kind from the id.
465    pub fn transform_kind(&self) -> TransformedRectKind {
466        if (self.0 >> 24) == 0 {
467            TransformedRectKind::AxisAligned
468        } else {
469            TransformedRectKind::Complex
470        }
471    }
472}
473
474/// The GPU data payload for a transform palette entry.
475#[derive(Debug, Clone)]
476#[cfg_attr(feature = "capture", derive(Serialize))]
477#[cfg_attr(feature = "replay", derive(Deserialize))]
478#[repr(C)]
479pub struct TransformData {
480    transform: LayoutToPictureTransform,
481    inv_transform: PictureToLayoutTransform,
482}
483
484impl TransformData {
485    fn invalid() -> Self {
486        TransformData {
487            transform: LayoutToPictureTransform::identity(),
488            inv_transform: PictureToLayoutTransform::identity(),
489        }
490    }
491}
492
493// Extra data stored about each transform palette entry.
494#[derive(Clone)]
495pub struct TransformMetadata {
496    transform_kind: TransformedRectKind,
497}
498
499impl TransformMetadata {
500    pub fn invalid() -> Self {
501        TransformMetadata {
502            transform_kind: TransformedRectKind::AxisAligned,
503        }
504    }
505}
506
507#[derive(Debug, Hash, Eq, PartialEq)]
508struct RelativeTransformKey {
509    from_index: SpatialNodeIndex,
510    to_index: SpatialNodeIndex,
511}
512
513// Stores a contiguous list of TransformData structs, that
514// are ready for upload to the GPU.
515// TODO(gw): For now, this only stores the complete local
516//           to world transform for each spatial node. In
517//           the future, the transform palette will support
518//           specifying a coordinate system that the transform
519//           should be relative to.
520pub struct TransformPalette {
521    transforms: Vec<TransformData>,
522    metadata: Vec<TransformMetadata>,
523    map: FastHashMap<RelativeTransformKey, usize>,
524}
525
526impl TransformPalette {
527    pub fn new(count: usize) -> Self {
528        let _ = VECS_PER_TRANSFORM;
529        TransformPalette {
530            transforms: vec![TransformData::invalid(); count],
531            metadata: vec![TransformMetadata::invalid(); count],
532            map: FastHashMap::default(),
533        }
534    }
535
536    pub fn finish(self) -> Vec<TransformData> {
537        self.transforms
538    }
539
540    pub fn set_world_transform(
541        &mut self,
542        index: SpatialNodeIndex,
543        transform: LayoutToWorldTransform,
544    ) {
545        register_transform(
546            &mut self.metadata,
547            &mut self.transforms,
548            index,
549            ROOT_SPATIAL_NODE_INDEX,
550            // We know the root picture space == world space
551            transform.with_destination::<PicturePixel>(),
552        );
553    }
554
555    fn get_index(
556        &mut self,
557        child_index: SpatialNodeIndex,
558        parent_index: SpatialNodeIndex,
559        clip_scroll_tree: &ClipScrollTree,
560    ) -> usize {
561        if parent_index == ROOT_SPATIAL_NODE_INDEX {
562            child_index.0 as usize
563        } else if child_index == parent_index {
564            0
565        } else {
566            let key = RelativeTransformKey {
567                from_index: child_index,
568                to_index: parent_index,
569            };
570
571            let metadata = &mut self.metadata;
572            let transforms = &mut self.transforms;
573
574            *self.map
575                .entry(key)
576                .or_insert_with(|| {
577                    let transform = clip_scroll_tree.get_relative_transform(
578                        child_index,
579                        parent_index,
580                    )
581                    .into_transform()
582                    .with_destination::<PicturePixel>();
583
584                    register_transform(
585                        metadata,
586                        transforms,
587                        child_index,
588                        parent_index,
589                        transform,
590                    )
591                })
592        }
593    }
594
595    // Get a transform palette id for the given spatial node.
596    // TODO(gw): In the future, it will be possible to specify
597    //           a coordinate system id here, to allow retrieving
598    //           transforms in the local space of a given spatial node.
599    pub fn get_id(
600        &mut self,
601        from_index: SpatialNodeIndex,
602        to_index: SpatialNodeIndex,
603        clip_scroll_tree: &ClipScrollTree,
604    ) -> TransformPaletteId {
605        let index = self.get_index(
606            from_index,
607            to_index,
608            clip_scroll_tree,
609        );
610        let transform_kind = self.metadata[index].transform_kind as u32;
611        TransformPaletteId(
612            (index as u32) |
613            (transform_kind << 24)
614        )
615    }
616}
617
618// Texture cache resources can be either a simple rect, or define
619// a polygon within a rect by specifying a UV coordinate for each
620// corner. This is useful for rendering screen-space rasterized
621// off-screen surfaces.
622#[derive(Debug, Copy, Clone)]
623#[cfg_attr(feature = "capture", derive(Serialize))]
624#[cfg_attr(feature = "replay", derive(Deserialize))]
625pub enum UvRectKind {
626    // The 2d bounds of the texture cache entry define the
627    // valid UV space for this texture cache entry.
628    Rect,
629    // The four vertices below define a quad within
630    // the texture cache entry rect. The shader can
631    // use a bilerp() to correctly interpolate a
632    // UV coord in the vertex shader.
633    Quad {
634        top_left: DeviceHomogeneousVector,
635        top_right: DeviceHomogeneousVector,
636        bottom_left: DeviceHomogeneousVector,
637        bottom_right: DeviceHomogeneousVector,
638    },
639}
640
641#[derive(Debug, Copy, Clone)]
642#[cfg_attr(feature = "capture", derive(Serialize))]
643#[cfg_attr(feature = "replay", derive(Deserialize))]
644pub struct ImageSource {
645    pub p0: DevicePoint,
646    pub p1: DevicePoint,
647    pub texture_layer: f32,
648    pub user_data: [f32; 3],
649    pub uv_rect_kind: UvRectKind,
650}
651
652impl ImageSource {
653    pub fn write_gpu_blocks(&self, request: &mut GpuDataRequest) {
654        // see fetch_image_resource in GLSL
655        // has to be VECS_PER_IMAGE_RESOURCE vectors
656        request.push([
657            self.p0.x,
658            self.p0.y,
659            self.p1.x,
660            self.p1.y,
661        ]);
662        request.push([
663            self.texture_layer,
664            self.user_data[0],
665            self.user_data[1],
666            self.user_data[2],
667        ]);
668
669        // If this is a polygon uv kind, then upload the four vertices.
670        if let UvRectKind::Quad { top_left, top_right, bottom_left, bottom_right } = self.uv_rect_kind {
671            // see fetch_image_resource_extra in GLSL
672            //Note: we really need only 3 components per point here: X, Y, and W
673            request.push(top_left);
674            request.push(top_right);
675            request.push(bottom_left);
676            request.push(bottom_right);
677        }
678    }
679}
680
681// Set the local -> world transform for a given spatial
682// node in the transform palette.
683fn register_transform(
684    metadatas: &mut Vec<TransformMetadata>,
685    transforms: &mut Vec<TransformData>,
686    from_index: SpatialNodeIndex,
687    to_index: SpatialNodeIndex,
688    transform: LayoutToPictureTransform,
689) -> usize {
690    // TODO: refactor the calling code to not even try
691    // registering a non-invertible transform.
692    let inv_transform = transform
693        .inverse()
694        .unwrap_or_else(PictureToLayoutTransform::identity);
695
696    let metadata = TransformMetadata {
697        transform_kind: transform.transform_kind()
698    };
699    let data = TransformData {
700        transform,
701        inv_transform,
702    };
703
704    if to_index == ROOT_SPATIAL_NODE_INDEX {
705        let index = from_index.0 as usize;
706        metadatas[index] = metadata;
707        transforms[index] = data;
708        index
709    } else {
710        let index = transforms.len();
711        metadatas.push(metadata);
712        transforms.push(data);
713        index
714    }
715}