From fdd09e0b1923495aab4044c6f237fa215bb107eb Mon Sep 17 00:00:00 2001 From: Maximilian Ammann Date: Wed, 2 Nov 2022 20:44:35 +0100 Subject: [PATCH] Add the possibility to late initialize the renderer --- benchmarks/benches/render.rs | 2 +- maplibre-demo/src/headless.rs | 2 +- maplibre-winit/src/lib.rs | 41 ++++++---- maplibre-winit/src/noweb.rs | 33 +++++---- maplibre/src/error.rs | 3 +- maplibre/src/event_loop.rs | 12 +-- maplibre/src/headless/mod.rs | 19 +++-- maplibre/src/map.rs | 132 ++++++++++++++++++++++++++------- maplibre/src/render/builder.rs | 115 ++++++++++++++-------------- web/src/lib.rs | 38 +++------- 10 files changed, 233 insertions(+), 164 deletions(-) diff --git a/benchmarks/benches/render.rs b/benchmarks/benches/render.rs index b231a730..705f5ad1 100644 --- a/benchmarks/benches/render.rs +++ b/benchmarks/benches/render.rs @@ -18,7 +18,7 @@ use maplibre::{ kernel::{Kernel, KernelBuilder}, platform::{http_client::ReqwestHttpClient, run_multithreaded, scheduler::TokioScheduler}, render::{ - builder::{InitializedRenderer, RenderBuilder}, + builder::{InitializedRenderer, RendererBuilder}, settings::{RendererSettings, TextureFormat}, }, style::Style, diff --git a/maplibre-demo/src/headless.rs b/maplibre-demo/src/headless.rs index a26e7d2b..de442415 100644 --- a/maplibre-demo/src/headless.rs +++ b/maplibre-demo/src/headless.rs @@ -6,7 +6,7 @@ use maplibre::{ kernel::KernelBuilder, platform::{http_client::ReqwestHttpClient, scheduler::TokioScheduler}, render::{ - builder::RenderBuilder, + builder::RendererBuilder, settings::{RendererSettings, TextureFormat}, }, style::Style, diff --git a/maplibre-winit/src/lib.rs b/maplibre-winit/src/lib.rs index 85306359..f2da920d 100644 --- a/maplibre-winit/src/lib.rs +++ b/maplibre-winit/src/lib.rs @@ -1,8 +1,9 @@ pub mod input; -use std::{cell::RefCell, marker::PhantomData, ops::Deref, rc::Rc}; +use std::{cell::RefCell, fmt::Debug, marker::PhantomData, ops::Deref, rc::Rc}; use instant::Instant; +use log::info; use maplibre::{ environment::Environment, error::Error, @@ -14,6 +15,10 @@ use maplibre::{ transferables::{DefaultTransferables, Transferables}, }, map::Map, + render::{ + builder::RendererBuilder, + settings::{Backends, WgpuSettings}, + }, window::{HeadedMapWindow, MapWindowConfig}, }; use winit::{ @@ -69,15 +74,11 @@ pub struct WinitEventLoop { event_loop: RawWinitEventLoop, } -impl EventLoop for WinitEventLoop { +impl EventLoop for WinitEventLoop { type EventLoopProxy = WinitEventLoopProxy; - fn run( - mut self, - mut window: ::MapWindow, - mut map: Map, - max_frames: Option, - ) where + fn run(mut self, mut map: Map, max_frames: Option) + where E: Environment, ::MapWindow: HeadedMapWindow, { @@ -89,12 +90,16 @@ impl EventLoop for WinitEventLoop { self.event_loop .run(move |event, window_target, control_flow| { #[cfg(target_os = "android")] - if !map.is_initialized() && event == Event::Resumed { + if !map.has_renderer() && event == Event::Resumed { use tokio::{runtime::Handle, task}; task::block_in_place(|| { Handle::current().block_on(async { - map.late_init().await; + map.initialize_renderer(RendererBuilder::new() + .with_wgpu_settings(WgpuSettings { + backends: Some(Backends::VULKAN), + ..WgpuSettings::default() + })).await.unwrap(); }) }); return; @@ -111,7 +116,7 @@ impl EventLoop for WinitEventLoop { Event::WindowEvent { ref event, window_id, - } if window_id == window.id().into() => { + } if window_id == map.window().id().into() => { if !input_controller.window_input(event) { match event { WindowEvent::CloseRequested @@ -125,10 +130,14 @@ impl EventLoop for WinitEventLoop { .. } => *control_flow = ControlFlow::Exit, WindowEvent::Resized(physical_size) => { - map.context_mut().resize(physical_size.width, physical_size.height); + if let Ok(map_context) = map.context_mut() { + map_context.resize(physical_size.width, physical_size.height); + } } WindowEvent::ScaleFactorChanged { new_inner_size, .. } => { - map.context_mut().resize(new_inner_size.width, new_inner_size.height); + if let Ok(map_context) = map.context_mut() { + map_context.resize(new_inner_size.width, new_inner_size.height); + } } _ => {} } @@ -139,7 +148,9 @@ impl EventLoop for WinitEventLoop { let dt = now - last_render_time; last_render_time = now; - input_controller.update_state(map.context_mut().world.view_state_mut(), dt); + if let Ok(map_context) = map.context_mut() { + input_controller.update_state(map_context.world.view_state_mut(), dt); + } match map.run_schedule() { Ok(_) => {} @@ -170,7 +181,7 @@ impl EventLoop for WinitEventLoop { Event::MainEventsCleared => { // RedrawRequested will only trigger once, unless we manually // request it. - window.request_redraw(); + map.window().request_redraw(); } _ => {} } diff --git a/maplibre-winit/src/noweb.rs b/maplibre-winit/src/noweb.rs index e72932a3..08f58959 100644 --- a/maplibre-winit/src/noweb.rs +++ b/maplibre-winit/src/noweb.rs @@ -11,7 +11,10 @@ use maplibre::{ kernel::{Kernel, KernelBuilder}, map::Map, platform::{http_client::ReqwestHttpClient, run_multithreaded, scheduler::TokioScheduler}, - render::builder::{InitializedRenderer, RenderBuilder}, + render::{ + builder::{InitializationResult, InitializedRenderer, RendererBuilder}, + settings::{Backends, RendererSettings, WgpuSettings}, + }, style::Style, window::{HeadedMapWindow, MapWindow, MapWindowConfig, WindowSize}, }; @@ -83,23 +86,21 @@ pub fn run_headed_map(cache_path: Option) { .with_scheduler(TokioScheduler::new()) .build(); - let InitializedRenderer { - mut window, - renderer, - } = RenderBuilder::new() - .build() - .initialize_with(&kernel) - .await - .expect("Failed to initialize renderer") - .unwarp(); + let mut map = Map::new(Style::default(), kernel).unwrap(); - window + #[cfg(not(target_os = "android"))] + { + map.initialize_renderer(RendererBuilder::new().with_wgpu_settings(WgpuSettings { + backends: Some(Backends::VULKAN), + ..WgpuSettings::default() + })) + .await + .unwrap(); + } + + map.window_mut() .take_event_loop() .expect("Event loop is not available") - .run( - window, - Map::new(Style::default(), kernel, renderer).unwrap(), - None, - ) + .run(map, None) }) } diff --git a/maplibre/src/error.rs b/maplibre/src/error.rs index 7b6bedf1..3b64d41a 100644 --- a/maplibre/src/error.rs +++ b/maplibre/src/error.rs @@ -1,6 +1,6 @@ //! Errors which can happen in various parts of the library. -use std::{fmt, fmt::Formatter, sync::mpsc::SendError}; +use std::{borrow::Cow, fmt, fmt::Formatter, sync::mpsc::SendError}; use lyon::tessellation::TessellationError; @@ -14,6 +14,7 @@ pub enum Error { Network(String), Tesselation(TessellationError), Render(RenderError), + Generic(Cow<'static, str>), } impl From for Error diff --git a/maplibre/src/event_loop.rs b/maplibre/src/event_loop.rs index 04089c64..7b796cbc 100644 --- a/maplibre/src/event_loop.rs +++ b/maplibre/src/event_loop.rs @@ -15,15 +15,11 @@ pub trait EventLoopProxy { fn send_event(&self, event: T); } -pub trait EventLoop { - type EventLoopProxy: EventLoopProxy; +pub trait EventLoop { + type EventLoopProxy: EventLoopProxy; - fn run( - self, - window: ::MapWindow, - map: Map, - max_frames: Option, - ) where + fn run(self, map: Map, max_frames: Option) + where E: Environment, ::MapWindow: HeadedMapWindow; diff --git a/maplibre/src/headless/mod.rs b/maplibre/src/headless/mod.rs index fb9ed25d..d248a68b 100644 --- a/maplibre/src/headless/mod.rs +++ b/maplibre/src/headless/mod.rs @@ -1,10 +1,16 @@ use crate::{ - headless::{environment::HeadlessEnvironment, window::HeadlessMapWindowConfig}, + headless::{ + environment::HeadlessEnvironment, + window::{HeadlessMapWindow, HeadlessMapWindowConfig}, + }, io::apc::SchedulerAsyncProcedureCall, kernel::{Kernel, KernelBuilder}, platform::{http_client::ReqwestHttpClient, scheduler::TokioScheduler}, - render::{builder::RenderBuilder, Renderer}, - window::WindowSize, + render::{ + builder::{InitializedRenderer, RendererBuilder}, + Renderer, + }, + window::{MapWindowConfig, WindowSize}, }; mod graph_node; @@ -31,9 +37,12 @@ pub async fn create_headless_renderer( .with_scheduler(TokioScheduler::new()) .build(); - let renderer = RenderBuilder::new() + let mwc: &HeadlessMapWindowConfig = kernel.map_window_config(); + let window: HeadlessMapWindow = mwc.create(); + + let renderer = RendererBuilder::new() .build() - .initialize_headless_with(&kernel) + .initialize_headless::(&window) .await .expect("Failed to initialize renderer"); diff --git a/maplibre/src/map.rs b/maplibre/src/map.rs index 178a6dce..15835fb9 100644 --- a/maplibre/src/map.rs +++ b/maplibre/src/map.rs @@ -6,31 +6,38 @@ use crate::{ environment::Environment, error::Error, kernel::Kernel, - render::{create_default_render_graph, register_default_render_stages, Renderer}, + render::{ + builder::{ + InitializationResult, InitializedRenderer, RendererBuilder, UninitializedRenderer, + }, + create_default_render_graph, register_default_render_stages, + settings::{RendererSettings, WgpuSettings}, + Renderer, + }, schedule::{Schedule, Stage}, stages::register_stages, style::Style, + window::{HeadedMapWindow, MapWindow, MapWindowConfig, WindowSize}, world::World, }; +pub enum MapContextState { + Ready(MapContext), + Pending { style: Style }, +} + pub struct Map { kernel: Rc>, schedule: Schedule, - map_context: MapContext, + map_context: MapContextState, + window: ::MapWindow, } -impl Map { - pub fn new(style: Style, kernel: Kernel, renderer: Renderer) -> Result { - let window_size = renderer.state().surface().size(); - - let center = style.center.unwrap_or_default(); - let world = World::new_at( - window_size, - LatLon::new(center[0], center[1]), - style.zoom.map(|zoom| Zoom::new(zoom)).unwrap_or_default(), - cgmath::Deg::(style.pitch.unwrap_or_default()), - ); - +impl Map +where + <::MapWindowConfig as MapWindowConfig>::MapWindow: HeadedMapWindow, +{ + pub fn new(style: Style, kernel: Kernel) -> Result { let mut schedule = Schedule::default(); let graph = create_default_render_graph().unwrap(); // TODO: Remove unwrap @@ -40,29 +47,100 @@ impl Map { register_stages::(&mut schedule, kernel.clone()); - Ok(Self { + let mut window = kernel.map_window_config().create(); + + let map = Self { kernel, - map_context: MapContext { - style, - world, - renderer, - }, + map_context: MapContextState::Pending { style }, schedule, - }) + window, + }; + Ok(map) + } + + pub async fn initialize_renderer( + &mut self, + render_builder: RendererBuilder, + ) -> Result<(), Error> { + let result = render_builder + .build() + .initialize_renderer::(&self.window) + .await + .expect("Failed to initialize renderer"); + + match &mut self.map_context { + MapContextState::Ready(_) => Err(Error::Generic("Renderer is already set".into())), + MapContextState::Pending { style } => { + let window_size = self.window.size(); + + let center = style.center.unwrap_or_default(); + + let world = World::new_at( + window_size, + LatLon::new(center[0], center[1]), + style.zoom.map(|zoom| Zoom::new(zoom)).unwrap_or_default(), + cgmath::Deg::(style.pitch.unwrap_or_default()), + ); + + match result { + InitializationResult::Initialized(InitializedRenderer { renderer, .. }) => { + *&mut self.map_context = MapContextState::Ready(MapContext { + world, + style: std::mem::take(style), + renderer, + }); + } + InitializationResult::Uninizalized(UninitializedRenderer { .. }) => {} + _ => panic!("Rendering context gone"), + }; + Ok(()) + } + } + } + + pub fn window_mut(&mut self) -> &mut ::MapWindow { + &mut self.window + } + pub fn window(&self) -> &::MapWindow { + &self.window + } + + pub fn has_renderer(&self) -> bool { + match &self.map_context { + MapContextState::Ready(_) => true, + MapContextState::Pending { .. } => false, + } } #[tracing::instrument(name = "update_and_redraw", skip_all)] pub fn run_schedule(&mut self) -> Result<(), Error> { - self.schedule.run(&mut self.map_context); - Ok(()) + match &mut self.map_context { + MapContextState::Ready(map_context) => { + self.schedule.run(map_context); + Ok(()) + } + MapContextState::Pending { .. } => { + Err(Error::Generic("Renderer is already set".into())) + } + } } - pub fn context(&self) -> &MapContext { - &self.map_context + pub fn context(&self) -> Result<&MapContext, Error> { + match &self.map_context { + MapContextState::Ready(map_context) => Ok(map_context), + MapContextState::Pending { .. } => { + Err(Error::Generic("Renderer is already set".into())) + } + } } - pub fn context_mut(&mut self) -> &mut MapContext { - &mut self.map_context + pub fn context_mut(&mut self) -> Result<&mut MapContext, Error> { + match &mut self.map_context { + MapContextState::Ready(map_context) => Ok(map_context), + MapContextState::Pending { .. } => { + Err(Error::Generic("Renderer is already set".into())) + } + } } pub fn kernel(&self) -> &Rc> { diff --git a/maplibre/src/render/builder.rs b/maplibre/src/render/builder.rs index b6383e3f..ff93b29e 100644 --- a/maplibre/src/render/builder.rs +++ b/maplibre/src/render/builder.rs @@ -12,18 +12,16 @@ use crate::{ window::{HeadedMapWindow, MapWindow, MapWindowConfig}, }; -pub struct RenderBuilder { +pub struct RendererBuilder { wgpu_settings: Option, renderer_settings: Option, - phatom_mwc: PhantomData, } -impl RenderBuilder { +impl RendererBuilder { pub fn new() -> Self { Self { wgpu_settings: None, renderer_settings: None, - phatom_mwc: Default::default(), } } @@ -37,97 +35,92 @@ impl RenderBuilder { self } - pub fn build(self) -> UninitializedRenderer { + pub fn build(self) -> UninitializedRenderer { UninitializedRenderer { - window: None, wgpu_settings: self.wgpu_settings.unwrap_or_default(), renderer_settings: self.renderer_settings.unwrap_or_default(), } } } -pub enum InitializationResult { - Initialized(InitializedRenderer), - Uninizalized(UninitializedRenderer), +pub enum InitializationResult { + Initialized(InitializedRenderer), + Uninizalized(UninitializedRenderer), + Gone, } -impl InitializationResult { - pub fn unwarp(self) -> InitializedRenderer { +impl Default for InitializationResult { + fn default() -> Self { + Self::Gone + } +} + +impl InitializationResult { + pub fn unwarp_renderer(self) -> InitializedRenderer { match self { InitializationResult::Initialized(renderer) => renderer, InitializationResult::Uninizalized(_) => panic!("Renderer is not initialized"), + InitializationResult::Gone => panic!("Initialization context is gone"), + } + } + + pub fn into_option(self) -> Option { + match self { + InitializationResult::Initialized(InitializedRenderer { renderer, .. }) => { + Some(renderer) + } + InitializationResult::Uninizalized(_) => None, + InitializationResult::Gone => panic!("Initialization context is gone"), } } } -pub struct UninitializedRenderer { - window: Option, - wgpu_settings: WgpuSettings, - renderer_settings: RendererSettings, +pub struct UninitializedRenderer { + pub wgpu_settings: WgpuSettings, + pub renderer_settings: RendererSettings, } -impl UninitializedRenderer -where - MWC::MapWindow: MapWindow + HeadedMapWindow, -{ +impl UninitializedRenderer { /// Initializes the whole rendering pipeline for the given configuration. /// Returns the initialized map, ready to be run. - async fn initialize(self, map_window_config: &MWC) -> Result, Error> { - let window = map_window_config.create(); - - #[cfg(target_os = "android")] - { - Ok(InitializationResult::Uninizalized(self)) - } - - #[cfg(not(target_os = "android"))] - { - let renderer = Renderer::initialize( - &window, - self.wgpu_settings.clone(), - self.renderer_settings.clone(), - ) - .await?; - Ok(InitializationResult::Initialized(InitializedRenderer { - window, - renderer, - })) - } - } - - pub async fn initialize_with( + pub async fn initialize_renderer( self, - kernel: &Kernel, - ) -> Result, Error> + existing_window: &MWC::MapWindow, + ) -> Result where - E: Environment, + MWC: MapWindowConfig, + ::MapWindow: HeadedMapWindow, { - self.initialize(kernel.map_window_config()).await + let renderer = Renderer::initialize( + existing_window, + self.wgpu_settings.clone(), + self.renderer_settings.clone(), + ) + .await?; + Ok(InitializationResult::Initialized(InitializedRenderer { + renderer, + })) } } #[cfg(feature = "headless")] -impl UninitializedRenderer { - async fn initialize_headless(self, map_window_config: &MWC) -> Result { - let window = map_window_config.create(); - +impl UninitializedRenderer { + pub(crate) async fn initialize_headless( + self, + existing_window: &MWC::MapWindow, + ) -> Result + where + MWC: MapWindowConfig, + { Ok(Renderer::initialize_headless( - &window, + existing_window, self.wgpu_settings.clone(), self.renderer_settings.clone(), ) .await?) } - - pub async fn initialize_headless_with(self, kernel: &Kernel) -> Result - where - E: Environment, - { - self.initialize_headless(kernel.map_window_config()).await - } } -pub struct InitializedRenderer { - pub window: MWC::MapWindow, +pub struct InitializedRenderer { pub renderer: Renderer, } diff --git a/web/src/lib.rs b/web/src/lib.rs index 18256d6b..5cc9614a 100644 --- a/web/src/lib.rs +++ b/web/src/lib.rs @@ -7,8 +7,9 @@ use maplibre::{ io::{apc::SchedulerAsyncProcedureCall, scheduler::NopScheduler}, kernel::{Kernel, KernelBuilder}, map::Map, - render::builder::{InitializedRenderer, RenderBuilder}, + render::builder::{InitializedRenderer, RendererBuilder}, style::Style, + window::MapWindowConfig, }; use maplibre_winit::{WinitEnvironment, WinitMapWindowConfig}; use wasm_bindgen::prelude::*; @@ -66,13 +67,8 @@ type CurrentEnvironment = WinitEnvironment< pub type MapType = Map; -pub struct InitResult { - initialized: InitializedRenderer>, - kernel: Kernel, -} - #[wasm_bindgen] -pub async fn init_maplibre(new_worker: js_sys::Function) -> u32 { +pub async fn run_maplibre(new_worker: js_sys::Function) { let mut kernel_builder = KernelBuilder::new() .with_map_window_config(WinitMapWindowConfig::new("maplibre".to_string())) .with_http_client(WHATWGFetchHttpClient::new()); @@ -100,31 +96,15 @@ pub async fn init_maplibre(new_worker: js_sys::Function) -> u32 { let kernel: Kernel> = kernel_builder.build(); - Box::into_raw(Box::new(InitResult { - initialized: RenderBuilder::new() - .build() - .initialize_with(&kernel) - .await - .expect("Failed to initialize renderer") - .unwarp(), - kernel, - })) as u32 -} + let mut map: MapType = Map::new(Style::default(), kernel).unwrap(); + map.initialize_renderer(RendererBuilder::new()) + .await + .unwrap(); -#[wasm_bindgen] -pub unsafe fn run(init_ptr: *mut InitResult) { - let mut init_result = Box::from_raw(init_ptr); - - let InitializedRenderer { - mut window, - renderer, - } = init_result.initialized; - let map: MapType = Map::new(Style::default(), init_result.kernel, renderer).unwrap(); - - window + map.window_mut() .take_event_loop() .expect("Event loop is not available") - .run(window, map, None) + .run(map, None) } #[cfg(test)]