Factor the scene thread out of the demo and into the renderer proper.
This simplifies Pathfinder's API considerably. Now users get off-main-thread scene building "for free".
This commit is contained in:
parent
a96ee73da6
commit
5c5e4e9313
|
@ -28,23 +28,20 @@ use pathfinder_gl::GLDevice;
|
||||||
use pathfinder_gpu::resources::ResourceLoader;
|
use pathfinder_gpu::resources::ResourceLoader;
|
||||||
use pathfinder_gpu::{ClearParams, DepthFunc, DepthState, Device, Primitive, RenderState};
|
use pathfinder_gpu::{ClearParams, DepthFunc, DepthState, Device, Primitive, RenderState};
|
||||||
use pathfinder_gpu::{StencilFunc, StencilState, TextureFormat, UniformData};
|
use pathfinder_gpu::{StencilFunc, StencilState, TextureFormat, UniformData};
|
||||||
use pathfinder_renderer::builder::{RenderOptions, RenderTransform, SceneBuilder};
|
use pathfinder_renderer::concurrent::scene_proxy::{RenderCommandStream, SceneProxy};
|
||||||
use pathfinder_renderer::gpu::renderer::{DestFramebuffer, RenderMode, RenderStats, Renderer};
|
use pathfinder_renderer::gpu::renderer::{DestFramebuffer, RenderMode, RenderStats, Renderer};
|
||||||
use pathfinder_renderer::gpu_data::RenderCommand;
|
use pathfinder_renderer::gpu_data::RenderCommand;
|
||||||
|
use pathfinder_renderer::options::{RenderOptions, RenderTransform};
|
||||||
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, SceneDescriptor};
|
use pathfinder_renderer::scene::Scene;
|
||||||
use pathfinder_svg::BuiltSVG;
|
use pathfinder_svg::BuiltSVG;
|
||||||
use pathfinder_ui::{MousePosition, UIEvent};
|
use pathfinder_ui::{MousePosition, UIEvent};
|
||||||
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::panic::{self, AssertUnwindSafe};
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::process;
|
|
||||||
use std::sync::mpsc::{self, Receiver, Sender, SyncSender};
|
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::Duration;
|
||||||
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";
|
||||||
|
@ -96,8 +93,6 @@ const APPROX_FONT_SIZE: f32 = 16.0;
|
||||||
|
|
||||||
const MESSAGE_TIMEOUT_SECS: u64 = 5;
|
const MESSAGE_TIMEOUT_SECS: u64 = 5;
|
||||||
|
|
||||||
const MAX_MESSAGES_IN_FLIGHT: usize = 256;
|
|
||||||
|
|
||||||
// Half of the eye separation distance.
|
// Half of the eye separation distance.
|
||||||
const DEFAULT_EYE_OFFSET: f32 = 0.025;
|
const DEFAULT_EYE_OFFSET: f32 = 0.025;
|
||||||
|
|
||||||
|
@ -116,8 +111,9 @@ pub struct DemoApp<W> where W: Window {
|
||||||
|
|
||||||
window_size: WindowSize,
|
window_size: WindowSize,
|
||||||
|
|
||||||
scene_view_box: RectF32,
|
scene_metadata: SceneMetadata,
|
||||||
monochrome_scene_color: Option<ColorU>,
|
render_transform: Option<RenderTransform>,
|
||||||
|
render_command_stream: Option<RenderCommandStream>,
|
||||||
|
|
||||||
camera: Camera,
|
camera: Camera,
|
||||||
frame_counter: u32,
|
frame_counter: u32,
|
||||||
|
@ -129,9 +125,10 @@ pub struct DemoApp<W> where W: Window {
|
||||||
last_mouse_position: Point2DI32,
|
last_mouse_position: Point2DI32,
|
||||||
|
|
||||||
current_frame: Option<Frame>,
|
current_frame: Option<Frame>,
|
||||||
|
build_time: Option<Duration>,
|
||||||
|
|
||||||
ui: DemoUI<GLDevice>,
|
ui: DemoUI<GLDevice>,
|
||||||
scene_thread_proxy: SceneThreadProxy,
|
scene_proxy: SceneProxy,
|
||||||
renderer: Renderer<GLDevice>,
|
renderer: Renderer<GLDevice>,
|
||||||
|
|
||||||
scene_framebuffer: Option<<GLDevice as Device>::Framebuffer>,
|
scene_framebuffer: Option<<GLDevice as Device>::Framebuffer>,
|
||||||
|
@ -154,10 +151,8 @@ impl<W> DemoApp<W> where W: Window {
|
||||||
// Set up the executor.
|
// Set up the executor.
|
||||||
let executor = DemoExecutor::new(options.jobs);
|
let executor = DemoExecutor::new(options.jobs);
|
||||||
|
|
||||||
let built_svg = load_scene(resources, &options.input_path);
|
let mut built_svg = load_scene(resources, &options.input_path);
|
||||||
let message = get_svg_building_message(&built_svg);
|
let message = get_svg_building_message(&built_svg);
|
||||||
let scene_view_box = built_svg.scene.view_box;
|
|
||||||
let monochrome_scene_color = built_svg.scene.monochrome_color();
|
|
||||||
|
|
||||||
let viewport = window.viewport(options.mode.view(0));
|
let viewport = window.viewport(options.mode.view(0));
|
||||||
let dest_framebuffer = DestFramebuffer::Default {
|
let dest_framebuffer = DestFramebuffer::Default {
|
||||||
|
@ -166,10 +161,11 @@ impl<W> DemoApp<W> where W: Window {
|
||||||
};
|
};
|
||||||
|
|
||||||
let renderer = Renderer::new(device, resources, dest_framebuffer);
|
let renderer = Renderer::new(device, resources, dest_framebuffer);
|
||||||
let scene_thread_proxy = SceneThreadProxy::new(built_svg.scene, executor);
|
let scene_metadata = SceneMetadata::new_clipping_view_box(&mut built_svg.scene,
|
||||||
scene_thread_proxy.set_drawable_size(viewport.size());
|
viewport.size());
|
||||||
|
let camera = Camera::new(options.mode, scene_metadata.view_box, viewport.size());
|
||||||
|
|
||||||
let camera = Camera::new(options.mode, scene_view_box, viewport.size());
|
let scene_proxy = SceneProxy::new(built_svg.scene, executor);
|
||||||
|
|
||||||
let ground_program = GroundProgram::new(&renderer.device, resources);
|
let ground_program = GroundProgram::new(&renderer.device, resources);
|
||||||
let ground_solid_vertex_array = GroundSolidVertexArray::new(
|
let ground_solid_vertex_array = GroundSolidVertexArray::new(
|
||||||
|
@ -196,8 +192,9 @@ impl<W> DemoApp<W> where W: Window {
|
||||||
|
|
||||||
window_size,
|
window_size,
|
||||||
|
|
||||||
scene_view_box,
|
scene_metadata,
|
||||||
monochrome_scene_color,
|
render_transform: None,
|
||||||
|
render_command_stream: None,
|
||||||
|
|
||||||
camera,
|
camera,
|
||||||
frame_counter: 0,
|
frame_counter: 0,
|
||||||
|
@ -209,9 +206,10 @@ impl<W> DemoApp<W> where W: Window {
|
||||||
last_mouse_position: Point2DI32::default(),
|
last_mouse_position: Point2DI32::default(),
|
||||||
|
|
||||||
current_frame: None,
|
current_frame: None,
|
||||||
|
build_time: None,
|
||||||
|
|
||||||
ui,
|
ui,
|
||||||
scene_thread_proxy,
|
scene_proxy,
|
||||||
renderer,
|
renderer,
|
||||||
|
|
||||||
scene_framebuffer: None,
|
scene_framebuffer: None,
|
||||||
|
@ -232,13 +230,10 @@ impl<W> DemoApp<W> where W: Window {
|
||||||
// Update the scene.
|
// Update the scene.
|
||||||
self.build_scene();
|
self.build_scene();
|
||||||
|
|
||||||
// Get the render message, and determine how many scenes it contains.
|
|
||||||
let transform = match self.scene_thread_proxy.receiver.recv().unwrap() {
|
|
||||||
SceneToMainMsg::BeginFrame { transform } => transform,
|
|
||||||
msg => panic!("Expected BeginFrame message, found {:?}!", msg),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Save the frame.
|
// Save the frame.
|
||||||
|
//
|
||||||
|
// FIXME(pcwalton): This is super ugly.
|
||||||
|
let transform = self.render_transform.clone().unwrap();
|
||||||
self.current_frame = Some(Frame::new(transform, ui_events));
|
self.current_frame = Some(Frame::new(transform, ui_events));
|
||||||
|
|
||||||
// Initialize and set the appropriate framebuffer.
|
// Initialize and set the appropriate framebuffer.
|
||||||
|
@ -299,7 +294,7 @@ impl<W> DemoApp<W> where W: Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_scene(&mut self) {
|
fn build_scene(&mut self) {
|
||||||
let render_transform = match self.camera {
|
self.render_transform = match self.camera {
|
||||||
Camera::ThreeD {
|
Camera::ThreeD {
|
||||||
ref scene_transform,
|
ref scene_transform,
|
||||||
ref mut modelview_transform,
|
ref mut modelview_transform,
|
||||||
|
@ -313,23 +308,25 @@ impl<W> DemoApp<W> where W: Window {
|
||||||
.perspective
|
.perspective
|
||||||
.post_mul(&scene_transform.modelview_to_eye)
|
.post_mul(&scene_transform.modelview_to_eye)
|
||||||
.post_mul(&modelview_transform.to_transform());
|
.post_mul(&modelview_transform.to_transform());
|
||||||
RenderTransform::Perspective(perspective)
|
Some(RenderTransform::Perspective(perspective))
|
||||||
}
|
}
|
||||||
Camera::TwoD(transform) => RenderTransform::Transform2D(transform),
|
Camera::TwoD(transform) => Some(RenderTransform::Transform2D(transform)),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.scene_thread_proxy
|
let render_options = RenderOptions {
|
||||||
.sender
|
transform: self.render_transform.clone().unwrap(),
|
||||||
.send(MainToSceneMsg::Build(BuildOptions {
|
dilation: if self.ui.stem_darkening_effect_enabled {
|
||||||
render_transform,
|
let font_size = APPROX_FONT_SIZE * self.window_size.backing_scale_factor;
|
||||||
stem_darkening_font_size: if self.ui.stem_darkening_effect_enabled {
|
let (x, y) = (STEM_DARKENING_FACTORS[0], STEM_DARKENING_FACTORS[1]);
|
||||||
Some(APPROX_FONT_SIZE * self.window_size.backing_scale_factor)
|
Point2DF32::new(x, y).scale(font_size)
|
||||||
} else {
|
} else {
|
||||||
None
|
Point2DF32::default()
|
||||||
},
|
},
|
||||||
subpixel_aa_enabled: self.ui.subpixel_aa_effect_enabled,
|
subpixel_aa_enabled: self.ui.subpixel_aa_effect_enabled,
|
||||||
}))
|
};
|
||||||
.unwrap();
|
|
||||||
|
let built_options = render_options.prepare(self.scene_metadata.bounds);
|
||||||
|
self.render_command_stream = Some(self.scene_proxy.build_with_stream(built_options));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_events(&mut self, events: Vec<Event>) -> Vec<UIEvent> {
|
fn handle_events(&mut self, events: Vec<Event>) -> Vec<UIEvent> {
|
||||||
|
@ -345,7 +342,8 @@ impl<W> DemoApp<W> where W: Window {
|
||||||
Event::WindowResized(new_size) => {
|
Event::WindowResized(new_size) => {
|
||||||
self.window_size = new_size;
|
self.window_size = new_size;
|
||||||
let viewport = self.window.viewport(self.ui.mode.view(0));
|
let viewport = self.window.viewport(self.ui.mode.view(0));
|
||||||
self.scene_thread_proxy.set_drawable_size(viewport.size());
|
self.scene_proxy.set_view_box(RectF32::new(Point2DF32::default(),
|
||||||
|
viewport.size().to_f32()));
|
||||||
self.renderer
|
self.renderer
|
||||||
.set_main_framebuffer_size(self.window_size.device_size());
|
.set_main_framebuffer_size(self.window_size.device_size());
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
|
@ -409,7 +407,7 @@ impl<W> DemoApp<W> where W: Window {
|
||||||
ref mut velocity, ..
|
ref mut velocity, ..
|
||||||
} = self.camera
|
} = self.camera
|
||||||
{
|
{
|
||||||
let scale_factor = scale_factor_for_view_box(self.scene_view_box);
|
let scale_factor = scale_factor_for_view_box(self.scene_metadata.view_box);
|
||||||
velocity.set_z(-CAMERA_VELOCITY / scale_factor);
|
velocity.set_z(-CAMERA_VELOCITY / scale_factor);
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
}
|
}
|
||||||
|
@ -419,7 +417,7 @@ impl<W> DemoApp<W> where W: Window {
|
||||||
ref mut velocity, ..
|
ref mut velocity, ..
|
||||||
} = self.camera
|
} = self.camera
|
||||||
{
|
{
|
||||||
let scale_factor = scale_factor_for_view_box(self.scene_view_box);
|
let scale_factor = scale_factor_for_view_box(self.scene_metadata.view_box);
|
||||||
velocity.set_z(CAMERA_VELOCITY / scale_factor);
|
velocity.set_z(CAMERA_VELOCITY / scale_factor);
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
}
|
}
|
||||||
|
@ -429,7 +427,7 @@ impl<W> DemoApp<W> where W: Window {
|
||||||
ref mut velocity, ..
|
ref mut velocity, ..
|
||||||
} = self.camera
|
} = self.camera
|
||||||
{
|
{
|
||||||
let scale_factor = scale_factor_for_view_box(self.scene_view_box);
|
let scale_factor = scale_factor_for_view_box(self.scene_metadata.view_box);
|
||||||
velocity.set_x(-CAMERA_VELOCITY / scale_factor);
|
velocity.set_x(-CAMERA_VELOCITY / scale_factor);
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
}
|
}
|
||||||
|
@ -439,7 +437,7 @@ impl<W> DemoApp<W> where W: Window {
|
||||||
ref mut velocity, ..
|
ref mut velocity, ..
|
||||||
} = self.camera
|
} = self.camera
|
||||||
{
|
{
|
||||||
let scale_factor = scale_factor_for_view_box(self.scene_view_box);
|
let scale_factor = scale_factor_for_view_box(self.scene_metadata.view_box);
|
||||||
velocity.set_x(CAMERA_VELOCITY / scale_factor);
|
velocity.set_x(CAMERA_VELOCITY / scale_factor);
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
}
|
}
|
||||||
|
@ -471,18 +469,23 @@ impl<W> DemoApp<W> where W: Window {
|
||||||
UIVisibility::All => UIVisibility::None,
|
UIVisibility::All => UIVisibility::None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Event::OpenSVG(ref svg_path) => {
|
Event::OpenSVG(ref svg_path) => {
|
||||||
let built_svg = load_scene(self.window.resource_loader(), svg_path);
|
let mut built_svg = load_scene(self.window.resource_loader(), svg_path);
|
||||||
self.ui.message = get_svg_building_message(&built_svg);
|
self.ui.message = get_svg_building_message(&built_svg);
|
||||||
|
|
||||||
let viewport_size = self.window.viewport(self.ui.mode.view(0)).size();
|
let viewport_size = self.window.viewport(self.ui.mode.view(0)).size();
|
||||||
self.scene_view_box = built_svg.scene.view_box;
|
self.scene_metadata =
|
||||||
self.monochrome_scene_color = built_svg.scene.monochrome_color();
|
SceneMetadata::new_clipping_view_box(&mut built_svg.scene, viewport_size);
|
||||||
self.camera = Camera::new(self.ui.mode, self.scene_view_box, viewport_size);
|
self.camera = Camera::new(self.ui.mode,
|
||||||
self.scene_thread_proxy
|
self.scene_metadata.view_box,
|
||||||
.load_scene(built_svg.scene, viewport_size);
|
viewport_size);
|
||||||
|
|
||||||
|
self.scene_proxy.replace_scene(built_svg.scene);
|
||||||
|
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Event::User {
|
Event::User {
|
||||||
message_type: event_id,
|
message_type: event_id,
|
||||||
message_data: expected_epoch,
|
message_data: expected_epoch,
|
||||||
|
@ -574,8 +577,8 @@ impl<W> DemoApp<W> where W: Window {
|
||||||
let scene_texture = self.renderer.device.framebuffer_texture(scene_framebuffer);
|
let scene_texture = self.renderer.device.framebuffer_texture(scene_framebuffer);
|
||||||
|
|
||||||
let quad_scale_transform = Transform3DF32::from_scale(
|
let quad_scale_transform = Transform3DF32::from_scale(
|
||||||
self.scene_view_box.size().x(),
|
self.scene_metadata.view_box.size().x(),
|
||||||
self.scene_view_box.size().y(),
|
self.scene_metadata.view_box.size().y(),
|
||||||
1.0,
|
1.0,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -618,11 +621,7 @@ impl<W> DemoApp<W> where W: Window {
|
||||||
.push(rendering_time);
|
.push(rendering_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
let tile_time = match self.scene_thread_proxy.receiver.recv().unwrap() {
|
let build_time = self.build_time.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();
|
||||||
|
|
||||||
if self.pending_screenshot_path.is_some() {
|
if self.pending_screenshot_path.is_some() {
|
||||||
|
@ -645,7 +644,7 @@ impl<W> DemoApp<W> where W: Window {
|
||||||
};
|
};
|
||||||
self.renderer
|
self.renderer
|
||||||
.debug_ui
|
.debug_ui
|
||||||
.add_sample(aggregate_stats, tile_time, total_rendering_time);
|
.add_sample(aggregate_stats, build_time, total_rendering_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.options.ui != UIVisibility::None {
|
if self.options.ui != UIVisibility::None {
|
||||||
|
@ -668,7 +667,7 @@ impl<W> DemoApp<W> where W: Window {
|
||||||
.last_mouse_position
|
.last_mouse_position
|
||||||
.to_f32()
|
.to_f32()
|
||||||
.scale(self.window_size.backing_scale_factor);
|
.scale(self.window_size.backing_scale_factor);
|
||||||
self.ui.show_text_effects = self.monochrome_scene_color.is_some();
|
self.ui.show_text_effects = self.scene_metadata.monochrome_color.is_some();
|
||||||
|
|
||||||
let mut ui_action = UIAction::None;
|
let mut ui_action = UIAction::None;
|
||||||
if self.options.ui == UIVisibility::All {
|
if self.options.ui == UIVisibility::All {
|
||||||
|
@ -687,7 +686,7 @@ impl<W> DemoApp<W> where W: Window {
|
||||||
// FIXME(pcwalton): This should really be an MVC setup.
|
// FIXME(pcwalton): This should really be an MVC setup.
|
||||||
if self.camera.mode() != self.ui.mode {
|
if self.camera.mode() != self.ui.mode {
|
||||||
let viewport_size = self.window.viewport(self.ui.mode.view(0)).size();
|
let viewport_size = self.window.viewport(self.ui.mode.view(0)).size();
|
||||||
self.camera = Camera::new(self.ui.mode, self.scene_view_box, viewport_size);
|
self.camera = Camera::new(self.ui.mode, self.scene_metadata.view_box, viewport_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
for ui_event in frame.ui_events {
|
for ui_event in frame.ui_events {
|
||||||
|
@ -723,12 +722,12 @@ impl<W> DemoApp<W> where W: Window {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let ground_scale = self.scene_view_box.max_x() * 2.0;
|
let ground_scale = self.scene_metadata.view_box.max_x() * 2.0;
|
||||||
|
|
||||||
let mut base_transform = perspective.transform;
|
let mut base_transform = perspective.transform;
|
||||||
base_transform = base_transform.post_mul(&Transform3DF32::from_translation(
|
base_transform = base_transform.post_mul(&Transform3DF32::from_translation(
|
||||||
-0.5 * self.scene_view_box.max_x(),
|
-0.5 * self.scene_metadata.view_box.max_x(),
|
||||||
self.scene_view_box.max_y(),
|
self.scene_metadata.view_box.max_y(),
|
||||||
-0.5 * ground_scale,
|
-0.5 * ground_scale,
|
||||||
));
|
));
|
||||||
|
|
||||||
|
@ -803,12 +802,7 @@ impl<W> DemoApp<W> where W: Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_vector_scene(&mut self) {
|
fn render_vector_scene(&mut self) {
|
||||||
let built_scene = match self.scene_thread_proxy.receiver.recv().unwrap() {
|
match self.scene_metadata.monochrome_color {
|
||||||
SceneToMainMsg::BeginRenderScene(built_scene) => built_scene,
|
|
||||||
_ => panic!("Expected `BeginRenderScene`!"),
|
|
||||||
};
|
|
||||||
|
|
||||||
match self.monochrome_scene_color {
|
|
||||||
None => self.renderer.set_render_mode(RenderMode::Multicolor),
|
None => self.renderer.set_render_mode(RenderMode::Multicolor),
|
||||||
Some(fg_color) => {
|
Some(fg_color) => {
|
||||||
self.renderer.set_render_mode(RenderMode::Monochrome {
|
self.renderer.set_render_mode(RenderMode::Monochrome {
|
||||||
|
@ -831,13 +825,14 @@ impl<W> DemoApp<W> where W: Window {
|
||||||
self.renderer.enable_depth();
|
self.renderer.enable_depth();
|
||||||
}
|
}
|
||||||
|
|
||||||
self.renderer.begin_scene(&built_scene);
|
self.renderer.begin_scene();
|
||||||
|
|
||||||
loop {
|
// Issue render commands!
|
||||||
match self.scene_thread_proxy.receiver.recv().unwrap() {
|
for command in self.render_command_stream.as_mut().unwrap() {
|
||||||
SceneToMainMsg::Execute(command) => self.renderer.render_command(&command),
|
self.renderer.render_command(&command);
|
||||||
SceneToMainMsg::EndRenderScene => break,
|
|
||||||
_ => panic!("Expected `Execute` or `EndRenderScene`!"),
|
if let RenderCommand::Finish { build_time } = command {
|
||||||
|
self.build_time = Some(build_time);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -918,166 +913,6 @@ impl<W> DemoApp<W> where W: Window {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SceneThreadProxy {
|
|
||||||
sender: Sender<MainToSceneMsg>,
|
|
||||||
receiver: Receiver<SceneToMainMsg>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SceneThreadProxy {
|
|
||||||
fn new(scene: Scene, executor: DemoExecutor) -> SceneThreadProxy {
|
|
||||||
let (main_to_scene_sender, main_to_scene_receiver) = mpsc::channel();
|
|
||||||
let (scene_to_main_sender, scene_to_main_receiver) =
|
|
||||||
mpsc::sync_channel(MAX_MESSAGES_IN_FLIGHT);
|
|
||||||
SceneThread::new(executor, scene, scene_to_main_sender, main_to_scene_receiver);
|
|
||||||
SceneThreadProxy {
|
|
||||||
sender: main_to_scene_sender,
|
|
||||||
receiver: scene_to_main_receiver,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_scene(&self, scene: Scene, view_box_size: Point2DI32) {
|
|
||||||
self.sender
|
|
||||||
.send(MainToSceneMsg::LoadScene {
|
|
||||||
scene,
|
|
||||||
view_box_size,
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_drawable_size(&self, drawable_size: Point2DI32) {
|
|
||||||
self.sender
|
|
||||||
.send(MainToSceneMsg::SetDrawableSize(drawable_size))
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct SceneThread {
|
|
||||||
executor: DemoExecutor,
|
|
||||||
scene: Scene,
|
|
||||||
sender: SyncSender<SceneToMainMsg>,
|
|
||||||
receiver: Receiver<MainToSceneMsg>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SceneThread {
|
|
||||||
fn new(
|
|
||||||
executor: DemoExecutor,
|
|
||||||
scene: Scene,
|
|
||||||
sender: SyncSender<SceneToMainMsg>,
|
|
||||||
receiver: Receiver<MainToSceneMsg>,
|
|
||||||
) {
|
|
||||||
thread::spawn(move || (SceneThread { executor, scene, sender, receiver }).run());
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(mut self) {
|
|
||||||
while let Ok(msg) = self.receiver.recv() {
|
|
||||||
match msg {
|
|
||||||
MainToSceneMsg::LoadScene {
|
|
||||||
scene,
|
|
||||||
view_box_size,
|
|
||||||
} => {
|
|
||||||
self.scene = scene;
|
|
||||||
self.scene.view_box =
|
|
||||||
RectF32::new(Point2DF32::default(), view_box_size.to_f32());
|
|
||||||
}
|
|
||||||
|
|
||||||
MainToSceneMsg::SetDrawableSize(size) => {
|
|
||||||
self.scene.view_box = RectF32::new(Point2DF32::default(), size.to_f32());
|
|
||||||
}
|
|
||||||
|
|
||||||
MainToSceneMsg::Build(build_options) => {
|
|
||||||
self.sender
|
|
||||||
.send(SceneToMainMsg::BeginFrame {
|
|
||||||
transform: build_options.render_transform.clone(),
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let start_time = Instant::now();
|
|
||||||
self.build_scene(build_options);
|
|
||||||
let tile_time = Instant::now() - start_time;
|
|
||||||
|
|
||||||
self.sender.send(SceneToMainMsg::EndFrame { tile_time }).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn build_scene(&mut self, build_options: BuildOptions) {
|
|
||||||
let render_options = RenderOptions {
|
|
||||||
transform: build_options.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)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
subpixel_aa_enabled: build_options.subpixel_aa_enabled,
|
|
||||||
};
|
|
||||||
|
|
||||||
let built_options = render_options.prepare(self.scene.bounds);
|
|
||||||
self.sender.send(SceneToMainMsg::BeginRenderScene(
|
|
||||||
self.scene.build_descriptor(&built_options),
|
|
||||||
))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let inner_sink = AssertUnwindSafe(self.sender.clone());
|
|
||||||
let inner_scene = AssertUnwindSafe(&self.scene);
|
|
||||||
let inner_executor = AssertUnwindSafe(&self.executor);
|
|
||||||
let result = panic::catch_unwind(move || {
|
|
||||||
let sink = (*inner_sink).clone();
|
|
||||||
let listener =
|
|
||||||
Box::new(move |command| sink.send(SceneToMainMsg::Execute(command)).unwrap());
|
|
||||||
SceneBuilder::new(*inner_scene, &built_options, listener).build(*inner_executor)
|
|
||||||
});
|
|
||||||
|
|
||||||
if result.is_err() {
|
|
||||||
error!("Scene building crashed! Dumping scene:");
|
|
||||||
println!("{:?}", self.scene);
|
|
||||||
process::exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.sender.send(SceneToMainMsg::EndRenderScene).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
enum MainToSceneMsg {
|
|
||||||
LoadScene {
|
|
||||||
scene: Scene,
|
|
||||||
view_box_size: Point2DI32,
|
|
||||||
},
|
|
||||||
SetDrawableSize(Point2DI32),
|
|
||||||
Build(BuildOptions),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
struct BuildOptions {
|
|
||||||
render_transform: RenderTransform,
|
|
||||||
stem_darkening_font_size: Option<f32>,
|
|
||||||
subpixel_aa_enabled: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
enum SceneToMainMsg {
|
|
||||||
BeginFrame { transform: RenderTransform },
|
|
||||||
EndFrame { tile_time: Duration },
|
|
||||||
BeginRenderScene(SceneDescriptor),
|
|
||||||
Execute(RenderCommand),
|
|
||||||
EndRenderScene,
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Options {
|
pub struct Options {
|
||||||
pub jobs: Option<usize>,
|
pub jobs: Option<usize>,
|
||||||
|
@ -1439,3 +1274,20 @@ impl BackgroundColor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct SceneMetadata {
|
||||||
|
view_box: RectF32,
|
||||||
|
bounds: RectF32,
|
||||||
|
monochrome_color: Option<ColorU>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SceneMetadata {
|
||||||
|
// FIXME(pcwalton): The fact that this mutates the scene is really ugly!
|
||||||
|
// Can we simplify this?
|
||||||
|
fn new_clipping_view_box(scene: &mut Scene, viewport_size: Point2DI32) -> SceneMetadata {
|
||||||
|
let view_box = scene.view_box();
|
||||||
|
let monochrome_color = scene.monochrome_color();
|
||||||
|
scene.set_view_box(RectF32::new(Point2DF32::default(), viewport_size.to_f32()));
|
||||||
|
SceneMetadata { view_box, monochrome_color, bounds: scene.bounds() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -12,21 +12,15 @@
|
||||||
|
|
||||||
use crate::concurrent::executor::Executor;
|
use crate::concurrent::executor::Executor;
|
||||||
use crate::gpu_data::{AlphaTileBatchPrimitive, RenderCommand};
|
use crate::gpu_data::{AlphaTileBatchPrimitive, RenderCommand};
|
||||||
|
use crate::options::{PreparedRenderOptions, RenderCommandListener};
|
||||||
use crate::scene::Scene;
|
use crate::scene::Scene;
|
||||||
use crate::tiles::Tiler;
|
use crate::tiles::Tiler;
|
||||||
use crate::z_buffer::ZBuffer;
|
use crate::z_buffer::ZBuffer;
|
||||||
use pathfinder_geometry::basic::point::{Point2DF32, Point3DF32};
|
|
||||||
use pathfinder_geometry::basic::rect::RectF32;
|
use pathfinder_geometry::basic::rect::RectF32;
|
||||||
use pathfinder_geometry::basic::transform2d::Transform2DF32;
|
|
||||||
use pathfinder_geometry::basic::transform3d::Perspective;
|
|
||||||
use pathfinder_geometry::clip::PolygonClipper3D;
|
|
||||||
use std::sync::atomic::AtomicUsize;
|
use std::sync::atomic::AtomicUsize;
|
||||||
|
use std::time::Instant;
|
||||||
use std::u16;
|
use std::u16;
|
||||||
|
|
||||||
pub trait RenderCommandListener: Send + Sync {
|
|
||||||
fn send(&self, command: RenderCommand);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct SceneBuilder<'a> {
|
pub struct SceneBuilder<'a> {
|
||||||
scene: &'a Scene,
|
scene: &'a Scene,
|
||||||
built_options: &'a PreparedRenderOptions,
|
built_options: &'a PreparedRenderOptions,
|
||||||
|
@ -54,12 +48,23 @@ impl<'a> SceneBuilder<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build<E>(&mut self, executor: &E) where E: Executor {
|
pub fn build<E>(&mut self, executor: &E) where E: Executor {
|
||||||
let effective_view_box = self.scene.effective_view_box(self.built_options);
|
let start_time = Instant::now();
|
||||||
|
|
||||||
|
let bounding_quad = self.built_options.bounding_quad();
|
||||||
let object_count = self.scene.objects.len();
|
let object_count = self.scene.objects.len();
|
||||||
|
self.listener.send(RenderCommand::Start { bounding_quad, object_count });
|
||||||
|
|
||||||
|
self.listener.send(RenderCommand::AddShaders(self.scene.build_shaders()));
|
||||||
|
|
||||||
|
let effective_view_box = self.scene.effective_view_box(self.built_options);
|
||||||
let alpha_tiles = executor.flatten_into_vector(object_count, |object_index| {
|
let alpha_tiles = executor.flatten_into_vector(object_count, |object_index| {
|
||||||
self.build_object(object_index, effective_view_box, &self.built_options, &self.scene)
|
self.build_object(object_index, effective_view_box, &self.built_options, &self.scene)
|
||||||
});
|
});
|
||||||
self.finish_building(alpha_tiles)
|
|
||||||
|
self.finish_building(alpha_tiles);
|
||||||
|
|
||||||
|
let build_time = Instant::now() - start_time;
|
||||||
|
self.listener.send(RenderCommand::Finish { build_time });
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_object(
|
fn build_object(
|
||||||
|
@ -75,8 +80,7 @@ impl<'a> SceneBuilder<'a> {
|
||||||
let mut tiler = Tiler::new(self, &outline, view_box, object_index as u16);
|
let mut tiler = Tiler::new(self, &outline, view_box, object_index as u16);
|
||||||
tiler.generate_tiles();
|
tiler.generate_tiles();
|
||||||
|
|
||||||
self.listener
|
self.listener.send(RenderCommand::AddFills(tiler.built_object.fills));
|
||||||
.send(RenderCommand::AddFills(tiler.built_object.fills));
|
|
||||||
tiler.built_object.alpha_tiles
|
tiler.built_object.alpha_tiles
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,140 +119,6 @@ impl<'a> SceneBuilder<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Default)]
|
|
||||||
pub struct RenderOptions {
|
|
||||||
pub transform: RenderTransform,
|
|
||||||
pub dilation: Point2DF32,
|
|
||||||
pub subpixel_aa_enabled: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RenderOptions {
|
|
||||||
pub fn prepare(self, bounds: RectF32) -> PreparedRenderOptions {
|
|
||||||
PreparedRenderOptions {
|
|
||||||
transform: self.transform.prepare(bounds),
|
|
||||||
dilation: self.dilation,
|
|
||||||
subpixel_aa_enabled: self.subpixel_aa_enabled,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub enum RenderTransform {
|
|
||||||
Transform2D(Transform2DF32),
|
|
||||||
Perspective(Perspective),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for RenderTransform {
|
|
||||||
#[inline]
|
|
||||||
fn default() -> RenderTransform {
|
|
||||||
RenderTransform::Transform2D(Transform2DF32::default())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RenderTransform {
|
|
||||||
fn prepare(&self, bounds: RectF32) -> PreparedRenderTransform {
|
|
||||||
let perspective = match self {
|
|
||||||
RenderTransform::Transform2D(ref transform) => {
|
|
||||||
if transform.is_identity() {
|
|
||||||
return PreparedRenderTransform::None;
|
|
||||||
}
|
|
||||||
return PreparedRenderTransform::Transform2D(*transform);
|
|
||||||
}
|
|
||||||
RenderTransform::Perspective(ref perspective) => *perspective,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut points = vec![
|
|
||||||
bounds.origin().to_3d(),
|
|
||||||
bounds.upper_right().to_3d(),
|
|
||||||
bounds.lower_right().to_3d(),
|
|
||||||
bounds.lower_left().to_3d(),
|
|
||||||
];
|
|
||||||
debug!("-----");
|
|
||||||
debug!("bounds={:?} ORIGINAL quad={:?}", bounds, points);
|
|
||||||
for point in &mut points {
|
|
||||||
*point = perspective.transform.transform_point(*point);
|
|
||||||
}
|
|
||||||
debug!("... PERSPECTIVE quad={:?}", points);
|
|
||||||
|
|
||||||
// Compute depth.
|
|
||||||
let quad = [
|
|
||||||
points[0].perspective_divide(),
|
|
||||||
points[1].perspective_divide(),
|
|
||||||
points[2].perspective_divide(),
|
|
||||||
points[3].perspective_divide(),
|
|
||||||
];
|
|
||||||
debug!("... PERSPECTIVE-DIVIDED points = {:?}", quad);
|
|
||||||
|
|
||||||
points = PolygonClipper3D::new(points).clip();
|
|
||||||
debug!("... CLIPPED quad={:?}", points);
|
|
||||||
for point in &mut points {
|
|
||||||
*point = point.perspective_divide()
|
|
||||||
}
|
|
||||||
|
|
||||||
let inverse_transform = perspective.transform.inverse();
|
|
||||||
let clip_polygon = points
|
|
||||||
.into_iter()
|
|
||||||
.map(|point| {
|
|
||||||
inverse_transform
|
|
||||||
.transform_point(point)
|
|
||||||
.perspective_divide()
|
|
||||||
.to_2d()
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
return PreparedRenderTransform::Perspective {
|
|
||||||
perspective,
|
|
||||||
clip_polygon,
|
|
||||||
quad,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct PreparedRenderOptions {
|
|
||||||
pub transform: PreparedRenderTransform,
|
|
||||||
pub dilation: Point2DF32,
|
|
||||||
pub subpixel_aa_enabled: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PreparedRenderOptions {
|
|
||||||
#[inline]
|
|
||||||
pub fn bounding_quad(&self) -> [Point3DF32; 4] {
|
|
||||||
match self.transform {
|
|
||||||
PreparedRenderTransform::Perspective { quad, .. } => quad,
|
|
||||||
_ => [Point3DF32::default(); 4],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum PreparedRenderTransform {
|
|
||||||
None,
|
|
||||||
Transform2D(Transform2DF32),
|
|
||||||
Perspective {
|
|
||||||
perspective: Perspective,
|
|
||||||
clip_polygon: Vec<Point2DF32>,
|
|
||||||
quad: [Point3DF32; 4],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PreparedRenderTransform {
|
|
||||||
#[inline]
|
|
||||||
pub fn is_2d(&self) -> bool {
|
|
||||||
match *self {
|
|
||||||
PreparedRenderTransform::Transform2D(_) => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<F> RenderCommandListener for F
|
|
||||||
where
|
|
||||||
F: Fn(RenderCommand) + Send + Sync,
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
fn send(&self, command: RenderCommand) {
|
|
||||||
(*self)(command)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default)]
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
pub struct TileStats {
|
pub struct TileStats {
|
||||||
pub solid_tile_count: u32,
|
pub solid_tile_count: u32,
|
||||||
|
|
|
@ -12,3 +12,4 @@
|
||||||
|
|
||||||
pub mod executor;
|
pub mod executor;
|
||||||
pub mod rayon;
|
pub mod rayon;
|
||||||
|
pub mod scene_proxy;
|
||||||
|
|
|
@ -0,0 +1,116 @@
|
||||||
|
// pathfinder/renderer/src/concurrent/scene_proxy.rs
|
||||||
|
//
|
||||||
|
// Copyright © 2019 The Pathfinder Project Developers.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
//! A version of `Scene` that proxies all method calls out to a separate
|
||||||
|
//! thread.
|
||||||
|
//!
|
||||||
|
//! This is useful for:
|
||||||
|
//!
|
||||||
|
//! * Avoiding GPU driver stalls on synchronous APIs such as OpenGL.
|
||||||
|
//!
|
||||||
|
//! * Avoiding UI latency by building scenes off the main thread.
|
||||||
|
//!
|
||||||
|
//! You don't need to use this API to use Pathfinder; it's only a convenience.
|
||||||
|
|
||||||
|
use crate::concurrent::executor::Executor;
|
||||||
|
use crate::gpu_data::RenderCommand;
|
||||||
|
use crate::options::{PreparedRenderOptions, RenderCommandListener};
|
||||||
|
use crate::scene::Scene;
|
||||||
|
use pathfinder_geometry::basic::rect::RectF32;
|
||||||
|
use std::sync::mpsc::{self, Receiver, Sender};
|
||||||
|
use std::thread;
|
||||||
|
|
||||||
|
const MAX_MESSAGES_IN_FLIGHT: usize = 1024;
|
||||||
|
|
||||||
|
pub struct SceneProxy {
|
||||||
|
sender: Sender<MainToWorkerMsg>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SceneProxy {
|
||||||
|
pub fn new<E>(scene: Scene, executor: E) -> SceneProxy where E: Executor + Send + 'static {
|
||||||
|
let (main_to_worker_sender, main_to_worker_receiver) = mpsc::channel();
|
||||||
|
thread::spawn(move || scene_thread(scene, executor, main_to_worker_receiver));
|
||||||
|
SceneProxy { sender: main_to_worker_sender }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn replace_scene(&self, new_scene: Scene) {
|
||||||
|
self.sender.send(MainToWorkerMsg::ReplaceScene(new_scene)).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_view_box(&self, new_view_box: RectF32) {
|
||||||
|
self.sender.send(MainToWorkerMsg::SetViewBox(new_view_box)).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn build_with_listener(&self,
|
||||||
|
built_options: PreparedRenderOptions,
|
||||||
|
listener: Box<dyn RenderCommandListener>) {
|
||||||
|
self.sender.send(MainToWorkerMsg::Build(built_options, listener)).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn build_with_stream(&self, built_options: PreparedRenderOptions) -> RenderCommandStream {
|
||||||
|
let (sender, receiver) = mpsc::sync_channel(MAX_MESSAGES_IN_FLIGHT);
|
||||||
|
let listener = Box::new(move |command| sender.send(command).unwrap());
|
||||||
|
self.build_with_listener(built_options, listener);
|
||||||
|
RenderCommandStream::new(receiver)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn scene_thread<E>(mut scene: Scene,
|
||||||
|
executor: E,
|
||||||
|
main_to_worker_receiver: Receiver<MainToWorkerMsg>)
|
||||||
|
where E: Executor {
|
||||||
|
while let Ok(msg) = main_to_worker_receiver.recv() {
|
||||||
|
match msg {
|
||||||
|
MainToWorkerMsg::ReplaceScene(new_scene) => scene = new_scene,
|
||||||
|
MainToWorkerMsg::SetViewBox(new_view_box) => scene.set_view_box(new_view_box),
|
||||||
|
MainToWorkerMsg::Build(options, listener) => {
|
||||||
|
scene.build(&options, listener, &executor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum MainToWorkerMsg {
|
||||||
|
ReplaceScene(Scene),
|
||||||
|
SetViewBox(RectF32),
|
||||||
|
Build(PreparedRenderOptions, Box<dyn RenderCommandListener>),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct RenderCommandStream {
|
||||||
|
receiver: Receiver<RenderCommand>,
|
||||||
|
done: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RenderCommandStream {
|
||||||
|
fn new(receiver: Receiver<RenderCommand>) -> RenderCommandStream {
|
||||||
|
RenderCommandStream { receiver, done: false }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for RenderCommandStream {
|
||||||
|
type Item = RenderCommand;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn next(&mut self) -> Option<RenderCommand> {
|
||||||
|
if self.done {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let command = self.receiver.recv().unwrap();
|
||||||
|
if let RenderCommand::Finish { .. } = command {
|
||||||
|
self.done = true;
|
||||||
|
}
|
||||||
|
Some(command)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,7 +12,7 @@ use crate::gpu::debug::DebugUI;
|
||||||
use crate::gpu_data::{AlphaTileBatchPrimitive, FillBatchPrimitive};
|
use crate::gpu_data::{AlphaTileBatchPrimitive, FillBatchPrimitive};
|
||||||
use crate::gpu_data::{RenderCommand, SolidTileBatchPrimitive};
|
use crate::gpu_data::{RenderCommand, SolidTileBatchPrimitive};
|
||||||
use crate::post::DefringingKernel;
|
use crate::post::DefringingKernel;
|
||||||
use crate::scene::{ObjectShader, SceneDescriptor};
|
use crate::scene::ObjectShader;
|
||||||
use crate::tiles::{TILE_HEIGHT, TILE_WIDTH};
|
use crate::tiles::{TILE_HEIGHT, TILE_WIDTH};
|
||||||
use pathfinder_geometry::basic::point::{Point2DI32, Point3DF32};
|
use pathfinder_geometry::basic::point::{Point2DI32, Point3DF32};
|
||||||
use pathfinder_geometry::basic::rect::RectI32;
|
use pathfinder_geometry::basic::rect::RectI32;
|
||||||
|
@ -221,7 +221,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn begin_scene(&mut self, scene: &SceneDescriptor) {
|
pub fn begin_scene(&mut self) {
|
||||||
self.init_postprocessing_framebuffer();
|
self.init_postprocessing_framebuffer();
|
||||||
|
|
||||||
let timer_query = self
|
let timer_query = self
|
||||||
|
@ -231,22 +231,19 @@ where
|
||||||
self.device.begin_timer_query(&timer_query);
|
self.device.begin_timer_query(&timer_query);
|
||||||
self.current_timer_query = Some(timer_query);
|
self.current_timer_query = Some(timer_query);
|
||||||
|
|
||||||
self.upload_shaders(&scene.shaders);
|
|
||||||
|
|
||||||
if self.use_depth {
|
|
||||||
self.draw_stencil(&scene.bounding_quad);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.mask_framebuffer_cleared = false;
|
self.mask_framebuffer_cleared = false;
|
||||||
|
self.stats = RenderStats::default();
|
||||||
self.stats = RenderStats {
|
|
||||||
object_count: scene.object_count,
|
|
||||||
..RenderStats::default()
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_command(&mut self, command: &RenderCommand) {
|
pub fn render_command(&mut self, command: &RenderCommand) {
|
||||||
match *command {
|
match *command {
|
||||||
|
RenderCommand::Start { bounding_quad, object_count } => {
|
||||||
|
if self.use_depth {
|
||||||
|
self.draw_stencil(&bounding_quad);
|
||||||
|
}
|
||||||
|
self.stats.object_count = object_count;
|
||||||
|
}
|
||||||
|
RenderCommand::AddShaders(ref shaders) => self.upload_shaders(shaders),
|
||||||
RenderCommand::AddFills(ref fills) => self.add_fills(fills),
|
RenderCommand::AddFills(ref fills) => self.add_fills(fills),
|
||||||
RenderCommand::FlushFills => self.draw_buffered_fills(),
|
RenderCommand::FlushFills => self.draw_buffered_fills(),
|
||||||
RenderCommand::SolidTile(ref solid_tiles) => {
|
RenderCommand::SolidTile(ref solid_tiles) => {
|
||||||
|
@ -261,6 +258,7 @@ where
|
||||||
self.upload_alpha_tiles(alpha_tiles);
|
self.upload_alpha_tiles(alpha_tiles);
|
||||||
self.draw_alpha_tiles(count as u32);
|
self.draw_alpha_tiles(count as u32);
|
||||||
}
|
}
|
||||||
|
RenderCommand::Finish { .. } => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
//! Packed data ready to be sent to the GPU.
|
//! Packed data ready to be sent to the GPU.
|
||||||
|
|
||||||
use crate::builder::SceneBuilder;
|
use crate::builder::SceneBuilder;
|
||||||
|
use crate::options::BoundingQuad;
|
||||||
|
use crate::scene::ObjectShader;
|
||||||
use crate::tile_map::DenseTileMap;
|
use crate::tile_map::DenseTileMap;
|
||||||
use crate::tiles::{self, TILE_HEIGHT, TILE_WIDTH};
|
use crate::tiles::{self, TILE_HEIGHT, TILE_WIDTH};
|
||||||
use pathfinder_geometry::basic::line_segment::{LineSegmentF32, LineSegmentU4, LineSegmentU8};
|
use pathfinder_geometry::basic::line_segment::{LineSegmentF32, LineSegmentU4, LineSegmentU8};
|
||||||
|
@ -20,6 +22,7 @@ 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::fmt::{Debug, Formatter, Result as DebugResult};
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct BuiltObject {
|
pub(crate) struct BuiltObject {
|
||||||
|
@ -30,10 +33,13 @@ pub(crate) struct BuiltObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum RenderCommand {
|
pub enum RenderCommand {
|
||||||
|
Start { object_count: usize, bounding_quad: BoundingQuad },
|
||||||
|
AddShaders(Vec<ObjectShader>),
|
||||||
AddFills(Vec<FillBatchPrimitive>),
|
AddFills(Vec<FillBatchPrimitive>),
|
||||||
FlushFills,
|
FlushFills,
|
||||||
AlphaTile(Vec<AlphaTileBatchPrimitive>),
|
AlphaTile(Vec<AlphaTileBatchPrimitive>),
|
||||||
SolidTile(Vec<SolidTileBatchPrimitive>),
|
SolidTile(Vec<SolidTileBatchPrimitive>),
|
||||||
|
Finish { build_time: Duration },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
@ -319,10 +325,15 @@ impl AlphaTileBatchPrimitive {
|
||||||
impl Debug for RenderCommand {
|
impl Debug for RenderCommand {
|
||||||
fn fmt(&self, formatter: &mut Formatter) -> DebugResult {
|
fn fmt(&self, formatter: &mut Formatter) -> DebugResult {
|
||||||
match *self {
|
match *self {
|
||||||
|
RenderCommand::Start { .. } => write!(formatter, "Start"),
|
||||||
|
RenderCommand::AddShaders(ref shaders) => {
|
||||||
|
write!(formatter, "AddShaders(x{})", shaders.len())
|
||||||
|
}
|
||||||
RenderCommand::AddFills(ref fills) => write!(formatter, "AddFills(x{})", fills.len()),
|
RenderCommand::AddFills(ref fills) => write!(formatter, "AddFills(x{})", fills.len()),
|
||||||
RenderCommand::FlushFills => write!(formatter, "FlushFills"),
|
RenderCommand::FlushFills => write!(formatter, "FlushFills"),
|
||||||
RenderCommand::AlphaTile(ref tiles) => write!(formatter, "AlphaTile(x{})", tiles.len()),
|
RenderCommand::AlphaTile(ref tiles) => write!(formatter, "AlphaTile(x{})", tiles.len()),
|
||||||
RenderCommand::SolidTile(ref tiles) => write!(formatter, "SolidTile(x{})", tiles.len()),
|
RenderCommand::SolidTile(ref tiles) => write!(formatter, "SolidTile(x{})", tiles.len()),
|
||||||
|
RenderCommand::Finish { .. } => write!(formatter, "Finish"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,14 +13,15 @@
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate log;
|
extern crate log;
|
||||||
|
|
||||||
pub mod builder;
|
|
||||||
pub mod concurrent;
|
pub mod concurrent;
|
||||||
pub mod gpu;
|
pub mod gpu;
|
||||||
pub mod gpu_data;
|
pub mod gpu_data;
|
||||||
|
pub mod options;
|
||||||
pub mod post;
|
pub mod post;
|
||||||
pub mod scene;
|
pub mod scene;
|
||||||
pub mod tiles;
|
|
||||||
|
|
||||||
|
mod builder;
|
||||||
mod sorted_vector;
|
mod sorted_vector;
|
||||||
mod tile_map;
|
mod tile_map;
|
||||||
|
mod tiles;
|
||||||
mod z_buffer;
|
mod z_buffer;
|
||||||
|
|
|
@ -0,0 +1,158 @@
|
||||||
|
// pathfinder/renderer/src/options.rs
|
||||||
|
//
|
||||||
|
// Copyright © 2019 The Pathfinder Project Developers.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
//! Options that control how rendering is to be performed.
|
||||||
|
|
||||||
|
use crate::gpu_data::RenderCommand;
|
||||||
|
use pathfinder_geometry::basic::point::{Point2DF32, Point3DF32};
|
||||||
|
use pathfinder_geometry::basic::rect::RectF32;
|
||||||
|
use pathfinder_geometry::basic::transform2d::Transform2DF32;
|
||||||
|
use pathfinder_geometry::basic::transform3d::Perspective;
|
||||||
|
use pathfinder_geometry::clip::PolygonClipper3D;
|
||||||
|
|
||||||
|
pub trait RenderCommandListener: Send + Sync {
|
||||||
|
fn send(&self, command: RenderCommand);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F> RenderCommandListener for F
|
||||||
|
where
|
||||||
|
F: Fn(RenderCommand) + Send + Sync,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn send(&self, command: RenderCommand) {
|
||||||
|
(*self)(command)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Default)]
|
||||||
|
pub struct RenderOptions {
|
||||||
|
pub transform: RenderTransform,
|
||||||
|
pub dilation: Point2DF32,
|
||||||
|
pub subpixel_aa_enabled: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RenderOptions {
|
||||||
|
pub fn prepare(self, bounds: RectF32) -> PreparedRenderOptions {
|
||||||
|
PreparedRenderOptions {
|
||||||
|
transform: self.transform.prepare(bounds),
|
||||||
|
dilation: self.dilation,
|
||||||
|
subpixel_aa_enabled: self.subpixel_aa_enabled,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub enum RenderTransform {
|
||||||
|
Transform2D(Transform2DF32),
|
||||||
|
Perspective(Perspective),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for RenderTransform {
|
||||||
|
#[inline]
|
||||||
|
fn default() -> RenderTransform {
|
||||||
|
RenderTransform::Transform2D(Transform2DF32::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RenderTransform {
|
||||||
|
fn prepare(&self, bounds: RectF32) -> PreparedRenderTransform {
|
||||||
|
let perspective = match self {
|
||||||
|
RenderTransform::Transform2D(ref transform) => {
|
||||||
|
if transform.is_identity() {
|
||||||
|
return PreparedRenderTransform::None;
|
||||||
|
}
|
||||||
|
return PreparedRenderTransform::Transform2D(*transform);
|
||||||
|
}
|
||||||
|
RenderTransform::Perspective(ref perspective) => *perspective,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut points = vec![
|
||||||
|
bounds.origin().to_3d(),
|
||||||
|
bounds.upper_right().to_3d(),
|
||||||
|
bounds.lower_right().to_3d(),
|
||||||
|
bounds.lower_left().to_3d(),
|
||||||
|
];
|
||||||
|
debug!("-----");
|
||||||
|
debug!("bounds={:?} ORIGINAL quad={:?}", bounds, points);
|
||||||
|
for point in &mut points {
|
||||||
|
*point = perspective.transform.transform_point(*point);
|
||||||
|
}
|
||||||
|
debug!("... PERSPECTIVE quad={:?}", points);
|
||||||
|
|
||||||
|
// Compute depth.
|
||||||
|
let quad = [
|
||||||
|
points[0].perspective_divide(),
|
||||||
|
points[1].perspective_divide(),
|
||||||
|
points[2].perspective_divide(),
|
||||||
|
points[3].perspective_divide(),
|
||||||
|
];
|
||||||
|
debug!("... PERSPECTIVE-DIVIDED points = {:?}", quad);
|
||||||
|
|
||||||
|
points = PolygonClipper3D::new(points).clip();
|
||||||
|
debug!("... CLIPPED quad={:?}", points);
|
||||||
|
for point in &mut points {
|
||||||
|
*point = point.perspective_divide()
|
||||||
|
}
|
||||||
|
|
||||||
|
let inverse_transform = perspective.transform.inverse();
|
||||||
|
let clip_polygon = points
|
||||||
|
.into_iter()
|
||||||
|
.map(|point| {
|
||||||
|
inverse_transform
|
||||||
|
.transform_point(point)
|
||||||
|
.perspective_divide()
|
||||||
|
.to_2d()
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
return PreparedRenderTransform::Perspective {
|
||||||
|
perspective,
|
||||||
|
clip_polygon,
|
||||||
|
quad,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PreparedRenderOptions {
|
||||||
|
pub transform: PreparedRenderTransform,
|
||||||
|
pub dilation: Point2DF32,
|
||||||
|
pub subpixel_aa_enabled: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PreparedRenderOptions {
|
||||||
|
#[inline]
|
||||||
|
pub fn bounding_quad(&self) -> BoundingQuad {
|
||||||
|
match self.transform {
|
||||||
|
PreparedRenderTransform::Perspective { quad, .. } => quad,
|
||||||
|
_ => [Point3DF32::default(); 4],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type BoundingQuad = [Point3DF32; 4];
|
||||||
|
|
||||||
|
pub enum PreparedRenderTransform {
|
||||||
|
None,
|
||||||
|
Transform2D(Transform2DF32),
|
||||||
|
Perspective {
|
||||||
|
perspective: Perspective,
|
||||||
|
clip_polygon: Vec<Point2DF32>,
|
||||||
|
quad: [Point3DF32; 4],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PreparedRenderTransform {
|
||||||
|
#[inline]
|
||||||
|
pub fn is_2d(&self) -> bool {
|
||||||
|
match *self {
|
||||||
|
PreparedRenderTransform::Transform2D(_) => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,9 +10,11 @@
|
||||||
|
|
||||||
//! A set of paths to be rendered.
|
//! A set of paths to be rendered.
|
||||||
|
|
||||||
use crate::builder::{PreparedRenderOptions, PreparedRenderTransform};
|
use crate::builder::SceneBuilder;
|
||||||
|
use crate::concurrent::executor::Executor;
|
||||||
|
use crate::options::{PreparedRenderOptions, PreparedRenderTransform, RenderCommandListener};
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use pathfinder_geometry::basic::point::{Point2DF32, Point3DF32};
|
use pathfinder_geometry::basic::point::Point2DF32;
|
||||||
use pathfinder_geometry::basic::rect::RectF32;
|
use pathfinder_geometry::basic::rect::RectF32;
|
||||||
use pathfinder_geometry::basic::transform2d::Transform2DF32;
|
use pathfinder_geometry::basic::transform2d::Transform2DF32;
|
||||||
use pathfinder_geometry::color::ColorU;
|
use pathfinder_geometry::color::ColorU;
|
||||||
|
@ -21,11 +23,11 @@ use std::fmt::{self, Debug, Formatter};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Scene {
|
pub struct Scene {
|
||||||
pub objects: Vec<PathObject>,
|
pub(crate) objects: Vec<PathObject>,
|
||||||
pub paints: Vec<Paint>,
|
paints: Vec<Paint>,
|
||||||
pub paint_cache: HashMap<Paint, PaintId>,
|
paint_cache: HashMap<Paint, PaintId>,
|
||||||
pub bounds: RectF32,
|
bounds: RectF32,
|
||||||
pub view_box: RectF32,
|
view_box: RectF32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Scene {
|
impl Scene {
|
||||||
|
@ -40,6 +42,10 @@ impl Scene {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn push_object(&mut self, object: PathObject) {
|
||||||
|
self.objects.push(object);
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(clippy::trivially_copy_pass_by_ref)]
|
#[allow(clippy::trivially_copy_pass_by_ref)]
|
||||||
pub fn push_paint(&mut self, paint: &Paint) -> PaintId {
|
pub fn push_paint(&mut self, paint: &Paint) -> PaintId {
|
||||||
if let Some(paint_id) = self.paint_cache.get(paint) {
|
if let Some(paint_id) = self.paint_cache.get(paint) {
|
||||||
|
@ -52,15 +58,32 @@ impl Scene {
|
||||||
paint_id
|
paint_id
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_descriptor(&self, built_options: &PreparedRenderOptions) -> SceneDescriptor {
|
#[inline]
|
||||||
SceneDescriptor {
|
pub fn object_count(&self) -> usize {
|
||||||
shaders: self.build_shaders(),
|
self.objects.len()
|
||||||
bounding_quad: built_options.bounding_quad(),
|
|
||||||
object_count: self.objects.len(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_shaders(&self) -> Vec<ObjectShader> {
|
#[inline]
|
||||||
|
pub fn bounds(&self) -> RectF32 {
|
||||||
|
self.bounds
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_bounds(&mut self, new_bounds: RectF32) {
|
||||||
|
self.bounds = new_bounds;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn view_box(&self) -> RectF32 {
|
||||||
|
self.view_box
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_view_box(&mut self, new_view_box: RectF32) {
|
||||||
|
self.view_box = new_view_box;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build_shaders(&self) -> Vec<ObjectShader> {
|
||||||
self.objects
|
self.objects
|
||||||
.iter()
|
.iter()
|
||||||
.map(|object| {
|
.map(|object| {
|
||||||
|
@ -149,6 +172,15 @@ impl Scene {
|
||||||
self.view_box
|
self.view_box
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn build<E>(&self,
|
||||||
|
built_options: &PreparedRenderOptions,
|
||||||
|
listener: Box<dyn RenderCommandListener>,
|
||||||
|
executor: &E)
|
||||||
|
where E: Executor {
|
||||||
|
SceneBuilder::new(self, built_options, listener).build(executor)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for Scene {
|
impl Debug for Scene {
|
||||||
|
@ -178,13 +210,6 @@ impl Debug for Scene {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct SceneDescriptor {
|
|
||||||
pub shaders: Vec<ObjectShader>,
|
|
||||||
pub bounding_quad: [Point3DF32; 4],
|
|
||||||
pub object_count: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct PathObject {
|
pub struct PathObject {
|
||||||
outline: Outline,
|
outline: Outline,
|
||||||
|
@ -201,7 +226,8 @@ pub enum PathObjectKind {
|
||||||
|
|
||||||
impl PathObject {
|
impl PathObject {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(outline: Outline, paint: PaintId, name: String, kind: PathObjectKind) -> PathObject {
|
pub fn new(outline: Outline, paint: PaintId, name: String, kind: PathObjectKind)
|
||||||
|
-> PathObject {
|
||||||
PathObject {
|
PathObject {
|
||||||
outline,
|
outline,
|
||||||
paint,
|
paint,
|
||||||
|
|
|
@ -70,7 +70,7 @@ impl BuiltSVG {
|
||||||
let root = &tree.root();
|
let root = &tree.root();
|
||||||
match *root.borrow() {
|
match *root.borrow() {
|
||||||
NodeKind::Svg(ref svg) => {
|
NodeKind::Svg(ref svg) => {
|
||||||
built_svg.scene.view_box = usvg_rect_to_euclid_rect(&svg.view_box.rect);
|
built_svg.scene.set_view_box(usvg_rect_to_euclid_rect(&svg.view_box.rect));
|
||||||
for kid in root.children() {
|
for kid in root.children() {
|
||||||
built_svg.process_node(&kid, &global_transform);
|
built_svg.process_node(&kid, &global_transform);
|
||||||
}
|
}
|
||||||
|
@ -122,8 +122,8 @@ impl BuiltSVG {
|
||||||
let path = Transform2DF32PathIter::new(path, &transform);
|
let path = Transform2DF32PathIter::new(path, &transform);
|
||||||
let outline = Outline::from_segments(path);
|
let outline = Outline::from_segments(path);
|
||||||
|
|
||||||
self.scene.bounds = self.scene.bounds.union_rect(outline.bounds());
|
self.scene.set_bounds(self.scene.bounds().union_rect(outline.bounds()));
|
||||||
self.scene.objects.push(PathObject::new(
|
self.scene.push_object(PathObject::new(
|
||||||
outline,
|
outline,
|
||||||
style,
|
style,
|
||||||
node.id().to_string(),
|
node.id().to_string(),
|
||||||
|
@ -146,8 +146,8 @@ impl BuiltSVG {
|
||||||
let mut outline = stroke_to_fill.outline;
|
let mut outline = stroke_to_fill.outline;
|
||||||
outline.transform(&transform);
|
outline.transform(&transform);
|
||||||
|
|
||||||
self.scene.bounds = self.scene.bounds.union_rect(outline.bounds());
|
self.scene.set_bounds(self.scene.bounds().union_rect(outline.bounds()));
|
||||||
self.scene.objects.push(PathObject::new(
|
self.scene.push_object(PathObject::new(
|
||||||
outline,
|
outline,
|
||||||
style,
|
style,
|
||||||
node.id().to_string(),
|
node.id().to_string(),
|
||||||
|
|
Loading…
Reference in New Issue