2019-01-14 17:20:36 -05:00
|
|
|
// pathfinder/renderer/src/builder.rs
|
|
|
|
//
|
|
|
|
// Copyright © 2019 The Pathfinder Project Developers.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
// except according to those terms.
|
|
|
|
|
|
|
|
//! Packs data onto the GPU.
|
|
|
|
|
|
|
|
use crate::gpu_data::{Batch, BuiltObject, FillBatchPrimitive};
|
|
|
|
use crate::gpu_data::{MaskTileBatchPrimitive, SolidTileScenePrimitive};
|
|
|
|
use crate::scene;
|
|
|
|
use crate::tiles;
|
|
|
|
use crate::z_buffer::ZBuffer;
|
2019-02-06 21:09:37 -05:00
|
|
|
use pathfinder_geometry::basic::point::Point2DF32;
|
2019-02-05 13:55:01 -05:00
|
|
|
use pathfinder_geometry::basic::rect::{RectF32, RectI32};
|
2019-02-06 21:09:37 -05:00
|
|
|
use pathfinder_geometry::basic::transform2d::Transform2DF32;
|
|
|
|
use pathfinder_geometry::basic::transform3d::Perspective;
|
|
|
|
use pathfinder_geometry::clip::PolygonClipper3D;
|
2019-01-14 17:20:36 -05:00
|
|
|
use std::iter;
|
|
|
|
use std::u16;
|
|
|
|
|
|
|
|
const MAX_FILLS_PER_BATCH: usize = 0x0002_0000;
|
|
|
|
const MAX_MASKS_PER_BATCH: u16 = 0xffff;
|
|
|
|
|
|
|
|
pub struct SceneBuilder {
|
|
|
|
objects: Vec<BuiltObject>,
|
|
|
|
z_buffer: ZBuffer,
|
2019-02-05 13:55:01 -05:00
|
|
|
tile_rect: RectI32,
|
2019-01-14 17:20:36 -05:00
|
|
|
|
|
|
|
current_object_index: usize,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl SceneBuilder {
|
2019-02-05 13:03:20 -05:00
|
|
|
pub fn new(objects: Vec<BuiltObject>, z_buffer: ZBuffer, view_box: RectF32) -> SceneBuilder {
|
2019-01-14 17:20:36 -05:00
|
|
|
SceneBuilder {
|
|
|
|
objects,
|
|
|
|
z_buffer,
|
2019-02-05 13:55:01 -05:00
|
|
|
tile_rect: tiles::round_rect_out_to_tile_bounds(view_box),
|
2019-01-14 17:20:36 -05:00
|
|
|
current_object_index: 0,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn build_solid_tiles(&self) -> Vec<SolidTileScenePrimitive> {
|
|
|
|
self.z_buffer
|
2019-02-05 13:55:01 -05:00
|
|
|
.build_solid_tiles(&self.objects, self.tile_rect)
|
2019-01-14 17:20:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn build_batch(&mut self) -> Option<Batch> {
|
|
|
|
let mut batch = Batch::new();
|
|
|
|
|
|
|
|
let mut object_tile_index_to_batch_mask_tile_index = vec![];
|
|
|
|
while self.current_object_index < self.objects.len() {
|
|
|
|
let object = &self.objects[self.current_object_index];
|
|
|
|
|
|
|
|
if batch.fills.len() + object.fills.len() > MAX_FILLS_PER_BATCH {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
object_tile_index_to_batch_mask_tile_index.clear();
|
|
|
|
object_tile_index_to_batch_mask_tile_index
|
|
|
|
.extend(iter::repeat(u16::MAX).take(object.tiles.len()));
|
|
|
|
|
|
|
|
// Copy mask tiles.
|
|
|
|
for (tile_index, tile) in object.tiles.iter().enumerate() {
|
|
|
|
// Skip solid tiles, since we handled them above already.
|
|
|
|
if object.solid_tiles[tile_index] {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Cull occluded tiles.
|
2019-02-05 13:55:01 -05:00
|
|
|
let scene_tile_index = scene::scene_tile_index(tile.tile_x as i32,
|
|
|
|
tile.tile_y as i32,
|
|
|
|
self.tile_rect);
|
2019-01-14 17:20:36 -05:00
|
|
|
if !self
|
|
|
|
.z_buffer
|
|
|
|
.test(scene_tile_index, self.current_object_index as u32)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Visible mask tile.
|
|
|
|
let batch_mask_tile_index = batch.mask_tiles.len() as u16;
|
|
|
|
if batch_mask_tile_index == MAX_MASKS_PER_BATCH {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
object_tile_index_to_batch_mask_tile_index[tile_index] = batch_mask_tile_index;
|
|
|
|
|
|
|
|
batch.mask_tiles.push(MaskTileBatchPrimitive {
|
|
|
|
tile: *tile,
|
|
|
|
shader: object.shader,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remap and copy fills, culling as necessary.
|
|
|
|
for fill in &object.fills {
|
2019-02-05 13:55:01 -05:00
|
|
|
let object_tile_index = object.tile_coords_to_index(fill.tile_x as i32,
|
|
|
|
fill.tile_y as i32);
|
2019-01-14 17:20:36 -05:00
|
|
|
let mask_tile_index =
|
|
|
|
object_tile_index_to_batch_mask_tile_index[object_tile_index as usize];
|
|
|
|
if mask_tile_index < u16::MAX {
|
|
|
|
batch.fills.push(FillBatchPrimitive {
|
|
|
|
px: fill.px,
|
|
|
|
subpx: fill.subpx,
|
|
|
|
mask_tile_index,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
self.current_object_index += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if batch.is_empty() {
|
|
|
|
None
|
|
|
|
} else {
|
|
|
|
Some(batch)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-02-06 21:09:37 -05:00
|
|
|
|
|
|
|
#[derive(Clone, Default)]
|
|
|
|
pub struct RenderOptions {
|
|
|
|
pub transform: RenderTransform,
|
|
|
|
pub dilation: Point2DF32,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl RenderOptions {
|
|
|
|
pub fn prepare(self, bounds: RectF32) -> PreparedRenderOptions {
|
|
|
|
PreparedRenderOptions {
|
|
|
|
transform: self.transform.prepare(bounds),
|
|
|
|
dilation: self.dilation,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
pub enum RenderTransform {
|
|
|
|
Transform2D(Transform2DF32),
|
|
|
|
Perspective(Perspective),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for RenderTransform {
|
|
|
|
#[inline]
|
|
|
|
fn default() -> RenderTransform {
|
|
|
|
RenderTransform::Transform2D(Transform2DF32::default())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl RenderTransform {
|
|
|
|
fn prepare(&self, bounds: RectF32) -> PreparedRenderTransform {
|
|
|
|
let perspective = match self {
|
|
|
|
RenderTransform::Transform2D(ref transform) => {
|
|
|
|
if transform.is_identity() {
|
|
|
|
return PreparedRenderTransform::None;
|
|
|
|
}
|
|
|
|
return PreparedRenderTransform::Transform2D(*transform);
|
|
|
|
}
|
|
|
|
RenderTransform::Perspective(ref perspective) => *perspective,
|
|
|
|
};
|
|
|
|
|
|
|
|
let mut points = vec![
|
|
|
|
bounds.origin().to_3d(),
|
|
|
|
bounds.upper_right().to_3d(),
|
|
|
|
bounds.lower_right().to_3d(),
|
|
|
|
bounds.lower_left().to_3d(),
|
|
|
|
];
|
|
|
|
//println!("-----");
|
|
|
|
//println!("bounds={:?} ORIGINAL quad={:?}", self.bounds, points);
|
|
|
|
for point in &mut points {
|
|
|
|
*point = perspective.transform.transform_point(*point);
|
|
|
|
}
|
|
|
|
//println!("... PERSPECTIVE quad={:?}", points);
|
|
|
|
points = PolygonClipper3D::new(points).clip();
|
|
|
|
//println!("... CLIPPED quad={:?}", points);
|
|
|
|
for point in &mut points {
|
|
|
|
*point = point.perspective_divide()
|
|
|
|
}
|
|
|
|
let inverse_transform = perspective.transform.inverse();
|
|
|
|
let clip_polygon = points.into_iter().map(|point| {
|
|
|
|
inverse_transform.transform_point(point).perspective_divide().to_2d()
|
|
|
|
}).collect();
|
|
|
|
PreparedRenderTransform::Perspective { perspective, clip_polygon }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct PreparedRenderOptions {
|
|
|
|
pub transform: PreparedRenderTransform,
|
|
|
|
pub dilation: Point2DF32,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub enum PreparedRenderTransform {
|
|
|
|
None,
|
|
|
|
Transform2D(Transform2DF32),
|
|
|
|
Perspective { perspective: Perspective, clip_polygon: Vec<Point2DF32> }
|
|
|
|
}
|