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 }