Add the possibility to late initialize the renderer

This commit is contained in:
Maximilian Ammann 2022-11-02 20:44:35 +01:00
parent d0cefe69de
commit fdd09e0b19
10 changed files with 233 additions and 164 deletions

View File

@ -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,

View File

@ -6,7 +6,7 @@ use maplibre::{
kernel::KernelBuilder,
platform::{http_client::ReqwestHttpClient, scheduler::TokioScheduler},
render::{
builder::RenderBuilder,
builder::RendererBuilder,
settings::{RendererSettings, TextureFormat},
},
style::Style,

View File

@ -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<ET: 'static> {
event_loop: RawWinitEventLoop<ET>,
}
impl<ET: 'static> EventLoop<ET> for WinitEventLoop<ET> {
impl<ET: 'static + PartialEq + Debug> EventLoop<ET> for WinitEventLoop<ET> {
type EventLoopProxy = WinitEventLoopProxy<ET>;
fn run<E>(
mut self,
mut window: <E::MapWindowConfig as MapWindowConfig>::MapWindow,
mut map: Map<E>,
max_frames: Option<u64>,
) where
fn run<E>(mut self, mut map: Map<E>, max_frames: Option<u64>)
where
E: Environment,
<E::MapWindowConfig as MapWindowConfig>::MapWindow: HeadedMapWindow,
{
@ -89,12 +90,16 @@ impl<ET: 'static> EventLoop<ET> for WinitEventLoop<ET> {
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<ET: 'static> EventLoop<ET> for WinitEventLoop<ET> {
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<ET: 'static> EventLoop<ET> for WinitEventLoop<ET> {
..
} => *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<ET: 'static> EventLoop<ET> for WinitEventLoop<ET> {
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<ET: 'static> EventLoop<ET> for WinitEventLoop<ET> {
Event::MainEventsCleared => {
// RedrawRequested will only trigger once, unless we manually
// request it.
window.request_redraw();
map.window().request_redraw();
}
_ => {}
}

View File

@ -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<String>) {
.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)
})
}

View File

@ -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<E> From<E> for Error

View File

@ -15,15 +15,11 @@ pub trait EventLoopProxy<T: 'static> {
fn send_event(&self, event: T);
}
pub trait EventLoop<T: 'static> {
type EventLoopProxy: EventLoopProxy<T>;
pub trait EventLoop<ET: 'static + PartialEq> {
type EventLoopProxy: EventLoopProxy<ET>;
fn run<E>(
self,
window: <E::MapWindowConfig as MapWindowConfig>::MapWindow,
map: Map<E>,
max_frames: Option<u64>,
) where
fn run<E>(self, map: Map<E>, max_frames: Option<u64>)
where
E: Environment,
<E::MapWindowConfig as MapWindowConfig>::MapWindow: HeadedMapWindow;

View File

@ -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::<HeadlessMapWindowConfig>(&window)
.await
.expect("Failed to initialize renderer");

View File

@ -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<E: Environment> {
kernel: Rc<Kernel<E>>,
schedule: Schedule,
map_context: MapContext,
map_context: MapContextState,
window: <E::MapWindowConfig as MapWindowConfig>::MapWindow,
}
impl<E: Environment> Map<E> {
pub fn new(style: Style, kernel: Kernel<E>, renderer: Renderer) -> Result<Self, Error> {
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::<f64>(style.pitch.unwrap_or_default()),
);
impl<E: Environment> Map<E>
where
<<E as Environment>::MapWindowConfig as MapWindowConfig>::MapWindow: HeadedMapWindow,
{
pub fn new(style: Style, kernel: Kernel<E>) -> Result<Self, Error> {
let mut schedule = Schedule::default();
let graph = create_default_render_graph().unwrap(); // TODO: Remove unwrap
@ -40,29 +47,100 @@ impl<E: Environment> Map<E> {
register_stages::<E>(&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::<E::MapWindowConfig>(&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::<f64>(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 <E::MapWindowConfig as MapWindowConfig>::MapWindow {
&mut self.window
}
pub fn window(&self) -> &<E::MapWindowConfig as MapWindowConfig>::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);
match &mut self.map_context {
MapContextState::Ready(map_context) => {
self.schedule.run(map_context);
Ok(())
}
pub fn context(&self) -> &MapContext {
&self.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(&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) -> 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<Kernel<E>> {

View File

@ -12,18 +12,16 @@ use crate::{
window::{HeadedMapWindow, MapWindow, MapWindowConfig},
};
pub struct RenderBuilder<MWC: MapWindowConfig> {
pub struct RendererBuilder {
wgpu_settings: Option<WgpuSettings>,
renderer_settings: Option<RendererSettings>,
phatom_mwc: PhantomData<MWC>,
}
impl<MWC: MapWindowConfig> RenderBuilder<MWC> {
impl RendererBuilder {
pub fn new() -> Self {
Self {
wgpu_settings: None,
renderer_settings: None,
phatom_mwc: Default::default(),
}
}
@ -37,97 +35,92 @@ impl<MWC: MapWindowConfig> RenderBuilder<MWC> {
self
}
pub fn build(self) -> UninitializedRenderer<MWC> {
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<MWC: MapWindowConfig> {
Initialized(InitializedRenderer<MWC>),
Uninizalized(UninitializedRenderer<MWC>),
pub enum InitializationResult {
Initialized(InitializedRenderer),
Uninizalized(UninitializedRenderer),
Gone,
}
impl<MWC: MapWindowConfig> InitializationResult<MWC> {
pub fn unwarp(self) -> InitializedRenderer<MWC> {
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<Renderer> {
match self {
InitializationResult::Initialized(InitializedRenderer { renderer, .. }) => {
Some(renderer)
}
InitializationResult::Uninizalized(_) => None,
InitializationResult::Gone => panic!("Initialization context is gone"),
}
}
}
pub struct UninitializedRenderer<MWC: MapWindowConfig> {
window: Option<MWC::MapWindow>,
wgpu_settings: WgpuSettings,
renderer_settings: RendererSettings,
pub struct UninitializedRenderer {
pub wgpu_settings: WgpuSettings,
pub renderer_settings: RendererSettings,
}
impl<MWC: MapWindowConfig> UninitializedRenderer<MWC>
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<InitializationResult<MWC>, Error> {
let window = map_window_config.create();
#[cfg(target_os = "android")]
{
Ok(InitializationResult::Uninizalized(self))
}
#[cfg(not(target_os = "android"))]
pub async fn initialize_renderer<MWC>(
self,
existing_window: &MWC::MapWindow,
) -> Result<InitializationResult, Error>
where
MWC: MapWindowConfig,
<MWC as MapWindowConfig>::MapWindow: HeadedMapWindow,
{
let renderer = Renderer::initialize(
&window,
existing_window,
self.wgpu_settings.clone(),
self.renderer_settings.clone(),
)
.await?;
Ok(InitializationResult::Initialized(InitializedRenderer {
window,
renderer,
}))
}
}
pub async fn initialize_with<E>(
self,
kernel: &Kernel<E>,
) -> Result<InitializationResult<MWC>, Error>
where
E: Environment<MapWindowConfig = MWC>,
{
self.initialize(kernel.map_window_config()).await
}
}
#[cfg(feature = "headless")]
impl<MWC: MapWindowConfig> UninitializedRenderer<MWC> {
async fn initialize_headless(self, map_window_config: &MWC) -> Result<Renderer, Error> {
let window = map_window_config.create();
impl UninitializedRenderer {
pub(crate) async fn initialize_headless<MWC>(
self,
existing_window: &MWC::MapWindow,
) -> Result<Renderer, Error>
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<E>(self, kernel: &Kernel<E>) -> Result<Renderer, Error>
where
E: Environment<MapWindowConfig = MWC>,
{
self.initialize_headless(kernel.map_window_config()).await
}
}
pub struct InitializedRenderer<MWC: MapWindowConfig> {
pub window: MWC::MapWindow,
pub struct InitializedRenderer {
pub renderer: Renderer,
}

View File

@ -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<CurrentEnvironment>;
pub struct InitResult {
initialized: InitializedRenderer<WinitMapWindowConfig<()>>,
kernel: Kernel<CurrentEnvironment>,
}
#[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<WinitEnvironment<_, _, _, ()>> = kernel_builder.build();
Box::into_raw(Box::new(InitResult {
initialized: RenderBuilder::new()
.build()
.initialize_with(&kernel)
let mut map: MapType = Map::new(Style::default(), kernel).unwrap();
map.initialize_renderer(RendererBuilder::new())
.await
.expect("Failed to initialize renderer")
.unwarp(),
kernel,
})) as u32
}
.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)]