tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

command_buffer.rs (17323B)


      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 use api::units::PictureRect;
      6 use crate::pattern::{PatternKind, PatternShaderInput};
      7 use crate::{spatial_tree::SpatialNodeIndex, render_task_graph::RenderTaskId, surface::SurfaceTileDescriptor, tile_cache::TileKey, renderer::GpuBufferAddress, FastHashMap, prim_store::PrimitiveInstanceIndex};
      8 use crate::gpu_types::{QuadSegment, TransformPaletteId};
      9 use 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)]
     15 pub struct Command(u32);
     16 
     17 impl 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 
     70 bitflags! {
     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 
     96 bitflags! {
     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))]
    109 pub enum PrimitiveCommand {
    110    Simple {
    111        prim_instance_index: PrimitiveInstanceIndex,
    112    },
    113    Complex {
    114        prim_instance_index: PrimitiveInstanceIndex,
    115        gpu_address: GpuBufferAddress,
    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 
    134 impl 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: GpuBufferAddress,
    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))]
    190 pub struct CommandBuffer {
    191    /// The encoded drawing commands.
    192    commands: Vec<Command>,
    193    /// Cached current spatial node.
    194    current_spatial_node_index: SpatialNodeIndex,
    195 }
    196 
    197 impl 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.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.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.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 = GpuBufferAddress::from_u32(data.0);
    288                    let cmd = PrimitiveCommand::complex(
    289                        prim_instance_index,
    290                        gpu_address,
    291                    );
    292                    f(&cmd, current_spatial_node_index, &[]);
    293                }
    294                Command::CMD_DRAW_QUAD => {
    295                    let prim_instance_index = PrimitiveInstanceIndex(param);
    296                    let pattern = PatternKind::from_u32(cmd_iter.next().unwrap().0);
    297                    let pattern_input = PatternShaderInput(
    298                        cmd_iter.next().unwrap().0 as i32,
    299                        cmd_iter.next().unwrap().0 as i32,
    300                    );
    301                    let src_color_task_id = RenderTaskId { index: cmd_iter.next().unwrap().0 };
    302                    let data = cmd_iter.next().unwrap();
    303                    let transform_id = TransformPaletteId(cmd_iter.next().unwrap().0);
    304                    let bits = cmd_iter.next().unwrap().0;
    305                    let quad_flags = QuadFlags::from_bits((bits >> 16) as u8).unwrap();
    306                    let edge_flags = EdgeAaSegmentMask::from_bits((bits & 0xff) as u8).unwrap();
    307                    let gpu_buffer_address = GpuBufferAddress::from_u32(data.0);
    308                    let cmd = PrimitiveCommand::quad(
    309                        pattern,
    310                        pattern_input,
    311                        src_color_task_id,
    312                        prim_instance_index,
    313                        gpu_buffer_address,
    314                        transform_id,
    315                        quad_flags,
    316                        edge_flags,
    317                    );
    318                    f(&cmd, current_spatial_node_index, &segments);
    319                    segments.clear()
    320                }
    321                Command::CMD_DRAW_INSTANCE => {
    322                    let prim_instance_index = PrimitiveInstanceIndex(param);
    323                    let data = cmd_iter.next().unwrap();
    324                    let gpu_buffer_address = GpuBufferAddress::from_u32(data.0);
    325                    let cmd = PrimitiveCommand::instance(
    326                        prim_instance_index,
    327                        gpu_buffer_address,
    328                    );
    329                    f(&cmd, current_spatial_node_index, &[]);
    330                }
    331                Command::CMD_SET_SEGMENTS => {
    332                    let count = param;
    333                    for _ in 0 .. count {
    334                        segments.push(RenderTaskId { index: cmd_iter.next().unwrap().0 });
    335                    }
    336                }
    337                _ => {
    338                    unreachable!();
    339                }
    340            }
    341        }
    342    }
    343 }
    344 
    345 /// Abstracts whether a command buffer is being built for a tiled (picture cache)
    346 /// or simple (child surface).
    347 #[cfg_attr(feature = "capture", derive(Serialize))]
    348 #[cfg_attr(feature = "replay", derive(Deserialize))]
    349 pub enum CommandBufferBuilderKind {
    350    Tiled {
    351        // TODO(gw): It might be worth storing this as a 2d-array instead
    352        //           of a hash map if it ever shows up in profiles. This is
    353        //           slightly complicated by the sub_slice_index in the
    354        //           TileKey structure - could have a 2 level array?
    355        tiles: FastHashMap<TileKey, SurfaceTileDescriptor>,
    356    },
    357    Simple {
    358        render_task_id: RenderTaskId,
    359        root_task_id: Option<RenderTaskId>,
    360        dirty_rect: PictureRect,
    361    },
    362    Invalid,
    363 }
    364 
    365 #[cfg_attr(feature = "capture", derive(Serialize))]
    366 #[cfg_attr(feature = "replay", derive(Deserialize))]
    367 pub struct CommandBufferBuilder {
    368    pub kind: CommandBufferBuilderKind,
    369 
    370    /// If a command buffer establishes a sub-graph, then at the end of constructing
    371    /// the surface, the parent surface is supplied as an input dependency, and the
    372    /// parent surface gets a duplicated (existing) task with the same location, and
    373    /// with the sub-graph output as an input dependency.
    374    pub establishes_sub_graph: bool,
    375 
    376    /// If this surface builds a sub-graph, it will mark a task in the filter sub-graph
    377    /// as a resolve source for the input from the parent surface.
    378    pub resolve_source: Option<RenderTaskId>,
    379 
    380    /// List of render tasks that depend on the task that will be created for this builder.
    381    pub extra_dependencies: Vec<RenderTaskId>,
    382 }
    383 
    384 impl CommandBufferBuilder {
    385    pub fn empty() -> Self {
    386        CommandBufferBuilder {
    387            kind: CommandBufferBuilderKind::Invalid,
    388            establishes_sub_graph: false,
    389            resolve_source: None,
    390            extra_dependencies: Vec::new(),
    391        }
    392    }
    393 
    394    /// Construct a tiled command buffer builder.
    395    pub fn new_tiled(
    396        tiles: FastHashMap<TileKey, SurfaceTileDescriptor>,
    397    ) -> Self {
    398        CommandBufferBuilder {
    399            kind: CommandBufferBuilderKind::Tiled {
    400                tiles,
    401            },
    402            establishes_sub_graph: false,
    403            resolve_source: None,
    404            extra_dependencies: Vec::new(),
    405        }
    406    }
    407 
    408    /// Construct a simple command buffer builder.
    409    pub fn new_simple(
    410        render_task_id: RenderTaskId,
    411        establishes_sub_graph: bool,
    412        root_task_id: Option<RenderTaskId>,
    413        dirty_rect: PictureRect,
    414    ) -> Self {
    415        CommandBufferBuilder {
    416            kind: CommandBufferBuilderKind::Simple {
    417                render_task_id,
    418                root_task_id,
    419                dirty_rect,
    420            },
    421            establishes_sub_graph,
    422            resolve_source: None,
    423            extra_dependencies: Vec::new(),
    424        }
    425    }
    426 }
    427 
    428 // Index into a command buffer stored in a `CommandBufferList`.
    429 #[cfg_attr(feature = "capture", derive(Serialize))]
    430 #[cfg_attr(feature = "replay", derive(Deserialize))]
    431 #[derive(Debug, Copy, Clone)]
    432 pub struct CommandBufferIndex(pub u32);
    433 
    434 // Container for a list of command buffers that are built for a frame.
    435 pub struct CommandBufferList {
    436    cmd_buffers: Vec<CommandBuffer>,
    437 }
    438 
    439 impl CommandBufferList {
    440    pub fn new() -> Self {
    441        CommandBufferList {
    442            cmd_buffers: Vec::new(),
    443        }
    444    }
    445 
    446    pub fn create_cmd_buffer(
    447        &mut self,
    448    ) -> CommandBufferIndex {
    449        let index = CommandBufferIndex(self.cmd_buffers.len() as u32);
    450        self.cmd_buffers.push(CommandBuffer::new());
    451        index
    452    }
    453 
    454    pub fn get(&self, index: CommandBufferIndex) -> &CommandBuffer {
    455        &self.cmd_buffers[index.0 as usize]
    456    }
    457 
    458    pub fn get_mut(&mut self, index: CommandBufferIndex) -> &mut CommandBuffer {
    459        &mut self.cmd_buffers[index.0 as usize]
    460    }
    461 }