webrender/
command_buffer.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::units::PictureRect;
6use crate::pattern::{PatternKind, PatternShaderInput};
7use crate::{spatial_tree::SpatialNodeIndex, render_task_graph::RenderTaskId, surface::SurfaceTileDescriptor, picture::TileKey, renderer::GpuBufferAddress, FastHashMap, prim_store::PrimitiveInstanceIndex, gpu_cache::GpuCacheAddress};
8use crate::gpu_types::{QuadSegment, TransformPaletteId};
9use crate::segment::EdgeAaSegmentMask;
10
11/// A tightly packed command stored in a command buffer
12#[cfg_attr(feature = "capture", derive(Serialize))]
13#[cfg_attr(feature = "replay", derive(Deserialize))]
14#[derive(Debug, Copy, Clone)]
15pub struct Command(u32);
16
17impl Command {
18    /// Draw a simple primitive that needs prim instance index only.
19    const CMD_DRAW_SIMPLE_PRIM: u32 = 0x00000000;
20    /// Change the current spatial node.
21    const CMD_SET_SPATIAL_NODE: u32 = 0x10000000;
22    /// Draw a complex (3d-split) primitive, that has multiple GPU cache addresses.
23    const CMD_DRAW_COMPLEX_PRIM: u32 = 0x20000000;
24    /// Draw a primitive, that has a single GPU buffer addresses.
25    const CMD_DRAW_INSTANCE: u32 = 0x30000000;
26    /// Draw a generic quad primitive
27    const CMD_DRAW_QUAD: u32 = 0x40000000;
28    /// Set a list of variable-length segments
29    const CMD_SET_SEGMENTS: u32 = 0x50000000;
30
31    /// Bitmask for command bits of the command.
32    const CMD_MASK: u32 = 0xf0000000;
33    /// Bitmask for param bits of the command.
34    const PARAM_MASK: u32 = 0x0fffffff;
35
36    /// Encode drawing a simple primitive.
37    fn draw_simple_prim(prim_instance_index: PrimitiveInstanceIndex) -> Self {
38        Command(Command::CMD_DRAW_SIMPLE_PRIM | prim_instance_index.0)
39    }
40
41    /// Encode changing spatial node.
42    fn set_spatial_node(spatial_node_index: SpatialNodeIndex) -> Self {
43        Command(Command::CMD_SET_SPATIAL_NODE | spatial_node_index.0)
44    }
45
46    /// Encode a list of segments that follow
47    fn set_segments(count: usize) -> Self {
48        Command(Command::CMD_SET_SEGMENTS | count as u32)
49    }
50
51    /// Encode drawing a complex prim.
52    fn draw_complex_prim(prim_instance_index: PrimitiveInstanceIndex) -> Self {
53        Command(Command::CMD_DRAW_COMPLEX_PRIM | prim_instance_index.0)
54    }
55
56    fn draw_instance(prim_instance_index: PrimitiveInstanceIndex) -> Self {
57        Command(Command::CMD_DRAW_INSTANCE | prim_instance_index.0)
58    }
59
60    /// Encode arbitrary data word.
61    fn data(data: u32) -> Self {
62        Command(data)
63    }
64
65    fn draw_quad(prim_instance_index: PrimitiveInstanceIndex) -> Self {
66        Command(Command::CMD_DRAW_QUAD | prim_instance_index.0)
67    }
68}
69
70bitflags! {
71    /// Flags related to quad primitives
72    #[repr(transparent)]
73    #[cfg_attr(feature = "capture", derive(Serialize))]
74    #[cfg_attr(feature = "replay", derive(Deserialize))]
75    #[derive(Debug, Copy, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)]
76    pub struct QuadFlags : u8 {
77        const IS_OPAQUE = 1 << 0;
78
79        /// If true, the prim is 2d and axis-aligned in device space. The render task rect can
80        /// cheaply be used as a device-space clip in the vertex shader.
81        const APPLY_RENDER_TASK_CLIP = 1 << 1;
82
83        /// If true, the device-pixel scale is already applied, so ignore in vertex shaders
84        const IGNORE_DEVICE_PIXEL_SCALE = 1 << 2;
85
86        /// If true, use segments for drawing the AA edges, to allow inner section to be opaque
87        const USE_AA_SEGMENTS = 1 << 3;
88
89        /// If true, render as a mask. This ignores the blue, green and alpha channels and replaces
90        /// them with the red channel in the fragment shader. Used with multiply blending, on top
91        /// of premultiplied alpha content, it has the effect of applying a mask to the content under ir.
92        const IS_MASK = 1 << 4;
93    }
94}
95
96bitflags! {
97    /// Defines the space that a quad primitive is drawn in
98    #[repr(transparent)]
99    #[cfg_attr(feature = "capture", derive(Serialize))]
100    #[cfg_attr(feature = "replay", derive(Deserialize))]
101    #[derive(Debug, Copy, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)]
102    pub struct MaskFlags : i32 {
103        const PRIM_SPACE = 1 << 0;
104    }
105}
106
107/// The unpacked equivalent to a `Command`.
108#[cfg_attr(feature = "capture", derive(Serialize))]
109pub enum PrimitiveCommand {
110    Simple {
111        prim_instance_index: PrimitiveInstanceIndex,
112    },
113    Complex {
114        prim_instance_index: PrimitiveInstanceIndex,
115        gpu_address: GpuCacheAddress,
116    },
117    Instance {
118        prim_instance_index: PrimitiveInstanceIndex,
119        gpu_buffer_address: GpuBufferAddress,
120    },
121    Quad {
122        pattern: PatternKind,
123        pattern_input: PatternShaderInput,
124        src_color_task_id: RenderTaskId,
125        // TODO(gw): Used for bounding rect only, could possibly remove
126        prim_instance_index: PrimitiveInstanceIndex,
127        gpu_buffer_address: GpuBufferAddress,
128        transform_id: TransformPaletteId,
129        quad_flags: QuadFlags,
130        edge_flags: EdgeAaSegmentMask,
131    },
132}
133
134impl PrimitiveCommand {
135    pub fn simple(
136        prim_instance_index: PrimitiveInstanceIndex,
137    ) -> Self {
138        PrimitiveCommand::Simple {
139            prim_instance_index,
140        }
141    }
142
143    pub fn complex(
144        prim_instance_index: PrimitiveInstanceIndex,
145        gpu_address: GpuCacheAddress,
146    ) -> Self {
147        PrimitiveCommand::Complex {
148            prim_instance_index,
149            gpu_address,
150        }
151    }
152
153    pub fn quad(
154        pattern: PatternKind,
155        pattern_input: PatternShaderInput,
156        src_color_task_id: RenderTaskId,
157        prim_instance_index: PrimitiveInstanceIndex,
158        gpu_buffer_address: GpuBufferAddress,
159        transform_id: TransformPaletteId,
160        quad_flags: QuadFlags,
161        edge_flags: EdgeAaSegmentMask,
162    ) -> Self {
163        PrimitiveCommand::Quad {
164            pattern,
165            pattern_input,
166            src_color_task_id,
167            prim_instance_index,
168            gpu_buffer_address,
169            transform_id,
170            quad_flags,
171            edge_flags,
172        }
173    }
174
175    pub fn instance(
176        prim_instance_index: PrimitiveInstanceIndex,
177        gpu_buffer_address: GpuBufferAddress,
178    ) -> Self {
179        PrimitiveCommand::Instance {
180            prim_instance_index,
181            gpu_buffer_address,
182        }
183    }
184}
185
186
187/// A list of commands describing how to draw a primitive list.
188#[cfg_attr(feature = "capture", derive(Serialize))]
189#[cfg_attr(feature = "replay", derive(Deserialize))]
190pub struct CommandBuffer {
191    /// The encoded drawing commands.
192    commands: Vec<Command>,
193    /// Cached current spatial node.
194    current_spatial_node_index: SpatialNodeIndex,
195}
196
197impl CommandBuffer {
198    /// Construct a new cmd buffer.
199    pub fn new() -> Self {
200        CommandBuffer {
201            commands: Vec::new(),
202            current_spatial_node_index: SpatialNodeIndex::INVALID,
203        }
204    }
205
206    /// Push a list of segments in to the cmd buffer
207    pub fn set_segments(
208        &mut self,
209        segments: &[QuadSegment],
210    ) {
211        self.commands.push(Command::set_segments(segments.len()));
212        for segment in segments {
213            self.commands.push(Command::data(segment.task_id.index));
214        }
215    }
216
217    /// Add a primitive to the command buffer.
218    pub fn add_prim(
219        &mut self,
220        prim_cmd: &PrimitiveCommand,
221        spatial_node_index: SpatialNodeIndex,
222    ) {
223        if self.current_spatial_node_index != spatial_node_index {
224            self.commands.push(Command::set_spatial_node(spatial_node_index));
225            self.current_spatial_node_index = spatial_node_index;
226        }
227
228        self.add_cmd(prim_cmd);
229    }
230
231    /// Add a cmd to the command buffer.
232    pub fn add_cmd(
233        &mut self,
234        prim_cmd: &PrimitiveCommand,
235    ) {
236        match *prim_cmd {
237            PrimitiveCommand::Simple { prim_instance_index } => {
238                self.commands.push(Command::draw_simple_prim(prim_instance_index));
239            }
240            PrimitiveCommand::Complex { prim_instance_index, gpu_address } => {
241                self.commands.push(Command::draw_complex_prim(prim_instance_index));
242                self.commands.push(Command::data((gpu_address.u as u32) << 16 | gpu_address.v as u32));
243            }
244            PrimitiveCommand::Instance { prim_instance_index, gpu_buffer_address } => {
245                self.commands.push(Command::draw_instance(prim_instance_index));
246                self.commands.push(Command::data((gpu_buffer_address.u as u32) << 16 | gpu_buffer_address.v as u32));
247            }
248            PrimitiveCommand::Quad { pattern, pattern_input, prim_instance_index, gpu_buffer_address, transform_id, quad_flags, edge_flags, src_color_task_id } => {
249                self.commands.push(Command::draw_quad(prim_instance_index));
250                self.commands.push(Command::data(pattern as u32));
251                self.commands.push(Command::data(pattern_input.0 as u32));
252                self.commands.push(Command::data(pattern_input.1 as u32));
253                self.commands.push(Command::data(src_color_task_id.index));
254                self.commands.push(Command::data((gpu_buffer_address.u as u32) << 16 | gpu_buffer_address.v as u32));
255                self.commands.push(Command::data(transform_id.0));
256                self.commands.push(Command::data((quad_flags.bits() as u32) << 16 | edge_flags.bits() as u32));
257            }
258        }
259    }
260
261    /// Iterate the command list, calling a provided closure for each primitive draw command.
262    pub fn iter_prims<F>(
263        &self,
264        f: &mut F,
265    ) where F: FnMut(&PrimitiveCommand, SpatialNodeIndex, &[RenderTaskId]) {
266        let mut current_spatial_node_index = SpatialNodeIndex::INVALID;
267        let mut cmd_iter = self.commands.iter();
268        // TODO(gw): Consider pre-allocating this / Smallvec if it shows up in profiles.
269        let mut segments = Vec::new();
270
271        while let Some(cmd) = cmd_iter.next() {
272            let command = cmd.0 & Command::CMD_MASK;
273            let param = cmd.0 & Command::PARAM_MASK;
274
275            match command {
276                Command::CMD_DRAW_SIMPLE_PRIM => {
277                    let prim_instance_index = PrimitiveInstanceIndex(param);
278                    let cmd = PrimitiveCommand::simple(prim_instance_index);
279                    f(&cmd, current_spatial_node_index, &[]);
280                }
281                Command::CMD_SET_SPATIAL_NODE => {
282                    current_spatial_node_index = SpatialNodeIndex(param);
283                }
284                Command::CMD_DRAW_COMPLEX_PRIM => {
285                    let prim_instance_index = PrimitiveInstanceIndex(param);
286                    let data = cmd_iter.next().unwrap();
287                    let gpu_address = GpuCacheAddress {
288                        u: (data.0 >> 16) as u16,
289                        v: (data.0 & 0xffff) as u16,
290                    };
291                    let cmd = PrimitiveCommand::complex(
292                        prim_instance_index,
293                        gpu_address,
294                    );
295                    f(&cmd, current_spatial_node_index, &[]);
296                }
297                Command::CMD_DRAW_QUAD => {
298                    let prim_instance_index = PrimitiveInstanceIndex(param);
299                    let pattern = PatternKind::from_u32(cmd_iter.next().unwrap().0);
300                    let pattern_input = PatternShaderInput(
301                        cmd_iter.next().unwrap().0 as i32,
302                        cmd_iter.next().unwrap().0 as i32,
303                    );
304                    let src_color_task_id = RenderTaskId { index: cmd_iter.next().unwrap().0 };
305                    let data = cmd_iter.next().unwrap();
306                    let transform_id = TransformPaletteId(cmd_iter.next().unwrap().0);
307                    let bits = cmd_iter.next().unwrap().0;
308                    let quad_flags = QuadFlags::from_bits((bits >> 16) as u8).unwrap();
309                    let edge_flags = EdgeAaSegmentMask::from_bits((bits & 0xff) as u8).unwrap();
310                    let gpu_buffer_address = GpuBufferAddress {
311                        u: (data.0 >> 16) as u16,
312                        v: (data.0 & 0xffff) as u16,
313                    };
314                    let cmd = PrimitiveCommand::quad(
315                        pattern,
316                        pattern_input,
317                        src_color_task_id,
318                        prim_instance_index,
319                        gpu_buffer_address,
320                        transform_id,
321                        quad_flags,
322                        edge_flags,
323                    );
324                    f(&cmd, current_spatial_node_index, &segments);
325                    segments.clear()
326                }
327                Command::CMD_DRAW_INSTANCE => {
328                    let prim_instance_index = PrimitiveInstanceIndex(param);
329                    let data = cmd_iter.next().unwrap();
330                    let gpu_buffer_address = GpuBufferAddress {
331                        u: (data.0 >> 16) as u16,
332                        v: (data.0 & 0xffff) as u16,
333                    };
334                    let cmd = PrimitiveCommand::instance(
335                        prim_instance_index,
336                        gpu_buffer_address,
337                    );
338                    f(&cmd, current_spatial_node_index, &[]);
339                }
340                Command::CMD_SET_SEGMENTS => {
341                    let count = param;
342                    for _ in 0 .. count {
343                        segments.push(RenderTaskId { index: cmd_iter.next().unwrap().0 });
344                    }
345                }
346                _ => {
347                    unreachable!();
348                }
349            }
350        }
351    }
352}
353
354/// Abstracts whether a command buffer is being built for a tiled (picture cache)
355/// or simple (child surface).
356#[cfg_attr(feature = "capture", derive(Serialize))]
357#[cfg_attr(feature = "replay", derive(Deserialize))]
358pub enum CommandBufferBuilderKind {
359    Tiled {
360        // TODO(gw): It might be worth storing this as a 2d-array instead
361        //           of a hash map if it ever shows up in profiles. This is
362        //           slightly complicated by the sub_slice_index in the
363        //           TileKey structure - could have a 2 level array?
364        tiles: FastHashMap<TileKey, SurfaceTileDescriptor>,
365    },
366    Simple {
367        render_task_id: RenderTaskId,
368        root_task_id: Option<RenderTaskId>,
369        dirty_rect: PictureRect,
370    },
371    Invalid,
372}
373
374#[cfg_attr(feature = "capture", derive(Serialize))]
375#[cfg_attr(feature = "replay", derive(Deserialize))]
376pub struct CommandBufferBuilder {
377    pub kind: CommandBufferBuilderKind,
378
379    /// If a command buffer establishes a sub-graph, then at the end of constructing
380    /// the surface, the parent surface is supplied as an input dependency, and the
381    /// parent surface gets a duplicated (existing) task with the same location, and
382    /// with the sub-graph output as an input dependency.
383    pub establishes_sub_graph: bool,
384
385    /// If this surface builds a sub-graph, it will mark a task in the filter sub-graph
386    /// as a resolve source for the input from the parent surface.
387    pub resolve_source: Option<RenderTaskId>,
388
389    /// List of render tasks that depend on the task that will be created for this builder.
390    pub extra_dependencies: Vec<RenderTaskId>,
391}
392
393impl CommandBufferBuilder {
394    pub fn empty() -> Self {
395        CommandBufferBuilder {
396            kind: CommandBufferBuilderKind::Invalid,
397            establishes_sub_graph: false,
398            resolve_source: None,
399            extra_dependencies: Vec::new(),
400        }
401    }
402
403    /// Construct a tiled command buffer builder.
404    pub fn new_tiled(
405        tiles: FastHashMap<TileKey, SurfaceTileDescriptor>,
406    ) -> Self {
407        CommandBufferBuilder {
408            kind: CommandBufferBuilderKind::Tiled {
409                tiles,
410            },
411            establishes_sub_graph: false,
412            resolve_source: None,
413            extra_dependencies: Vec::new(),
414        }
415    }
416
417    /// Construct a simple command buffer builder.
418    pub fn new_simple(
419        render_task_id: RenderTaskId,
420        establishes_sub_graph: bool,
421        root_task_id: Option<RenderTaskId>,
422        dirty_rect: PictureRect,
423    ) -> Self {
424        CommandBufferBuilder {
425            kind: CommandBufferBuilderKind::Simple {
426                render_task_id,
427                root_task_id,
428                dirty_rect,
429            },
430            establishes_sub_graph,
431            resolve_source: None,
432            extra_dependencies: Vec::new(),
433        }
434    }
435}
436
437// Index into a command buffer stored in a `CommandBufferList`.
438#[cfg_attr(feature = "capture", derive(Serialize))]
439#[cfg_attr(feature = "replay", derive(Deserialize))]
440#[derive(Debug, Copy, Clone)]
441pub struct CommandBufferIndex(pub u32);
442
443// Container for a list of command buffers that are built for a frame.
444pub struct CommandBufferList {
445    cmd_buffers: Vec<CommandBuffer>,
446}
447
448impl CommandBufferList {
449    pub fn new() -> Self {
450        CommandBufferList {
451            cmd_buffers: Vec::new(),
452        }
453    }
454
455    pub fn create_cmd_buffer(
456        &mut self,
457    ) -> CommandBufferIndex {
458        let index = CommandBufferIndex(self.cmd_buffers.len() as u32);
459        self.cmd_buffers.push(CommandBuffer::new());
460        index
461    }
462
463    pub fn get(&self, index: CommandBufferIndex) -> &CommandBuffer {
464        &self.cmd_buffers[index.0 as usize]
465    }
466
467    pub fn get_mut(&mut self, index: CommandBufferIndex) -> &mut CommandBuffer {
468        &mut self.cmd_buffers[index.0 as usize]
469    }
470}