Skip to main content

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