Initial software tessellation code

This commit is contained in:
Patrick Walton 2017-07-05 17:03:05 -07:00
parent a066e77c72
commit f2835071c7
5 changed files with 366 additions and 1 deletions

View File

@ -11,4 +11,5 @@ crate-type = ["dylib", "rlib"]
bit-vec = "0.4"
env_logger = "0.4"
euclid = "0.15"
half = "1.0"
log = "0.3"

View File

@ -1,10 +1,23 @@
// partitionfinder/capi.rs
use env_logger;
use euclid::Transform2D;
use partitioner::Partitioner;
use tessellator::{QuadTessLevels, Tessellator};
use std::mem;
use std::slice;
use {Bezieroid, ControlPoints, Endpoint, Subpath};
use {AntialiasingMode, Bezieroid, ControlPoints, Endpoint, Subpath, Vertex};
#[derive(Clone, Copy)]
#[repr(C)]
pub struct Matrix2DF32 {
pub m00: f32,
pub m01: f32,
pub m02: f32,
pub m10: f32,
pub m11: f32,
pub m12: f32,
}
#[no_mangle]
pub unsafe extern fn pf_partitioner_new() -> *mut Partitioner<'static> {
@ -50,6 +63,88 @@ pub unsafe extern fn pf_partitioner_bezieroids<'a>(partitioner: *mut Partitioner
bezieroids.as_ptr()
}
#[no_mangle]
pub unsafe extern fn pf_tessellator_new(endpoints: *const Endpoint,
endpoint_count: u32,
control_points: *const ControlPoints,
control_points_count: u32,
b_quads: *const Bezieroid,
b_quad_count: u32,
antialiasing_mode: AntialiasingMode)
-> *mut Tessellator<'static> {
let mut tessellator =
Box::new(Tessellator::new(slice::from_raw_parts(endpoints, endpoint_count as usize),
slice::from_raw_parts(control_points,
control_points_count as usize),
slice::from_raw_parts(b_quads, b_quad_count as usize),
antialiasing_mode));
let tessellator_ptr: *mut Tessellator<'static> = &mut *tessellator;
mem::forget(tessellator);
tessellator_ptr
}
#[no_mangle]
pub unsafe extern fn pf_tessellator_destroy<'a>(tessellator: *mut Tessellator<'a>) {
drop(mem::transmute::<*mut Tessellator<'a>, Box<Tessellator>>(tessellator))
}
#[no_mangle]
pub unsafe extern fn pf_tessellator_compute_hull<'a>(tessellator: *mut Tessellator<'a>,
transform: *const Matrix2DF32) {
(*tessellator).compute_hull(&Transform2D::column_major((*transform).m00,
(*transform).m01,
(*transform).m02,
(*transform).m10,
(*transform).m11,
(*transform).m12))
}
#[no_mangle]
pub unsafe extern fn pf_tessellator_compute_domain<'a>(tessellator: *mut Tessellator<'a>) {
(*tessellator).compute_domain()
}
#[no_mangle]
pub unsafe extern fn pf_tessellator_tess_levels<'a>(tessellator: *mut Tessellator<'a>,
out_tess_levels_count: *mut u32)
-> *const QuadTessLevels {
let tess_levels = (*tessellator).tess_levels();
if !out_tess_levels_count.is_null() {
*out_tess_levels_count = tess_levels.len() as u32
}
tess_levels.as_ptr()
}
pub unsafe extern fn pf_tessellator_vertices<'a>(tessellator: *mut Tessellator<'a>,
out_vertex_count: *mut u32)
-> *const Vertex {
let vertices = (*tessellator).vertices();
if !out_vertex_count.is_null() {
*out_vertex_count = vertices.len() as u32
}
vertices.as_ptr()
}
pub unsafe extern fn pf_tessellator_msaa_indices<'a>(tessellator: *mut Tessellator<'a>,
out_msaa_index_count: *mut u32)
-> *const u32 {
let msaa_indices = (*tessellator).msaa_indices();
if !out_msaa_index_count.is_null() {
*out_msaa_index_count = msaa_indices.len() as u32
}
msaa_indices.as_ptr()
}
pub unsafe extern fn pf_tessellator_levien_indices<'a>(tessellator: *mut Tessellator<'a>,
out_levien_index_count: *mut u32)
-> *const u32 {
let levien_indices = (*tessellator).levien_indices();
if !out_levien_index_count.is_null() {
*out_levien_index_count = levien_indices.len() as u32
}
levien_indices.as_ptr()
}
#[no_mangle]
pub unsafe extern fn pf_init_env_logger() -> u32 {
env_logger::init().is_ok() as u32

View File

@ -7,6 +7,7 @@ extern crate alloc_jemalloc;
extern crate bit_vec;
extern crate env_logger;
extern crate euclid;
extern crate half;
#[macro_use]
extern crate log;
@ -16,6 +17,7 @@ use std::u32;
pub mod capi;
pub mod geometry;
pub mod partitioner;
pub mod tessellator;
#[repr(C)]
#[derive(Debug, Clone, Copy)]
@ -52,3 +54,31 @@ pub struct Subpath {
pub first_endpoint_index: u32,
pub last_endpoint_index: u32,
}
#[derive(Clone, Copy, PartialEq, Debug)]
#[repr(u8)]
pub enum AntialiasingMode {
Msaa = 0,
Levien = 1,
}
#[derive(Clone, Copy, Debug)]
#[repr(C)]
pub struct Vertex {
pub prev_endpoint_index: u32,
pub next_endpoint_index: u32,
pub time: f32,
padding: u32,
}
impl Vertex {
#[inline]
pub fn new(prev_endpoint_index: u32, next_endpoint_index: u32, time: f32) -> Vertex {
Vertex {
prev_endpoint_index: prev_endpoint_index,
next_endpoint_index: next_endpoint_index,
time: time,
padding: 0,
}
}
}

View File

@ -5,16 +5,46 @@
#include <stdint.h>
#define PF_ANTIALIASING_MODE_MSAA 0
#define PF_ANTIALIASING_MODE_LEVIEN 1
#ifdef __cplusplus
extern "C" {
#endif
typedef uint8_t pf_antialiasing_mode_t;
typedef uint16_t pf_float16_t;
struct pf_point2d_f32 {
float x, y;
};
typedef struct pf_point2d_f32 pf_point2d_f32_t;
struct pf_matrix2d_f32 {
float m00, m01, m02;
float m10, m11, m12;
};
typedef struct pf_matrix2d_f32 pf_matrix2d_f32_t;
struct pf_vertex {
uint32_t prev_endpoint_index;
uint32_t next_endpoint_index;
float time;
uint32_t padding;
};
typedef struct pf_vertex pf_vertex_t;
struct pf_quad_tess_levels {
pf_float16_t outer[4];
pf_float16_t inner[2];
};
typedef struct pf_quad_tess_levels pf_quad_tess_levels_t;
struct pf_bezieroid {
uint32_t upper_prev_endpoint, upper_next_endpoint;
uint32_t lower_prev_endpoint, lower_next_endpoint;
@ -49,6 +79,10 @@ struct pf_partitioner;
typedef struct pf_partitioner pf_partitioner_t;
struct pf_tessellator;
typedef struct pf_tessellator pf_tessellator_t;
pf_partitioner_t *pf_partitioner_new();
void pf_partitioner_destroy(pf_partitioner_t *partitioner);
@ -68,6 +102,32 @@ void pf_partitioner_partition(pf_partitioner_t *partitioner,
const pf_bezieroid_t *pf_partitioner_bezieroids(pf_partitioner_t *partitioner,
uint32_t *out_bezieroid_count);
pf_tessellator_t *pf_tessellator_new(const pf_endpoint_t *endpoints,
uint32_t endpoint_count,
const pf_control_points_t *control_points,
uint32_t control_points_index,
const pf_bezieroid_t *b_quads,
uint32_t b_quad_count,
pf_antialiasing_mode_t antialiasing_mode);
void pf_tessellator_destroy(pf_tessellator_t *tessellator);
void pf_tessellator_compute_hull(pf_tessellator_t *tessellator, const pf_matrix2d_f32_t *transform);
void pf_tessellator_compute_domain(pf_tessellator_t *tessellator);
const pf_quad_tess_levels_t *pf_tessellator_tess_levels(const pf_tessellator_t *tessellator,
uint32_t *out_tess_levels_count);
const pf_vertex_t *pf_tessellator_vertices(const pf_tessellator_t *tessellator,
uint32_t *out_vertex_count);
const uint32_t *pf_tessellator_msaa_indices(const pf_tessellator_t *tessellator,
uint32_t *out_msaa_index_count);
const uint32_t *pf_tessellator_levien_indices(const pf_tessellator_t *tessellator,
uint32_t *out_levien_index_count);
uint32_t pf_init_env_logger();
#ifdef __cplusplus

View File

@ -0,0 +1,179 @@
// partitionfinder/tessellator.rs
#![allow(dead_code)]
use euclid::{Length, Transform2D};
use half::{f16, self};
use std::cmp;
use std::u32;
use {AntialiasingMode, Bezieroid, ControlPoints, Endpoint, Vertex};
const TOLERANCE: f32 = 0.25;
pub struct Tessellator<'a> {
endpoints: &'a [Endpoint],
control_points: &'a [ControlPoints],
b_quads: &'a [Bezieroid],
antialiasing_mode: AntialiasingMode,
tess_levels: Vec<QuadTessLevels>,
vertices: Vec<Vertex>,
msaa_indices: Vec<u32>,
levien_indices: Vec<u32>,
}
// NB: This must match the layout of `MTLQuadTessellationFactorsHalf` in Metal in order for the
// Pathfinder demo to work.
#[derive(Clone, Copy, Debug)]
#[repr(C)]
pub struct QuadTessLevels {
pub outer: [f16; 4],
pub inner: [f16; 2],
}
impl QuadTessLevels {
fn new() -> QuadTessLevels {
QuadTessLevels {
outer: [half::consts::ZERO; 4],
inner: [half::consts::ZERO; 2],
}
}
}
impl<'a> Tessellator<'a> {
pub fn new<'b>(endpoints: &'b [Endpoint],
control_points: &'b [ControlPoints],
b_quads: &'b [Bezieroid],
antialiasing_mode: AntialiasingMode)
-> Tessellator<'b> {
Tessellator {
endpoints: endpoints,
control_points: control_points,
b_quads: b_quads,
antialiasing_mode: antialiasing_mode,
tess_levels: vec![QuadTessLevels::new(); b_quads.len()],
vertices: vec![],
msaa_indices: vec![],
levien_indices: vec![],
}
}
pub fn compute_hull(&mut self, transform: &Transform2D<f32>) {
for (tess_levels, bquad) in (self.tess_levels.iter_mut()).zip(self.b_quads.iter()) {
let upper_tess_level = tess_level_for_edge(bquad.upper_prev_endpoint,
bquad.upper_next_endpoint,
bquad.upper_left_time,
bquad.upper_right_time,
transform,
self.endpoints,
self.control_points);
let lower_tess_level = tess_level_for_edge(bquad.lower_prev_endpoint,
bquad.lower_prev_endpoint,
bquad.lower_left_time,
bquad.lower_right_time,
transform,
self.endpoints,
self.control_points);
// TODO(pcwalton): Use fewer thin triangles.
tess_levels.outer[0] = half::consts::ONE;
tess_levels.outer[1] = f16::from_f32(upper_tess_level as f32);
tess_levels.outer[2] = half::consts::ONE;
tess_levels.outer[3] = f16::from_f32(lower_tess_level as f32);
tess_levels.inner[0] = f16::from_f32(cmp::max(upper_tess_level,
lower_tess_level) as f32);
tess_levels.inner[1] = half::consts::ZERO;
}
}
// TODO(pcwalton): Do a better tessellation that doesn't make so many sliver triangles.
pub fn compute_domain(&mut self) {
for (b_quad, tess_levels) in self.b_quads.iter().zip(self.tess_levels.iter()) {
let upper_tess_level = f32::from(tess_levels.outer[1]) as u32;
let lower_tess_level = f32::from(tess_levels.outer[3]) as u32;
let tess_level = cmp::max(upper_tess_level, lower_tess_level);
let first_upper_vertex_index = self.vertices.len() as u32;
self.vertices.extend((0..(tess_level + 1)).map(|index| {
let left_time: Length<f32, ()> = Length::new(b_quad.upper_left_time);
let right_time: Length<f32, ()> = Length::new(b_quad.upper_right_time);
Vertex::new(b_quad.upper_prev_endpoint,
b_quad.upper_next_endpoint,
left_time.lerp(right_time, index as f32 / tess_level as f32).get())
}));
let first_lower_vertex_index = self.vertices.len() as u32;
self.vertices.extend((0..(tess_level + 1)).map(|index| {
let left_time: Length<f32, ()> = Length::new(b_quad.lower_left_time);
let right_time: Length<f32, ()> = Length::new(b_quad.lower_right_time);
Vertex::new(b_quad.lower_prev_endpoint,
b_quad.lower_next_endpoint,
left_time.lerp(right_time, index as f32 / tess_level as f32).get())
}));
// Emit a triangle strip.
self.msaa_indices.reserve(tess_level as usize * 6);
for index in 0..tess_level {
self.msaa_indices.extend([
first_upper_vertex_index + index + 0,
first_upper_vertex_index + index + 1,
first_lower_vertex_index + index + 0,
first_upper_vertex_index + index + 1,
first_lower_vertex_index + index + 1,
first_lower_vertex_index + index + 0,
].into_iter())
}
}
}
#[inline]
pub fn tess_levels(&self) -> &[QuadTessLevels] {
&self.tess_levels
}
#[inline]
pub fn vertices(&self) -> &[Vertex] {
&self.vertices
}
#[inline]
pub fn msaa_indices(&self) -> &[u32] {
&self.msaa_indices
}
#[inline]
pub fn levien_indices(&self) -> &[u32] {
&self.levien_indices
}
}
// http://antigrain.com/research/adaptive_bezier/
fn tess_level_for_edge(prev_endpoint_index: u32,
next_endpoint_index: u32,
left_time: f32,
right_time: f32,
transform: &Transform2D<f32>,
endpoints: &[Endpoint],
control_points: &[ControlPoints])
-> u32 {
let control_points_index = endpoints[next_endpoint_index as usize].control_points_index;
if control_points_index == u32::MAX {
return 1
}
let (prev_time, next_time) = (left_time.min(right_time), left_time.max(right_time));
let prev_endpoint = &endpoints[prev_endpoint_index as usize];
let next_endpoint = &endpoints[next_endpoint_index as usize];
let control_points = &control_points[control_points_index as usize];
let p0 = transform.transform_point(&prev_endpoint.position);
let p1 = transform.transform_point(&control_points.point1);
let p2 = transform.transform_point(&control_points.point2);
let p3 = transform.transform_point(&next_endpoint.position);
let length = (p1 - p0).length() + (p2 - p1).length() + (p3 - p2).length();
1 + (length * TOLERANCE * (next_time - prev_time)) as u32
}