Pipeline between CPU and GPU at a more fine-grained level.

This makes us stop running one frame behind.
This commit is contained in:
Patrick Walton 2019-03-29 19:11:38 -07:00
parent d090dd459b
commit 3d4f8bd008
9 changed files with 615 additions and 369 deletions

21
Cargo.lock generated
View File

@ -169,6 +169,15 @@ dependencies = [
"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]]
name = "crossbeam-deque"
version = "0.2.0"
@ -200,6 +209,15 @@ dependencies = [
"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]]
name = "deflate"
version = "0.7.19"
@ -699,6 +717,7 @@ name = "pathfinder_renderer"
version = "0.1.0"
dependencies = [
"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)",
"hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
"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 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 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-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.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 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"

View File

@ -25,25 +25,26 @@ use pathfinder_gl::GLDevice;
use pathfinder_gpu::resources::ResourceLoader;
use pathfinder_gpu::{DepthFunc, DepthState, Device, Primitive, RenderState, StencilFunc};
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_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::scene::Scene;
use pathfinder_renderer::z_buffer::ZBuffer;
use pathfinder_svg::BuiltSVG;
use pathfinder_ui::{MousePosition, UIEvent};
use rayon::ThreadPoolBuilder;
use std::f32::consts::FRAC_PI_4;
use std::fmt::{Debug, Formatter, Result as DebugResult};
use std::fs::File;
use std::io::Read;
use std::iter;
use std::panic;
use std::panic::{self, AssertUnwindSafe};
use std::path::PathBuf;
use std::process;
use std::sync::mpsc::{self, Receiver, Sender};
use std::thread;
use std::time::{Duration, Instant};
use std::time::{Duration, Instant, SystemTime};
use usvg::{Options as UsvgOptions, Tree};
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);
// Get the render message, and determine how many scenes it contains.
let render_msg = self.scene_thread_proxy.receiver.recv().unwrap();
let render_scene_count = render_msg.render_scenes.len() as u32;
let transforms = match self.scene_thread_proxy.receiver.recv().unwrap() {
SceneToMainMsg::BeginFrame { transforms } => transforms,
msg => panic!("Expected BeginFrame message, found {:?}!", msg),
};
let render_scene_count = transforms.len() as u32;
// 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.
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),
};
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 {
Mode::VR => Some(self.window.barrel_distortion_coefficients()),
_ => None,
};
for _ in 0..frame_count {
let viewport_count = self.ui.mode.viewport_count();
let render_transforms = iter::repeat(render_transform.clone()).take(viewport_count)
.collect();
self.scene_thread_proxy.sender.send(MainToSceneMsg::Build(BuildOptions {
render_transforms,
stem_darkening_font_size: if self.ui.stem_darkening_effect_enabled {
Some(APPROX_FONT_SIZE * self.window_size.backing_scale_factor)
} else {
None
},
subpixel_aa_enabled: self.ui.subpixel_aa_effect_enabled,
barrel_distortion,
})).unwrap();
}
if is_first_frame {
self.dirty = true;
}
let viewport_count = self.ui.mode.viewport_count();
let render_transforms =
iter::repeat(render_transform.clone()).take(viewport_count).collect();
self.scene_thread_proxy.sender.send(MainToSceneMsg::Build(BuildOptions {
render_transforms,
stem_darkening_font_size: if self.ui.stem_darkening_effect_enabled {
Some(APPROX_FONT_SIZE * self.window_size.backing_scale_factor)
} else {
None
},
subpixel_aa_enabled: self.ui.subpixel_aa_effect_enabled,
barrel_distortion,
})).unwrap();
}
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.render_vector_scene(render_scene_index);
let frame = self.current_frame.as_mut().unwrap();
let render_scene = &frame.render_msg.render_scenes[render_scene_index as usize];
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()
}
if let Some(rendering_time) = self.renderer.shift_timer_query() {
self.current_frame.as_mut().unwrap().scene_rendering_times.push(rendering_time)
}
}
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 drawable_size = self.window_size.device_size();
@ -414,34 +405,41 @@ impl<W> DemoApp<W> where W: Window {
self.take_screenshot();
}
if self.options.ui != UIVisibility::None {
if let Some(render_stats) = frame.render_stats.take() {
self.renderer.debug_ui.add_sample(render_stats.stats,
frame.render_msg.tile_time,
render_stats.rendering_time);
if !frame.scene_stats.is_empty() || !frame.scene_rendering_times.is_empty() {
let zero = Stats::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, tile_time, total_rendering_time);
if self.options.ui != UIVisibility::None {
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.
//
// 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) {
let render_msg = &self.current_frame.as_ref().unwrap().render_msg;
let render_transform = &render_msg.render_scenes[viewport_index as usize].transform;
let frame = &self.current_frame.as_ref().unwrap();
let render_transform = &frame.transforms[viewport_index as usize].clone();
let perspective = match *render_transform {
RenderTransform::Transform2D(..) => return,
@ -546,8 +544,12 @@ impl<W> DemoApp<W> where W: Window {
}
fn render_vector_scene(&mut self, viewport_index: u32) {
let render_msg = &self.current_frame.as_ref().unwrap().render_msg;
let built_scene = &render_msg.render_scenes[viewport_index as usize].built_scene;
let built_scene = match self.scene_thread_proxy.receiver.recv().unwrap() {
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 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.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) {
match ui_action {
UIAction::None => {}
UIAction::TakeScreenshot(ref path) => {
self.pending_screenshot_path = Some((*path).clone());
self.dirty = true;
}
UIAction::ZoomIn => {
if let Camera::TwoD(ref mut transform) = self.camera {
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 {
if self.ui.dark_background_enabled { DARK_BG_COLOR } else { LIGHT_BG_COLOR }
}
}
struct SceneThreadProxy {
@ -647,7 +656,12 @@ impl SceneThreadProxy {
fn new(scene: Scene, options: Options) -> SceneThreadProxy {
let (main_to_scene_sender, main_to_scene_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 }
}
@ -664,6 +678,7 @@ struct SceneThread {
scene: Scene,
sender: Sender<SceneToMainMsg>,
receiver: Receiver<MainToSceneMsg>,
context: SceneBuilderContext,
options: Options,
}
@ -671,8 +686,9 @@ impl SceneThread {
fn new(scene: Scene,
sender: Sender<SceneToMainMsg>,
receiver: Receiver<MainToSceneMsg>,
context: SceneBuilderContext,
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) {
@ -687,18 +703,20 @@ impl SceneThread {
self.scene.view_box = RectF32::new(Point2DF32::default(), size.to_f32());
}
MainToSceneMsg::Build(build_options) => {
self.sender.send(SceneToMainMsg::BeginFrame {
transforms: build_options.render_transforms.clone(),
}).unwrap();
let start_time = Instant::now();
let render_scenes = build_options.render_transforms
.iter()
.map(|render_transform| {
let built_scene = build_scene(&self.scene,
&build_options,
(*render_transform).clone(),
self.options.jobs);
RenderScene { built_scene, transform: (*render_transform).clone() }
}).collect();
for render_transform in &build_options.render_transforms {
build_scene(&self.context,
&self.scene,
&build_options,
(*render_transform).clone(),
self.options.jobs,
&mut self.sender);
}
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,
}
struct SceneToMainMsg {
render_scenes: Vec<RenderScene>,
tile_time: Duration,
enum SceneToMainMsg {
BeginFrame { transforms: Vec<RenderTransform> },
EndFrame { tile_time: Duration },
BeginRenderScene(BuiltScene),
Execute(RenderCommand),
EndRenderScene,
}
pub struct RenderScene {
built_scene: BuiltScene,
transform: RenderTransform,
impl Debug for SceneToMainMsg {
fn fmt(&self, formatter: &mut Formatter) -> DebugResult {
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)]
@ -824,12 +903,6 @@ pub enum UIVisibility {
All,
}
#[derive(Clone, Copy)]
struct RenderStats {
rendering_time: Option<Duration>,
stats: Stats,
}
fn load_scene(resource_loader: &dyn ResourceLoader, input_path: &SVGPath) -> BuiltSVG {
let mut data;
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())
}
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 {
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 {
render_msg: SceneToMainMsg,
transforms: Vec<RenderTransform>,
ui_events: Vec<UIEvent>,
render_stats: Option<RenderStats>,
scene_rendering_times: Vec<Duration>,
scene_stats: Vec<Stats>,
}
impl Frame {
fn new(render_msg: SceneToMainMsg, ui_events: Vec<UIEvent>) -> Frame {
Frame { render_msg, ui_events, render_stats: None }
fn new(transforms: Vec<RenderTransform>, ui_events: Vec<UIEvent>) -> Frame {
Frame { transforms, ui_events, scene_rendering_times: vec![], scene_stats: vec![] }
}
}

View File

@ -6,6 +6,7 @@ authors = ["Patrick Walton <pcwalton@mimiga.net>"]
[dependencies]
byteorder = "1.2"
crossbeam-channel = "0.3"
fixedbitset = "0.1"
hashbrown = "0.1"
rayon = "1.0"

View File

@ -10,116 +10,339 @@
//! Packs data onto the GPU.
use crate::gpu_data::{Batch, BuiltObject, FillBatchPrimitive};
use crate::gpu_data::{AlphaTileBatchPrimitive, SolidTileScenePrimitive};
use crate::scene;
use crate::tiles;
use crate::gpu_data::{AlphaTileBatchPrimitive, BuiltObject, FillBatchPrimitive};
use crate::gpu_data::{RenderCommand, SolidTileBatchPrimitive};
use crate::scene::{self, Scene};
use crate::sorted_vector::SortedVector;
use crate::tiles::{self, Tiler};
use crate::z_buffer::ZBuffer;
use crossbeam_channel::{self, Receiver, Sender};
use pathfinder_geometry::basic::point::{Point2DF32, Point3DF32};
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::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;
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 {
objects: Vec<BuiltObject>,
z_buffer: ZBuffer,
tile_rect: RectI32,
current_object_index: usize,
pub struct SceneBuilderContext {
sender: Sender<MainToSceneAssemblyMsg>,
receiver: Receiver<SceneAssemblyToMainMsg>,
}
impl SceneBuilder {
pub fn new(objects: Vec<BuiltObject>, z_buffer: ZBuffer, view_box: RectF32) -> SceneBuilder {
SceneBuilder {
objects,
z_buffer,
tile_rect: tiles::round_rect_out_to_tile_bounds(view_box),
current_object_index: 0,
struct SceneAssemblyThread {
receiver: Receiver<MainToSceneAssemblyMsg>,
sender: Sender<SceneAssemblyToMainMsg>,
info: Option<SceneAssemblyThreadInfo>,
}
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> {
self.z_buffer
.build_solid_tiles(&self.objects, self.tile_rect)
fn add_object(&mut self, object: BuiltObject) {
// Flush current pass if necessary.
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> {
let mut batch = Batch::new();
fn flush_current_pass(&mut self) {
self.cull_alpha_tiles();
let mut object_tile_index_to_batch_alpha_tile_index = vec![];
while self.current_object_index < self.objects.len() {
let object = &self.objects[self.current_object_index];
let mut info = self.info.as_mut().unwrap();
info.current_pass.solid_tiles =
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 {
break;
}
object_tile_index_to_batch_alpha_tile_index.clear();
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;
let have_solid_tiles = !info.current_pass.solid_tiles.is_empty();
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 {
return
}
if batch.is_empty() {
None
} else {
Some(batch)
info.listener.send(RenderCommand::ClearMaskFramebuffer);
if have_solid_tiles {
let tiles = mem::replace(&mut info.current_pass.solid_tiles, vec![]);
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) }
}

View File

@ -9,7 +9,8 @@
// except according to those terms.
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::scene::ObjectShader;
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];
const MASK_FRAMEBUFFER_WIDTH: i32 = TILE_WIDTH as i32 * 256;
const MASK_FRAMEBUFFER_HEIGHT: i32 = TILE_HEIGHT as i32 * 256;
const MASK_FRAMEBUFFER_WIDTH: i32 = TILE_WIDTH as i32 * 64;
const MASK_FRAMEBUFFER_HEIGHT: i32 = TILE_HEIGHT as i32 * 64;
// TODO(pcwalton): Replace with `mem::size_of` calls?
const FILL_INSTANCE_SIZE: usize = 8;
@ -68,6 +69,7 @@ pub struct Renderer<D> where D: Device {
stencil_vertex_array: StencilVertexArray<D>,
// Debug
current_timer_query: Option<D::TimerQuery>,
pending_timer_queries: VecDeque<D::TimerQuery>,
free_timer_queries: Vec<D::TimerQuery>,
pub debug_ui: DebugUI<D>,
@ -164,6 +166,7 @@ impl<D> Renderer<D> where D: Device {
stencil_program,
stencil_vertex_array,
current_timer_query: None,
pending_timer_queries: VecDeque::new(),
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();
let timer_query = self.free_timer_queries
.pop()
.unwrap_or_else(|| self.device.create_timer_query());
self.device.begin_timer_query(&timer_query);
self.current_timer_query = Some(timer_query);
self.upload_shaders(&built_scene.shaders);
if self.use_depth {
self.draw_stencil(&built_scene.quad);
}
}
self.upload_solid_tiles(&built_scene.solid_tiles);
self.draw_solid_tiles(&built_scene);
for batch in &built_scene.batches {
self.upload_batch(batch);
self.draw_batch_fills(batch);
self.draw_batch_alpha_tiles(batch);
pub fn render_command(&mut self, command: &RenderCommand) {
match *command {
RenderCommand::ClearMaskFramebuffer => self.clear_mask_framebuffer(),
RenderCommand::Fill(ref fills) => {
let count = fills.len() as u32;
self.upload_fills(fills);
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() {
self.postprocess();
}
let timer_query = self.current_timer_query.take().unwrap();
self.device.end_timer_query(&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);
}
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,
solid_tiles,
&solid_tiles,
BufferTarget::Vertex,
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,
&batch.fills,
BufferTarget::Vertex,
BufferUploadMode::Dynamic);
self.device.upload_to_buffer(&self.alpha_tile_vertex_array().vertex_buffer,
&batch.alpha_tiles,
&fills,
BufferTarget::Vertex,
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);
// TODO(pcwalton): Only clear the appropriate portion?
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.use_program(&self.fill_program.program);
@ -306,13 +333,10 @@ impl<D> Renderer<D> where D: Device {
blend: BlendState::RGBOneAlphaOne,
..RenderState::default()
};
self.device.draw_arrays_instanced(Primitive::TriangleFan,
4,
batch.fills.len() as u32,
&render_state);
self.device.draw_arrays_instanced(Primitive::TriangleFan, 4, count, &render_state);
}
fn draw_batch_alpha_tiles(&mut self, batch: &Batch) {
fn draw_alpha_tiles(&mut self, count: u32) {
self.bind_draw_framebuffer();
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(),
..RenderState::default()
};
self.device.draw_arrays_instanced(Primitive::TriangleFan,
4,
batch.alpha_tiles.len() as u32,
&render_state);
self.device.draw_arrays_instanced(Primitive::TriangleFan, 4, count, &render_state);
}
fn draw_solid_tiles(&mut self, built_scene: &BuiltScene) {
fn draw_solid_tiles(&mut self, count: u32) {
self.bind_draw_framebuffer();
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(),
..RenderState::default()
};
let count = built_scene.solid_tiles.len() as u32;
self.device.draw_arrays_instanced(Primitive::TriangleFan, 4, count, &render_state);
}

View File

@ -18,6 +18,7 @@ use pathfinder_geometry::basic::point::{Point2DF32, Point3DF32};
use pathfinder_geometry::basic::rect::{RectF32, RectI32};
use pathfinder_geometry::util;
use pathfinder_simd::default::{F32x4, I32x4};
use std::fmt::{Debug, Formatter, Result as DebugResult};
use std::ops::Add;
#[derive(Debug)]
@ -34,15 +35,14 @@ pub struct BuiltScene {
pub view_box: RectF32,
pub quad: [Point3DF32; 4],
pub object_count: u32,
pub batches: Vec<Batch>,
pub solid_tiles: Vec<SolidTileScenePrimitive>,
pub shaders: Vec<ObjectShader>,
}
#[derive(Debug)]
pub struct Batch {
pub fills: Vec<FillBatchPrimitive>,
pub alpha_tiles: Vec<AlphaTileBatchPrimitive>,
pub enum RenderCommand {
ClearMaskFramebuffer,
Fill(Vec<FillBatchPrimitive>),
AlphaTile(Vec<AlphaTileBatchPrimitive>),
SolidTile(Vec<SolidTileBatchPrimitive>),
}
#[derive(Clone, Copy, Debug)]
@ -72,7 +72,7 @@ pub struct FillBatchPrimitive {
#[derive(Clone, Copy, Debug)]
#[repr(C)]
pub struct SolidTileScenePrimitive {
pub struct SolidTileBatchPrimitive {
pub tile_x: i16,
pub tile_y: i16,
pub object_index: u16,
@ -261,52 +261,38 @@ impl BuiltObject {
impl BuiltScene {
#[inline]
pub fn new(view_box: RectF32, quad: &[Point3DF32; 4], object_count: u32) -> BuiltScene {
BuiltScene {
view_box,
quad: *quad,
object_count,
batches: vec![],
solid_tiles: vec![],
shaders: vec![],
}
BuiltScene { view_box, quad: *quad, object_count, shaders: vec![] }
}
pub fn stats(&self) -> Stats {
Stats {
object_count: self.object_count,
solid_tile_count: self.solid_tiles.len() as u32,
alpha_tile_count: self.batches
.iter()
.map(|batch| batch.alpha_tiles.len() as u32)
.sum(),
fill_count: self.batches.iter().map(|batch| batch.fills.len() as u32).sum(),
solid_tile_count: 0,
alpha_tile_count: 0,
fill_count: 0,
}
}
}
impl Batch {
#[inline]
pub fn new() -> Batch {
Batch {
fills: vec![],
alpha_tiles: vec![],
impl Debug for RenderCommand {
fn fmt(&self, formatter: &mut Formatter) -> DebugResult {
match *self {
RenderCommand::ClearMaskFramebuffer => write!(formatter, "ClearMaskFramebuffer"),
RenderCommand::Fill(ref fills) => write!(formatter, "Fill(x{})", fills.len()),
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 {
#[inline]
fn new(tile_x: i16, tile_y: i16) -> TileObjectPrimitive {
TileObjectPrimitive {
tile_x,
tile_y,
backdrop: 0,
}
TileObjectPrimitive { tile_x, tile_y, backdrop: 0 }
}
}

View File

@ -16,6 +16,6 @@ pub mod gpu_data;
pub mod post;
pub mod scene;
pub mod tiles;
pub mod z_buffer;
mod sorted_vector;
mod z_buffer;

View File

@ -11,16 +11,12 @@
//! A set of paths to be rendered.
use crate::builder::{PreparedRenderOptions, PreparedRenderTransform};
use crate::gpu_data::BuiltObject;
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::color::ColorU;
use pathfinder_geometry::outline::Outline;
use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator};
use std::fmt::{self, Debug, Formatter};
#[derive(Clone)]
@ -63,48 +59,10 @@ impl Scene {
}).collect()
}
pub fn build_objects_sequentially(&self,
built_options: PreparedRenderOptions,
z_buffer: &ZBuffer)
-> Vec<BuiltObject> {
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 {
pub(crate) fn apply_render_options(&self,
original_outline: &Outline,
options: &PreparedRenderOptions)
-> Outline {
let effective_view_box = self.effective_view_box(options);
let mut outline;
@ -214,12 +172,7 @@ impl PathObject {
#[inline]
pub fn new(outline: Outline, paint: PaintId, name: String, kind: PathObjectKind)
-> PathObject {
PathObject {
outline,
paint,
name,
kind,
}
PathObject { outline, paint, name, kind }
}
#[inline]

View File

@ -10,10 +10,11 @@
//! Software occlusion culling.
use crate::gpu_data::{BuiltObject, SolidTileScenePrimitive};
use crate::gpu_data::SolidTileBatchPrimitive;
use crate::scene;
use crate::tiles;
use pathfinder_geometry::basic::rect::{RectF32, RectI32};
use std::ops::Range;
use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering};
pub struct ZBuffer {
@ -54,11 +55,8 @@ impl ZBuffer {
}
}
pub fn build_solid_tiles(
&self,
objects: &[BuiltObject],
tile_rect: RectI32,
) -> Vec<SolidTileScenePrimitive> {
pub fn build_solid_tiles(&self, tile_rect: RectI32, object_range: Range<u32>)
-> Vec<SolidTileBatchPrimitive> {
let mut solid_tiles = vec![];
for scene_tile_y in 0..tile_rect.size().y() {
for scene_tile_x in 0..tile_rect.size().x() {
@ -68,8 +66,11 @@ impl ZBuffer {
if depth == 0 {
continue;
}
let object_index = (depth - 1) as usize;
solid_tiles.push(SolidTileScenePrimitive {
let object_index = (depth - 1) as u32;
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_y: (scene_tile_y + tile_rect.min_y()) as i16,
object_index: object_index as u16,