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 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<u32>) {
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<u32>),
Build(Option<Perspective>),
}
@ -279,40 +298,29 @@ impl Options {
}
}
fn load_scene(options: &Options, window_size: &Size2D<u32>) -> 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<Perspective>, 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),
}
});

View File

@ -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 {

View File

@ -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<Matrix2x2F32> 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.

View File

@ -148,6 +148,11 @@ impl Renderer {
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]) {
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];

View File

@ -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<Point2DF32> }
}

View File

@ -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<BuiltObject> {
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<BuiltObject> {
let build_transform = build_transform.prepare(self.bounds);
pub fn build_objects(&self, options: RenderOptions, z_buffer: &ZBuffer) -> Vec<BuiltObject> {
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<Point2DF32>),
}