Skip to main content

webrender/renderer/
vertex.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
5//! Rendering logic related to the vertex shaders and their states, uncluding
6//!  - Vertex Array Objects
7//!  - vertex layout descriptors
8//!  - textures bound at vertex stage
9
10use std::{marker::PhantomData, mem, num::NonZeroUsize, ops};
11use api::units::*;
12use crate::{
13    device::{
14        Device, Texture, TextureFilter, TextureUploader, UploadPBOPool, VertexUsageHint, VAO,
15    },
16    frame_builder::Frame,
17    gpu_types::{PrimitiveHeaderI, PrimitiveHeaderF},
18    internal_types::Swizzle,
19    render_task::RenderTaskData,
20    transform::TransformData,
21};
22
23use crate::internal_types::FrameVec;
24
25pub const VERTEX_TEXTURE_EXTRA_ROWS: i32 = 10;
26
27pub const MAX_VERTEX_TEXTURE_WIDTH: usize = webrender_build::MAX_VERTEX_TEXTURE_WIDTH;
28
29pub mod desc {
30    use crate::device::{VertexAttribute, VertexAttributeKind, VertexDescriptor};
31
32    pub const PRIM_INSTANCES: VertexDescriptor = VertexDescriptor {
33        vertex_attributes: &[VertexAttribute::quad_instance_vertex()],
34        instance_attributes: &[VertexAttribute {
35            name: "aData",
36            count: 4,
37            kind: VertexAttributeKind::I32,
38        }],
39    };
40
41    pub const BLUR: VertexDescriptor = VertexDescriptor {
42        vertex_attributes: &[VertexAttribute::quad_instance_vertex()],
43        instance_attributes: &[
44            VertexAttribute::gpu_buffer_address("aBlurRenderTaskAddress"),
45            VertexAttribute::gpu_buffer_address("aBlurSourceTaskAddress"),
46            VertexAttribute::i32("aBlurDirection"),
47            VertexAttribute::i32("aBlurEdgeMode"),
48            VertexAttribute::f32x3("aBlurParams"),
49        ],
50    };
51
52    pub const LINE: VertexDescriptor = VertexDescriptor {
53        vertex_attributes: &[VertexAttribute::quad_instance_vertex()],
54        instance_attributes: &[
55            VertexAttribute::f32x4("aTaskRect"),
56            VertexAttribute::f32x2("aLocalSize"),
57            VertexAttribute::f32("aWavyLineThickness"),
58            VertexAttribute::i32("aStyle"),
59            VertexAttribute::f32("aAxisSelect"),
60        ],
61    };
62
63
64    pub const BORDER: VertexDescriptor = VertexDescriptor {
65        vertex_attributes: &[VertexAttribute::quad_instance_vertex()],
66        instance_attributes: &[
67            VertexAttribute::f32x2("aTaskOrigin"),
68            VertexAttribute::f32x4("aRect"),
69            VertexAttribute::f32x4("aColor0"),
70            VertexAttribute::f32x4("aColor1"),
71            VertexAttribute::i32("aFlags"),
72            VertexAttribute::f32x2("aWidths"),
73            VertexAttribute::f32x2("aRadii"),
74            VertexAttribute::f32x4("aClipParams1"),
75            VertexAttribute::f32x4("aClipParams2"),
76        ],
77    };
78
79    pub const SCALE: VertexDescriptor = VertexDescriptor {
80        vertex_attributes: &[VertexAttribute::quad_instance_vertex()],
81        instance_attributes: &[
82            VertexAttribute::f32x4("aScaleTargetRect"),
83            VertexAttribute::f32x4("aScaleSourceRect"),
84            VertexAttribute::f32("aSourceRectType"),
85        ],
86    };
87
88    pub const CLIP_RECT: VertexDescriptor = VertexDescriptor {
89        vertex_attributes: &[VertexAttribute::quad_instance_vertex()],
90        instance_attributes: &[
91            // common clip attributes
92            VertexAttribute::f32x4("aClipDeviceArea"),
93            VertexAttribute::f32x4("aClipOrigins"),
94            VertexAttribute::f32("aDevicePixelScale"),
95            VertexAttribute::i32x2("aTransformIds"),
96            // specific clip attributes
97            VertexAttribute::f32x2("aClipLocalPos"),
98            VertexAttribute::f32x4("aClipLocalRect"),
99            VertexAttribute::f32("aClipMode"),
100            VertexAttribute::f32x4("aClipRect_TL"),
101            VertexAttribute::f32x4("aClipRadii_TL"),
102            VertexAttribute::f32x4("aClipRect_TR"),
103            VertexAttribute::f32x4("aClipRadii_TR"),
104            VertexAttribute::f32x4("aClipRect_BL"),
105            VertexAttribute::f32x4("aClipRadii_BL"),
106            VertexAttribute::f32x4("aClipRect_BR"),
107            VertexAttribute::f32x4("aClipRadii_BR"),
108        ],
109    };
110
111    pub const SVG_FILTER_NODE: VertexDescriptor = VertexDescriptor {
112        vertex_attributes: &[VertexAttribute::quad_instance_vertex()],
113        instance_attributes: &[
114            VertexAttribute::f32x4("aFilterTargetRect"),
115            VertexAttribute::f32x4("aFilterInput1ContentScaleAndOffset"),
116            VertexAttribute::f32x4("aFilterInput2ContentScaleAndOffset"),
117            VertexAttribute::gpu_buffer_address("aFilterInput1TaskAddress"),
118            VertexAttribute::gpu_buffer_address("aFilterInput2TaskAddress"),
119            VertexAttribute::u16("aFilterKind"),
120            VertexAttribute::u16("aFilterInputCount"),
121            VertexAttribute::gpu_buffer_address("aFilterExtraDataAddress"),
122        ],
123    };
124
125    pub const MASK: VertexDescriptor = VertexDescriptor {
126        vertex_attributes: &[VertexAttribute::quad_instance_vertex()],
127        instance_attributes: &[
128            VertexAttribute::i32x4("aData"),
129            VertexAttribute::i32x4("aClipData"),
130        ],
131    };
132
133    pub const COMPOSITE: VertexDescriptor = VertexDescriptor {
134        vertex_attributes: &[VertexAttribute::quad_instance_vertex()],
135        instance_attributes: &[
136            VertexAttribute::f32x4("aDeviceRect"),
137            VertexAttribute::f32x4("aDeviceClipRect"),
138            VertexAttribute::f32x4("aColor"),
139            VertexAttribute::f32x4("aParams"),
140            VertexAttribute::f32x4("aUvRect0"),
141            VertexAttribute::f32x4("aUvRect1"),
142            VertexAttribute::f32x4("aUvRect2"),
143            VertexAttribute::f32x2("aFlip"),
144            VertexAttribute::f32x4("aDeviceRoundedClipRect"),
145            VertexAttribute::f32x4("aDeviceRoundedClipRadii"),
146        ],
147    };
148
149    pub const CLEAR: VertexDescriptor = VertexDescriptor {
150        vertex_attributes: &[VertexAttribute::quad_instance_vertex()],
151        instance_attributes: &[
152            VertexAttribute::f32x4("aRect"),
153            VertexAttribute::f32x4("aColor"),
154        ],
155    };
156
157    pub const COPY: VertexDescriptor = VertexDescriptor {
158        vertex_attributes: &[VertexAttribute::quad_instance_vertex()],
159        instance_attributes: &[
160            VertexAttribute::f32x4("a_src_rect"),
161            VertexAttribute::f32x4("a_dst_rect"),
162            VertexAttribute::f32x2("a_dst_texture_size"),
163        ],
164    };
165}
166
167#[derive(Debug, Copy, Clone, PartialEq)]
168pub enum VertexArrayKind {
169    Primitive,
170    Blur,
171    ClipRect,
172    Border,
173    Scale,
174    LineDecoration,
175    SvgFilterNode,
176    Composite,
177    Clear,
178    Copy,
179    Mask,
180}
181
182pub struct VertexDataTexture<T> {
183    texture: Option<Texture>,
184    format: api::ImageFormat,
185    _marker: PhantomData<T>,
186}
187
188impl<T> VertexDataTexture<T> {
189    pub fn new(format: api::ImageFormat) -> Self {
190        Self {
191            texture: None,
192            format,
193            _marker: PhantomData,
194        }
195    }
196
197    /// Returns a borrow of the GPU texture. Panics if it hasn't been initialized.
198    pub fn texture(&self) -> &Texture {
199        self.texture.as_ref().unwrap()
200    }
201
202    /// Returns an estimate of the GPU memory consumed by this VertexDataTexture.
203    pub fn size_in_bytes(&self) -> usize {
204        self.texture.as_ref().map_or(0, |t| t.size_in_bytes())
205    }
206
207    pub fn update<'a>(
208        &'a mut self,
209        device: &mut Device,
210        texture_uploader: &mut TextureUploader<'a>,
211        data: &mut FrameVec<T>,
212    ) {
213        debug_assert!(mem::size_of::<T>() % 16 == 0);
214        let texels_per_item = mem::size_of::<T>() / 16;
215        let items_per_row = MAX_VERTEX_TEXTURE_WIDTH / texels_per_item;
216        debug_assert_ne!(items_per_row, 0);
217
218        // Ensure we always end up with a texture when leaving this method.
219        let mut len = data.len();
220        if len == 0 {
221            if self.texture.is_some() {
222                return;
223            }
224            data.reserve(items_per_row);
225            len = items_per_row;
226        } else {
227            // Extend the data array to have enough capacity to upload at least
228            // a multiple of the row size.  This ensures memory safety when the
229            // array is passed to OpenGL to upload to the GPU.
230            let extra = len % items_per_row;
231            if extra != 0 {
232                let padding = items_per_row - extra;
233                data.reserve(padding);
234                len += padding;
235            }
236        }
237
238        let needed_height = (len / items_per_row) as i32;
239        let existing_height = self
240            .texture
241            .as_ref()
242            .map_or(0, |t| t.get_dimensions().height);
243
244        // Create a new texture if needed.
245        //
246        // These textures are generally very small, which is why we don't bother
247        // with incremental updates and just re-upload every frame. For most pages
248        // they're one row each, and on stress tests like css-francine they end up
249        // in the 6-14 range. So we size the texture tightly to what we need (usually
250        // 1), and shrink it if the waste would be more than `VERTEX_TEXTURE_EXTRA_ROWS`
251        // rows. This helps with memory overhead, especially because there are several
252        // instances of these textures per Renderer.
253        if needed_height > existing_height
254            || needed_height + VERTEX_TEXTURE_EXTRA_ROWS < existing_height
255        {
256            // Drop the existing texture, if any.
257            if let Some(t) = self.texture.take() {
258                device.delete_texture(t);
259            }
260
261            let texture = device.create_texture(
262                api::ImageBufferKind::Texture2D,
263                self.format,
264                MAX_VERTEX_TEXTURE_WIDTH as i32,
265                // Ensure height is at least two to work around
266                // https://bugs.chromium.org/p/angleproject/issues/detail?id=3039
267                needed_height.max(2),
268                TextureFilter::Nearest,
269                None,
270            );
271            self.texture = Some(texture);
272        }
273
274        // Note: the actual width can be larger than the logical one, with a few texels
275        // of each row unused at the tail. This is needed because there is still hardware
276        // (like Intel iGPUs) that prefers power-of-two sizes of textures ([1]).
277        //
278        // [1] https://software.intel.com/en-us/articles/opengl-performance-tips-power-of-two-textures-have-better-performance
279        let logical_width = if needed_height == 1 {
280            data.len() * texels_per_item
281        } else {
282            MAX_VERTEX_TEXTURE_WIDTH - (MAX_VERTEX_TEXTURE_WIDTH % texels_per_item)
283        };
284
285        let rect = DeviceIntRect::from_size(
286            DeviceIntSize::new(logical_width as i32, needed_height),
287        );
288
289        debug_assert!(len <= data.capacity(), "CPU copy will read out of bounds");
290        texture_uploader.upload(
291            device,
292            self.texture(),
293            rect,
294            None,
295            None,
296            data.as_ptr(),
297            len,
298        );
299    }
300
301    pub fn deinit(mut self, device: &mut Device) {
302        if let Some(t) = self.texture.take() {
303            device.delete_texture(t);
304        }
305    }
306}
307
308pub struct VertexDataTextures {
309    prim_header_f_texture: VertexDataTexture<PrimitiveHeaderF>,
310    prim_header_i_texture: VertexDataTexture<PrimitiveHeaderI>,
311    transforms_texture: VertexDataTexture<TransformData>,
312    render_task_texture: VertexDataTexture<RenderTaskData>,
313}
314
315impl VertexDataTextures {
316    pub fn new() -> Self {
317        VertexDataTextures {
318            prim_header_f_texture: VertexDataTexture::new(api::ImageFormat::RGBAF32),
319            prim_header_i_texture: VertexDataTexture::new(api::ImageFormat::RGBAI32),
320            transforms_texture: VertexDataTexture::new(api::ImageFormat::RGBAF32),
321            render_task_texture: VertexDataTexture::new(api::ImageFormat::RGBAF32),
322        }
323    }
324
325    pub fn update(&mut self, device: &mut Device, pbo_pool: &mut UploadPBOPool, frame: &mut Frame) {
326        let mut texture_uploader = device.upload_texture(pbo_pool);
327        self.prim_header_f_texture.update(
328            device,
329            &mut texture_uploader,
330            &mut frame.prim_headers.headers_float,
331        );
332        self.prim_header_i_texture.update(
333            device,
334            &mut texture_uploader,
335            &mut frame.prim_headers.headers_int,
336        );
337        self.transforms_texture
338            .update(device, &mut texture_uploader, &mut frame.transform_palette);
339        self.render_task_texture.update(
340            device,
341            &mut texture_uploader,
342            &mut frame.render_tasks.task_data,
343        );
344
345        // Flush and drop the texture uploader now, so that
346        // we can borrow the textures to bind them.
347        texture_uploader.flush(device);
348
349        device.bind_texture(
350            super::TextureSampler::PrimitiveHeadersF,
351            &self.prim_header_f_texture.texture(),
352            Swizzle::default(),
353        );
354        device.bind_texture(
355            super::TextureSampler::PrimitiveHeadersI,
356            &self.prim_header_i_texture.texture(),
357            Swizzle::default(),
358        );
359        device.bind_texture(
360            super::TextureSampler::TransformPalette,
361            &self.transforms_texture.texture(),
362            Swizzle::default(),
363        );
364        device.bind_texture(
365            super::TextureSampler::RenderTasks,
366            &self.render_task_texture.texture(),
367            Swizzle::default(),
368        );
369    }
370
371    pub fn size_in_bytes(&self) -> usize {
372        self.prim_header_f_texture.size_in_bytes()
373            + self.prim_header_i_texture.size_in_bytes()
374            + self.transforms_texture.size_in_bytes()
375            + self.render_task_texture.size_in_bytes()
376    }
377
378    pub fn deinit(self, device: &mut Device) {
379        self.transforms_texture.deinit(device);
380        self.prim_header_f_texture.deinit(device);
381        self.prim_header_i_texture.deinit(device);
382        self.render_task_texture.deinit(device);
383    }
384}
385
386pub struct RendererVAOs {
387    prim_vao: VAO,
388    blur_vao: VAO,
389    clip_rect_vao: VAO,
390    border_vao: VAO,
391    line_vao: VAO,
392    scale_vao: VAO,
393    svg_filter_node_vao: VAO,
394    composite_vao: VAO,
395    clear_vao: VAO,
396    copy_vao: VAO,
397    mask_vao: VAO,
398}
399
400impl RendererVAOs {
401    pub fn new(device: &mut Device, indexed_quads: Option<NonZeroUsize>) -> Self {
402        const QUAD_INDICES: [u16; 6] = [0, 1, 2, 2, 1, 3];
403        const QUAD_VERTICES: [[u8; 2]; 4] = [[0, 0], [0xFF, 0], [0, 0xFF], [0xFF, 0xFF]];
404
405        let instance_divisor = if indexed_quads.is_some() { 0 } else { 1 };
406        let prim_vao = device.create_vao(&desc::PRIM_INSTANCES, instance_divisor);
407
408        device.bind_vao(&prim_vao);
409        match indexed_quads {
410            Some(count) => {
411                assert!(count.get() < u16::MAX as usize);
412                let quad_indices = (0 .. count.get() as u16)
413                    .flat_map(|instance| QUAD_INDICES.iter().map(move |&index| instance * 4 + index))
414                    .collect::<Vec<_>>();
415                device.update_vao_indices(&prim_vao, &quad_indices, VertexUsageHint::Static);
416                let quad_vertices = (0 .. count.get() as u16)
417                    .flat_map(|_| QUAD_VERTICES.iter().cloned())
418                    .collect::<Vec<_>>();
419                device.update_vao_main_vertices(&prim_vao, &quad_vertices, VertexUsageHint::Static);
420            }
421            None => {
422                device.update_vao_indices(&prim_vao, &QUAD_INDICES, VertexUsageHint::Static);
423                device.update_vao_main_vertices(&prim_vao, &QUAD_VERTICES, VertexUsageHint::Static);
424            }
425        }
426
427        RendererVAOs {
428            blur_vao: device.create_vao_with_new_instances(&desc::BLUR, &prim_vao),
429            clip_rect_vao: device.create_vao_with_new_instances(&desc::CLIP_RECT, &prim_vao),
430            border_vao: device.create_vao_with_new_instances(&desc::BORDER, &prim_vao),
431            scale_vao: device.create_vao_with_new_instances(&desc::SCALE, &prim_vao),
432            line_vao: device.create_vao_with_new_instances(&desc::LINE, &prim_vao),
433            svg_filter_node_vao: device.create_vao_with_new_instances(&desc::SVG_FILTER_NODE, &prim_vao),
434            composite_vao: device.create_vao_with_new_instances(&desc::COMPOSITE, &prim_vao),
435            clear_vao: device.create_vao_with_new_instances(&desc::CLEAR, &prim_vao),
436            copy_vao: device.create_vao_with_new_instances(&desc::COPY, &prim_vao),
437            mask_vao: device.create_vao_with_new_instances(&desc::MASK, &prim_vao),
438            prim_vao,
439        }
440    }
441
442    pub fn deinit(self, device: &mut Device) {
443        device.delete_vao(self.prim_vao);
444        device.delete_vao(self.clip_rect_vao);
445        device.delete_vao(self.blur_vao);
446        device.delete_vao(self.line_vao);
447        device.delete_vao(self.border_vao);
448        device.delete_vao(self.scale_vao);
449        device.delete_vao(self.svg_filter_node_vao);
450        device.delete_vao(self.composite_vao);
451        device.delete_vao(self.clear_vao);
452        device.delete_vao(self.copy_vao);
453        device.delete_vao(self.mask_vao);
454    }
455}
456
457impl ops::Index<VertexArrayKind> for RendererVAOs {
458    type Output = VAO;
459    fn index(&self, kind: VertexArrayKind) -> &VAO {
460        match kind {
461            VertexArrayKind::Primitive => &self.prim_vao,
462            VertexArrayKind::ClipRect => &self.clip_rect_vao,
463            VertexArrayKind::Blur => &self.blur_vao,
464            VertexArrayKind::Border => &self.border_vao,
465            VertexArrayKind::Scale => &self.scale_vao,
466            VertexArrayKind::LineDecoration => &self.line_vao,
467            VertexArrayKind::SvgFilterNode => &self.svg_filter_node_vao,
468            VertexArrayKind::Composite => &self.composite_vao,
469            VertexArrayKind::Clear => &self.clear_vao,
470            VertexArrayKind::Copy => &self.copy_vao,
471            VertexArrayKind::Mask => &self.mask_vao,
472        }
473    }
474}