Source code

Revision control

Copy as Markdown

Other Tools

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::units::PictureRect;
use crate::pattern::{PatternKind, PatternShaderInput};
use crate::{spatial_tree::SpatialNodeIndex, render_task_graph::RenderTaskId, surface::SurfaceTileDescriptor, picture::TileKey, renderer::GpuBufferAddress, FastHashMap, prim_store::PrimitiveInstanceIndex, gpu_cache::GpuCacheAddress};
use crate::gpu_types::{QuadSegment, TransformPaletteId};
use crate::segment::EdgeAaSegmentMask;
/// A tightly packed command stored in a command buffer
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[derive(Debug, Copy, Clone)]
pub struct Command(u32);
impl Command {
/// Draw a simple primitive that needs prim instance index only.
const CMD_DRAW_SIMPLE_PRIM: u32 = 0x00000000;
/// Change the current spatial node.
const CMD_SET_SPATIAL_NODE: u32 = 0x10000000;
/// Draw a complex (3d-split) primitive, that has multiple GPU cache addresses.
const CMD_DRAW_COMPLEX_PRIM: u32 = 0x20000000;
/// Draw a primitive, that has a single GPU buffer addresses.
const CMD_DRAW_INSTANCE: u32 = 0x30000000;
/// Draw a generic quad primitive
const CMD_DRAW_QUAD: u32 = 0x40000000;
/// Set a list of variable-length segments
const CMD_SET_SEGMENTS: u32 = 0x50000000;
/// Bitmask for command bits of the command.
const CMD_MASK: u32 = 0xf0000000;
/// Bitmask for param bits of the command.
const PARAM_MASK: u32 = 0x0fffffff;
/// Encode drawing a simple primitive.
fn draw_simple_prim(prim_instance_index: PrimitiveInstanceIndex) -> Self {
Command(Command::CMD_DRAW_SIMPLE_PRIM | prim_instance_index.0)
}
/// Encode changing spatial node.
fn set_spatial_node(spatial_node_index: SpatialNodeIndex) -> Self {
Command(Command::CMD_SET_SPATIAL_NODE | spatial_node_index.0)
}
/// Encode a list of segments that follow
fn set_segments(count: usize) -> Self {
Command(Command::CMD_SET_SEGMENTS | count as u32)
}
/// Encode drawing a complex prim.
fn draw_complex_prim(prim_instance_index: PrimitiveInstanceIndex) -> Self {
Command(Command::CMD_DRAW_COMPLEX_PRIM | prim_instance_index.0)
}
fn draw_instance(prim_instance_index: PrimitiveInstanceIndex) -> Self {
Command(Command::CMD_DRAW_INSTANCE | prim_instance_index.0)
}
/// Encode arbitrary data word.
fn data(data: u32) -> Self {
Command(data)
}
fn draw_quad(prim_instance_index: PrimitiveInstanceIndex) -> Self {
Command(Command::CMD_DRAW_QUAD | prim_instance_index.0)
}
}
bitflags! {
/// Flags related to quad primitives
#[repr(transparent)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[derive(Debug, Copy, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)]
pub struct QuadFlags : u8 {
const IS_OPAQUE = 1 << 0;
/// If true, the prim is 2d and axis-aligned in device space. The render task rect can
/// cheaply be used as a device-space clip in the vertex shader.
const APPLY_RENDER_TASK_CLIP = 1 << 1;
/// If true, the device-pixel scale is already applied, so ignore in vertex shaders
const IGNORE_DEVICE_PIXEL_SCALE = 1 << 2;
/// If true, use segments for drawing the AA edges, to allow inner section to be opaque
const USE_AA_SEGMENTS = 1 << 3;
/// If true, render as a mask. This ignores the blue, green and alpha channels and replaces
/// them with the red channel in the fragment shader. Used with multiply blending, on top
/// of premultiplied alpha content, it has the effect of applying a mask to the content under ir.
const IS_MASK = 1 << 4;
}
}
bitflags! {
/// Defines the space that a quad primitive is drawn in
#[repr(transparent)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[derive(Debug, Copy, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)]
pub struct MaskFlags : i32 {
const PRIM_SPACE = 1 << 0;
}
}
/// The unpacked equivalent to a `Command`.
#[cfg_attr(feature = "capture", derive(Serialize))]
pub enum PrimitiveCommand {
Simple {
prim_instance_index: PrimitiveInstanceIndex,
},
Complex {
prim_instance_index: PrimitiveInstanceIndex,
gpu_address: GpuCacheAddress,
},
Instance {
prim_instance_index: PrimitiveInstanceIndex,
gpu_buffer_address: GpuBufferAddress,
},
Quad {
pattern: PatternKind,
pattern_input: PatternShaderInput,
src_color_task_id: RenderTaskId,
// TODO(gw): Used for bounding rect only, could possibly remove
prim_instance_index: PrimitiveInstanceIndex,
gpu_buffer_address: GpuBufferAddress,
transform_id: TransformPaletteId,
quad_flags: QuadFlags,
edge_flags: EdgeAaSegmentMask,
},
}
impl PrimitiveCommand {
pub fn simple(
prim_instance_index: PrimitiveInstanceIndex,
) -> Self {
PrimitiveCommand::Simple {
prim_instance_index,
}
}
pub fn complex(
prim_instance_index: PrimitiveInstanceIndex,
gpu_address: GpuCacheAddress,
) -> Self {
PrimitiveCommand::Complex {
prim_instance_index,
gpu_address,
}
}
pub fn quad(
pattern: PatternKind,
pattern_input: PatternShaderInput,
src_color_task_id: RenderTaskId,
prim_instance_index: PrimitiveInstanceIndex,
gpu_buffer_address: GpuBufferAddress,
transform_id: TransformPaletteId,
quad_flags: QuadFlags,
edge_flags: EdgeAaSegmentMask,
) -> Self {
PrimitiveCommand::Quad {
pattern,
pattern_input,
src_color_task_id,
prim_instance_index,
gpu_buffer_address,
transform_id,
quad_flags,
edge_flags,
}
}
pub fn instance(
prim_instance_index: PrimitiveInstanceIndex,
gpu_buffer_address: GpuBufferAddress,
) -> Self {
PrimitiveCommand::Instance {
prim_instance_index,
gpu_buffer_address,
}
}
}
/// A list of commands describing how to draw a primitive list.
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct CommandBuffer {
/// The encoded drawing commands.
commands: Vec<Command>,
/// Cached current spatial node.
current_spatial_node_index: SpatialNodeIndex,
}
impl CommandBuffer {
/// Construct a new cmd buffer.
pub fn new() -> Self {
CommandBuffer {
commands: Vec::new(),
current_spatial_node_index: SpatialNodeIndex::INVALID,
}
}
/// Push a list of segments in to the cmd buffer
pub fn set_segments(
&mut self,
segments: &[QuadSegment],
) {
self.commands.push(Command::set_segments(segments.len()));
for segment in segments {
self.commands.push(Command::data(segment.task_id.index));
}
}
/// Add a primitive to the command buffer.
pub fn add_prim(
&mut self,
prim_cmd: &PrimitiveCommand,
spatial_node_index: SpatialNodeIndex,
) {
if self.current_spatial_node_index != spatial_node_index {
self.commands.push(Command::set_spatial_node(spatial_node_index));
self.current_spatial_node_index = spatial_node_index;
}
self.add_cmd(prim_cmd);
}
/// Add a cmd to the command buffer.
pub fn add_cmd(
&mut self,
prim_cmd: &PrimitiveCommand,
) {
match *prim_cmd {
PrimitiveCommand::Simple { prim_instance_index } => {
self.commands.push(Command::draw_simple_prim(prim_instance_index));
}
PrimitiveCommand::Complex { prim_instance_index, gpu_address } => {
self.commands.push(Command::draw_complex_prim(prim_instance_index));
self.commands.push(Command::data((gpu_address.u as u32) << 16 | gpu_address.v as u32));
}
PrimitiveCommand::Instance { prim_instance_index, gpu_buffer_address } => {
self.commands.push(Command::draw_instance(prim_instance_index));
self.commands.push(Command::data((gpu_buffer_address.u as u32) << 16 | gpu_buffer_address.v as u32));
}
PrimitiveCommand::Quad { pattern, pattern_input, prim_instance_index, gpu_buffer_address, transform_id, quad_flags, edge_flags, src_color_task_id } => {
self.commands.push(Command::draw_quad(prim_instance_index));
self.commands.push(Command::data(pattern as u32));
self.commands.push(Command::data(pattern_input.0 as u32));
self.commands.push(Command::data(pattern_input.1 as u32));
self.commands.push(Command::data(src_color_task_id.index));
self.commands.push(Command::data((gpu_buffer_address.u as u32) << 16 | gpu_buffer_address.v as u32));
self.commands.push(Command::data(transform_id.0));
self.commands.push(Command::data((quad_flags.bits() as u32) << 16 | edge_flags.bits() as u32));
}
}
}
/// Iterate the command list, calling a provided closure for each primitive draw command.
pub fn iter_prims<F>(
&self,
f: &mut F,
) where F: FnMut(&PrimitiveCommand, SpatialNodeIndex, &[RenderTaskId]) {
let mut current_spatial_node_index = SpatialNodeIndex::INVALID;
let mut cmd_iter = self.commands.iter();
// TODO(gw): Consider pre-allocating this / Smallvec if it shows up in profiles.
let mut segments = Vec::new();
while let Some(cmd) = cmd_iter.next() {
let command = cmd.0 & Command::CMD_MASK;
let param = cmd.0 & Command::PARAM_MASK;
match command {
Command::CMD_DRAW_SIMPLE_PRIM => {
let prim_instance_index = PrimitiveInstanceIndex(param);
let cmd = PrimitiveCommand::simple(prim_instance_index);
f(&cmd, current_spatial_node_index, &[]);
}
Command::CMD_SET_SPATIAL_NODE => {
current_spatial_node_index = SpatialNodeIndex(param);
}
Command::CMD_DRAW_COMPLEX_PRIM => {
let prim_instance_index = PrimitiveInstanceIndex(param);
let data = cmd_iter.next().unwrap();
let gpu_address = GpuCacheAddress {
u: (data.0 >> 16) as u16,
v: (data.0 & 0xffff) as u16,
};
let cmd = PrimitiveCommand::complex(
prim_instance_index,
gpu_address,
);
f(&cmd, current_spatial_node_index, &[]);
}
Command::CMD_DRAW_QUAD => {
let prim_instance_index = PrimitiveInstanceIndex(param);
let pattern = PatternKind::from_u32(cmd_iter.next().unwrap().0);
let pattern_input = PatternShaderInput(
cmd_iter.next().unwrap().0 as i32,
cmd_iter.next().unwrap().0 as i32,
);
let src_color_task_id = RenderTaskId { index: cmd_iter.next().unwrap().0 };
let data = cmd_iter.next().unwrap();
let transform_id = TransformPaletteId(cmd_iter.next().unwrap().0);
let bits = cmd_iter.next().unwrap().0;
let quad_flags = QuadFlags::from_bits((bits >> 16) as u8).unwrap();
let edge_flags = EdgeAaSegmentMask::from_bits((bits & 0xff) as u8).unwrap();
let gpu_buffer_address = GpuBufferAddress {
u: (data.0 >> 16) as u16,
v: (data.0 & 0xffff) as u16,
};
let cmd = PrimitiveCommand::quad(
pattern,
pattern_input,
src_color_task_id,
prim_instance_index,
gpu_buffer_address,
transform_id,
quad_flags,
edge_flags,
);
f(&cmd, current_spatial_node_index, &segments);
segments.clear()
}
Command::CMD_DRAW_INSTANCE => {
let prim_instance_index = PrimitiveInstanceIndex(param);
let data = cmd_iter.next().unwrap();
let gpu_buffer_address = GpuBufferAddress {
u: (data.0 >> 16) as u16,
v: (data.0 & 0xffff) as u16,
};
let cmd = PrimitiveCommand::instance(
prim_instance_index,
gpu_buffer_address,
);
f(&cmd, current_spatial_node_index, &[]);
}
Command::CMD_SET_SEGMENTS => {
let count = param;
for _ in 0 .. count {
segments.push(RenderTaskId { index: cmd_iter.next().unwrap().0 });
}
}
_ => {
unreachable!();
}
}
}
}
}
/// Abstracts whether a command buffer is being built for a tiled (picture cache)
/// or simple (child surface).
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub enum CommandBufferBuilderKind {
Tiled {
// TODO(gw): It might be worth storing this as a 2d-array instead
// of a hash map if it ever shows up in profiles. This is
// slightly complicated by the sub_slice_index in the
// TileKey structure - could have a 2 level array?
tiles: FastHashMap<TileKey, SurfaceTileDescriptor>,
},
Simple {
render_task_id: RenderTaskId,
root_task_id: Option<RenderTaskId>,
dirty_rect: PictureRect,
},
Invalid,
}
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct CommandBufferBuilder {
pub kind: CommandBufferBuilderKind,
/// If a command buffer establishes a sub-graph, then at the end of constructing
/// the surface, the parent surface is supplied as an input dependency, and the
/// parent surface gets a duplicated (existing) task with the same location, and
/// with the sub-graph output as an input dependency.
pub establishes_sub_graph: bool,
/// If this surface builds a sub-graph, it will mark a task in the filter sub-graph
/// as a resolve source for the input from the parent surface.
pub resolve_source: Option<RenderTaskId>,
/// List of render tasks that depend on the task that will be created for this builder.
pub extra_dependencies: Vec<RenderTaskId>,
}
impl CommandBufferBuilder {
pub fn empty() -> Self {
CommandBufferBuilder {
kind: CommandBufferBuilderKind::Invalid,
establishes_sub_graph: false,
resolve_source: None,
extra_dependencies: Vec::new(),
}
}
/// Construct a tiled command buffer builder.
pub fn new_tiled(
tiles: FastHashMap<TileKey, SurfaceTileDescriptor>,
) -> Self {
CommandBufferBuilder {
kind: CommandBufferBuilderKind::Tiled {
tiles,
},
establishes_sub_graph: false,
resolve_source: None,
extra_dependencies: Vec::new(),
}
}
/// Construct a simple command buffer builder.
pub fn new_simple(
render_task_id: RenderTaskId,
establishes_sub_graph: bool,
root_task_id: Option<RenderTaskId>,
dirty_rect: PictureRect,
) -> Self {
CommandBufferBuilder {
kind: CommandBufferBuilderKind::Simple {
render_task_id,
root_task_id,
dirty_rect,
},
establishes_sub_graph,
resolve_source: None,
extra_dependencies: Vec::new(),
}
}
}
// Index into a command buffer stored in a `CommandBufferList`.
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[derive(Debug, Copy, Clone)]
pub struct CommandBufferIndex(pub u32);
// Container for a list of command buffers that are built for a frame.
pub struct CommandBufferList {
cmd_buffers: Vec<CommandBuffer>,
}
impl CommandBufferList {
pub fn new() -> Self {
CommandBufferList {
cmd_buffers: Vec::new(),
}
}
pub fn create_cmd_buffer(
&mut self,
) -> CommandBufferIndex {
let index = CommandBufferIndex(self.cmd_buffers.len() as u32);
self.cmd_buffers.push(CommandBuffer::new());
index
}
pub fn get(&self, index: CommandBufferIndex) -> &CommandBuffer {
&self.cmd_buffers[index.0 as usize]
}
pub fn get_mut(&mut self, index: CommandBufferIndex) -> &mut CommandBuffer {
&mut self.cmd_buffers[index.0 as usize]
}
}