Refactor the demo some more.

Move rendering code into its own module and split up `finish_drawing_frame()`.
This commit is contained in:
Patrick Walton 2019-05-02 17:57:12 -07:00
parent 29b137c81d
commit 4a4011d303
2 changed files with 389 additions and 349 deletions

View File

@ -17,22 +17,18 @@ use crate::camera::{Camera, Mode};
use crate::concurrent::DemoExecutor;
use crate::device::{GroundProgram, GroundVertexArray};
use crate::ui::{DemoUI, UIAction};
use crate::window::{Event, Keycode, SVGPath, View, Window, WindowSize};
use crate::window::{Event, Keycode, SVGPath, Window, WindowSize};
use clap::{App, Arg};
use image::ColorType;
use pathfinder_geometry::basic::point::{Point2DF32, Point2DI32};
use pathfinder_geometry::basic::rect::RectF32;
use pathfinder_geometry::basic::transform3d::Transform3DF32;
use pathfinder_geometry::color::{ColorF, ColorU};
use pathfinder_geometry::color::ColorU;
use pathfinder_gl::GLDevice;
use pathfinder_gpu::Device;
use pathfinder_gpu::resources::ResourceLoader;
use pathfinder_gpu::{ClearParams, DepthFunc, DepthState, Device, Primitive, RenderState};
use pathfinder_gpu::{TextureFormat, UniformData};
use pathfinder_renderer::concurrent::scene_proxy::{RenderCommandStream, SceneProxy};
use pathfinder_renderer::gpu::renderer::{DestFramebuffer, RenderMode, RenderStats, Renderer};
use pathfinder_renderer::gpu_data::RenderCommand;
use pathfinder_renderer::gpu::renderer::{DestFramebuffer, RenderStats, Renderer};
use pathfinder_renderer::options::{RenderOptions, RenderTransform};
use pathfinder_renderer::post::{DEFRINGING_KERNEL_CORE_GRAPHICS, STEM_DARKENING_FACTORS};
use pathfinder_renderer::post::STEM_DARKENING_FACTORS;
use pathfinder_renderer::scene::Scene;
use pathfinder_svg::BuiltSVG;
use pathfinder_ui::{MousePosition, UIEvent};
@ -72,30 +68,16 @@ const TRANSPARENT_BG_COLOR: ColorU = ColorU {
a: 0,
};
const GROUND_SOLID_COLOR: ColorU = ColorU {
r: 80,
g: 80,
b: 80,
a: 255,
};
const GROUND_LINE_COLOR: ColorU = ColorU {
r: 127,
g: 127,
b: 127,
a: 255,
};
const APPROX_FONT_SIZE: f32 = 16.0;
const MESSAGE_TIMEOUT_SECS: u64 = 5;
pub const GRIDLINE_COUNT: i32 = 10;
pub mod window;
mod camera;
mod concurrent;
mod device;
mod renderer;
mod ui;
pub struct DemoApp<W> where W: Window {
@ -224,61 +206,8 @@ impl<W> DemoApp<W> where W: Window {
let transform = self.render_transform.clone().unwrap();
self.current_frame = Some(Frame::new(transform, ui_events));
// Initialize and set the appropriate framebuffer.
let view = self.ui.mode.view(0);
self.window.make_current(view);
let window_size = self.window_size.device_size();
let scene_count = match self.camera.mode() {
Mode::VR => {
let viewport = self.window.viewport(View::Stereo(0));
if self.scene_framebuffer.is_none()
|| self.renderer.device.texture_size(
&self
.renderer
.device
.framebuffer_texture(self.scene_framebuffer.as_ref().unwrap()),
) != viewport.size()
{
let scene_texture = self
.renderer
.device
.create_texture(TextureFormat::RGBA8, viewport.size());
self.scene_framebuffer =
Some(self.renderer.device.create_framebuffer(scene_texture));
}
self.renderer
.replace_dest_framebuffer(DestFramebuffer::Other(
self.scene_framebuffer.take().unwrap(),
));
2
}
_ => {
self.renderer
.replace_dest_framebuffer(DestFramebuffer::Default {
viewport: self.window.viewport(View::Mono),
window_size,
});
1
}
};
// Begin drawing the scene.
self.renderer.bind_dest_framebuffer();
// Clear to the appropriate color.
let clear_color = if scene_count == 2 {
ColorF::transparent_black()
} else {
self.background_color().to_f32()
};
self.renderer.device.clear(&ClearParams {
color: Some(clear_color),
depth: Some(1.0),
stencil: Some(0),
..ClearParams::default()
});
scene_count
// Prepare to render the frame.
self.prepare_frame_rendering()
}
fn build_scene(&mut self) {
@ -501,155 +430,12 @@ impl<W> DemoApp<W> where W: Window {
MousePosition { absolute, relative }
}
pub fn draw_scene(&mut self) {
let view = self.ui.mode.view(0);
self.window.make_current(view);
if self.camera.mode() != Mode::VR {
self.draw_environment();
}
self.render_vector_scene();
// Reattach default framebuffer.
if self.camera.mode() != Mode::VR {
return;
}
if let DestFramebuffer::Other(scene_framebuffer) =
self.renderer
.replace_dest_framebuffer(DestFramebuffer::Default {
viewport: self.window.viewport(View::Mono),
window_size: self.window_size.device_size(),
})
{
self.scene_framebuffer = Some(scene_framebuffer);
}
}
pub fn composite_scene(&mut self, render_scene_index: u32) {
let (eye_transforms, scene_transform, modelview_transform) = match self.camera {
Camera::ThreeD {
ref eye_transforms,
ref scene_transform,
ref modelview_transform,
..
} if eye_transforms.len() > 1 => (eye_transforms, scene_transform, modelview_transform),
_ => return,
};
debug!(
"scene_transform.perspective={:?}",
scene_transform.perspective
);
debug!(
"scene_transform.modelview_to_eye={:?}",
scene_transform.modelview_to_eye
);
debug!("modelview transform={:?}", modelview_transform);
let viewport = self.window.viewport(View::Stereo(render_scene_index));
self.renderer
.replace_dest_framebuffer(DestFramebuffer::Default {
viewport,
window_size: self.window_size.device_size(),
});
self.renderer.bind_draw_framebuffer();
self.renderer.device.clear(&ClearParams {
color: Some(self.background_color().to_f32()),
depth: Some(1.0),
stencil: Some(0),
rect: Some(viewport),
});
self.draw_environment();
let scene_framebuffer = self.scene_framebuffer.as_ref().unwrap();
let scene_texture = self.renderer.device.framebuffer_texture(scene_framebuffer);
let quad_scale_transform = Transform3DF32::from_scale(
self.scene_metadata.view_box.size().x(),
self.scene_metadata.view_box.size().y(),
1.0,
);
let scene_transform_matrix = scene_transform
.perspective
.post_mul(&scene_transform.modelview_to_eye)
.post_mul(&modelview_transform.to_transform())
.post_mul(&quad_scale_transform);
let eye_transform = &eye_transforms[render_scene_index as usize];
let eye_transform_matrix = eye_transform
.perspective
.post_mul(&eye_transform.modelview_to_eye)
.post_mul(&modelview_transform.to_transform())
.post_mul(&quad_scale_transform);
debug!(
"eye transform({}).modelview_to_eye={:?}",
render_scene_index, eye_transform.modelview_to_eye
);
debug!(
"eye transform_matrix({})={:?}",
render_scene_index, eye_transform_matrix
);
debug!("---");
self.renderer.reproject_texture(
scene_texture,
&scene_transform_matrix.transform,
&eye_transform_matrix.transform,
);
}
pub fn finish_drawing_frame(&mut self) {
if let Some(rendering_time) = self.renderer.shift_timer_query() {
self.current_frame
.as_mut()
.unwrap()
.scene_rendering_times
.push(rendering_time);
}
let build_time = self.build_time.unwrap();
let mut frame = self.current_frame.take().unwrap();
if self.pending_screenshot_path.is_some() {
self.take_screenshot();
}
if !frame.scene_stats.is_empty() || !frame.scene_rendering_times.is_empty() {
let zero = RenderStats::default();
let aggregate_stats = frame.scene_stats.iter().fold(zero, |sum, item| sum + *item);
let total_rendering_time = if frame.scene_rendering_times.is_empty() {
None
} else {
let zero = Duration::new(0, 0);
Some(
frame
.scene_rendering_times
.iter()
.fold(zero, |sum, item| sum + *item),
)
};
self.renderer
.debug_ui
.add_sample(aggregate_stats, build_time, total_rendering_time);
}
if self.options.ui != UIVisibility::None {
let viewport = self.window.viewport(View::Mono);
self.window.make_current(View::Mono);
self.renderer
.replace_dest_framebuffer(DestFramebuffer::Default {
viewport,
window_size: self.window_size.device_size(),
});
self.renderer.draw_debug_ui();
}
self.maybe_take_screenshot();
self.update_stats();
self.draw_debug_ui();
let frame = self.current_frame.take().unwrap();
for ui_event in &frame.ui_events {
self.dirty = true;
self.renderer.debug_ui.ui.event_queue.push(*ui_event);
@ -670,8 +456,46 @@ impl<W> DemoApp<W> where W: Window {
&mut ui_action,
);
}
self.handle_ui_events(frame, &mut ui_action);
self.window.present();
self.frame_counter += 1;
}
fn update_stats(&mut self) {
let frame = self.current_frame.as_mut().unwrap();
if let Some(rendering_time) = self.renderer.shift_timer_query() {
frame.scene_rendering_times.push(rendering_time);
}
if frame.scene_stats.is_empty() && frame.scene_rendering_times.is_empty() {
return
}
let build_time = self.build_time.unwrap();
let zero = RenderStats::default();
let aggregate_stats = frame.scene_stats.iter().fold(zero, |sum, item| sum + *item);
let total_rendering_time = if frame.scene_rendering_times.is_empty() {
None
} else {
let zero = Duration::new(0, 0);
Some(
frame
.scene_rendering_times
.iter()
.fold(zero, |sum, item| sum + *item),
)
};
self.renderer.debug_ui.add_sample(aggregate_stats, build_time, total_rendering_time);
}
fn handle_ui_events(&mut self, mut frame: Frame, ui_action: &mut UIAction) {
frame.ui_events = self.renderer.debug_ui.ui.event_queue.drain();
self.handle_ui_action(&mut ui_action);
self.handle_ui_action(ui_action);
// Switch camera mode (2D/3D) if requested.
//
@ -695,111 +519,6 @@ impl<W> DemoApp<W> where W: Window {
_ => {}
}
}
self.window.present();
self.frame_counter += 1;
}
fn draw_environment(&self) {
// TODO(pcwalton): Use the viewport index!
let frame = &self.current_frame.as_ref().unwrap();
let perspective = match frame.transform {
RenderTransform::Transform2D(..) => return,
RenderTransform::Perspective(perspective) => perspective,
};
if self.ui.background_color == BackgroundColor::Transparent {
return;
}
let ground_scale = self.scene_metadata.view_box.max_x() * 2.0;
let mut base_transform = perspective.transform;
base_transform = base_transform.post_mul(&Transform3DF32::from_translation(
-0.5 * self.scene_metadata.view_box.max_x(),
self.scene_metadata.view_box.max_y(),
-0.5 * ground_scale,
));
// Fill ground.
let mut transform = base_transform;
transform =
transform.post_mul(&Transform3DF32::from_scale(ground_scale, 1.0, ground_scale));
let device = &self.renderer.device;
device.bind_vertex_array(&self.ground_vertex_array.vertex_array);
device.use_program(&self.ground_program.program);
device.set_uniform(
&self.ground_program.transform_uniform,
UniformData::from_transform_3d(&transform),
);
device.set_uniform(
&self.ground_program.ground_color_uniform,
UniformData::Vec4(GROUND_SOLID_COLOR.to_f32().0),
);
device.set_uniform(
&self.ground_program.gridline_color_uniform,
UniformData::Vec4(GROUND_LINE_COLOR.to_f32().0),
);
device.set_uniform(&self.ground_program.gridline_count_uniform,
UniformData::Int(GRIDLINE_COUNT));
device.draw_arrays(
Primitive::TriangleFan,
4,
&RenderState {
depth: Some(DepthState {
func: DepthFunc::Less,
write: true,
}),
stencil: None,
..RenderState::default()
},
);
}
fn render_vector_scene(&mut self) {
match self.scene_metadata.monochrome_color {
None => self.renderer.set_render_mode(RenderMode::Multicolor),
Some(fg_color) => {
self.renderer.set_render_mode(RenderMode::Monochrome {
fg_color: fg_color.to_f32(),
bg_color: self.background_color().to_f32(),
gamma_correction: self.ui.gamma_correction_effect_enabled,
defringing_kernel: if self.ui.subpixel_aa_effect_enabled {
// TODO(pcwalton): Select FreeType defringing kernel as necessary.
Some(DEFRINGING_KERNEL_CORE_GRAPHICS)
} else {
None
},
})
}
}
if self.ui.mode == Mode::TwoD {
self.renderer.disable_depth();
} else {
self.renderer.enable_depth();
}
self.renderer.begin_scene();
// Issue render commands!
for command in self.render_command_stream.as_mut().unwrap() {
self.renderer.render_command(&command);
if let RenderCommand::Finish { build_time } = command {
self.build_time = Some(build_time);
}
}
self.current_frame
.as_mut()
.unwrap()
.scene_stats
.push(self.renderer.stats);
self.renderer.end_scene();
}
fn handle_ui_action(&mut self, ui_action: &mut UIAction) {
@ -845,23 +564,6 @@ impl<W> DemoApp<W> where W: Window {
}
}
fn take_screenshot(&mut self) {
let screenshot_path = self.pending_screenshot_path.take().unwrap();
let drawable_size = self.window_size.device_size();
let pixels = self
.renderer
.device
.read_pixels_from_default_framebuffer(drawable_size);
image::save_buffer(
screenshot_path,
&pixels,
drawable_size.x() as u32,
drawable_size.y() as u32,
ColorType::RGBA(8),
)
.unwrap();
}
fn background_color(&self) -> ColorU {
match self.ui.background_color {
BackgroundColor::Light => LIGHT_BG_COLOR,

338
demo/common/src/renderer.rs Normal file
View File

@ -0,0 +1,338 @@
// pathfinder/demo/common/src/renderer.rs
//
// Copyright © 2019 The Pathfinder Project Developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Rendering functionality for the demo.
use crate::camera::{Camera, Mode};
use crate::window::{View, Window};
use crate::{BackgroundColor, DemoApp, UIVisibility};
use image::ColorType;
use pathfinder_geometry::color::{ColorF, ColorU};
use pathfinder_gpu::{ClearParams, DepthFunc, DepthState, Device, Primitive, RenderState};
use pathfinder_gpu::{TextureFormat, UniformData};
use pathfinder_geometry::basic::transform3d::Transform3DF32;
use pathfinder_renderer::gpu::renderer::{DestFramebuffer, RenderMode};
use pathfinder_renderer::gpu_data::RenderCommand;
use pathfinder_renderer::options::RenderTransform;
use pathfinder_renderer::post::DEFRINGING_KERNEL_CORE_GRAPHICS;
const GROUND_SOLID_COLOR: ColorU = ColorU {
r: 80,
g: 80,
b: 80,
a: 255,
};
const GROUND_LINE_COLOR: ColorU = ColorU {
r: 127,
g: 127,
b: 127,
a: 255,
};
const GRIDLINE_COUNT: i32 = 10;
impl<W> DemoApp<W> where W: Window {
pub fn prepare_frame_rendering(&mut self) -> u32 {
// Make the GL context current.
let view = self.ui.mode.view(0);
self.window.make_current(view);
// Set up framebuffers.
let window_size = self.window_size.device_size();
let scene_count = match self.camera.mode() {
Mode::VR => {
let viewport = self.window.viewport(View::Stereo(0));
if self.scene_framebuffer.is_none()
|| self.renderer.device.texture_size(
&self
.renderer
.device
.framebuffer_texture(self.scene_framebuffer.as_ref().unwrap()),
) != viewport.size()
{
let scene_texture = self
.renderer
.device
.create_texture(TextureFormat::RGBA8, viewport.size());
self.scene_framebuffer =
Some(self.renderer.device.create_framebuffer(scene_texture));
}
self.renderer
.replace_dest_framebuffer(DestFramebuffer::Other(
self.scene_framebuffer.take().unwrap(),
));
2
}
_ => {
self.renderer
.replace_dest_framebuffer(DestFramebuffer::Default {
viewport: self.window.viewport(View::Mono),
window_size,
});
1
}
};
// Begin drawing the scene.
self.renderer.bind_dest_framebuffer();
// Clear to the appropriate color.
let clear_color = if scene_count == 2 {
ColorF::transparent_black()
} else {
self.background_color().to_f32()
};
self.renderer.device.clear(&ClearParams {
color: Some(clear_color),
depth: Some(1.0),
stencil: Some(0),
..ClearParams::default()
});
scene_count
}
pub fn draw_scene(&mut self) {
let view = self.ui.mode.view(0);
self.window.make_current(view);
if self.camera.mode() != Mode::VR {
self.draw_environment();
}
self.render_vector_scene();
// Reattach default framebuffer.
if self.camera.mode() != Mode::VR {
return;
}
if let DestFramebuffer::Other(scene_framebuffer) =
self.renderer
.replace_dest_framebuffer(DestFramebuffer::Default {
viewport: self.window.viewport(View::Mono),
window_size: self.window_size.device_size(),
})
{
self.scene_framebuffer = Some(scene_framebuffer);
}
}
pub fn composite_scene(&mut self, render_scene_index: u32) {
let (eye_transforms, scene_transform, modelview_transform) = match self.camera {
Camera::ThreeD {
ref eye_transforms,
ref scene_transform,
ref modelview_transform,
..
} if eye_transforms.len() > 1 => (eye_transforms, scene_transform, modelview_transform),
_ => return,
};
debug!(
"scene_transform.perspective={:?}",
scene_transform.perspective
);
debug!(
"scene_transform.modelview_to_eye={:?}",
scene_transform.modelview_to_eye
);
debug!("modelview transform={:?}", modelview_transform);
let viewport = self.window.viewport(View::Stereo(render_scene_index));
self.renderer
.replace_dest_framebuffer(DestFramebuffer::Default {
viewport,
window_size: self.window_size.device_size(),
});
self.renderer.bind_draw_framebuffer();
self.renderer.device.clear(&ClearParams {
color: Some(self.background_color().to_f32()),
depth: Some(1.0),
stencil: Some(0),
rect: Some(viewport),
});
self.draw_environment();
let scene_framebuffer = self.scene_framebuffer.as_ref().unwrap();
let scene_texture = self.renderer.device.framebuffer_texture(scene_framebuffer);
let quad_scale_transform = Transform3DF32::from_scale(
self.scene_metadata.view_box.size().x(),
self.scene_metadata.view_box.size().y(),
1.0,
);
let scene_transform_matrix = scene_transform
.perspective
.post_mul(&scene_transform.modelview_to_eye)
.post_mul(&modelview_transform.to_transform())
.post_mul(&quad_scale_transform);
let eye_transform = &eye_transforms[render_scene_index as usize];
let eye_transform_matrix = eye_transform
.perspective
.post_mul(&eye_transform.modelview_to_eye)
.post_mul(&modelview_transform.to_transform())
.post_mul(&quad_scale_transform);
debug!(
"eye transform({}).modelview_to_eye={:?}",
render_scene_index, eye_transform.modelview_to_eye
);
debug!(
"eye transform_matrix({})={:?}",
render_scene_index, eye_transform_matrix
);
debug!("---");
self.renderer.reproject_texture(
scene_texture,
&scene_transform_matrix.transform,
&eye_transform_matrix.transform,
);
}
// Draws the ground, if applicable.
fn draw_environment(&self) {
let frame = &self.current_frame.as_ref().unwrap();
let perspective = match frame.transform {
RenderTransform::Transform2D(..) => return,
RenderTransform::Perspective(perspective) => perspective,
};
if self.ui.background_color == BackgroundColor::Transparent {
return;
}
let ground_scale = self.scene_metadata.view_box.max_x() * 2.0;
let mut base_transform = perspective.transform;
base_transform = base_transform.post_mul(&Transform3DF32::from_translation(
-0.5 * self.scene_metadata.view_box.max_x(),
self.scene_metadata.view_box.max_y(),
-0.5 * ground_scale,
));
// Fill ground.
let mut transform = base_transform;
transform =
transform.post_mul(&Transform3DF32::from_scale(ground_scale, 1.0, ground_scale));
let device = &self.renderer.device;
device.bind_vertex_array(&self.ground_vertex_array.vertex_array);
device.use_program(&self.ground_program.program);
device.set_uniform(
&self.ground_program.transform_uniform,
UniformData::from_transform_3d(&transform),
);
device.set_uniform(
&self.ground_program.ground_color_uniform,
UniformData::Vec4(GROUND_SOLID_COLOR.to_f32().0),
);
device.set_uniform(
&self.ground_program.gridline_color_uniform,
UniformData::Vec4(GROUND_LINE_COLOR.to_f32().0),
);
device.set_uniform(&self.ground_program.gridline_count_uniform,
UniformData::Int(GRIDLINE_COUNT));
device.draw_arrays(
Primitive::TriangleFan,
4,
&RenderState {
depth: Some(DepthState { func: DepthFunc::Less, write: true }),
..RenderState::default()
},
);
}
fn render_vector_scene(&mut self) {
match self.scene_metadata.monochrome_color {
None => self.renderer.set_render_mode(RenderMode::Multicolor),
Some(fg_color) => {
self.renderer.set_render_mode(RenderMode::Monochrome {
fg_color: fg_color.to_f32(),
bg_color: self.background_color().to_f32(),
gamma_correction: self.ui.gamma_correction_effect_enabled,
defringing_kernel: if self.ui.subpixel_aa_effect_enabled {
// TODO(pcwalton): Select FreeType defringing kernel as necessary.
Some(DEFRINGING_KERNEL_CORE_GRAPHICS)
} else {
None
},
})
}
}
if self.ui.mode == Mode::TwoD {
self.renderer.disable_depth();
} else {
self.renderer.enable_depth();
}
self.renderer.begin_scene();
// Issue render commands!
for command in self.render_command_stream.as_mut().unwrap() {
self.renderer.render_command(&command);
if let RenderCommand::Finish { build_time } = command {
self.build_time = Some(build_time);
}
}
self.current_frame
.as_mut()
.unwrap()
.scene_stats
.push(self.renderer.stats);
self.renderer.end_scene();
}
pub fn maybe_take_screenshot(&mut self) {
let screenshot_path = match self.pending_screenshot_path.take() {
None => return,
Some(screenshot_path) => screenshot_path,
};
let drawable_size = self.window_size.device_size();
let pixels = self
.renderer
.device
.read_pixels_from_default_framebuffer(drawable_size);
image::save_buffer(
screenshot_path,
&pixels,
drawable_size.x() as u32,
drawable_size.y() as u32,
ColorType::RGBA(8),
)
.unwrap();
}
pub fn draw_debug_ui(&mut self) {
if self.options.ui == UIVisibility::None {
return;
}
let viewport = self.window.viewport(View::Mono);
self.window.make_current(View::Mono);
self.renderer.replace_dest_framebuffer(DestFramebuffer::Default {
viewport,
window_size: self.window_size.device_size(),
});
self.renderer.draw_debug_ui();
}
}