Pipeline between CPU and GPU at a more fine-grained level.
This makes us stop running one frame behind.
This commit is contained in:
parent
d090dd459b
commit
3d4f8bd008
|
@ -169,6 +169,15 @@ dependencies = [
|
||||||
"cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-channel"
|
||||||
|
version = "0.3.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-deque"
|
name = "crossbeam-deque"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
|
@ -200,6 +209,15 @@ dependencies = [
|
||||||
"cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-utils"
|
||||||
|
version = "0.6.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "deflate"
|
name = "deflate"
|
||||||
version = "0.7.19"
|
version = "0.7.19"
|
||||||
|
@ -699,6 +717,7 @@ name = "pathfinder_renderer"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"crossbeam-channel 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"fixedbitset 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
"fixedbitset 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"pathfinder_geometry 0.3.0",
|
"pathfinder_geometry 0.3.0",
|
||||||
|
@ -1406,9 +1425,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
"checksum color_quant 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0dbbb57365263e881e805dc77d94697c9118fd94d8da011240555aa7b23445bd"
|
"checksum color_quant 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0dbbb57365263e881e805dc77d94697c9118fd94d8da011240555aa7b23445bd"
|
||||||
"checksum combine 3.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "d2623b3542b48f4427e15ddd4995186decb594ebbd70271463886584b4a114b9"
|
"checksum combine 3.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "d2623b3542b48f4427e15ddd4995186decb594ebbd70271463886584b4a114b9"
|
||||||
"checksum crc32fast 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e91d5240c6975ef33aeb5f148f35275c25eda8e8a5f95abe421978b05b8bf192"
|
"checksum crc32fast 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e91d5240c6975ef33aeb5f148f35275c25eda8e8a5f95abe421978b05b8bf192"
|
||||||
|
"checksum crossbeam-channel 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "0f0ed1a4de2235cabda8558ff5840bffb97fcb64c97827f354a451307df5f72b"
|
||||||
"checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3"
|
"checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3"
|
||||||
"checksum crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "927121f5407de9956180ff5e936fe3cf4324279280001cd56b669d28ee7e9150"
|
"checksum crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "927121f5407de9956180ff5e936fe3cf4324279280001cd56b669d28ee7e9150"
|
||||||
"checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9"
|
"checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9"
|
||||||
|
"checksum crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f8306fcef4a7b563b76b7dd949ca48f52bc1141aa067d2ea09565f3e2652aa5c"
|
||||||
"checksum deflate 0.7.19 (registry+https://github.com/rust-lang/crates.io-index)" = "8a6abb26e16e8d419b5c78662aa9f82857c2386a073da266840e474d5055ec86"
|
"checksum deflate 0.7.19 (registry+https://github.com/rust-lang/crates.io-index)" = "8a6abb26e16e8d419b5c78662aa9f82857c2386a073da266840e474d5055ec86"
|
||||||
"checksum egl 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a373bc9844200b1ff15bd1b245931d1c20d09d06e4ec09f361171f29a4b0752d"
|
"checksum egl 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a373bc9844200b1ff15bd1b245931d1c20d09d06e4ec09f361171f29a4b0752d"
|
||||||
"checksum either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3be565ca5c557d7f59e7cfcf1844f9e3033650c929c6566f511e8005f205c1d0"
|
"checksum either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3be565ca5c557d7f59e7cfcf1844f9e3033650c929c6566f511e8005f205c1d0"
|
||||||
|
|
|
@ -25,25 +25,26 @@ use pathfinder_gl::GLDevice;
|
||||||
use pathfinder_gpu::resources::ResourceLoader;
|
use pathfinder_gpu::resources::ResourceLoader;
|
||||||
use pathfinder_gpu::{DepthFunc, DepthState, Device, Primitive, RenderState, StencilFunc};
|
use pathfinder_gpu::{DepthFunc, DepthState, Device, Primitive, RenderState, StencilFunc};
|
||||||
use pathfinder_gpu::{StencilState, UniformData};
|
use pathfinder_gpu::{StencilState, UniformData};
|
||||||
use pathfinder_renderer::builder::{RenderOptions, RenderTransform, SceneBuilder};
|
use pathfinder_renderer::builder::{RenderOptions, RenderTransform};
|
||||||
|
use pathfinder_renderer::builder::{SceneBuilder, SceneBuilderContext};
|
||||||
use pathfinder_renderer::gpu::renderer::{RenderMode, Renderer};
|
use pathfinder_renderer::gpu::renderer::{RenderMode, Renderer};
|
||||||
use pathfinder_renderer::gpu_data::{BuiltScene, Stats};
|
use pathfinder_renderer::gpu_data::{BuiltScene, RenderCommand, Stats};
|
||||||
use pathfinder_renderer::post::{DEFRINGING_KERNEL_CORE_GRAPHICS, STEM_DARKENING_FACTORS};
|
use pathfinder_renderer::post::{DEFRINGING_KERNEL_CORE_GRAPHICS, STEM_DARKENING_FACTORS};
|
||||||
use pathfinder_renderer::scene::Scene;
|
use pathfinder_renderer::scene::Scene;
|
||||||
use pathfinder_renderer::z_buffer::ZBuffer;
|
|
||||||
use pathfinder_svg::BuiltSVG;
|
use pathfinder_svg::BuiltSVG;
|
||||||
use pathfinder_ui::{MousePosition, UIEvent};
|
use pathfinder_ui::{MousePosition, UIEvent};
|
||||||
use rayon::ThreadPoolBuilder;
|
use rayon::ThreadPoolBuilder;
|
||||||
use std::f32::consts::FRAC_PI_4;
|
use std::f32::consts::FRAC_PI_4;
|
||||||
|
use std::fmt::{Debug, Formatter, Result as DebugResult};
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use std::iter;
|
use std::iter;
|
||||||
use std::panic;
|
use std::panic::{self, AssertUnwindSafe};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::process;
|
use std::process;
|
||||||
use std::sync::mpsc::{self, Receiver, Sender};
|
use std::sync::mpsc::{self, Receiver, Sender};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant, SystemTime};
|
||||||
use usvg::{Options as UsvgOptions, Tree};
|
use usvg::{Options as UsvgOptions, Tree};
|
||||||
|
|
||||||
static DEFAULT_SVG_VIRTUAL_PATH: &'static str = "svg/Ghostscript_Tiger.svg";
|
static DEFAULT_SVG_VIRTUAL_PATH: &'static str = "svg/Ghostscript_Tiger.svg";
|
||||||
|
@ -191,11 +192,14 @@ impl<W> DemoApp<W> where W: Window {
|
||||||
let ui_events = self.handle_events(events);
|
let ui_events = self.handle_events(events);
|
||||||
|
|
||||||
// Get the render message, and determine how many scenes it contains.
|
// Get the render message, and determine how many scenes it contains.
|
||||||
let render_msg = self.scene_thread_proxy.receiver.recv().unwrap();
|
let transforms = match self.scene_thread_proxy.receiver.recv().unwrap() {
|
||||||
let render_scene_count = render_msg.render_scenes.len() as u32;
|
SceneToMainMsg::BeginFrame { transforms } => transforms,
|
||||||
|
msg => panic!("Expected BeginFrame message, found {:?}!", msg),
|
||||||
|
};
|
||||||
|
let render_scene_count = transforms.len() as u32;
|
||||||
|
|
||||||
// Save the frame.
|
// Save the frame.
|
||||||
self.current_frame = Some(Frame::new(render_msg, ui_events));
|
self.current_frame = Some(Frame::new(transforms, ui_events));
|
||||||
|
|
||||||
// Begin drawing the scene.
|
// Begin drawing the scene.
|
||||||
self.renderer.device.clear(Some(self.background_color().to_f32().0), Some(1.0), Some(0));
|
self.renderer.device.clear(Some(self.background_color().to_f32().0), Some(1.0), Some(0));
|
||||||
|
@ -217,32 +221,24 @@ impl<W> DemoApp<W> where W: Window {
|
||||||
Camera::TwoD(transform) => RenderTransform::Transform2D(transform),
|
Camera::TwoD(transform) => RenderTransform::Transform2D(transform),
|
||||||
};
|
};
|
||||||
|
|
||||||
let is_first_frame = self.frame_counter == 0;
|
|
||||||
let frame_count = if is_first_frame { 2 } else { 1 };
|
|
||||||
let barrel_distortion = match self.ui.mode {
|
let barrel_distortion = match self.ui.mode {
|
||||||
Mode::VR => Some(self.window.barrel_distortion_coefficients()),
|
Mode::VR => Some(self.window.barrel_distortion_coefficients()),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
for _ in 0..frame_count {
|
let viewport_count = self.ui.mode.viewport_count();
|
||||||
let viewport_count = self.ui.mode.viewport_count();
|
let render_transforms =
|
||||||
let render_transforms = iter::repeat(render_transform.clone()).take(viewport_count)
|
iter::repeat(render_transform.clone()).take(viewport_count).collect();
|
||||||
.collect();
|
self.scene_thread_proxy.sender.send(MainToSceneMsg::Build(BuildOptions {
|
||||||
self.scene_thread_proxy.sender.send(MainToSceneMsg::Build(BuildOptions {
|
render_transforms,
|
||||||
render_transforms,
|
stem_darkening_font_size: if self.ui.stem_darkening_effect_enabled {
|
||||||
stem_darkening_font_size: if self.ui.stem_darkening_effect_enabled {
|
Some(APPROX_FONT_SIZE * self.window_size.backing_scale_factor)
|
||||||
Some(APPROX_FONT_SIZE * self.window_size.backing_scale_factor)
|
} else {
|
||||||
} else {
|
None
|
||||||
None
|
},
|
||||||
},
|
subpixel_aa_enabled: self.ui.subpixel_aa_effect_enabled,
|
||||||
subpixel_aa_enabled: self.ui.subpixel_aa_effect_enabled,
|
barrel_distortion,
|
||||||
barrel_distortion,
|
})).unwrap();
|
||||||
})).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
if is_first_frame {
|
|
||||||
self.dirty = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_events(&mut self, events: Vec<Event>) -> Vec<UIEvent> {
|
fn handle_events(&mut self, events: Vec<Event>) -> Vec<UIEvent> {
|
||||||
|
@ -389,22 +385,17 @@ impl<W> DemoApp<W> where W: Window {
|
||||||
self.draw_environment(render_scene_index);
|
self.draw_environment(render_scene_index);
|
||||||
self.render_vector_scene(render_scene_index);
|
self.render_vector_scene(render_scene_index);
|
||||||
|
|
||||||
let frame = self.current_frame.as_mut().unwrap();
|
if let Some(rendering_time) = self.renderer.shift_timer_query() {
|
||||||
let render_scene = &frame.render_msg.render_scenes[render_scene_index as usize];
|
self.current_frame.as_mut().unwrap().scene_rendering_times.push(rendering_time)
|
||||||
match frame.render_stats {
|
|
||||||
None => {
|
|
||||||
frame.render_stats = Some(RenderStats {
|
|
||||||
rendering_time: self.renderer.shift_timer_query(),
|
|
||||||
stats: render_scene.built_scene.stats(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
Some(ref mut render_stats) => {
|
|
||||||
render_stats.stats = render_stats.stats + render_scene.built_scene.stats()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn finish_drawing_frame(&mut self) {
|
pub fn finish_drawing_frame(&mut self) {
|
||||||
|
let tile_time = match self.scene_thread_proxy.receiver.recv().unwrap() {
|
||||||
|
SceneToMainMsg::EndFrame { tile_time } => tile_time,
|
||||||
|
_ => panic!("Expected `EndFrame`!"),
|
||||||
|
};
|
||||||
|
|
||||||
let mut frame = self.current_frame.take().unwrap();
|
let mut frame = self.current_frame.take().unwrap();
|
||||||
|
|
||||||
let drawable_size = self.window_size.device_size();
|
let drawable_size = self.window_size.device_size();
|
||||||
|
@ -414,34 +405,41 @@ impl<W> DemoApp<W> where W: Window {
|
||||||
self.take_screenshot();
|
self.take_screenshot();
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.options.ui != UIVisibility::None {
|
if !frame.scene_stats.is_empty() || !frame.scene_rendering_times.is_empty() {
|
||||||
if let Some(render_stats) = frame.render_stats.take() {
|
let zero = Stats::default();
|
||||||
self.renderer.debug_ui.add_sample(render_stats.stats,
|
let aggregate_stats = frame.scene_stats.iter().fold(zero, |sum, item| sum + *item);
|
||||||
frame.render_msg.tile_time,
|
let total_rendering_time = if frame.scene_rendering_times.is_empty() {
|
||||||
render_stats.rendering_time);
|
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, tile_time, total_rendering_time);
|
||||||
|
|
||||||
|
if self.options.ui != UIVisibility::None {
|
||||||
self.renderer.draw_debug_ui();
|
self.renderer.draw_debug_ui();
|
||||||
}
|
}
|
||||||
|
|
||||||
for ui_event in &frame.ui_events {
|
|
||||||
self.dirty = true;
|
|
||||||
self.renderer.debug_ui.ui.event_queue.push(*ui_event);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.renderer.debug_ui.ui.mouse_position =
|
|
||||||
get_mouse_position(&self.window, self.window_size.backing_scale_factor);
|
|
||||||
self.ui.show_text_effects = self.monochrome_scene_color.is_some();
|
|
||||||
|
|
||||||
let mut ui_action = UIAction::None;
|
|
||||||
if self.options.ui == UIVisibility::All {
|
|
||||||
self.ui.update(&self.renderer.device,
|
|
||||||
&mut self.window,
|
|
||||||
&mut self.renderer.debug_ui,
|
|
||||||
&mut ui_action);
|
|
||||||
}
|
|
||||||
frame.ui_events = self.renderer.debug_ui.ui.event_queue.drain();
|
|
||||||
self.handle_ui_action(&mut ui_action);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for ui_event in &frame.ui_events {
|
||||||
|
self.dirty = true;
|
||||||
|
self.renderer.debug_ui.ui.event_queue.push(*ui_event);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.renderer.debug_ui.ui.mouse_position =
|
||||||
|
get_mouse_position(&self.window, self.window_size.backing_scale_factor);
|
||||||
|
self.ui.show_text_effects = self.monochrome_scene_color.is_some();
|
||||||
|
|
||||||
|
let mut ui_action = UIAction::None;
|
||||||
|
if self.options.ui == UIVisibility::All {
|
||||||
|
self.ui.update(&self.renderer.device,
|
||||||
|
&mut self.window,
|
||||||
|
&mut self.renderer.debug_ui,
|
||||||
|
&mut ui_action);
|
||||||
|
}
|
||||||
|
frame.ui_events = self.renderer.debug_ui.ui.event_queue.drain();
|
||||||
|
self.handle_ui_action(&mut ui_action);
|
||||||
|
|
||||||
// Switch camera mode (2D/3D) if requested.
|
// Switch camera mode (2D/3D) if requested.
|
||||||
//
|
//
|
||||||
// FIXME(pcwalton): This mess should really be an MVC setup.
|
// FIXME(pcwalton): This mess should really be an MVC setup.
|
||||||
|
@ -476,8 +474,8 @@ impl<W> DemoApp<W> where W: Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_environment(&self, viewport_index: u32) {
|
fn draw_environment(&self, viewport_index: u32) {
|
||||||
let render_msg = &self.current_frame.as_ref().unwrap().render_msg;
|
let frame = &self.current_frame.as_ref().unwrap();
|
||||||
let render_transform = &render_msg.render_scenes[viewport_index as usize].transform;
|
let render_transform = &frame.transforms[viewport_index as usize].clone();
|
||||||
|
|
||||||
let perspective = match *render_transform {
|
let perspective = match *render_transform {
|
||||||
RenderTransform::Transform2D(..) => return,
|
RenderTransform::Transform2D(..) => return,
|
||||||
|
@ -546,8 +544,12 @@ impl<W> DemoApp<W> where W: Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_vector_scene(&mut self, viewport_index: u32) {
|
fn render_vector_scene(&mut self, viewport_index: u32) {
|
||||||
let render_msg = &self.current_frame.as_ref().unwrap().render_msg;
|
let built_scene = match self.scene_thread_proxy.receiver.recv().unwrap() {
|
||||||
let built_scene = &render_msg.render_scenes[viewport_index as usize].built_scene;
|
SceneToMainMsg::BeginRenderScene(built_scene) => built_scene,
|
||||||
|
_ => panic!("Expected `BeginRenderScene`!"),
|
||||||
|
};
|
||||||
|
|
||||||
|
self.current_frame.as_mut().unwrap().scene_stats.push(built_scene.stats());
|
||||||
|
|
||||||
let view_box_size = view_box_size(self.ui.mode, &self.window_size);
|
let view_box_size = view_box_size(self.ui.mode, &self.window_size);
|
||||||
let viewport_origin_x = viewport_index as i32 * view_box_size.x();
|
let viewport_origin_x = viewport_index as i32 * view_box_size.x();
|
||||||
|
@ -577,18 +579,26 @@ impl<W> DemoApp<W> where W: Window {
|
||||||
self.renderer.enable_depth();
|
self.renderer.enable_depth();
|
||||||
}
|
}
|
||||||
|
|
||||||
self.renderer.render_scene(&built_scene);
|
self.renderer.begin_scene(&built_scene);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match self.scene_thread_proxy.receiver.recv().unwrap() {
|
||||||
|
SceneToMainMsg::Execute(command) => self.renderer.render_command(&command),
|
||||||
|
SceneToMainMsg::EndRenderScene => break,
|
||||||
|
_ => panic!("Expected `Execute` or `EndRenderScene`!"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.renderer.end_scene();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_ui_action(&mut self, ui_action: &mut UIAction) {
|
fn handle_ui_action(&mut self, ui_action: &mut UIAction) {
|
||||||
match ui_action {
|
match ui_action {
|
||||||
UIAction::None => {}
|
UIAction::None => {}
|
||||||
|
|
||||||
UIAction::TakeScreenshot(ref path) => {
|
UIAction::TakeScreenshot(ref path) => {
|
||||||
self.pending_screenshot_path = Some((*path).clone());
|
self.pending_screenshot_path = Some((*path).clone());
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
UIAction::ZoomIn => {
|
UIAction::ZoomIn => {
|
||||||
if let Camera::TwoD(ref mut transform) = self.camera {
|
if let Camera::TwoD(ref mut transform) = self.camera {
|
||||||
let scale = Point2DF32::splat(1.0 + CAMERA_ZOOM_AMOUNT_2D);
|
let scale = Point2DF32::splat(1.0 + CAMERA_ZOOM_AMOUNT_2D);
|
||||||
|
@ -635,7 +645,6 @@ impl<W> DemoApp<W> where W: Window {
|
||||||
fn background_color(&self) -> ColorU {
|
fn background_color(&self) -> ColorU {
|
||||||
if self.ui.dark_background_enabled { DARK_BG_COLOR } else { LIGHT_BG_COLOR }
|
if self.ui.dark_background_enabled { DARK_BG_COLOR } else { LIGHT_BG_COLOR }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SceneThreadProxy {
|
struct SceneThreadProxy {
|
||||||
|
@ -647,7 +656,12 @@ impl SceneThreadProxy {
|
||||||
fn new(scene: Scene, options: Options) -> SceneThreadProxy {
|
fn new(scene: Scene, options: Options) -> SceneThreadProxy {
|
||||||
let (main_to_scene_sender, main_to_scene_receiver) = mpsc::channel();
|
let (main_to_scene_sender, main_to_scene_receiver) = mpsc::channel();
|
||||||
let (scene_to_main_sender, scene_to_main_receiver) = mpsc::channel();
|
let (scene_to_main_sender, scene_to_main_receiver) = mpsc::channel();
|
||||||
SceneThread::new(scene, scene_to_main_sender, main_to_scene_receiver, options);
|
let scene_builder_context = SceneBuilderContext::new();
|
||||||
|
SceneThread::new(scene,
|
||||||
|
scene_to_main_sender,
|
||||||
|
main_to_scene_receiver,
|
||||||
|
scene_builder_context,
|
||||||
|
options);
|
||||||
SceneThreadProxy { sender: main_to_scene_sender, receiver: scene_to_main_receiver }
|
SceneThreadProxy { sender: main_to_scene_sender, receiver: scene_to_main_receiver }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -664,6 +678,7 @@ struct SceneThread {
|
||||||
scene: Scene,
|
scene: Scene,
|
||||||
sender: Sender<SceneToMainMsg>,
|
sender: Sender<SceneToMainMsg>,
|
||||||
receiver: Receiver<MainToSceneMsg>,
|
receiver: Receiver<MainToSceneMsg>,
|
||||||
|
context: SceneBuilderContext,
|
||||||
options: Options,
|
options: Options,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -671,8 +686,9 @@ impl SceneThread {
|
||||||
fn new(scene: Scene,
|
fn new(scene: Scene,
|
||||||
sender: Sender<SceneToMainMsg>,
|
sender: Sender<SceneToMainMsg>,
|
||||||
receiver: Receiver<MainToSceneMsg>,
|
receiver: Receiver<MainToSceneMsg>,
|
||||||
|
context: SceneBuilderContext,
|
||||||
options: Options) {
|
options: Options) {
|
||||||
thread::spawn(move || (SceneThread { scene, sender, receiver, options }).run());
|
thread::spawn(move || (SceneThread { scene, sender, receiver, context, options }).run());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(mut self) {
|
fn run(mut self) {
|
||||||
|
@ -687,18 +703,20 @@ impl SceneThread {
|
||||||
self.scene.view_box = RectF32::new(Point2DF32::default(), size.to_f32());
|
self.scene.view_box = RectF32::new(Point2DF32::default(), size.to_f32());
|
||||||
}
|
}
|
||||||
MainToSceneMsg::Build(build_options) => {
|
MainToSceneMsg::Build(build_options) => {
|
||||||
|
self.sender.send(SceneToMainMsg::BeginFrame {
|
||||||
|
transforms: build_options.render_transforms.clone(),
|
||||||
|
}).unwrap();
|
||||||
let start_time = Instant::now();
|
let start_time = Instant::now();
|
||||||
let render_scenes = build_options.render_transforms
|
for render_transform in &build_options.render_transforms {
|
||||||
.iter()
|
build_scene(&self.context,
|
||||||
.map(|render_transform| {
|
&self.scene,
|
||||||
let built_scene = build_scene(&self.scene,
|
&build_options,
|
||||||
&build_options,
|
(*render_transform).clone(),
|
||||||
(*render_transform).clone(),
|
self.options.jobs,
|
||||||
self.options.jobs);
|
&mut self.sender);
|
||||||
RenderScene { built_scene, transform: (*render_transform).clone() }
|
}
|
||||||
}).collect();
|
|
||||||
let tile_time = Instant::now() - start_time;
|
let tile_time = Instant::now() - start_time;
|
||||||
self.sender.send(SceneToMainMsg { render_scenes, tile_time }).unwrap();
|
self.sender.send(SceneToMainMsg::EndFrame { tile_time }).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -718,14 +736,75 @@ struct BuildOptions {
|
||||||
subpixel_aa_enabled: bool,
|
subpixel_aa_enabled: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SceneToMainMsg {
|
enum SceneToMainMsg {
|
||||||
render_scenes: Vec<RenderScene>,
|
BeginFrame { transforms: Vec<RenderTransform> },
|
||||||
tile_time: Duration,
|
EndFrame { tile_time: Duration },
|
||||||
|
BeginRenderScene(BuiltScene),
|
||||||
|
Execute(RenderCommand),
|
||||||
|
EndRenderScene,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RenderScene {
|
impl Debug for SceneToMainMsg {
|
||||||
built_scene: BuiltScene,
|
fn fmt(&self, formatter: &mut Formatter) -> DebugResult {
|
||||||
transform: RenderTransform,
|
let ident = match *self {
|
||||||
|
SceneToMainMsg::BeginFrame { .. } => "BeginFrame",
|
||||||
|
SceneToMainMsg::EndFrame { .. } => "EndFrame",
|
||||||
|
SceneToMainMsg::BeginRenderScene(..) => "BeginRenderScene",
|
||||||
|
SceneToMainMsg::Execute(..) => "Execute",
|
||||||
|
SceneToMainMsg::EndRenderScene => "EndRenderScene",
|
||||||
|
};
|
||||||
|
ident.fmt(formatter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_scene(context: &SceneBuilderContext,
|
||||||
|
scene: &Scene,
|
||||||
|
build_options: &BuildOptions,
|
||||||
|
render_transform: RenderTransform,
|
||||||
|
jobs: Option<usize>,
|
||||||
|
sink: &mut Sender<SceneToMainMsg>) {
|
||||||
|
let render_options = RenderOptions {
|
||||||
|
transform: render_transform.clone(),
|
||||||
|
dilation: match build_options.stem_darkening_font_size {
|
||||||
|
None => Point2DF32::default(),
|
||||||
|
Some(font_size) => {
|
||||||
|
let (x, y) = (STEM_DARKENING_FACTORS[0], STEM_DARKENING_FACTORS[1]);
|
||||||
|
Point2DF32::new(x, y).scale(font_size)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
barrel_distortion: build_options.barrel_distortion,
|
||||||
|
subpixel_aa_enabled: build_options.subpixel_aa_enabled,
|
||||||
|
};
|
||||||
|
|
||||||
|
let built_options = render_options.prepare(scene.bounds);
|
||||||
|
let quad = built_options.quad();
|
||||||
|
|
||||||
|
let mut built_scene = BuiltScene::new(scene.view_box, &quad, scene.objects.len() as u32);
|
||||||
|
built_scene.shaders = scene.build_shaders();
|
||||||
|
sink.send(SceneToMainMsg::BeginRenderScene(built_scene)).unwrap();
|
||||||
|
|
||||||
|
let (context, inner_sink) = (AssertUnwindSafe(context), AssertUnwindSafe(sink.clone()));
|
||||||
|
let result = panic::catch_unwind(move || {
|
||||||
|
let mut scene_builder = SceneBuilder::new(&context, scene, &built_options);
|
||||||
|
let sink = (*inner_sink).clone();
|
||||||
|
let listener = Box::new(move |command| {
|
||||||
|
sink.send(SceneToMainMsg::Execute(command)).unwrap()
|
||||||
|
});
|
||||||
|
|
||||||
|
// FIXME(pcwalton): Actually take the number of jobs into account.
|
||||||
|
match jobs {
|
||||||
|
Some(1) => scene_builder.build_sequentially(listener),
|
||||||
|
_ => scene_builder.build_in_parallel(listener),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if result.is_err() {
|
||||||
|
eprintln!("Scene building crashed! Dumping scene:");
|
||||||
|
println!("{:?}", scene);
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
sink.send(SceneToMainMsg::EndRenderScene).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -824,12 +903,6 @@ pub enum UIVisibility {
|
||||||
All,
|
All,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
struct RenderStats {
|
|
||||||
rendering_time: Option<Duration>,
|
|
||||||
stats: Stats,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_scene(resource_loader: &dyn ResourceLoader, input_path: &SVGPath) -> BuiltSVG {
|
fn load_scene(resource_loader: &dyn ResourceLoader, input_path: &SVGPath) -> BuiltSVG {
|
||||||
let mut data;
|
let mut data;
|
||||||
match *input_path {
|
match *input_path {
|
||||||
|
@ -844,58 +917,6 @@ fn load_scene(resource_loader: &dyn ResourceLoader, input_path: &SVGPath) -> Bui
|
||||||
BuiltSVG::from_tree(Tree::from_data(&data, &UsvgOptions::default()).unwrap())
|
BuiltSVG::from_tree(Tree::from_data(&data, &UsvgOptions::default()).unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_scene(scene: &Scene,
|
|
||||||
build_options: &BuildOptions,
|
|
||||||
render_transform: RenderTransform,
|
|
||||||
jobs: Option<usize>)
|
|
||||||
-> BuiltScene {
|
|
||||||
let render_options = RenderOptions {
|
|
||||||
transform: render_transform,
|
|
||||||
dilation: match build_options.stem_darkening_font_size {
|
|
||||||
None => Point2DF32::default(),
|
|
||||||
Some(font_size) => {
|
|
||||||
let (x, y) = (STEM_DARKENING_FACTORS[0], STEM_DARKENING_FACTORS[1]);
|
|
||||||
Point2DF32::new(x, y).scale(font_size)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
barrel_distortion: build_options.barrel_distortion,
|
|
||||||
subpixel_aa_enabled: build_options.subpixel_aa_enabled,
|
|
||||||
};
|
|
||||||
|
|
||||||
let built_options = render_options.prepare(scene.bounds);
|
|
||||||
let effective_view_box = scene.effective_view_box(&built_options);
|
|
||||||
let z_buffer = ZBuffer::new(effective_view_box);
|
|
||||||
|
|
||||||
let quad = built_options.quad();
|
|
||||||
|
|
||||||
let built_objects = panic::catch_unwind(|| {
|
|
||||||
match jobs {
|
|
||||||
Some(1) => scene.build_objects_sequentially(built_options, &z_buffer),
|
|
||||||
_ => scene.build_objects(built_options, &z_buffer),
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let built_objects = match built_objects {
|
|
||||||
Ok(built_objects) => built_objects,
|
|
||||||
Err(_) => {
|
|
||||||
eprintln!("Scene building crashed! Dumping scene:");
|
|
||||||
println!("{:?}", scene);
|
|
||||||
process::exit(1);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut built_scene = BuiltScene::new(scene.view_box, &quad, scene.objects.len() as u32);
|
|
||||||
built_scene.shaders = scene.build_shaders();
|
|
||||||
|
|
||||||
let mut scene_builder = SceneBuilder::new(built_objects, z_buffer, effective_view_box);
|
|
||||||
built_scene.solid_tiles = scene_builder.build_solid_tiles();
|
|
||||||
while let Some(batch) = scene_builder.build_batch() {
|
|
||||||
built_scene.batches.push(batch);
|
|
||||||
}
|
|
||||||
|
|
||||||
built_scene
|
|
||||||
}
|
|
||||||
|
|
||||||
fn center_of_window(window_size: &WindowSize) -> Point2DF32 {
|
fn center_of_window(window_size: &WindowSize) -> Point2DF32 {
|
||||||
window_size.device_size().to_f32().scale(0.5)
|
window_size.device_size().to_f32().scale(0.5)
|
||||||
}
|
}
|
||||||
|
@ -1016,13 +1037,14 @@ fn view_box_size(mode: Mode, window_size: &WindowSize) -> Point2DI32 {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Frame {
|
struct Frame {
|
||||||
render_msg: SceneToMainMsg,
|
transforms: Vec<RenderTransform>,
|
||||||
ui_events: Vec<UIEvent>,
|
ui_events: Vec<UIEvent>,
|
||||||
render_stats: Option<RenderStats>,
|
scene_rendering_times: Vec<Duration>,
|
||||||
|
scene_stats: Vec<Stats>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Frame {
|
impl Frame {
|
||||||
fn new(render_msg: SceneToMainMsg, ui_events: Vec<UIEvent>) -> Frame {
|
fn new(transforms: Vec<RenderTransform>, ui_events: Vec<UIEvent>) -> Frame {
|
||||||
Frame { render_msg, ui_events, render_stats: None }
|
Frame { transforms, ui_events, scene_rendering_times: vec![], scene_stats: vec![] }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ authors = ["Patrick Walton <pcwalton@mimiga.net>"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
byteorder = "1.2"
|
byteorder = "1.2"
|
||||||
|
crossbeam-channel = "0.3"
|
||||||
fixedbitset = "0.1"
|
fixedbitset = "0.1"
|
||||||
hashbrown = "0.1"
|
hashbrown = "0.1"
|
||||||
rayon = "1.0"
|
rayon = "1.0"
|
||||||
|
|
|
@ -10,116 +10,339 @@
|
||||||
|
|
||||||
//! Packs data onto the GPU.
|
//! Packs data onto the GPU.
|
||||||
|
|
||||||
use crate::gpu_data::{Batch, BuiltObject, FillBatchPrimitive};
|
use crate::gpu_data::{AlphaTileBatchPrimitive, BuiltObject, FillBatchPrimitive};
|
||||||
use crate::gpu_data::{AlphaTileBatchPrimitive, SolidTileScenePrimitive};
|
use crate::gpu_data::{RenderCommand, SolidTileBatchPrimitive};
|
||||||
use crate::scene;
|
use crate::scene::{self, Scene};
|
||||||
use crate::tiles;
|
use crate::sorted_vector::SortedVector;
|
||||||
|
use crate::tiles::{self, Tiler};
|
||||||
use crate::z_buffer::ZBuffer;
|
use crate::z_buffer::ZBuffer;
|
||||||
|
use crossbeam_channel::{self, Receiver, Sender};
|
||||||
use pathfinder_geometry::basic::point::{Point2DF32, Point3DF32};
|
use pathfinder_geometry::basic::point::{Point2DF32, Point3DF32};
|
||||||
use pathfinder_geometry::basic::rect::{RectF32, RectI32};
|
use pathfinder_geometry::basic::rect::{RectF32, RectI32};
|
||||||
use pathfinder_geometry::basic::transform2d::Transform2DF32;
|
use pathfinder_geometry::basic::transform2d::Transform2DF32;
|
||||||
use pathfinder_geometry::basic::transform3d::Perspective;
|
use pathfinder_geometry::basic::transform3d::Perspective;
|
||||||
use pathfinder_geometry::clip::PolygonClipper3D;
|
use pathfinder_geometry::clip::PolygonClipper3D;
|
||||||
use pathfinder_geometry::distortion::BarrelDistortionCoefficients;
|
use pathfinder_geometry::distortion::BarrelDistortionCoefficients;
|
||||||
use std::iter;
|
use rayon::iter::{IntoParallelIterator, ParallelIterator};
|
||||||
|
use std::cmp::{Ordering, PartialOrd};
|
||||||
|
use std::mem;
|
||||||
|
use std::ops::Range;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::thread;
|
||||||
use std::u16;
|
use std::u16;
|
||||||
|
|
||||||
const MAX_FILLS_PER_BATCH: usize = 0x0002_0000;
|
const MAX_FILLS_PER_BATCH: usize = 0x0002_0000;
|
||||||
const MAX_ALPHA_TILES_PER_BATCH: u16 = 0xffff;
|
const MAX_ALPHA_TILES_PER_BATCH: usize = 0x1000;
|
||||||
|
|
||||||
pub struct SceneBuilder {
|
pub struct SceneBuilderContext {
|
||||||
objects: Vec<BuiltObject>,
|
sender: Sender<MainToSceneAssemblyMsg>,
|
||||||
z_buffer: ZBuffer,
|
receiver: Receiver<SceneAssemblyToMainMsg>,
|
||||||
tile_rect: RectI32,
|
|
||||||
|
|
||||||
current_object_index: usize,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SceneBuilder {
|
struct SceneAssemblyThread {
|
||||||
pub fn new(objects: Vec<BuiltObject>, z_buffer: ZBuffer, view_box: RectF32) -> SceneBuilder {
|
receiver: Receiver<MainToSceneAssemblyMsg>,
|
||||||
SceneBuilder {
|
sender: Sender<SceneAssemblyToMainMsg>,
|
||||||
objects,
|
info: Option<SceneAssemblyThreadInfo>,
|
||||||
z_buffer,
|
}
|
||||||
tile_rect: tiles::round_rect_out_to_tile_bounds(view_box),
|
|
||||||
current_object_index: 0,
|
struct SceneAssemblyThreadInfo {
|
||||||
|
listener: Box<dyn RenderCommandListener>,
|
||||||
|
built_object_queue: SortedVector<IndexedBuiltObject>,
|
||||||
|
next_object_index: u32,
|
||||||
|
|
||||||
|
pub(crate) z_buffer: Arc<ZBuffer>,
|
||||||
|
tile_rect: RectI32,
|
||||||
|
current_pass: Pass,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum MainToSceneAssemblyMsg {
|
||||||
|
NewScene {
|
||||||
|
listener: Box<dyn RenderCommandListener>,
|
||||||
|
effective_view_box: RectF32,
|
||||||
|
z_buffer: Arc<ZBuffer>,
|
||||||
|
},
|
||||||
|
AddObject(IndexedBuiltObject),
|
||||||
|
SceneFinished,
|
||||||
|
Exit,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum SceneAssemblyToMainMsg {
|
||||||
|
FrameFinished,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for SceneBuilderContext {
|
||||||
|
#[inline]
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.sender.send(MainToSceneAssemblyMsg::Exit).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait RenderCommandListener: Send {
|
||||||
|
fn send(&mut self, command: RenderCommand);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SceneBuilder<'a> {
|
||||||
|
context: &'a SceneBuilderContext,
|
||||||
|
scene: &'a Scene,
|
||||||
|
built_options: &'a PreparedRenderOptions,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct IndexedBuiltObject {
|
||||||
|
object: BuiltObject,
|
||||||
|
index: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SceneBuilderContext {
|
||||||
|
#[inline]
|
||||||
|
pub fn new() -> SceneBuilderContext {
|
||||||
|
let (main_to_scene_assembly_sender,
|
||||||
|
main_to_scene_assembly_receiver) = crossbeam_channel::unbounded();
|
||||||
|
let (scene_assembly_to_main_sender,
|
||||||
|
scene_assembly_to_main_receiver) = crossbeam_channel::unbounded();
|
||||||
|
thread::spawn(move || {
|
||||||
|
SceneAssemblyThread::new(main_to_scene_assembly_receiver,
|
||||||
|
scene_assembly_to_main_sender).run()
|
||||||
|
});
|
||||||
|
SceneBuilderContext {
|
||||||
|
sender: main_to_scene_assembly_sender,
|
||||||
|
receiver: scene_assembly_to_main_receiver,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SceneAssemblyThread {
|
||||||
|
#[inline]
|
||||||
|
fn new(receiver: Receiver<MainToSceneAssemblyMsg>, sender: Sender<SceneAssemblyToMainMsg>)
|
||||||
|
-> SceneAssemblyThread {
|
||||||
|
SceneAssemblyThread { receiver, sender, info: None }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(&mut self) {
|
||||||
|
while let Ok(msg) = self.receiver.recv() {
|
||||||
|
match msg {
|
||||||
|
MainToSceneAssemblyMsg::Exit => break,
|
||||||
|
MainToSceneAssemblyMsg::NewScene { listener, effective_view_box, z_buffer } => {
|
||||||
|
self.info = Some(SceneAssemblyThreadInfo {
|
||||||
|
listener,
|
||||||
|
built_object_queue: SortedVector::new(),
|
||||||
|
next_object_index: 0,
|
||||||
|
|
||||||
|
z_buffer,
|
||||||
|
tile_rect: tiles::round_rect_out_to_tile_bounds(effective_view_box),
|
||||||
|
current_pass: Pass::new(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
MainToSceneAssemblyMsg::AddObject(indexed_built_object) => {
|
||||||
|
self.info.as_mut().unwrap().built_object_queue.push(indexed_built_object);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let next_object_index = self.info.as_ref().unwrap().next_object_index;
|
||||||
|
match self.info.as_mut().unwrap().built_object_queue.peek() {
|
||||||
|
Some(ref indexed_object) if
|
||||||
|
next_object_index == indexed_object.index => {}
|
||||||
|
_ => break,
|
||||||
|
}
|
||||||
|
let indexed_object = self.info.as_mut().unwrap().built_object_queue.pop();
|
||||||
|
self.add_object(indexed_object.unwrap().object);
|
||||||
|
self.info.as_mut().unwrap().next_object_index += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MainToSceneAssemblyMsg::SceneFinished => {
|
||||||
|
self.flush_current_pass();
|
||||||
|
self.sender.send(SceneAssemblyToMainMsg::FrameFinished).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_solid_tiles(&self) -> Vec<SolidTileScenePrimitive> {
|
fn add_object(&mut self, object: BuiltObject) {
|
||||||
self.z_buffer
|
// Flush current pass if necessary.
|
||||||
.build_solid_tiles(&self.objects, self.tile_rect)
|
if self.info.as_ref().unwrap().current_pass.fills.len() + object.fills.len() >
|
||||||
|
MAX_FILLS_PER_BATCH {
|
||||||
|
self.flush_current_pass();
|
||||||
|
}
|
||||||
|
|
||||||
|
// See whether we have room for the alpha tiles. If we don't, then flush.
|
||||||
|
let mut alpha_tile_count = 0;
|
||||||
|
for tile_index in 0..object.tiles.len() {
|
||||||
|
if !object.solid_tiles[tile_index] {
|
||||||
|
alpha_tile_count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if self.info.as_ref().unwrap().current_pass.alpha_tiles.len() + alpha_tile_count >
|
||||||
|
MAX_ALPHA_TILES_PER_BATCH {
|
||||||
|
self.flush_current_pass();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy alpha tiles.
|
||||||
|
let mut current_pass = &mut self.info.as_mut().unwrap().current_pass;
|
||||||
|
let mut object_tile_index_to_batch_alpha_tile_index = vec![u16::MAX; object.tiles.len()];
|
||||||
|
for (tile_index, tile) in object.tiles.iter().enumerate() {
|
||||||
|
// Skip solid tiles.
|
||||||
|
if object.solid_tiles[tile_index] {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let batch_alpha_tile_index = current_pass.alpha_tiles.len() as u16;
|
||||||
|
object_tile_index_to_batch_alpha_tile_index[tile_index] = batch_alpha_tile_index;
|
||||||
|
|
||||||
|
current_pass.alpha_tiles.push(AlphaTileBatchPrimitive {
|
||||||
|
tile: *tile,
|
||||||
|
object_index: current_pass.object_range.end as u16,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remap and copy fills, culling as necessary.
|
||||||
|
for fill in &object.fills {
|
||||||
|
let object_tile_index = object.tile_coords_to_index(fill.tile_x as i32,
|
||||||
|
fill.tile_y as i32).unwrap();
|
||||||
|
let object_tile_index = object_tile_index as usize;
|
||||||
|
let alpha_tile_index = object_tile_index_to_batch_alpha_tile_index[object_tile_index];
|
||||||
|
current_pass.fills.push(FillBatchPrimitive {
|
||||||
|
px: fill.px,
|
||||||
|
subpx: fill.subpx,
|
||||||
|
alpha_tile_index,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
current_pass.object_range.end += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_batch(&mut self) -> Option<Batch> {
|
fn flush_current_pass(&mut self) {
|
||||||
let mut batch = Batch::new();
|
self.cull_alpha_tiles();
|
||||||
|
|
||||||
let mut object_tile_index_to_batch_alpha_tile_index = vec![];
|
let mut info = self.info.as_mut().unwrap();
|
||||||
while self.current_object_index < self.objects.len() {
|
info.current_pass.solid_tiles =
|
||||||
let object = &self.objects[self.current_object_index];
|
info.z_buffer.build_solid_tiles(info.tile_rect,
|
||||||
|
info.current_pass.object_range.clone());
|
||||||
|
|
||||||
if batch.fills.len() + object.fills.len() > MAX_FILLS_PER_BATCH {
|
let have_solid_tiles = !info.current_pass.solid_tiles.is_empty();
|
||||||
break;
|
let have_alpha_tiles = !info.current_pass.alpha_tiles.is_empty();
|
||||||
}
|
let have_fills = !info.current_pass.fills.is_empty();
|
||||||
|
if !have_solid_tiles && !have_alpha_tiles && !have_fills {
|
||||||
object_tile_index_to_batch_alpha_tile_index.clear();
|
return
|
||||||
object_tile_index_to_batch_alpha_tile_index
|
|
||||||
.extend(iter::repeat(u16::MAX).take(object.tiles.len()));
|
|
||||||
|
|
||||||
// Copy alpha tiles.
|
|
||||||
for (tile_index, tile) in object.tiles.iter().enumerate() {
|
|
||||||
// Skip solid tiles, since we handled them above already.
|
|
||||||
if object.solid_tiles[tile_index] {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cull occluded tiles.
|
|
||||||
let scene_tile_index = scene::scene_tile_index(tile.tile_x as i32,
|
|
||||||
tile.tile_y as i32,
|
|
||||||
self.tile_rect);
|
|
||||||
if !self
|
|
||||||
.z_buffer
|
|
||||||
.test(scene_tile_index, self.current_object_index as u32)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Visible alpha tile.
|
|
||||||
let batch_alpha_tile_index = batch.alpha_tiles.len() as u16;
|
|
||||||
if batch_alpha_tile_index == MAX_ALPHA_TILES_PER_BATCH {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
object_tile_index_to_batch_alpha_tile_index[tile_index] = batch_alpha_tile_index;
|
|
||||||
|
|
||||||
batch.alpha_tiles.push(AlphaTileBatchPrimitive {
|
|
||||||
tile: *tile,
|
|
||||||
object_index: self.current_object_index as u16,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remap and copy fills, culling as necessary.
|
|
||||||
for fill in &object.fills {
|
|
||||||
let object_tile_index =
|
|
||||||
object.tile_coords_to_index(fill.tile_x as i32, fill.tile_y as i32).unwrap();
|
|
||||||
let alpha_tile_index =
|
|
||||||
object_tile_index_to_batch_alpha_tile_index[object_tile_index as usize];
|
|
||||||
if alpha_tile_index < u16::MAX {
|
|
||||||
batch.fills.push(FillBatchPrimitive {
|
|
||||||
px: fill.px,
|
|
||||||
subpx: fill.subpx,
|
|
||||||
alpha_tile_index,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.current_object_index += 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if batch.is_empty() {
|
info.listener.send(RenderCommand::ClearMaskFramebuffer);
|
||||||
None
|
if have_solid_tiles {
|
||||||
} else {
|
let tiles = mem::replace(&mut info.current_pass.solid_tiles, vec![]);
|
||||||
Some(batch)
|
info.listener.send(RenderCommand::SolidTile(tiles));
|
||||||
}
|
}
|
||||||
|
if have_fills {
|
||||||
|
let fills = mem::replace(&mut info.current_pass.fills, vec![]);
|
||||||
|
info.listener.send(RenderCommand::Fill(fills));
|
||||||
|
}
|
||||||
|
if have_alpha_tiles {
|
||||||
|
let tiles = mem::replace(&mut info.current_pass.alpha_tiles, vec![]);
|
||||||
|
info.listener.send(RenderCommand::AlphaTile(tiles));
|
||||||
|
}
|
||||||
|
|
||||||
|
info.current_pass.object_range.start = info.current_pass.object_range.end;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cull_alpha_tiles(&mut self) {
|
||||||
|
let info = self.info.as_mut().unwrap();
|
||||||
|
for alpha_tile in &mut info.current_pass.alpha_tiles {
|
||||||
|
let scene_tile_index = scene::scene_tile_index(alpha_tile.tile.tile_x as i32,
|
||||||
|
alpha_tile.tile.tile_y as i32,
|
||||||
|
info.tile_rect);
|
||||||
|
if info.z_buffer.test(scene_tile_index, alpha_tile.object_index as u32) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// FIXME(pcwalton): Hack!
|
||||||
|
alpha_tile.tile.tile_x = -1;
|
||||||
|
alpha_tile.tile.tile_y = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> SceneBuilder<'a> {
|
||||||
|
pub fn new(context: &'a SceneBuilderContext,
|
||||||
|
scene: &'a Scene,
|
||||||
|
built_options: &'a PreparedRenderOptions)
|
||||||
|
-> SceneBuilder<'a> {
|
||||||
|
SceneBuilder { context, scene, built_options }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build_sequentially(&mut self, listener: Box<dyn RenderCommandListener>) {
|
||||||
|
let effective_view_box = self.scene.effective_view_box(self.built_options);
|
||||||
|
let z_buffer = Arc::new(ZBuffer::new(effective_view_box));
|
||||||
|
self.send_new_scene_message_to_assembly_thread(listener, effective_view_box, &z_buffer);
|
||||||
|
|
||||||
|
for object_index in 0..self.scene.objects.len() {
|
||||||
|
build_object(object_index,
|
||||||
|
effective_view_box,
|
||||||
|
&z_buffer,
|
||||||
|
&self.built_options,
|
||||||
|
&self.scene,
|
||||||
|
&self.context.sender);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.finish_and_wait_for_scene_assembly_thread();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build_in_parallel(&mut self, listener: Box<dyn RenderCommandListener>) {
|
||||||
|
let effective_view_box = self.scene.effective_view_box(self.built_options);
|
||||||
|
let z_buffer = Arc::new(ZBuffer::new(effective_view_box));
|
||||||
|
self.send_new_scene_message_to_assembly_thread(listener, effective_view_box, &z_buffer);
|
||||||
|
|
||||||
|
(0..self.scene.objects.len()).into_par_iter().for_each(|object_index| {
|
||||||
|
build_object(object_index,
|
||||||
|
effective_view_box,
|
||||||
|
&z_buffer,
|
||||||
|
&self.built_options,
|
||||||
|
&self.scene,
|
||||||
|
&self.context.sender);
|
||||||
|
});
|
||||||
|
|
||||||
|
self.finish_and_wait_for_scene_assembly_thread();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_new_scene_message_to_assembly_thread(&mut self,
|
||||||
|
listener: Box<dyn RenderCommandListener>,
|
||||||
|
effective_view_box: RectF32,
|
||||||
|
z_buffer: &Arc<ZBuffer>) {
|
||||||
|
self.context.sender.send(MainToSceneAssemblyMsg::NewScene {
|
||||||
|
listener,
|
||||||
|
effective_view_box,
|
||||||
|
z_buffer: z_buffer.clone(),
|
||||||
|
}).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finish_and_wait_for_scene_assembly_thread(&mut self) {
|
||||||
|
self.context.sender.send(MainToSceneAssemblyMsg::SceneFinished).unwrap();
|
||||||
|
self.context.receiver.recv().unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_object(object_index: usize,
|
||||||
|
effective_view_box: RectF32,
|
||||||
|
z_buffer: &ZBuffer,
|
||||||
|
built_options: &PreparedRenderOptions,
|
||||||
|
scene: &Scene,
|
||||||
|
sender: &Sender<MainToSceneAssemblyMsg>) {
|
||||||
|
let object = &scene.objects[object_index];
|
||||||
|
let outline = scene.apply_render_options(object.outline(), built_options);
|
||||||
|
|
||||||
|
let mut tiler = Tiler::new(&outline, effective_view_box, object_index as u16, z_buffer);
|
||||||
|
tiler.generate_tiles();
|
||||||
|
|
||||||
|
sender.send(MainToSceneAssemblyMsg::AddObject(IndexedBuiltObject {
|
||||||
|
index: object_index as u32,
|
||||||
|
object: tiler.built_object,
|
||||||
|
})).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Pass {
|
||||||
|
solid_tiles: Vec<SolidTileBatchPrimitive>,
|
||||||
|
alpha_tiles: Vec<AlphaTileBatchPrimitive>,
|
||||||
|
fills: Vec<FillBatchPrimitive>,
|
||||||
|
object_range: Range<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pass {
|
||||||
|
fn new() -> Pass {
|
||||||
|
Pass { solid_tiles: vec![], alpha_tiles: vec![], fills: vec![], object_range: 0..0 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,3 +458,22 @@ impl PreparedRenderTransform {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PartialEq for IndexedBuiltObject {
|
||||||
|
#[inline]
|
||||||
|
fn eq(&self, other: &IndexedBuiltObject) -> bool {
|
||||||
|
other.index == self.index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for IndexedBuiltObject {
|
||||||
|
#[inline]
|
||||||
|
fn partial_cmp(&self, other: &IndexedBuiltObject) -> Option<Ordering> {
|
||||||
|
other.index.partial_cmp(&self.index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F> RenderCommandListener for F where F: FnMut(RenderCommand) + Send {
|
||||||
|
#[inline]
|
||||||
|
fn send(&mut self, command: RenderCommand) { (*self)(command) }
|
||||||
|
}
|
||||||
|
|
|
@ -9,7 +9,8 @@
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
use crate::gpu::debug::DebugUI;
|
use crate::gpu::debug::DebugUI;
|
||||||
use crate::gpu_data::{Batch, BuiltScene, SolidTileScenePrimitive};
|
use crate::gpu_data::{AlphaTileBatchPrimitive, BuiltScene, FillBatchPrimitive};
|
||||||
|
use crate::gpu_data::{RenderCommand, SolidTileBatchPrimitive};
|
||||||
use crate::post::DefringingKernel;
|
use crate::post::DefringingKernel;
|
||||||
use crate::scene::ObjectShader;
|
use crate::scene::ObjectShader;
|
||||||
use crate::tiles::{TILE_HEIGHT, TILE_WIDTH};
|
use crate::tiles::{TILE_HEIGHT, TILE_WIDTH};
|
||||||
|
@ -26,8 +27,8 @@ use std::time::Duration;
|
||||||
|
|
||||||
static QUAD_VERTEX_POSITIONS: [u8; 8] = [0, 0, 1, 0, 1, 1, 0, 1];
|
static QUAD_VERTEX_POSITIONS: [u8; 8] = [0, 0, 1, 0, 1, 1, 0, 1];
|
||||||
|
|
||||||
const MASK_FRAMEBUFFER_WIDTH: i32 = TILE_WIDTH as i32 * 256;
|
const MASK_FRAMEBUFFER_WIDTH: i32 = TILE_WIDTH as i32 * 64;
|
||||||
const MASK_FRAMEBUFFER_HEIGHT: i32 = TILE_HEIGHT as i32 * 256;
|
const MASK_FRAMEBUFFER_HEIGHT: i32 = TILE_HEIGHT as i32 * 64;
|
||||||
|
|
||||||
// TODO(pcwalton): Replace with `mem::size_of` calls?
|
// TODO(pcwalton): Replace with `mem::size_of` calls?
|
||||||
const FILL_INSTANCE_SIZE: usize = 8;
|
const FILL_INSTANCE_SIZE: usize = 8;
|
||||||
|
@ -68,6 +69,7 @@ pub struct Renderer<D> where D: Device {
|
||||||
stencil_vertex_array: StencilVertexArray<D>,
|
stencil_vertex_array: StencilVertexArray<D>,
|
||||||
|
|
||||||
// Debug
|
// Debug
|
||||||
|
current_timer_query: Option<D::TimerQuery>,
|
||||||
pending_timer_queries: VecDeque<D::TimerQuery>,
|
pending_timer_queries: VecDeque<D::TimerQuery>,
|
||||||
free_timer_queries: Vec<D::TimerQuery>,
|
free_timer_queries: Vec<D::TimerQuery>,
|
||||||
pub debug_ui: DebugUI<D>,
|
pub debug_ui: DebugUI<D>,
|
||||||
|
@ -164,6 +166,7 @@ impl<D> Renderer<D> where D: Device {
|
||||||
stencil_program,
|
stencil_program,
|
||||||
stencil_vertex_array,
|
stencil_vertex_array,
|
||||||
|
|
||||||
|
current_timer_query: None,
|
||||||
pending_timer_queries: VecDeque::new(),
|
pending_timer_queries: VecDeque::new(),
|
||||||
free_timer_queries: vec![],
|
free_timer_queries: vec![],
|
||||||
|
|
||||||
|
@ -175,33 +178,49 @@ impl<D> Renderer<D> where D: Device {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_scene(&mut self, built_scene: &BuiltScene) {
|
pub fn begin_scene(&mut self, built_scene: &BuiltScene) {
|
||||||
self.init_postprocessing_framebuffer();
|
self.init_postprocessing_framebuffer();
|
||||||
|
|
||||||
let timer_query = self.free_timer_queries
|
let timer_query = self.free_timer_queries
|
||||||
.pop()
|
.pop()
|
||||||
.unwrap_or_else(|| self.device.create_timer_query());
|
.unwrap_or_else(|| self.device.create_timer_query());
|
||||||
self.device.begin_timer_query(&timer_query);
|
self.device.begin_timer_query(&timer_query);
|
||||||
|
self.current_timer_query = Some(timer_query);
|
||||||
|
|
||||||
self.upload_shaders(&built_scene.shaders);
|
self.upload_shaders(&built_scene.shaders);
|
||||||
|
|
||||||
if self.use_depth {
|
if self.use_depth {
|
||||||
self.draw_stencil(&built_scene.quad);
|
self.draw_stencil(&built_scene.quad);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self.upload_solid_tiles(&built_scene.solid_tiles);
|
pub fn render_command(&mut self, command: &RenderCommand) {
|
||||||
self.draw_solid_tiles(&built_scene);
|
match *command {
|
||||||
|
RenderCommand::ClearMaskFramebuffer => self.clear_mask_framebuffer(),
|
||||||
for batch in &built_scene.batches {
|
RenderCommand::Fill(ref fills) => {
|
||||||
self.upload_batch(batch);
|
let count = fills.len() as u32;
|
||||||
self.draw_batch_fills(batch);
|
self.upload_fills(fills);
|
||||||
self.draw_batch_alpha_tiles(batch);
|
self.draw_fills(count);
|
||||||
|
}
|
||||||
|
RenderCommand::SolidTile(ref solid_tiles) => {
|
||||||
|
let count = solid_tiles.len() as u32;
|
||||||
|
self.upload_solid_tiles(solid_tiles);
|
||||||
|
self.draw_solid_tiles(count);
|
||||||
|
}
|
||||||
|
RenderCommand::AlphaTile(ref alpha_tiles) => {
|
||||||
|
let count = alpha_tiles.len() as u32;
|
||||||
|
self.upload_alpha_tiles(alpha_tiles);
|
||||||
|
self.draw_alpha_tiles(count);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn end_scene(&mut self) {
|
||||||
if self.postprocessing_needed() {
|
if self.postprocessing_needed() {
|
||||||
self.postprocess();
|
self.postprocess();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let timer_query = self.current_timer_query.take().unwrap();
|
||||||
self.device.end_timer_query(&timer_query);
|
self.device.end_timer_query(&timer_query);
|
||||||
self.pending_timer_queries.push_back(timer_query);
|
self.pending_timer_queries.push_back(timer_query);
|
||||||
}
|
}
|
||||||
|
@ -264,28 +283,36 @@ impl<D> Renderer<D> where D: Device {
|
||||||
self.device.upload_to_texture(&self.fill_colors_texture, size, &fill_colors);
|
self.device.upload_to_texture(&self.fill_colors_texture, size, &fill_colors);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn upload_solid_tiles(&mut self, solid_tiles: &[SolidTileScenePrimitive]) {
|
fn upload_solid_tiles(&mut self, solid_tiles: &[SolidTileBatchPrimitive]) {
|
||||||
self.device.upload_to_buffer(&self.solid_tile_vertex_array().vertex_buffer,
|
self.device.upload_to_buffer(&self.solid_tile_vertex_array().vertex_buffer,
|
||||||
solid_tiles,
|
&solid_tiles,
|
||||||
BufferTarget::Vertex,
|
BufferTarget::Vertex,
|
||||||
BufferUploadMode::Dynamic);
|
BufferUploadMode::Dynamic);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn upload_batch(&mut self, batch: &Batch) {
|
fn upload_fills(&mut self, fills: &[FillBatchPrimitive]) {
|
||||||
self.device.upload_to_buffer(&self.fill_vertex_array.vertex_buffer,
|
self.device.upload_to_buffer(&self.fill_vertex_array.vertex_buffer,
|
||||||
&batch.fills,
|
&fills,
|
||||||
BufferTarget::Vertex,
|
|
||||||
BufferUploadMode::Dynamic);
|
|
||||||
self.device.upload_to_buffer(&self.alpha_tile_vertex_array().vertex_buffer,
|
|
||||||
&batch.alpha_tiles,
|
|
||||||
BufferTarget::Vertex,
|
BufferTarget::Vertex,
|
||||||
BufferUploadMode::Dynamic);
|
BufferUploadMode::Dynamic);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_batch_fills(&mut self, batch: &Batch) {
|
fn upload_alpha_tiles(&mut self, alpha_tiles: &[AlphaTileBatchPrimitive]) {
|
||||||
|
self.device.upload_to_buffer(&self.alpha_tile_vertex_array().vertex_buffer,
|
||||||
|
&alpha_tiles,
|
||||||
|
BufferTarget::Vertex,
|
||||||
|
BufferUploadMode::Dynamic);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear_mask_framebuffer(&mut self) {
|
||||||
self.device.bind_framebuffer(&self.mask_framebuffer);
|
self.device.bind_framebuffer(&self.mask_framebuffer);
|
||||||
|
|
||||||
// TODO(pcwalton): Only clear the appropriate portion?
|
// TODO(pcwalton): Only clear the appropriate portion?
|
||||||
self.device.clear(Some(F32x4::splat(0.0)), None, None);
|
self.device.clear(Some(F32x4::splat(0.0)), None, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_fills(&mut self, count: u32) {
|
||||||
|
self.device.bind_framebuffer(&self.mask_framebuffer);
|
||||||
|
|
||||||
self.device.bind_vertex_array(&self.fill_vertex_array.vertex_array);
|
self.device.bind_vertex_array(&self.fill_vertex_array.vertex_array);
|
||||||
self.device.use_program(&self.fill_program.program);
|
self.device.use_program(&self.fill_program.program);
|
||||||
|
@ -306,13 +333,10 @@ impl<D> Renderer<D> where D: Device {
|
||||||
blend: BlendState::RGBOneAlphaOne,
|
blend: BlendState::RGBOneAlphaOne,
|
||||||
..RenderState::default()
|
..RenderState::default()
|
||||||
};
|
};
|
||||||
self.device.draw_arrays_instanced(Primitive::TriangleFan,
|
self.device.draw_arrays_instanced(Primitive::TriangleFan, 4, count, &render_state);
|
||||||
4,
|
|
||||||
batch.fills.len() as u32,
|
|
||||||
&render_state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_batch_alpha_tiles(&mut self, batch: &Batch) {
|
fn draw_alpha_tiles(&mut self, count: u32) {
|
||||||
self.bind_draw_framebuffer();
|
self.bind_draw_framebuffer();
|
||||||
|
|
||||||
let alpha_tile_vertex_array = self.alpha_tile_vertex_array();
|
let alpha_tile_vertex_array = self.alpha_tile_vertex_array();
|
||||||
|
@ -367,13 +391,10 @@ impl<D> Renderer<D> where D: Device {
|
||||||
stencil: self.stencil_state(),
|
stencil: self.stencil_state(),
|
||||||
..RenderState::default()
|
..RenderState::default()
|
||||||
};
|
};
|
||||||
self.device.draw_arrays_instanced(Primitive::TriangleFan,
|
self.device.draw_arrays_instanced(Primitive::TriangleFan, 4, count, &render_state);
|
||||||
4,
|
|
||||||
batch.alpha_tiles.len() as u32,
|
|
||||||
&render_state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_solid_tiles(&mut self, built_scene: &BuiltScene) {
|
fn draw_solid_tiles(&mut self, count: u32) {
|
||||||
self.bind_draw_framebuffer();
|
self.bind_draw_framebuffer();
|
||||||
|
|
||||||
let solid_tile_vertex_array = self.solid_tile_vertex_array();
|
let solid_tile_vertex_array = self.solid_tile_vertex_array();
|
||||||
|
@ -419,7 +440,6 @@ impl<D> Renderer<D> where D: Device {
|
||||||
stencil: self.stencil_state(),
|
stencil: self.stencil_state(),
|
||||||
..RenderState::default()
|
..RenderState::default()
|
||||||
};
|
};
|
||||||
let count = built_scene.solid_tiles.len() as u32;
|
|
||||||
self.device.draw_arrays_instanced(Primitive::TriangleFan, 4, count, &render_state);
|
self.device.draw_arrays_instanced(Primitive::TriangleFan, 4, count, &render_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@ use pathfinder_geometry::basic::point::{Point2DF32, Point3DF32};
|
||||||
use pathfinder_geometry::basic::rect::{RectF32, RectI32};
|
use pathfinder_geometry::basic::rect::{RectF32, RectI32};
|
||||||
use pathfinder_geometry::util;
|
use pathfinder_geometry::util;
|
||||||
use pathfinder_simd::default::{F32x4, I32x4};
|
use pathfinder_simd::default::{F32x4, I32x4};
|
||||||
|
use std::fmt::{Debug, Formatter, Result as DebugResult};
|
||||||
use std::ops::Add;
|
use std::ops::Add;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -34,15 +35,14 @@ pub struct BuiltScene {
|
||||||
pub view_box: RectF32,
|
pub view_box: RectF32,
|
||||||
pub quad: [Point3DF32; 4],
|
pub quad: [Point3DF32; 4],
|
||||||
pub object_count: u32,
|
pub object_count: u32,
|
||||||
pub batches: Vec<Batch>,
|
|
||||||
pub solid_tiles: Vec<SolidTileScenePrimitive>,
|
|
||||||
pub shaders: Vec<ObjectShader>,
|
pub shaders: Vec<ObjectShader>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
pub enum RenderCommand {
|
||||||
pub struct Batch {
|
ClearMaskFramebuffer,
|
||||||
pub fills: Vec<FillBatchPrimitive>,
|
Fill(Vec<FillBatchPrimitive>),
|
||||||
pub alpha_tiles: Vec<AlphaTileBatchPrimitive>,
|
AlphaTile(Vec<AlphaTileBatchPrimitive>),
|
||||||
|
SolidTile(Vec<SolidTileBatchPrimitive>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
@ -72,7 +72,7 @@ pub struct FillBatchPrimitive {
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct SolidTileScenePrimitive {
|
pub struct SolidTileBatchPrimitive {
|
||||||
pub tile_x: i16,
|
pub tile_x: i16,
|
||||||
pub tile_y: i16,
|
pub tile_y: i16,
|
||||||
pub object_index: u16,
|
pub object_index: u16,
|
||||||
|
@ -261,52 +261,38 @@ impl BuiltObject {
|
||||||
impl BuiltScene {
|
impl BuiltScene {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(view_box: RectF32, quad: &[Point3DF32; 4], object_count: u32) -> BuiltScene {
|
pub fn new(view_box: RectF32, quad: &[Point3DF32; 4], object_count: u32) -> BuiltScene {
|
||||||
BuiltScene {
|
BuiltScene { view_box, quad: *quad, object_count, shaders: vec![] }
|
||||||
view_box,
|
|
||||||
quad: *quad,
|
|
||||||
object_count,
|
|
||||||
batches: vec![],
|
|
||||||
solid_tiles: vec![],
|
|
||||||
shaders: vec![],
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn stats(&self) -> Stats {
|
pub fn stats(&self) -> Stats {
|
||||||
Stats {
|
Stats {
|
||||||
object_count: self.object_count,
|
object_count: self.object_count,
|
||||||
solid_tile_count: self.solid_tiles.len() as u32,
|
solid_tile_count: 0,
|
||||||
alpha_tile_count: self.batches
|
alpha_tile_count: 0,
|
||||||
.iter()
|
fill_count: 0,
|
||||||
.map(|batch| batch.alpha_tiles.len() as u32)
|
|
||||||
.sum(),
|
|
||||||
fill_count: self.batches.iter().map(|batch| batch.fills.len() as u32).sum(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Batch {
|
impl Debug for RenderCommand {
|
||||||
#[inline]
|
fn fmt(&self, formatter: &mut Formatter) -> DebugResult {
|
||||||
pub fn new() -> Batch {
|
match *self {
|
||||||
Batch {
|
RenderCommand::ClearMaskFramebuffer => write!(formatter, "ClearMaskFramebuffer"),
|
||||||
fills: vec![],
|
RenderCommand::Fill(ref fills) => write!(formatter, "Fill(x{})", fills.len()),
|
||||||
alpha_tiles: vec![],
|
RenderCommand::AlphaTile(ref tiles) => {
|
||||||
|
write!(formatter, "AlphaTile(x{})", tiles.len())
|
||||||
|
}
|
||||||
|
RenderCommand::SolidTile(ref tiles) => {
|
||||||
|
write!(formatter, "SolidTile(x{})", tiles.len())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn is_empty(&self) -> bool {
|
|
||||||
self.alpha_tiles.is_empty()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TileObjectPrimitive {
|
impl TileObjectPrimitive {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn new(tile_x: i16, tile_y: i16) -> TileObjectPrimitive {
|
fn new(tile_x: i16, tile_y: i16) -> TileObjectPrimitive {
|
||||||
TileObjectPrimitive {
|
TileObjectPrimitive { tile_x, tile_y, backdrop: 0 }
|
||||||
tile_x,
|
|
||||||
tile_y,
|
|
||||||
backdrop: 0,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,6 @@ pub mod gpu_data;
|
||||||
pub mod post;
|
pub mod post;
|
||||||
pub mod scene;
|
pub mod scene;
|
||||||
pub mod tiles;
|
pub mod tiles;
|
||||||
pub mod z_buffer;
|
|
||||||
|
|
||||||
mod sorted_vector;
|
mod sorted_vector;
|
||||||
|
mod z_buffer;
|
||||||
|
|
|
@ -11,16 +11,12 @@
|
||||||
//! A set of paths to be rendered.
|
//! A set of paths to be rendered.
|
||||||
|
|
||||||
use crate::builder::{PreparedRenderOptions, PreparedRenderTransform};
|
use crate::builder::{PreparedRenderOptions, PreparedRenderTransform};
|
||||||
use crate::gpu_data::BuiltObject;
|
|
||||||
use crate::tiles::Tiler;
|
|
||||||
use crate::z_buffer::ZBuffer;
|
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use pathfinder_geometry::basic::point::Point2DF32;
|
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::transform2d::Transform2DF32;
|
||||||
use pathfinder_geometry::color::ColorU;
|
use pathfinder_geometry::color::ColorU;
|
||||||
use pathfinder_geometry::outline::Outline;
|
use pathfinder_geometry::outline::Outline;
|
||||||
use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator};
|
|
||||||
use std::fmt::{self, Debug, Formatter};
|
use std::fmt::{self, Debug, Formatter};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -63,48 +59,10 @@ impl Scene {
|
||||||
}).collect()
|
}).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_objects_sequentially(&self,
|
pub(crate) fn apply_render_options(&self,
|
||||||
built_options: PreparedRenderOptions,
|
original_outline: &Outline,
|
||||||
z_buffer: &ZBuffer)
|
options: &PreparedRenderOptions)
|
||||||
-> Vec<BuiltObject> {
|
-> Outline {
|
||||||
self.objects
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(object_index, object)| {
|
|
||||||
let outline = self.apply_render_options(&object.outline, &built_options);
|
|
||||||
let mut tiler = Tiler::new(
|
|
||||||
&outline,
|
|
||||||
self.effective_view_box(&built_options),
|
|
||||||
object_index as u16,
|
|
||||||
z_buffer,
|
|
||||||
);
|
|
||||||
tiler.generate_tiles();
|
|
||||||
tiler.built_object
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn build_objects(&self, built_options: PreparedRenderOptions, z_buffer: &ZBuffer)
|
|
||||||
-> Vec<BuiltObject> {
|
|
||||||
self.objects
|
|
||||||
.par_iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(object_index, object)| {
|
|
||||||
let outline = self.apply_render_options(&object.outline, &built_options);
|
|
||||||
let mut tiler = Tiler::new(
|
|
||||||
&outline,
|
|
||||||
self.effective_view_box(&built_options),
|
|
||||||
object_index as u16,
|
|
||||||
z_buffer,
|
|
||||||
);
|
|
||||||
tiler.generate_tiles();
|
|
||||||
tiler.built_object
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn apply_render_options(&self, original_outline: &Outline, options: &PreparedRenderOptions)
|
|
||||||
-> Outline {
|
|
||||||
let effective_view_box = self.effective_view_box(options);
|
let effective_view_box = self.effective_view_box(options);
|
||||||
|
|
||||||
let mut outline;
|
let mut outline;
|
||||||
|
@ -214,12 +172,7 @@ impl PathObject {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(outline: Outline, paint: PaintId, name: String, kind: PathObjectKind)
|
pub fn new(outline: Outline, paint: PaintId, name: String, kind: PathObjectKind)
|
||||||
-> PathObject {
|
-> PathObject {
|
||||||
PathObject {
|
PathObject { outline, paint, name, kind }
|
||||||
outline,
|
|
||||||
paint,
|
|
||||||
name,
|
|
||||||
kind,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
@ -10,10 +10,11 @@
|
||||||
|
|
||||||
//! Software occlusion culling.
|
//! Software occlusion culling.
|
||||||
|
|
||||||
use crate::gpu_data::{BuiltObject, SolidTileScenePrimitive};
|
use crate::gpu_data::SolidTileBatchPrimitive;
|
||||||
use crate::scene;
|
use crate::scene;
|
||||||
use crate::tiles;
|
use crate::tiles;
|
||||||
use pathfinder_geometry::basic::rect::{RectF32, RectI32};
|
use pathfinder_geometry::basic::rect::{RectF32, RectI32};
|
||||||
|
use std::ops::Range;
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering};
|
use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering};
|
||||||
|
|
||||||
pub struct ZBuffer {
|
pub struct ZBuffer {
|
||||||
|
@ -54,11 +55,8 @@ impl ZBuffer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_solid_tiles(
|
pub fn build_solid_tiles(&self, tile_rect: RectI32, object_range: Range<u32>)
|
||||||
&self,
|
-> Vec<SolidTileBatchPrimitive> {
|
||||||
objects: &[BuiltObject],
|
|
||||||
tile_rect: RectI32,
|
|
||||||
) -> Vec<SolidTileScenePrimitive> {
|
|
||||||
let mut solid_tiles = vec![];
|
let mut solid_tiles = vec![];
|
||||||
for scene_tile_y in 0..tile_rect.size().y() {
|
for scene_tile_y in 0..tile_rect.size().y() {
|
||||||
for scene_tile_x in 0..tile_rect.size().x() {
|
for scene_tile_x in 0..tile_rect.size().x() {
|
||||||
|
@ -68,8 +66,11 @@ impl ZBuffer {
|
||||||
if depth == 0 {
|
if depth == 0 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let object_index = (depth - 1) as usize;
|
let object_index = (depth - 1) as u32;
|
||||||
solid_tiles.push(SolidTileScenePrimitive {
|
if object_index < object_range.start || object_index >= object_range.end {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
solid_tiles.push(SolidTileBatchPrimitive {
|
||||||
tile_x: (scene_tile_x + tile_rect.min_x()) as i16,
|
tile_x: (scene_tile_x + tile_rect.min_x()) as i16,
|
||||||
tile_y: (scene_tile_y + tile_rect.min_y()) as i16,
|
tile_y: (scene_tile_y + tile_rect.min_y()) as i16,
|
||||||
object_index: object_index as u16,
|
object_index: object_index as u16,
|
||||||
|
|
Loading…
Reference in New Issue