From a81850a899d0641e17631cbe8897c629f8f91449 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Wed, 6 Feb 2019 18:09:37 -0800 Subject: [PATCH] Add window resizing support to the demo --- demo3/src/main.rs | 78 +++++++++++++++------------- geometry/src/basic/point.rs | 5 ++ geometry/src/basic/transform2d.rs | 9 +++- gl/src/renderer.rs | 5 ++ renderer/src/builder.rs | 80 +++++++++++++++++++++++++++++ renderer/src/scene.rs | 84 +++++++------------------------ 6 files changed, 158 insertions(+), 103 deletions(-) diff --git a/demo3/src/main.rs b/demo3/src/main.rs index 80a33640..fc55c26c 100644 --- a/demo3/src/main.rs +++ b/demo3/src/main.rs @@ -13,15 +13,16 @@ use euclid::Size2D; use jemallocator; use pathfinder_geometry::basic::point::{Point2DF32, Point3DF32}; use pathfinder_geometry::basic::rect::RectF32; +use pathfinder_geometry::basic::transform2d::Transform2DF32; use pathfinder_geometry::basic::transform3d::{Perspective, Transform3DF32}; 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::scene::{BuildTransform, Scene}; +use pathfinder_renderer::scene::Scene; use pathfinder_renderer::z_buffer::ZBuffer; use pathfinder_svg::SceneExt; use rayon::ThreadPoolBuilder; -use sdl2::event::Event; +use sdl2::event::{Event, WindowEvent}; use sdl2::keyboard::Keycode; use sdl2::video::GLProfile; use std::f32::consts::FRAC_PI_4; @@ -55,6 +56,7 @@ fn main() { let window = sdl_video.window("Pathfinder Demo", MAIN_FRAMEBUFFER_WIDTH, MAIN_FRAMEBUFFER_HEIGHT) .opengl() + .resizable() .allow_highdpi() .build() .unwrap(); @@ -66,20 +68,20 @@ fn main() { let mut exit = false; 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_velocity = Point3DF32::new(0.0, 0.0, 0.0, 1.0); let (mut camera_yaw, mut camera_pitch) = (0.0, 0.0); - let window_size = Size2D::new(drawable_width, drawable_height); - renderer.debug_renderer.set_framebuffer_size(&window_size); - - let base_scene = load_scene(&options, &window_size); + let base_scene = load_scene(&options); let scene_thread_proxy = SceneThreadProxy::new(base_scene, options.clone()); + scene_thread_proxy.set_window_size(&window_size); let mut events = vec![]; let mut first_frame = true; + let mut mouselook_enabled = false; while !exit { // Update the scene. @@ -87,8 +89,8 @@ fn main() { let rotation = Transform3DF32::from_rotation(-camera_yaw, -camera_pitch, 0.0); camera_position = camera_position + rotation.transform_point(camera_velocity); - let mut transform = - Transform3DF32::from_perspective(FRAC_PI_4, 4.0 / 3.0, 0.025, 100.0); + let aspect = window_size.width as f32 / window_size.height as f32; + 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, 1.0 / 800.0, @@ -142,7 +144,14 @@ fn main() { Event::Quit { .. } | Event::KeyDown { keycode: Some(Keycode::Escape), .. } => { 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_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); SceneThreadProxy { sender: main_to_scene_sender, receiver: scene_to_main_receiver } } + + fn set_window_size(&self, window_size: &Size2D) { + self.sender.send(MainToSceneMsg::SetWindowSize(*window_size)).unwrap(); + } } struct SceneThread { @@ -209,9 +222,14 @@ impl SceneThread { thread::spawn(move || (SceneThread { scene, sender, receiver, options }).run()); } - fn run(self) { + fn run(mut self) { while let Ok(msg) = self.receiver.recv() { 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) => { let start_time = Instant::now(); let built_scene = build_scene(&self.scene, perspective, &self.options); @@ -224,6 +242,7 @@ impl SceneThread { } enum MainToSceneMsg { + SetWindowSize(Size2D), Build(Option), } @@ -279,40 +298,29 @@ impl Options { } } -fn load_scene(options: &Options, window_size: &Size2D) -> Scene { - // Build scene. +fn load_scene(options: &Options) -> Scene { let usvg = Tree::from_file(&options.input_path, &UsvgOptions::default()).unwrap(); - - let mut scene = Scene::from_tree(usvg); - scene.view_box = - 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() - ); - + let scene = Scene::from_tree(usvg); + println!("Scene bounds: {:?}", scene.bounds); + println!("{} objects, {} paints", scene.objects.len(), scene.paints.len()); scene } fn build_scene(scene: &Scene, perspective: Option, options: &Options) -> BuiltScene { let z_buffer = ZBuffer::new(scene.view_box); - let build_transform = match perspective { - None => BuildTransform::None, - Some(perspective) => BuildTransform::Perspective(perspective), + let render_options = RenderOptions { + transform: match perspective { + None => RenderTransform::Transform2D(Transform2DF32::default()), + Some(perspective) => RenderTransform::Perspective(perspective), + }, + dilation: Point2DF32::default(), }; let built_objects = panic::catch_unwind(|| { match options.jobs { - Some(1) => scene.build_objects_sequentially(&build_transform, &z_buffer), - _ => scene.build_objects(&build_transform, &z_buffer), + Some(1) => scene.build_objects_sequentially(render_options, &z_buffer), + _ => scene.build_objects(render_options, &z_buffer), } }); diff --git a/geometry/src/basic/point.rs b/geometry/src/basic/point.rs index b59c8234..692a77ea 100644 --- a/geometry/src/basic/point.rs +++ b/geometry/src/basic/point.rs @@ -122,6 +122,11 @@ impl Point2DF32 { pub fn yx(&self) -> Point2DF32 { Point2DF32(self.0.yxwz()) } + + #[inline] + pub fn is_zero(&self) -> bool { + *self == Point2DF32::default() + } } impl PartialEq for Point2DF32 { diff --git a/geometry/src/basic/transform2d.rs b/geometry/src/basic/transform2d.rs index d8a3438d..62fc9b22 100644 --- a/geometry/src/basic/transform2d.rs +++ b/geometry/src/basic/transform2d.rs @@ -20,7 +20,7 @@ use pathfinder_simd::default::F32x4; use std::ops::Sub; /// A 2x2 matrix, optimized with SIMD, in column-major order. -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, PartialEq)] pub struct Matrix2x2F32(pub F32x4); impl Default for Matrix2x2F32 { @@ -93,7 +93,7 @@ impl Sub for Matrix2x2F32 { } /// An affine transform, optimized with SIMD. -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, PartialEq)] pub struct Transform2DF32 { // Row-major order. matrix: Matrix2x2F32, @@ -177,6 +177,11 @@ impl Transform2DF32 { 0.0, 0.0, 0.0, 0.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. diff --git a/gl/src/renderer.rs b/gl/src/renderer.rs index debf2ec4..1dd0b020 100644 --- a/gl/src/renderer.rs +++ b/gl/src/renderer.rs @@ -148,6 +148,11 @@ impl Renderer { Some(result) } + pub fn set_main_framebuffer_size(&mut self, new_framebuffer_size: &Size2D) { + self.main_framebuffer_size = *new_framebuffer_size; + self.debug_renderer.set_framebuffer_size(new_framebuffer_size); + } + fn upload_shaders(&mut self, shaders: &[ObjectShader]) { 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]; diff --git a/renderer/src/builder.rs b/renderer/src/builder.rs index 5ec9f2d8..c3688116 100644 --- a/renderer/src/builder.rs +++ b/renderer/src/builder.rs @@ -15,7 +15,11 @@ use crate::gpu_data::{MaskTileBatchPrimitive, SolidTileScenePrimitive}; use crate::scene; use crate::tiles; use crate::z_buffer::ZBuffer; +use pathfinder_geometry::basic::point::Point2DF32; 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::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 } +} diff --git a/renderer/src/scene.rs b/renderer/src/scene.rs index 7b76a5b1..7c2c67e9 100644 --- a/renderer/src/scene.rs +++ b/renderer/src/scene.rs @@ -10,16 +10,13 @@ //! A set of paths to be rendered. +use crate::builder::{PreparedRenderOptions, PreparedRenderTransform, RenderOptions}; use crate::gpu_data::BuiltObject; use crate::paint::{ObjectShader, Paint, PaintId, ShaderId}; use crate::tiles::Tiler; use crate::z_buffer::ZBuffer; use hashbrown::HashMap; -use pathfinder_geometry::basic::point::Point2DF32; 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 rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator}; use std::fmt::{self, Debug, Formatter}; @@ -66,14 +63,14 @@ impl Scene { .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 { - let build_transform = build_transform.prepare(self.bounds); + let built_options = options.prepare(self.bounds); self.objects .iter() .enumerate() .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( &outline, self.view_box, @@ -87,14 +84,13 @@ impl Scene { .collect() } - pub fn build_objects(&self, build_transform: &BuildTransform, z_buffer: &ZBuffer) - -> Vec { - let build_transform = build_transform.prepare(self.bounds); + pub fn build_objects(&self, options: RenderOptions, z_buffer: &ZBuffer) -> Vec { + let built_options = options.prepare(self.bounds); self.objects .par_iter() .enumerate() .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( &outline, self.view_box, @@ -108,24 +104,27 @@ impl Scene { .collect() } - fn apply_build_transform(&self, outline: &Outline, build_transform: &PreparedBuildTransform) - -> Outline { + fn apply_render_options(&self, outline: &Outline, options: &PreparedRenderOptions) -> Outline { // FIXME(pcwalton): Don't clone? let mut outline = (*outline).clone(); - match *build_transform { - PreparedBuildTransform::Perspective(ref perspective, ref quad) => { - outline.clip_against_polygon(quad); + match options.transform { + PreparedRenderTransform::Perspective { ref perspective, ref clip_polygon } => { + outline.clip_against_polygon(clip_polygon); outline.apply_perspective(perspective); - outline.dilate(Point2DF32::splat(4.0)); } - PreparedBuildTransform::Transform2D(ref transform) => { + PreparedRenderTransform::Transform2D(ref transform) => { outline.transform(transform); outline.clip_against_rect(self.view_box); } - PreparedBuildTransform::None => { + PreparedRenderTransform::None => { outline.clip_against_rect(self.view_box); } } + + if !options.dilation.is_zero() { + outline.dilate(options.dilation); + } + outline.prepare_for_tiling(self.view_box); 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_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), -}