Add window resizing support to the demo

This commit is contained in:
Patrick Walton 2019-02-06 18:09:37 -08:00
parent a6963d5f3b
commit a81850a899
6 changed files with 158 additions and 103 deletions

View File

@ -13,15 +13,16 @@ use euclid::Size2D;
use jemallocator; use jemallocator;
use pathfinder_geometry::basic::point::{Point2DF32, Point3DF32}; use pathfinder_geometry::basic::point::{Point2DF32, Point3DF32};
use pathfinder_geometry::basic::rect::RectF32; use pathfinder_geometry::basic::rect::RectF32;
use pathfinder_geometry::basic::transform2d::Transform2DF32;
use pathfinder_geometry::basic::transform3d::{Perspective, Transform3DF32}; use pathfinder_geometry::basic::transform3d::{Perspective, Transform3DF32};
use pathfinder_gl::renderer::Renderer; use pathfinder_gl::renderer::Renderer;
use pathfinder_renderer::builder::SceneBuilder; use pathfinder_renderer::builder::{RenderOptions, RenderTransform, SceneBuilder};
use pathfinder_renderer::gpu_data::BuiltScene; use pathfinder_renderer::gpu_data::BuiltScene;
use pathfinder_renderer::scene::{BuildTransform, Scene}; use pathfinder_renderer::scene::Scene;
use pathfinder_renderer::z_buffer::ZBuffer; use pathfinder_renderer::z_buffer::ZBuffer;
use pathfinder_svg::SceneExt; use pathfinder_svg::SceneExt;
use rayon::ThreadPoolBuilder; use rayon::ThreadPoolBuilder;
use sdl2::event::Event; use sdl2::event::{Event, WindowEvent};
use sdl2::keyboard::Keycode; use sdl2::keyboard::Keycode;
use sdl2::video::GLProfile; use sdl2::video::GLProfile;
use std::f32::consts::FRAC_PI_4; use std::f32::consts::FRAC_PI_4;
@ -55,6 +56,7 @@ fn main() {
let window = let window =
sdl_video.window("Pathfinder Demo", MAIN_FRAMEBUFFER_WIDTH, MAIN_FRAMEBUFFER_HEIGHT) sdl_video.window("Pathfinder Demo", MAIN_FRAMEBUFFER_WIDTH, MAIN_FRAMEBUFFER_HEIGHT)
.opengl() .opengl()
.resizable()
.allow_highdpi() .allow_highdpi()
.build() .build()
.unwrap(); .unwrap();
@ -66,20 +68,20 @@ fn main() {
let mut exit = false; let mut exit = false;
let (drawable_width, drawable_height) = window.drawable_size(); let (drawable_width, drawable_height) = window.drawable_size();
let mut renderer = Renderer::new(&Size2D::new(drawable_width, drawable_height)); let mut window_size = Size2D::new(drawable_width, drawable_height);
let mut renderer = Renderer::new(&window_size);
let mut camera_position = Point3DF32::new(500.0, 500.0, 3000.0, 1.0); let mut camera_position = Point3DF32::new(500.0, 500.0, 3000.0, 1.0);
let mut camera_velocity = Point3DF32::new(0.0, 0.0, 0.0, 1.0); let mut camera_velocity = Point3DF32::new(0.0, 0.0, 0.0, 1.0);
let (mut camera_yaw, mut camera_pitch) = (0.0, 0.0); let (mut camera_yaw, mut camera_pitch) = (0.0, 0.0);
let window_size = Size2D::new(drawable_width, drawable_height); let base_scene = load_scene(&options);
renderer.debug_renderer.set_framebuffer_size(&window_size);
let base_scene = load_scene(&options, &window_size);
let scene_thread_proxy = SceneThreadProxy::new(base_scene, options.clone()); let scene_thread_proxy = SceneThreadProxy::new(base_scene, options.clone());
scene_thread_proxy.set_window_size(&window_size);
let mut events = vec![]; let mut events = vec![];
let mut first_frame = true; let mut first_frame = true;
let mut mouselook_enabled = false;
while !exit { while !exit {
// Update the scene. // Update the scene.
@ -87,8 +89,8 @@ fn main() {
let rotation = Transform3DF32::from_rotation(-camera_yaw, -camera_pitch, 0.0); let rotation = Transform3DF32::from_rotation(-camera_yaw, -camera_pitch, 0.0);
camera_position = camera_position + rotation.transform_point(camera_velocity); camera_position = camera_position + rotation.transform_point(camera_velocity);
let mut transform = let aspect = window_size.width as f32 / window_size.height as f32;
Transform3DF32::from_perspective(FRAC_PI_4, 4.0 / 3.0, 0.025, 100.0); let mut transform = Transform3DF32::from_perspective(FRAC_PI_4, aspect, 0.025, 100.0);
transform = transform.post_mul(&Transform3DF32::from_scale(1.0 / 800.0, transform = transform.post_mul(&Transform3DF32::from_scale(1.0 / 800.0,
1.0 / 800.0, 1.0 / 800.0,
@ -142,7 +144,14 @@ fn main() {
Event::Quit { .. } | Event::KeyDown { keycode: Some(Keycode::Escape), .. } => { Event::Quit { .. } | Event::KeyDown { keycode: Some(Keycode::Escape), .. } => {
exit = true; exit = true;
} }
Event::MouseMotion { xrel, yrel, .. } => { Event::Window { win_event: WindowEvent::SizeChanged(..), .. } => {
let (window_width, window_height) = window.drawable_size();
window_size = Size2D::new(window_width as u32, window_height as u32);
scene_thread_proxy.set_window_size(&window_size);
renderer.set_main_framebuffer_size(&window_size);
}
Event::MouseButtonDown { .. } => mouselook_enabled = !mouselook_enabled,
Event::MouseMotion { xrel, yrel, .. } if mouselook_enabled => {
camera_yaw += xrel as f32 * MOUSELOOK_ROTATION_SPEED; camera_yaw += xrel as f32 * MOUSELOOK_ROTATION_SPEED;
camera_pitch -= yrel as f32 * MOUSELOOK_ROTATION_SPEED; camera_pitch -= yrel as f32 * MOUSELOOK_ROTATION_SPEED;
} }
@ -192,6 +201,10 @@ impl SceneThreadProxy {
SceneThread::new(scene, scene_to_main_sender, main_to_scene_receiver, options); SceneThread::new(scene, scene_to_main_sender, main_to_scene_receiver, options);
SceneThreadProxy { sender: main_to_scene_sender, receiver: scene_to_main_receiver } SceneThreadProxy { sender: main_to_scene_sender, receiver: scene_to_main_receiver }
} }
fn set_window_size(&self, window_size: &Size2D<u32>) {
self.sender.send(MainToSceneMsg::SetWindowSize(*window_size)).unwrap();
}
} }
struct SceneThread { struct SceneThread {
@ -209,9 +222,14 @@ impl SceneThread {
thread::spawn(move || (SceneThread { scene, sender, receiver, options }).run()); thread::spawn(move || (SceneThread { scene, sender, receiver, options }).run());
} }
fn run(self) { fn run(mut self) {
while let Ok(msg) = self.receiver.recv() { while let Ok(msg) = self.receiver.recv() {
match msg { match msg {
MainToSceneMsg::SetWindowSize(size) => {
self.scene.view_box =
RectF32::new(Point2DF32::default(),
Point2DF32::new(size.width as f32, size.height as f32));
}
MainToSceneMsg::Build(perspective) => { MainToSceneMsg::Build(perspective) => {
let start_time = Instant::now(); let start_time = Instant::now();
let built_scene = build_scene(&self.scene, perspective, &self.options); let built_scene = build_scene(&self.scene, perspective, &self.options);
@ -224,6 +242,7 @@ impl SceneThread {
} }
enum MainToSceneMsg { enum MainToSceneMsg {
SetWindowSize(Size2D<u32>),
Build(Option<Perspective>), Build(Option<Perspective>),
} }
@ -279,40 +298,29 @@ impl Options {
} }
} }
fn load_scene(options: &Options, window_size: &Size2D<u32>) -> Scene { fn load_scene(options: &Options) -> Scene {
// Build scene.
let usvg = Tree::from_file(&options.input_path, &UsvgOptions::default()).unwrap(); let usvg = Tree::from_file(&options.input_path, &UsvgOptions::default()).unwrap();
let scene = Scene::from_tree(usvg);
let mut scene = Scene::from_tree(usvg); println!("Scene bounds: {:?}", scene.bounds);
scene.view_box = println!("{} objects, {} paints", scene.objects.len(), scene.paints.len());
RectF32::new(Point2DF32::default(),
Point2DF32::new(window_size.width as f32, window_size.height as f32));
println!(
"Scene bounds: {:?} View box: {:?}",
scene.bounds, scene.view_box
);
println!(
"{} objects, {} paints",
scene.objects.len(),
scene.paints.len()
);
scene scene
} }
fn build_scene(scene: &Scene, perspective: Option<Perspective>, options: &Options) -> BuiltScene { fn build_scene(scene: &Scene, perspective: Option<Perspective>, options: &Options) -> BuiltScene {
let z_buffer = ZBuffer::new(scene.view_box); let z_buffer = ZBuffer::new(scene.view_box);
let build_transform = match perspective { let render_options = RenderOptions {
None => BuildTransform::None, transform: match perspective {
Some(perspective) => BuildTransform::Perspective(perspective), None => RenderTransform::Transform2D(Transform2DF32::default()),
Some(perspective) => RenderTransform::Perspective(perspective),
},
dilation: Point2DF32::default(),
}; };
let built_objects = panic::catch_unwind(|| { let built_objects = panic::catch_unwind(|| {
match options.jobs { match options.jobs {
Some(1) => scene.build_objects_sequentially(&build_transform, &z_buffer), Some(1) => scene.build_objects_sequentially(render_options, &z_buffer),
_ => scene.build_objects(&build_transform, &z_buffer), _ => scene.build_objects(render_options, &z_buffer),
} }
}); });

View File

@ -122,6 +122,11 @@ impl Point2DF32 {
pub fn yx(&self) -> Point2DF32 { pub fn yx(&self) -> Point2DF32 {
Point2DF32(self.0.yxwz()) Point2DF32(self.0.yxwz())
} }
#[inline]
pub fn is_zero(&self) -> bool {
*self == Point2DF32::default()
}
} }
impl PartialEq for Point2DF32 { impl PartialEq for Point2DF32 {

View File

@ -20,7 +20,7 @@ use pathfinder_simd::default::F32x4;
use std::ops::Sub; use std::ops::Sub;
/// A 2x2 matrix, optimized with SIMD, in column-major order. /// A 2x2 matrix, optimized with SIMD, in column-major order.
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug, PartialEq)]
pub struct Matrix2x2F32(pub F32x4); pub struct Matrix2x2F32(pub F32x4);
impl Default for Matrix2x2F32 { impl Default for Matrix2x2F32 {
@ -93,7 +93,7 @@ impl Sub<Matrix2x2F32> for Matrix2x2F32 {
} }
/// An affine transform, optimized with SIMD. /// An affine transform, optimized with SIMD.
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug, PartialEq)]
pub struct Transform2DF32 { pub struct Transform2DF32 {
// Row-major order. // Row-major order.
matrix: Matrix2x2F32, matrix: Matrix2x2F32,
@ -177,6 +177,11 @@ impl Transform2DF32 {
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 1.0) 0.0, 0.0, 0.0, 1.0)
} }
#[inline]
pub fn is_identity(&self) -> bool {
*self == Transform2DF32::default()
}
} }
/// Transforms a path with a SIMD 2D transform. /// Transforms a path with a SIMD 2D transform.

View File

@ -148,6 +148,11 @@ impl Renderer {
Some(result) Some(result)
} }
pub fn set_main_framebuffer_size(&mut self, new_framebuffer_size: &Size2D<u32>) {
self.main_framebuffer_size = *new_framebuffer_size;
self.debug_renderer.set_framebuffer_size(new_framebuffer_size);
}
fn upload_shaders(&mut self, shaders: &[ObjectShader]) { fn upload_shaders(&mut self, shaders: &[ObjectShader]) {
let size = Size2D::new(FILL_COLORS_TEXTURE_WIDTH, FILL_COLORS_TEXTURE_HEIGHT); let size = Size2D::new(FILL_COLORS_TEXTURE_WIDTH, FILL_COLORS_TEXTURE_HEIGHT);
let mut fill_colors = vec![0; size.width as usize * size.height as usize * 4]; let mut fill_colors = vec![0; size.width as usize * size.height as usize * 4];

View File

@ -15,7 +15,11 @@ use crate::gpu_data::{MaskTileBatchPrimitive, SolidTileScenePrimitive};
use crate::scene; use crate::scene;
use crate::tiles; use crate::tiles;
use crate::z_buffer::ZBuffer; use crate::z_buffer::ZBuffer;
use pathfinder_geometry::basic::point::Point2DF32;
use pathfinder_geometry::basic::rect::{RectF32, RectI32}; use pathfinder_geometry::basic::rect::{RectF32, RectI32};
use pathfinder_geometry::basic::transform2d::Transform2DF32;
use pathfinder_geometry::basic::transform3d::Perspective;
use pathfinder_geometry::clip::PolygonClipper3D;
use std::iter; use std::iter;
use std::u16; use std::u16;
@ -117,3 +121,79 @@ impl SceneBuilder {
} }
} }
} }
#[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> }
}

View File

@ -10,16 +10,13 @@
//! A set of paths to be rendered. //! A set of paths to be rendered.
use crate::builder::{PreparedRenderOptions, PreparedRenderTransform, RenderOptions};
use crate::gpu_data::BuiltObject; use crate::gpu_data::BuiltObject;
use crate::paint::{ObjectShader, Paint, PaintId, ShaderId}; use crate::paint::{ObjectShader, Paint, PaintId, ShaderId};
use crate::tiles::Tiler; use crate::tiles::Tiler;
use crate::z_buffer::ZBuffer; use crate::z_buffer::ZBuffer;
use hashbrown::HashMap; use hashbrown::HashMap;
use pathfinder_geometry::basic::point::Point2DF32;
use pathfinder_geometry::basic::rect::{RectF32, RectI32}; use pathfinder_geometry::basic::rect::{RectF32, RectI32};
use pathfinder_geometry::basic::transform2d::Transform2DF32;
use pathfinder_geometry::basic::transform3d::Perspective;
use pathfinder_geometry::clip::PolygonClipper3D;
use pathfinder_geometry::outline::Outline; use pathfinder_geometry::outline::Outline;
use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator}; use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator};
use std::fmt::{self, Debug, Formatter}; use std::fmt::{self, Debug, Formatter};
@ -66,14 +63,14 @@ impl Scene {
.collect() .collect()
} }
pub fn build_objects_sequentially(&self, build_transform: &BuildTransform, z_buffer: &ZBuffer) pub fn build_objects_sequentially(&self, options: RenderOptions, z_buffer: &ZBuffer)
-> Vec<BuiltObject> { -> Vec<BuiltObject> {
let build_transform = build_transform.prepare(self.bounds); let built_options = options.prepare(self.bounds);
self.objects self.objects
.iter() .iter()
.enumerate() .enumerate()
.map(|(object_index, object)| { .map(|(object_index, object)| {
let outline = self.apply_build_transform(&object.outline, &build_transform); let outline = self.apply_render_options(&object.outline, &built_options);
let mut tiler = Tiler::new( let mut tiler = Tiler::new(
&outline, &outline,
self.view_box, self.view_box,
@ -87,14 +84,13 @@ impl Scene {
.collect() .collect()
} }
pub fn build_objects(&self, build_transform: &BuildTransform, z_buffer: &ZBuffer) pub fn build_objects(&self, options: RenderOptions, z_buffer: &ZBuffer) -> Vec<BuiltObject> {
-> Vec<BuiltObject> { let built_options = options.prepare(self.bounds);
let build_transform = build_transform.prepare(self.bounds);
self.objects self.objects
.par_iter() .par_iter()
.enumerate() .enumerate()
.map(|(object_index, object)| { .map(|(object_index, object)| {
let outline = self.apply_build_transform(&object.outline, &build_transform); let outline = self.apply_render_options(&object.outline, &built_options);
let mut tiler = Tiler::new( let mut tiler = Tiler::new(
&outline, &outline,
self.view_box, self.view_box,
@ -108,24 +104,27 @@ impl Scene {
.collect() .collect()
} }
fn apply_build_transform(&self, outline: &Outline, build_transform: &PreparedBuildTransform) fn apply_render_options(&self, outline: &Outline, options: &PreparedRenderOptions) -> Outline {
-> Outline {
// FIXME(pcwalton): Don't clone? // FIXME(pcwalton): Don't clone?
let mut outline = (*outline).clone(); let mut outline = (*outline).clone();
match *build_transform { match options.transform {
PreparedBuildTransform::Perspective(ref perspective, ref quad) => { PreparedRenderTransform::Perspective { ref perspective, ref clip_polygon } => {
outline.clip_against_polygon(quad); outline.clip_against_polygon(clip_polygon);
outline.apply_perspective(perspective); outline.apply_perspective(perspective);
outline.dilate(Point2DF32::splat(4.0));
} }
PreparedBuildTransform::Transform2D(ref transform) => { PreparedRenderTransform::Transform2D(ref transform) => {
outline.transform(transform); outline.transform(transform);
outline.clip_against_rect(self.view_box); outline.clip_against_rect(self.view_box);
} }
PreparedBuildTransform::None => { PreparedRenderTransform::None => {
outline.clip_against_rect(self.view_box); outline.clip_against_rect(self.view_box);
} }
} }
if !options.dilation.is_zero() {
outline.dilate(options.dilation);
}
outline.prepare_for_tiling(self.view_box); outline.prepare_for_tiling(self.view_box);
outline outline
} }
@ -190,50 +189,3 @@ pub fn scene_tile_index(tile_x: i32, tile_y: i32, tile_rect: RectI32) -> u32 {
(tile_y - tile_rect.min_y()) as u32 * tile_rect.size().x() as u32 (tile_y - tile_rect.min_y()) as u32 * tile_rect.size().x() as u32
+ (tile_x - tile_rect.min_x()) as u32 + (tile_x - tile_rect.min_x()) as u32
} }
pub enum BuildTransform {
None,
Transform2D(Transform2DF32),
Perspective(Perspective),
}
impl BuildTransform {
fn prepare(&self, bounds: RectF32) -> PreparedBuildTransform {
let perspective = match self {
BuildTransform::None => return PreparedBuildTransform::None,
BuildTransform::Transform2D(ref transform) => {
return PreparedBuildTransform::Transform2D(*transform)
}
BuildTransform::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 points = points.into_iter().map(|point| {
inverse_transform.transform_point(point).perspective_divide().to_2d()
}).collect();
PreparedBuildTransform::Perspective(perspective, points)
}
}
enum PreparedBuildTransform {
None,
Transform2D(Transform2DF32),
Perspective(Perspective, Vec<Point2DF32>),
}