Use de Casteljau subdivision to split curves instead of doing that repeatedly in the shader.

This doesn't seem to negatively affect performance on the CPU and reduces complexity overall.
This commit is contained in:
Patrick Walton 2017-07-13 16:44:11 -07:00
parent 8730df37a7
commit 0e39b409ae
6 changed files with 1022 additions and 423 deletions

View File

@ -7,7 +7,8 @@ use partitioner::Partitioner;
use tessellator::{QuadTessLevels, Tessellator};
use std::mem;
use std::slice;
use {AntialiasingMode, BQuad, EdgeInstance, Endpoint, Subpath, Vertex};
use {AntialiasingMode, BQuad, BVertex, CurveIndices, EdgeInstance, Endpoint, LineIndices};
use {Subpath, Vertex};
#[derive(Clone, Copy)]
#[repr(C)]
@ -34,6 +35,28 @@ pub struct Matrix2DF32 {
pub m12: f32,
}
#[derive(Clone, Copy)]
#[repr(C)]
pub struct CoverIndices {
pub interior_indices: *const u32,
pub interior_indices_len: u32,
pub curve_indices: *const u32,
pub curve_indices_len: u32,
}
#[derive(Clone, Copy)]
#[repr(C)]
pub struct EdgeIndices {
pub upper_line_indices: *const LineIndices,
pub upper_line_indices_len: u32,
pub upper_curve_indices: *const CurveIndices,
pub upper_curve_indices_len: u32,
pub lower_line_indices: *const LineIndices,
pub lower_line_indices_len: u32,
pub lower_curve_indices: *const CurveIndices,
pub lower_curve_indices_len: u32,
}
#[no_mangle]
pub unsafe extern fn pf_legalizer_new() -> *mut Legalizer {
let mut legalizer = Box::new(Legalizer::new());
@ -145,13 +168,14 @@ pub unsafe extern fn pf_partitioner_init<'a>(partitioner: *mut Partitioner<'a>,
#[no_mangle]
pub unsafe extern fn pf_partitioner_partition<'a>(partitioner: *mut Partitioner<'a>,
path_id: u32,
first_subpath_index: u32,
last_subpath_index: u32) {
(*partitioner).partition(first_subpath_index, last_subpath_index)
(*partitioner).partition(path_id, first_subpath_index, last_subpath_index)
}
#[no_mangle]
pub unsafe extern fn pf_partitioner_b_quads<'a>(partitioner: *mut Partitioner<'a>,
pub unsafe extern fn pf_partitioner_b_quads<'a>(partitioner: *const Partitioner<'a>,
out_b_quad_count: *mut u32)
-> *const BQuad {
let b_quads = (*partitioner).b_quads();
@ -162,21 +186,45 @@ pub unsafe extern fn pf_partitioner_b_quads<'a>(partitioner: *mut Partitioner<'a
}
#[no_mangle]
pub unsafe extern fn pf_tessellator_new(endpoints: *const Endpoint,
endpoint_count: u32,
control_points: *const Point2D<f32>,
control_point_count: u32,
b_quads: *const BQuad,
b_quad_count: u32,
antialiasing_mode: AntialiasingMode)
-> *mut Tessellator<'static> {
pub unsafe extern fn pf_partitioner_b_vertices<'a>(partitioner: *const Partitioner<'a>,
out_b_vertex_count: *mut u32)
-> *const BVertex {
// FIXME(pcwalton): This is unsafe! `Point2D<f32>` and `Point2DF32` may have different layouts!
let mut tessellator =
Box::new(Tessellator::new(slice::from_raw_parts(endpoints, endpoint_count as usize),
slice::from_raw_parts(control_points,
control_point_count as usize),
slice::from_raw_parts(b_quads, b_quad_count as usize),
antialiasing_mode));
let b_vertices = (*partitioner).b_vertices();
if !out_b_vertex_count.is_null() {
*out_b_vertex_count = b_vertices.len() as u32
}
b_vertices.as_ptr() as *const BVertex
}
#[no_mangle]
pub unsafe extern fn pf_partitioner_cover_indices<'a>(partitioner: *const Partitioner<'a>,
out_cover_indices: *mut CoverIndices) {
let cover_indices = (*partitioner).cover_indices();
(*out_cover_indices).interior_indices = cover_indices.interior_indices.as_ptr();
(*out_cover_indices).interior_indices_len = cover_indices.interior_indices.len() as u32;
(*out_cover_indices).curve_indices = cover_indices.curve_indices.as_ptr();
(*out_cover_indices).curve_indices_len = cover_indices.curve_indices.len() as u32;
}
#[no_mangle]
pub unsafe extern fn pf_partitioner_edge_indices<'a>(partitioner: *const Partitioner<'a>,
out_edge_indices: *mut EdgeIndices) {
let edge_indices = (*partitioner).edge_indices();
(*out_edge_indices).upper_line_indices = edge_indices.upper_line_indices.as_ptr();
(*out_edge_indices).upper_line_indices_len = edge_indices.upper_line_indices.len() as u32;
(*out_edge_indices).upper_curve_indices = edge_indices.upper_curve_indices.as_ptr();
(*out_edge_indices).upper_curve_indices_len = edge_indices.upper_curve_indices.len() as u32;
(*out_edge_indices).lower_line_indices = edge_indices.lower_line_indices.as_ptr();
(*out_edge_indices).lower_line_indices_len = edge_indices.lower_line_indices.len() as u32;
(*out_edge_indices).lower_curve_indices = edge_indices.lower_curve_indices.as_ptr();
(*out_edge_indices).lower_curve_indices_len = edge_indices.lower_curve_indices.len() as u32;
}
#[no_mangle]
pub unsafe extern fn pf_tessellator_new(antialiasing_mode: AntialiasingMode)
-> *mut Tessellator<'static> {
let mut tessellator = Box::new(Tessellator::new(antialiasing_mode));
let tessellator_ptr: *mut Tessellator<'static> = &mut *tessellator;
mem::forget(tessellator);
tessellator_ptr
@ -187,6 +235,21 @@ 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_init<'a>(tessellator: *mut Tessellator<'a>,
b_quads: *const BQuad,
b_quad_count: u32,
b_vertices: *const BVertex,
b_vertex_count: u32,
b_indices: *const u32,
b_index_count: u32) {
// FIXME(pcwalton): This is unsafe! `Point2D<f32>` and `Point2DF32` may have different layouts!
(*tessellator).init(slice::from_raw_parts(b_quads, b_quad_count as usize),
slice::from_raw_parts(b_vertices as *const BVertex,
b_vertex_count as usize),
slice::from_raw_parts(b_indices as *const u32, b_index_count as usize))
}
#[no_mangle]
pub unsafe extern fn pf_tessellator_compute_hull<'a>(tessellator: *mut Tessellator<'a>,
transform: *const Matrix2DF32) {

View File

@ -59,11 +59,11 @@ pub fn line_line_crossing_point(a_p0: &Point2D<f32>,
}
// TODO(pcwalton): Implement this.
pub fn line_quadratic_bezier_crossing_point(_a_p0: &Point2D<f32>,
_a_p1: &Point2D<f32>,
_b_p0: &Point2D<f32>,
_b_p1: &Point2D<f32>,
_b_p2: &Point2D<f32>)
pub fn line_quadratic_bezier_crossing_point(a_p0: &Point2D<f32>,
a_p1: &Point2D<f32>,
b_p0: &Point2D<f32>,
b_p1: &Point2D<f32>,
b_p2: &Point2D<f32>)
-> Option<Point2D<f32>> {
None
}
@ -79,7 +79,7 @@ pub fn quadratic_bezier_quadratic_bezier_crossing_point(_a_p0: &Point2D<f32>,
None
}
fn sample_quadratic_bezier(t: f32, p0: &Point2D<f32>, p1: &Point2D<f32>, p2: &Point2D<f32>)
pub fn sample_quadratic_bezier(t: f32, p0: &Point2D<f32>, p1: &Point2D<f32>, p2: &Point2D<f32>)
-> Point2D<f32> {
p0.lerp(*p1, t).lerp(p1.lerp(*p2, t), t)
}
@ -91,11 +91,22 @@ pub fn sample_quadratic_bezier_deriv(t: f32,
-> Vector2D<f32> {
// https://en.wikipedia.org/wiki/B%C3%A9zier_curve#Quadratic_B.C3.A9zier_curves
// FIXME(pcwalton): Can this be made faster?
return ((*p1 - *p0) * (1.0 - t) + (*p2 - *p1) * t) * 2.0
((*p1 - *p0) * (1.0 - t) + (*p2 - *p1) * t) * 2.0
}
pub fn solve_line_y_for_x(x: f32, a: &Point2D<f32>, b: &Point2D<f32>) -> f32 {
a.lerp(*b, (x - a.x) / (b.x - a.x)).y
pub fn sample_quadratic_bezier_deriv_deriv(p0: &Point2D<f32>, p1: &Point2D<f32>, p2: &Point2D<f32>)
-> Vector2D<f32> {
// https://en.wikipedia.org/wiki/B%C3%A9zier_curve#Quadratic_B.C3.A9zier_curves
// FIXME(pcwalton): Can this be made faster?
(*p2 - *p1 * 2.0 + p0.to_vector()) * 2.0
}
pub fn solve_line_t_for_x(x: f32, a: &Point2D<f32>, b: &Point2D<f32>) -> f32 {
if b.x == a.x {
0.0
} else {
(x - a.x) / (b.x - a.x)
}
}
pub(crate) fn newton_raphson<F, DFDX>(f: F, dfdx: DFDX, mut x_guess: f32) -> f32
@ -148,11 +159,18 @@ pub struct QuadraticBezierInflectionPoints {
impl QuadraticBezierInflectionPoints {
pub fn calculate(p0: &Point2D<f32>, p1: &Point2D<f32>, p2: &Point2D<f32>)
-> QuadraticBezierInflectionPoints {
if ((*p1 - *p0).length() + (*p2 - *p1).length()).abs() < f32::approx_epsilon() {
QuadraticBezierInflectionPoints {
xt: None,
yt: None,
}
} else {
QuadraticBezierInflectionPoints {
xt: quadratic_bezier_axis_inflection_point(p0.x, p1.x, p2.x),
yt: quadratic_bezier_axis_inflection_point(p0.y, p1.y, p2.y),
}
}
}
}
#[derive(Clone, Copy, Debug)]

View File

@ -2,7 +2,7 @@
#![feature(alloc_jemalloc)]
// Needed to work around a problem with `heapsize`
// needed to work around a problem with `heapsize`
extern crate alloc_jemalloc;
extern crate bit_vec;
extern crate env_logger;
@ -23,14 +23,34 @@ pub mod tessellator;
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct BQuad {
pub upper_prev_endpoint: u32,
pub upper_next_endpoint: u32,
pub lower_prev_endpoint: u32,
pub lower_next_endpoint: u32,
pub upper_left_time: f32,
pub upper_right_time: f32,
pub lower_left_time: f32,
pub lower_right_time: f32,
pub upper_left_vertex_index: u32,
pub upper_control_point_vertex_index: u32,
pub upper_right_vertex_index: u32,
pub lower_left_vertex_index: u32,
pub lower_control_point_vertex_index: u32,
pub lower_right_vertex_index: u32,
pad: [u32; 2],
}
impl BQuad {
#[inline]
pub fn new(upper_left_vertex_index: u32,
upper_control_point_vertex_index: u32,
upper_right_vertex_index: u32,
lower_left_vertex_index: u32,
lower_control_point_vertex_index: u32,
lower_right_vertex_index: u32)
-> BQuad {
BQuad {
upper_left_vertex_index: upper_left_vertex_index,
upper_control_point_vertex_index: upper_control_point_vertex_index,
upper_right_vertex_index: upper_right_vertex_index,
lower_left_vertex_index: lower_left_vertex_index,
lower_control_point_vertex_index: lower_control_point_vertex_index,
lower_right_vertex_index: lower_right_vertex_index,
pad: [0; 2],
}
}
}
#[repr(C)]
@ -53,26 +73,128 @@ pub struct Subpath {
#[repr(u8)]
pub enum AntialiasingMode {
Msaa = 0,
Levien = 1,
Ecaa = 1,
}
#[derive(Clone, Copy, PartialEq, Debug)]
#[repr(u8)]
pub enum BVertexKind {
Endpoint0 = 0,
Endpoint1 = 1,
ConvexControlPoint = 2,
ConcaveControlPoint = 3,
}
#[derive(Clone, Copy, Debug)]
#[repr(C)]
pub struct BVertex {
pub position: Point2D<f32>,
pub path_id: u32,
pub tex_coord: [u8; 2],
pub kind: BVertexKind,
pad: u8,
}
impl BVertex {
#[inline]
pub fn new(position: &Point2D<f32>, kind: BVertexKind, path_id: u32) -> BVertex {
let tex_coord = match kind {
BVertexKind::Endpoint0 => [0, 0],
BVertexKind::Endpoint1 => [2, 2],
BVertexKind::ConcaveControlPoint | BVertexKind::ConvexControlPoint => [1, 0],
};
BVertex {
position: *position,
path_id: path_id,
tex_coord: tex_coord,
kind: kind,
pad: 0,
}
}
pub(crate) fn control_point(left_endpoint_position: &Point2D<f32>,
control_point_position: &Point2D<f32>,
right_endpoint_position: &Point2D<f32>,
path_id: u32,
bottom: bool)
-> BVertex {
let control_point_vector = *control_point_position - *left_endpoint_position;
let right_vector = *right_endpoint_position - *left_endpoint_position;
let determinant = right_vector.cross(control_point_vector);
let endpoint_kind = if (determinant < 0.0) ^ bottom {
BVertexKind::ConvexControlPoint
} else {
BVertexKind::ConcaveControlPoint
};
BVertex::new(control_point_position, endpoint_kind, path_id)
}
}
#[derive(Clone, Copy, Debug)]
#[repr(C)]
pub struct Vertex {
pub prev_endpoint_index: u32,
pub next_endpoint_index: u32,
pub left_b_vertex_index: u32,
pub control_point_b_vertex_index: u32,
pub right_b_vertex_index: u32,
pub time: f32,
padding: u32,
pub path_id: u32,
pad: u32,
}
impl Vertex {
#[inline]
pub fn new(prev_endpoint_index: u32, next_endpoint_index: u32, time: f32) -> Vertex {
pub fn new(path_id: u32,
left_b_vertex_index: u32,
control_point_b_vertex_index: u32,
right_b_vertex_index: u32,
time: f32)
-> Vertex {
Vertex {
prev_endpoint_index: prev_endpoint_index,
next_endpoint_index: next_endpoint_index,
path_id: path_id,
left_b_vertex_index: left_b_vertex_index,
control_point_b_vertex_index: control_point_b_vertex_index,
right_b_vertex_index: right_b_vertex_index,
time: time,
padding: 0,
pad: 0,
}
}
}
#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct LineIndices {
pub left_vertex_index: u32,
pub right_vertex_index: u32,
}
impl LineIndices {
#[inline]
pub fn new(left_vertex_index: u32, right_vertex_index: u32) -> LineIndices {
LineIndices {
left_vertex_index: left_vertex_index,
right_vertex_index: right_vertex_index,
}
}
}
#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct CurveIndices {
pub left_vertex_index: u32,
pub right_vertex_index: u32,
pub control_point_vertex_index: u32,
pad: u32,
}
impl CurveIndices {
#[inline]
pub fn new(left_vertex_index: u32, control_point_vertex_index: u32, right_vertex_index: u32)
-> CurveIndices {
CurveIndices {
left_vertex_index: left_vertex_index,
right_vertex_index: right_vertex_index,
control_point_vertex_index: control_point_vertex_index,
pad: 0,
}
}
}
@ -80,21 +202,16 @@ impl Vertex {
#[derive(Clone, Copy, Debug)]
#[repr(C)]
pub struct EdgeInstance {
pub prev_endpoint_index: u32,
pub next_endpoint_index: u32,
pub prev_time: f32,
pub next_time: f32,
pub left_vertex_index: u32,
pub right_vertex_index: u32,
}
impl EdgeInstance {
#[inline]
pub fn new(prev_endpoint_index: u32, next_endpoint_index: u32, prev_time: f32, next_time: f32)
-> EdgeInstance {
pub fn new(left_vertex_index: u32, right_vertex_index: u32) -> EdgeInstance {
EdgeInstance {
prev_endpoint_index: prev_endpoint_index,
next_endpoint_index: next_endpoint_index,
prev_time: prev_time,
next_time: next_time,
left_vertex_index: left_vertex_index,
right_vertex_index: right_vertex_index,
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -3,10 +3,16 @@
#ifndef PARTITIONFINDER_H
#define PARTITIONFINDER_H
#include <limits.h>
#include <stdint.h>
#define PF_ANTIALIASING_MODE_MSAA 0
#define PF_ANTIALIASING_MODE_LEVIEN 1
#define PF_ANTIALIASING_MODE_ECAA 1
#define PF_B_VERTEX_KIND_ENDPOINT_0 0
#define PF_B_VERTEX_KIND_ENDPOINT_1 1
#define PF_B_VERTEX_KIND_CONVEX_CONTROL_POINT 2
#define PF_B_VERTEX_KIND_CONCAVE_CONTROL_POINT 3
#ifdef __cplusplus
extern "C" {
@ -16,6 +22,8 @@ typedef uint8_t pf_antialiasing_mode_t;
typedef uint16_t pf_float16_t;
typedef uint8_t pf_b_vertex_kind_t;
struct pf_point2d_f32 {
float x, y;
};
@ -29,20 +37,69 @@ struct pf_matrix2d_f32 {
typedef struct pf_matrix2d_f32 pf_matrix2d_f32_t;
struct pf_b_vertex {
pf_point2d_f32_t position;
uint32_t path_id;
uint8_t tex_coord[2];
pf_b_vertex_kind_t kind;
uint8_t pad;
};
typedef struct pf_b_vertex pf_b_vertex_t;
struct pf_vertex {
uint32_t prev_endpoint_index;
uint32_t next_endpoint_index;
uint32_t left_b_vertex_index;
uint32_t control_point_b_vertex_index;
uint32_t right_b_vertex_index;
float time;
uint32_t padding;
uint32_t path_id;
uint8_t bottom;
uint8_t pad0, pad1, pad2;
};
typedef struct pf_vertex pf_vertex_t;
struct pf_cover_indices {
const uint32_t *interior_indices;
uint32_t interior_indices_len;
const uint32_t *curve_indices;
uint32_t curve_indices_len;
};
typedef struct pf_cover_indices pf_cover_indices_t;
struct pf_line_indices {
uint32_t left_vertex_index;
uint32_t right_vertex_index;
};
typedef struct pf_line_indices pf_line_indices_t;
struct pf_curve_indices {
uint32_t left_vertex_index;
uint32_t right_vertex_index;
uint32_t control_point_vertex_index;
uint32_t pad;
};
typedef struct pf_curve_indices pf_curve_indices_t;
struct pf_edge_indices {
const pf_line_indices_t *top_line_indices;
uint32_t top_line_indices_len;
const pf_curve_indices_t *top_curve_indices;
uint32_t top_curve_indices_len;
const pf_line_indices_t *bottom_line_indices;
uint32_t bottom_line_indices_len;
const pf_curve_indices_t *bottom_curve_indices;
uint32_t bottom_curve_indices_len;
};
typedef struct pf_edge_indices pf_edge_indices_t;
struct pf_edge_instance {
uint32_t prev_endpoint_index;
uint32_t next_endpoint_index;
float prev_time;
float next_time;
uint32_t left_vertex;
uint32_t right_vertex;
};
typedef struct pf_edge_instance pf_edge_instance_t;
@ -55,10 +112,13 @@ struct pf_quad_tess_levels {
typedef struct pf_quad_tess_levels pf_quad_tess_levels_t;
struct pf_b_quad {
uint32_t upper_prev_endpoint, upper_next_endpoint;
uint32_t lower_prev_endpoint, lower_next_endpoint;
float upper_left_time, upper_right_time;
float lower_left_time, lower_right_time;
uint32_t upper_left_vertex_index;
uint32_t upper_control_point_vertex_index;
uint32_t upper_right_vertex_index;
uint32_t lower_left_vertex_index;
uint32_t lower_control_point_vertex_index;
uint32_t lower_right_vertex_index;
uint32_t pad[2];
};
typedef struct pf_b_quad pf_b_quad_t;
@ -131,22 +191,34 @@ void pf_partitioner_init(pf_partitioner_t *partitioner,
uint32_t subpath_count);
void pf_partitioner_partition(pf_partitioner_t *partitioner,
uint32_t path_id,
uint32_t first_subpath_index,
uint32_t last_subpath_index);
const pf_b_quad_t *pf_partitioner_b_quads(pf_partitioner_t *partitioner,
const pf_b_quad_t *pf_partitioner_b_quads(const pf_partitioner_t *partitioner,
uint32_t *out_b_quad_count);
pf_tessellator_t *pf_tessellator_new(const pf_endpoint_t *endpoints,
uint32_t endpoint_count,
const pf_point2d_f32_t *control_points,
uint32_t control_point_index,
const pf_b_quad_t *b_quads,
uint32_t b_quad_count,
pf_antialiasing_mode_t antialiasing_mode);
const pf_b_vertex_t *pf_partitioner_b_vertices(const pf_partitioner_t *partitioner,
uint32_t *out_b_vertex_count);
const void pf_partitioner_cover_indices(const pf_partitioner_t *partitioner,
pf_cover_indices_t *out_cover_indices);
const void pf_partitioner_edge_indices(const pf_partitioner_t *partitioner,
pf_edge_indices_t *out_edge_indices);
pf_tessellator_t *pf_tessellator_new(pf_antialiasing_mode_t antialiasing_mode);
void pf_tessellator_destroy(pf_tessellator_t *tessellator);
void pf_tessellator_init(pf_tessellator_t *tessellator,
const pf_b_quad_t *b_quads,
uint32_t b_quad_count,
const pf_point2d_f32_t *b_vertices,
uint32_t b_vertex_count,
const uint32_t *b_indices,
uint32_t b_index_count);
void pf_tessellator_compute_hull(pf_tessellator_t *tessellator, const pf_matrix2d_f32_t *transform);
void pf_tessellator_compute_domain(pf_tessellator_t *tessellator);

View File

@ -2,18 +2,18 @@
#![allow(dead_code)]
use euclid::{Length, Point2D, Transform2D};
use euclid::Transform2D;
use half::{f16, self};
use std::cmp;
use std::u32;
use {AntialiasingMode, BQuad, EdgeInstance, Endpoint, Vertex};
use {AntialiasingMode, BQuad, BVertex, EdgeInstance, Vertex};
const TOLERANCE: f32 = 0.25;
pub struct Tessellator<'a> {
endpoints: &'a [Endpoint],
control_points: &'a [Point2D<f32>],
b_quads: &'a [BQuad],
b_vertices: &'a [BVertex],
b_indices: &'a [u32],
antialiasing_mode: AntialiasingMode,
tess_levels: Vec<QuadTessLevels>,
@ -41,42 +41,44 @@ impl QuadTessLevels {
}
impl<'a> Tessellator<'a> {
pub fn new<'b>(endpoints: &'b [Endpoint],
control_points: &'b [Point2D<f32>],
b_quads: &'b [BQuad],
antialiasing_mode: AntialiasingMode)
-> Tessellator<'b> {
pub fn new<'b>(antialiasing_mode: AntialiasingMode) -> Tessellator<'b> {
Tessellator {
endpoints: endpoints,
control_points: control_points,
b_quads: b_quads,
b_quads: &[],
b_vertices: &[],
b_indices: &[],
antialiasing_mode: antialiasing_mode,
tess_levels: vec![QuadTessLevels::new(); b_quads.len()],
tess_levels: vec![],
vertices: vec![],
msaa_indices: vec![],
edge_instances: vec![],
}
}
pub fn init(&mut self, b_quads: &'a [BQuad], b_vertices: &'a [BVertex], b_indices: &'a [u32]) {
self.b_quads = b_quads;
self.b_vertices = b_vertices;
self.b_indices = b_indices;
self.tess_levels = vec![QuadTessLevels::new(); b_quads.len()];
}
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,
for b_quad_index in 0..self.tess_levels.len() {
let b_quad = &self.b_quads[b_quad_index as usize];
let upper_tess_level = tess_level_for_edge(b_quad.upper_left_vertex_index,
b_quad.upper_control_point_vertex_index,
b_quad.upper_right_vertex_index,
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,
self.b_vertices);
let lower_tess_level = tess_level_for_edge(b_quad.lower_left_vertex_index,
b_quad.lower_control_point_vertex_index,
b_quad.lower_right_vertex_index,
transform,
self.endpoints,
self.control_points);
self.b_vertices);
// TODO(pcwalton): Use fewer thin triangles.
let mut tess_levels = &mut self.tess_levels[b_quad_index as usize];
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;
@ -89,27 +91,31 @@ impl<'a> Tessellator<'a> {
// 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()) {
for (b_quad_index, tess_levels) in self.tess_levels.iter().enumerate() {
let b_quad = &self.b_quads[b_quad_index as usize];
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 path_id = self.b_vertices[b_quad.upper_left_vertex_index as usize].path_id;
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())
Vertex::new(path_id,
b_quad.upper_left_vertex_index,
b_quad.upper_control_point_vertex_index,
b_quad.upper_right_vertex_index,
index as f32 / tess_level as f32)
}));
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())
Vertex::new(path_id,
b_quad.lower_left_vertex_index,
b_quad.lower_control_point_vertex_index,
b_quad.lower_right_vertex_index,
index as f32 / tess_level as f32)
}));
// Emit a triangle strip.
@ -125,37 +131,14 @@ impl<'a> Tessellator<'a> {
].into_iter())
}
// If Levien-style antialiasing is in use, then emit edge instances.
if self.antialiasing_mode == AntialiasingMode::Levien {
let upper_left_endpoint_time: Length<f32, ()> = Length::new(b_quad.upper_left_time);
let upper_right_endpoint_time: Length<f32, ()> =
Length::new(b_quad.upper_right_time);
let lower_left_endpoint_time: Length<f32, ()> = Length::new(b_quad.lower_left_time);
let lower_right_endpoint_time: Length<f32, ()> =
Length::new(b_quad.lower_right_time);
// If ECAA is in use, then emit edge instances.
if self.antialiasing_mode == AntialiasingMode::Ecaa {
for index in 0..tess_level {
let left_tess_coord = index as f32 / tess_level as f32;
let right_tess_coord = (index + 1) as f32 / tess_level as f32;
let upper_left_time = upper_left_endpoint_time.lerp(upper_right_endpoint_time,
left_tess_coord).get();
let upper_right_time = upper_left_endpoint_time.lerp(upper_right_endpoint_time,
right_tess_coord).get();
let lower_left_time = lower_left_endpoint_time.lerp(lower_right_endpoint_time,
left_tess_coord).get();
let lower_right_time = lower_left_endpoint_time.lerp(lower_right_endpoint_time,
right_tess_coord).get();
self.edge_instances.extend([
EdgeInstance::new(b_quad.upper_prev_endpoint,
b_quad.upper_next_endpoint,
upper_left_time,
upper_right_time),
EdgeInstance::new(b_quad.lower_prev_endpoint,
b_quad.lower_next_endpoint,
lower_left_time,
lower_right_time),
EdgeInstance::new(first_upper_vertex_index + index + 0,
first_upper_vertex_index + index + 1),
EdgeInstance::new(first_lower_vertex_index + index + 0,
first_lower_vertex_index + index + 1)
].into_iter())
}
}
@ -184,30 +167,25 @@ impl<'a> Tessellator<'a> {
}
// 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,
fn tess_level_for_edge(left_endpoint_index: u32,
control_point_index: u32,
right_endpoint_index: u32,
transform: &Transform2D<f32>,
endpoints: &[Endpoint],
control_points: &[Point2D<f32>])
b_vertices: &[BVertex])
-> u32 {
let control_point_index = endpoints[next_endpoint_index as usize].control_point_index;
if control_point_index == u32::MAX {
return 1
}
let (prev_time, next_time) = (left_time.min(right_time), left_time.max(right_time));
let left_endpoint = &b_vertices[left_endpoint_index as usize].position;
let right_endpoint = &b_vertices[right_endpoint_index as usize].position;
let control_point = &b_vertices[control_point_index as usize].position;
let prev_endpoint = &endpoints[prev_endpoint_index as usize];
let next_endpoint = &endpoints[next_endpoint_index as usize];
let control_point = &control_points[control_point_index as usize];
let p0 = transform.transform_point(&prev_endpoint.position);
let p0 = transform.transform_point(left_endpoint);
let p1 = transform.transform_point(control_point);
let p2 = transform.transform_point(&next_endpoint.position);
let p2 = transform.transform_point(right_endpoint);
// FIXME(pcwalton): Is this good for quadratics?
let length = (p1 - p0).length() + (p2 - p1).length();
1 + (length * TOLERANCE * (next_time - prev_time)) as u32
1 + (length * TOLERANCE) as u32
}