// pathfinder/renderer/src/gpu_data.rs // // Copyright © 2019 The Pathfinder Project Developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! Packed data ready to be sent to the GPU. use crate::paint::{ObjectShader, ShaderId}; use crate::tiles::{self, TILE_HEIGHT, TILE_WIDTH}; use euclid::Rect; use fixedbitset::FixedBitSet; use pathfinder_geometry::line_segment::{LineSegmentF32, LineSegmentU4, LineSegmentU8}; use pathfinder_geometry::point::Point2DF32; use pathfinder_geometry::simd::{F32x4, I32x4}; use pathfinder_geometry::util; #[derive(Debug)] pub struct BuiltObject { pub bounds: Rect, pub tile_rect: Rect, pub tiles: Vec, pub fills: Vec, pub solid_tiles: FixedBitSet, pub shader: ShaderId, } #[derive(Debug)] pub struct BuiltScene { pub view_box: Rect, pub batches: Vec, pub solid_tiles: Vec, pub shaders: Vec, } #[derive(Debug)] pub struct Batch { pub fills: Vec, pub mask_tiles: Vec, } #[derive(Clone, Copy, Debug)] pub struct FillObjectPrimitive { pub px: LineSegmentU4, pub subpx: LineSegmentU8, pub tile_x: i16, pub tile_y: i16, } #[derive(Clone, Copy, Debug)] #[repr(C)] pub struct TileObjectPrimitive { pub tile_x: i16, pub tile_y: i16, pub backdrop: i16, } #[derive(Clone, Copy, Debug)] #[repr(C)] pub struct FillBatchPrimitive { pub px: LineSegmentU4, pub subpx: LineSegmentU8, pub mask_tile_index: u16, } #[derive(Clone, Copy, Debug)] #[repr(C)] pub struct SolidTileScenePrimitive { pub tile_x: i16, pub tile_y: i16, pub shader: ShaderId, } #[derive(Clone, Copy, Debug)] #[repr(C)] pub struct MaskTileBatchPrimitive { pub tile: TileObjectPrimitive, pub shader: ShaderId, } // Utilities for built objects impl BuiltObject { pub fn new(bounds: &Rect, shader: ShaderId) -> BuiltObject { // Compute the tile rect. let tile_rect = tiles::round_rect_out_to_tile_bounds(&bounds); // Allocate tiles. let tile_count = tile_rect.size.width as usize * tile_rect.size.height as usize; let mut tiles = Vec::with_capacity(tile_count); for y in tile_rect.origin.y..tile_rect.max_y() { for x in tile_rect.origin.x..tile_rect.max_x() { tiles.push(TileObjectPrimitive::new(x, y)); } } let mut solid_tiles = FixedBitSet::with_capacity(tile_count); solid_tiles.insert_range(..); BuiltObject { bounds: *bounds, tile_rect, tiles, fills: vec![], solid_tiles, shader, } } // TODO(pcwalton): SIMD-ify `tile_x` and `tile_y`. fn add_fill(&mut self, segment: &LineSegmentF32, tile_x: i16, tile_y: i16) { //println!("add_fill({:?} ({}, {}))", segment, tile_x, tile_y); let mut segment = (segment.0 * F32x4::splat(256.0)).to_i32x4(); let tile_origin_x = (TILE_WIDTH as i32) * 256 * (tile_x as i32); let tile_origin_y = (TILE_HEIGHT as i32) * 256 * (tile_y as i32); let tile_origin = I32x4::new(tile_origin_x, tile_origin_y, tile_origin_x, tile_origin_y); segment = segment - tile_origin; /* println!("... before min: {} {} {} {}", segment[0], segment[1], segment[2], segment[3]); */ //segment = Sse41::max_epi32(segment, Sse41::setzero_epi32()); segment = segment.min(I32x4::splat(0x0fff)); //println!("... after min: {} {} {} {}", segment[0], segment[1], segment[2], segment[3]); let shuffle_mask = I32x4::new(0x0c08_0400, 0x0d05_0901, 0, 0); segment = segment .as_u8x16() .shuffle(shuffle_mask.as_u8x16()) .as_i32x4(); let px = LineSegmentU4((segment[1] | (segment[1] >> 12)) as u16); let subpx = LineSegmentU8(segment[0] as u32); let tile_index = self.tile_coords_to_index(tile_x, tile_y); /* // TODO(pcwalton): Cull degenerate fills again. // Cull degenerate fills. let (from_px, to_px) = (from.to_u8(), to.to_u8()); if from_px.x == to_px.x && from_subpx.x == to_subpx.x { return } */ self.fills.push(FillObjectPrimitive { px, subpx, tile_x, tile_y, }); self.solid_tiles.set(tile_index as usize, false); } pub fn add_active_fill( &mut self, left: f32, right: f32, mut winding: i16, tile_x: i16, tile_y: i16, ) { let tile_origin_y = (i32::from(tile_y) * TILE_HEIGHT as i32) as f32; let left = Point2DF32::new(left, tile_origin_y); let right = Point2DF32::new(right, tile_origin_y); let segment = if winding < 0 { LineSegmentF32::new(&left, &right) } else { LineSegmentF32::new(&right, &left) }; /* println!("... emitting active fill {} -> {} winding {} @ tile {}", left.x(), right.x(), winding, tile_x); */ while winding != 0 { self.add_fill(&segment, tile_x, tile_y); if winding < 0 { winding += 1 } else { winding -= 1 } } } // TODO(pcwalton): Optimize this better with SIMD! pub fn generate_fill_primitives_for_line(&mut self, mut segment: LineSegmentF32, tile_y: i16) { /* println!("... generate_fill_primitives_for_line(): segment={:?} tile_y={} ({}-{})", segment, tile_y, tile_y as f32 * TILE_HEIGHT as f32, (tile_y + 1) as f32 * TILE_HEIGHT as f32); */ let winding = segment.from_x() > segment.to_x(); let (segment_left, segment_right) = if !winding { (segment.from_x(), segment.to_x()) } else { (segment.to_x(), segment.from_x()) }; let segment_tile_left = (f32::floor(segment_left) as i32 / TILE_WIDTH as i32) as i16; let segment_tile_right = util::alignup_i32(f32::ceil(segment_right) as i32, TILE_WIDTH as i32) as i16; for subsegment_tile_x in segment_tile_left..segment_tile_right { let (mut fill_from, mut fill_to) = (segment.from(), segment.to()); let subsegment_tile_right = ((i32::from(subsegment_tile_x) + 1) * TILE_HEIGHT as i32) as f32; if subsegment_tile_right < segment_right { let x = subsegment_tile_right; let point = Point2DF32::new(x, segment.solve_y_for_x(x)); if !winding { fill_to = point; segment = LineSegmentF32::new(&point, &segment.to()); } else { fill_from = point; segment = LineSegmentF32::new(&segment.from(), &point); } } let fill_segment = LineSegmentF32::new(&fill_from, &fill_to); self.add_fill(&fill_segment, subsegment_tile_x, tile_y); } } // FIXME(pcwalton): Use a `Point2D` instead? pub fn tile_coords_to_index(&self, tile_x: i16, tile_y: i16) -> u32 { /*println!("tile_coords_to_index(x={}, y={}, tile_rect={:?})", tile_x, tile_y, self.tile_rect);*/ (tile_y - self.tile_rect.origin.y) as u32 * self.tile_rect.size.width as u32 + (tile_x - self.tile_rect.origin.x) as u32 } pub fn get_tile_mut(&mut self, tile_x: i16, tile_y: i16) -> &mut TileObjectPrimitive { let tile_index = self.tile_coords_to_index(tile_x, tile_y); &mut self.tiles[tile_index as usize] } } impl BuiltScene { #[inline] pub fn new(view_box: &Rect) -> BuiltScene { BuiltScene { view_box: *view_box, batches: vec![], solid_tiles: vec![], shaders: vec![], } } } impl Batch { #[inline] pub fn new() -> Batch { Batch { fills: vec![], mask_tiles: vec![], } } #[inline] pub fn is_empty(&self) -> bool { self.mask_tiles.is_empty() } } impl TileObjectPrimitive { #[inline] fn new(tile_x: i16, tile_y: i16) -> TileObjectPrimitive { TileObjectPrimitive { tile_x, tile_y, backdrop: 0, } } }