Update the scene proxy

This commit is contained in:
Patrick Walton 2020-06-23 13:06:39 -07:00
parent f92c631f97
commit 13b2c91dde
1 changed files with 37 additions and 53 deletions

View File

@ -20,10 +20,11 @@
//! You don't need to use this API to use Pathfinder; it's only a convenience. //! You don't need to use this API to use Pathfinder; it's only a convenience.
use crate::concurrent::executor::Executor; use crate::concurrent::executor::Executor;
use crate::gpu::options::RendererLevel;
use crate::gpu::renderer::Renderer; use crate::gpu::renderer::Renderer;
use crate::gpu_data::RenderCommand; use crate::gpu_data::RenderCommand;
use crate::options::{BuildOptions, RenderCommandListener}; use crate::options::{BuildOptions, RenderCommandListener};
use crate::scene::Scene; use crate::scene::{Scene, SceneSink};
use crossbeam_channel::{self, Receiver, Sender}; use crossbeam_channel::{self, Receiver, Sender};
use pathfinder_geometry::rect::RectF; use pathfinder_geometry::rect::RectF;
use pathfinder_gpu::Device; use pathfinder_gpu::Device;
@ -33,19 +34,28 @@ const MAX_MESSAGES_IN_FLIGHT: usize = 1024;
pub struct SceneProxy { pub struct SceneProxy {
sender: Sender<MainToWorkerMsg>, sender: Sender<MainToWorkerMsg>,
receiver: Receiver<RenderCommand>,
} }
impl SceneProxy { impl SceneProxy {
pub fn new<E>(executor: E) -> SceneProxy where E: Executor + Send + 'static { pub fn new<E>(renderer_level: RendererLevel, executor: E) -> SceneProxy
SceneProxy::from_scene(Scene::new(), executor) where E: Executor + Send + 'static {
SceneProxy::from_scene(Scene::new(), renderer_level, executor)
} }
pub fn from_scene<E>(scene: Scene, executor: E) -> SceneProxy pub fn from_scene<E>(scene: Scene, renderer_level: RendererLevel, executor: E)
-> SceneProxy
where E: Executor + Send + 'static { where E: Executor + Send + 'static {
let (main_to_worker_sender, main_to_worker_receiver) = let (main_to_worker_sender, main_to_worker_receiver) =
crossbeam_channel::bounded(MAX_MESSAGES_IN_FLIGHT); crossbeam_channel::bounded(MAX_MESSAGES_IN_FLIGHT);
thread::spawn(move || scene_thread(scene, executor, main_to_worker_receiver)); let (worker_to_main_sender, worker_to_main_receiver) =
SceneProxy { sender: main_to_worker_sender } crossbeam_channel::bounded(MAX_MESSAGES_IN_FLIGHT);
let listener = RenderCommandListener::new(Box::new(move |command| {
drop(worker_to_main_sender.send(command))
}));
let sink = SceneSink::new(listener, renderer_level);
thread::spawn(move || scene_thread(scene, executor, sink, main_to_worker_receiver));
SceneProxy { sender: main_to_worker_sender, receiver: worker_to_main_receiver }
} }
#[inline] #[inline]
@ -59,18 +69,22 @@ impl SceneProxy {
} }
#[inline] #[inline]
pub fn build_with_listener(&self, pub fn build(&self, options: BuildOptions) {
options: BuildOptions, self.sender.send(MainToWorkerMsg::Build(options)).unwrap();
listener: Box<dyn RenderCommandListener>) {
self.sender.send(MainToWorkerMsg::Build(options, listener)).unwrap();
} }
/// Sends all queued commands to the given renderer.
#[inline] #[inline]
pub fn build_with_stream(&self, options: BuildOptions) -> RenderCommandStream { pub fn render<D>(&mut self, renderer: &mut Renderer<D>) where D: Device {
let (sender, receiver) = crossbeam_channel::bounded(MAX_MESSAGES_IN_FLIGHT); renderer.begin_scene();
let listener = Box::new(move |command| drop(sender.send(command))); while let Ok(command) = self.receiver.recv() {
self.build_with_listener(options, listener); renderer.render_command(&command);
RenderCommandStream::new(receiver) match command {
RenderCommand::Finish { .. } => break,
_ => {}
}
}
renderer.end_scene();
} }
/// A convenience method to build a scene and send the resulting commands /// A convenience method to build a scene and send the resulting commands
@ -79,18 +93,15 @@ impl SceneProxy {
/// Exactly equivalent to: /// Exactly equivalent to:
/// ///
/// ```norun /// ```norun
/// for command in scene_proxy.build_with_stream(options) { /// scene_proxy.build(build_options);
/// renderer.render_command(&command) /// scene_proxy.render(renderer);
/// } /// }
/// ``` /// ```
#[inline] #[inline]
pub fn build_and_render<D>(&self, renderer: &mut Renderer<D>, build_options: BuildOptions) pub fn build_and_render<D>(&mut self, renderer: &mut Renderer<D>, build_options: BuildOptions)
where D: Device { where D: Device {
renderer.begin_scene(); self.build(build_options);
for command in self.build_with_stream(build_options) { self.render(renderer);
renderer.render_command(&command);
}
renderer.end_scene();
} }
#[inline] #[inline]
@ -103,6 +114,7 @@ impl SceneProxy {
fn scene_thread<E>(mut scene: Scene, fn scene_thread<E>(mut scene: Scene,
executor: E, executor: E,
mut sink: SceneSink<'static>,
main_to_worker_receiver: Receiver<MainToWorkerMsg>) main_to_worker_receiver: Receiver<MainToWorkerMsg>)
where E: Executor { where E: Executor {
while let Ok(msg) = main_to_worker_receiver.recv() { while let Ok(msg) = main_to_worker_receiver.recv() {
@ -110,7 +122,7 @@ fn scene_thread<E>(mut scene: Scene,
MainToWorkerMsg::ReplaceScene(new_scene) => scene = new_scene, MainToWorkerMsg::ReplaceScene(new_scene) => scene = new_scene,
MainToWorkerMsg::CopyScene(sender) => sender.send(scene.clone()).unwrap(), MainToWorkerMsg::CopyScene(sender) => sender.send(scene.clone()).unwrap(),
MainToWorkerMsg::SetViewBox(new_view_box) => scene.set_view_box(new_view_box), MainToWorkerMsg::SetViewBox(new_view_box) => scene.set_view_box(new_view_box),
MainToWorkerMsg::Build(options, listener) => scene.build(options, listener, &executor) MainToWorkerMsg::Build(options) => scene.build(options, &mut sink, &executor),
} }
} }
} }
@ -119,33 +131,5 @@ enum MainToWorkerMsg {
ReplaceScene(Scene), ReplaceScene(Scene),
CopyScene(Sender<Scene>), CopyScene(Sender<Scene>),
SetViewBox(RectF), SetViewBox(RectF),
Build(BuildOptions, Box<dyn RenderCommandListener>), Build(BuildOptions),
}
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)
}
}
} }