diff --git a/Cargo.lock b/Cargo.lock index fac3793c..cea8c7c9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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" diff --git a/demo/common/src/lib.rs b/demo/common/src/lib.rs index 4f1abcb7..9995574f 100644 --- a/demo/common/src/lib.rs +++ b/demo/common/src/lib.rs @@ -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 DemoApp 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 DemoApp 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) -> Vec { @@ -389,22 +385,17 @@ impl DemoApp 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 DemoApp 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 DemoApp 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 DemoApp 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 DemoApp 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 DemoApp 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, receiver: Receiver, + context: SceneBuilderContext, options: Options, } @@ -671,8 +686,9 @@ impl SceneThread { fn new(scene: Scene, sender: Sender, receiver: Receiver, + 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, - tile_time: Duration, +enum SceneToMainMsg { + BeginFrame { transforms: Vec }, + 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, + sink: &mut Sender) { + 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, - 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) - -> 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, ui_events: Vec, - render_stats: Option, + scene_rendering_times: Vec, + scene_stats: Vec, } impl Frame { - fn new(render_msg: SceneToMainMsg, ui_events: Vec) -> Frame { - Frame { render_msg, ui_events, render_stats: None } + fn new(transforms: Vec, ui_events: Vec) -> Frame { + Frame { transforms, ui_events, scene_rendering_times: vec![], scene_stats: vec![] } } } diff --git a/renderer/Cargo.toml b/renderer/Cargo.toml index b87d7a08..74bd2a5d 100644 --- a/renderer/Cargo.toml +++ b/renderer/Cargo.toml @@ -6,6 +6,7 @@ authors = ["Patrick Walton "] [dependencies] byteorder = "1.2" +crossbeam-channel = "0.3" fixedbitset = "0.1" hashbrown = "0.1" rayon = "1.0" diff --git a/renderer/src/builder.rs b/renderer/src/builder.rs index 6d73c438..49a5b5d1 100644 --- a/renderer/src/builder.rs +++ b/renderer/src/builder.rs @@ -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, - z_buffer: ZBuffer, - tile_rect: RectI32, - - current_object_index: usize, +pub struct SceneBuilderContext { + sender: Sender, + receiver: Receiver, } -impl SceneBuilder { - pub fn new(objects: Vec, 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, + sender: Sender, + info: Option, +} + +struct SceneAssemblyThreadInfo { + listener: Box, + built_object_queue: SortedVector, + next_object_index: u32, + + pub(crate) z_buffer: Arc, + tile_rect: RectI32, + current_pass: Pass, +} + +enum MainToSceneAssemblyMsg { + NewScene { + listener: Box, + effective_view_box: RectF32, + z_buffer: Arc, + }, + 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, sender: Sender) + -> 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 { - 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 { - 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) { + 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) { + 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, + effective_view_box: RectF32, + z_buffer: &Arc) { + 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) { + 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, + alpha_tiles: Vec, + fills: Vec, + object_range: Range, +} + +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 { + other.index.partial_cmp(&self.index) + } +} + +impl RenderCommandListener for F where F: FnMut(RenderCommand) + Send { + #[inline] + fn send(&mut self, command: RenderCommand) { (*self)(command) } +} diff --git a/renderer/src/gpu/renderer.rs b/renderer/src/gpu/renderer.rs index d07a1f97..27610ba2 100644 --- a/renderer/src/gpu/renderer.rs +++ b/renderer/src/gpu/renderer.rs @@ -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 where D: Device { stencil_vertex_array: StencilVertexArray, // Debug + current_timer_query: Option, pending_timer_queries: VecDeque, free_timer_queries: Vec, pub debug_ui: DebugUI, @@ -164,6 +166,7 @@ impl Renderer 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 Renderer 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 Renderer 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 Renderer 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 Renderer 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 Renderer 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); } diff --git a/renderer/src/gpu_data.rs b/renderer/src/gpu_data.rs index 614e24b7..0aadd0fa 100644 --- a/renderer/src/gpu_data.rs +++ b/renderer/src/gpu_data.rs @@ -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, - pub solid_tiles: Vec, pub shaders: Vec, } -#[derive(Debug)] -pub struct Batch { - pub fills: Vec, - pub alpha_tiles: Vec, +pub enum RenderCommand { + ClearMaskFramebuffer, + Fill(Vec), + AlphaTile(Vec), + SolidTile(Vec), } #[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 } } } diff --git a/renderer/src/lib.rs b/renderer/src/lib.rs index 1dde6461..a8203bb4 100644 --- a/renderer/src/lib.rs +++ b/renderer/src/lib.rs @@ -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; diff --git a/renderer/src/scene.rs b/renderer/src/scene.rs index 3940c1a2..ec836bdb 100644 --- a/renderer/src/scene.rs +++ b/renderer/src/scene.rs @@ -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 { - 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 { - 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] diff --git a/renderer/src/z_buffer.rs b/renderer/src/z_buffer.rs index f34cf5c7..c12ddc89 100644 --- a/renderer/src/z_buffer.rs +++ b/renderer/src/z_buffer.rs @@ -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 { + pub fn build_solid_tiles(&self, tile_rect: RectI32, object_range: Range) + -> Vec { 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,