Initial software tessellation code
This commit is contained in:
parent
a066e77c72
commit
f2835071c7
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
Loading…
Reference in New Issue