mirror of
https://github.com/maplibre/maplibre-rs.git
synced 2025-12-08 19:05:57 +00:00
Allow late initialization of render state
This commit is contained in:
parent
c289c184d3
commit
630a1e4bf7
@ -31,7 +31,7 @@ pub struct MapState<W> {
|
||||
camera: ChangeObserver<camera::Camera>,
|
||||
perspective: camera::Perspective,
|
||||
|
||||
render_state: RenderState,
|
||||
render_state: Option<RenderState>,
|
||||
scheduler: Scheduler,
|
||||
message_receiver: mpsc::Receiver<TessellateMessage>,
|
||||
shared_thread_state: SharedThreadState,
|
||||
@ -46,7 +46,7 @@ impl<W> MapState<W> {
|
||||
pub fn new(
|
||||
window: W,
|
||||
window_size: WindowSize,
|
||||
render_state: RenderState,
|
||||
render_state: Option<RenderState>,
|
||||
scheduler: Scheduler,
|
||||
style: Style,
|
||||
) -> Self {
|
||||
@ -100,7 +100,7 @@ impl<W> MapState<W> {
|
||||
self.prepare_render();
|
||||
|
||||
// Render buffers
|
||||
let result = self.render_state.render();
|
||||
let result = self.render_state_mut().render();
|
||||
|
||||
#[cfg(all(feature = "enable-tracing", not(target_arch = "wasm32")))]
|
||||
tracy_client::finish_continuous_frame!();
|
||||
@ -122,7 +122,7 @@ impl<W> MapState<W> {
|
||||
}
|
||||
TessellateMessage::Tile(TileTessellateMessage { request_id, coords }) => loop {
|
||||
if let Ok(mut tile_request_state) =
|
||||
self.shared_thread_state.tile_request_state.try_lock()
|
||||
self.shared_thread_state.tile_request_state.try_lock()
|
||||
{
|
||||
tile_request_state.finish_tile_request(request_id);
|
||||
tracing::trace!("Tile at {} finished loading", coords);
|
||||
@ -170,13 +170,12 @@ impl<W> MapState<W> {
|
||||
drop(_guard);
|
||||
|
||||
if let Some(view_region) = &view_region {
|
||||
self.render_state
|
||||
.upload_tile_geometry(view_region, &self.style, &self.tile_cache);
|
||||
self.render_state.as_mut().expect("render state not yet initialized. Call reinitialize().").upload_tile_geometry(view_region, &self.style, &self.tile_cache);
|
||||
|
||||
self.render_state
|
||||
.update_tile_view_pattern(view_region, &view_proj, self.zoom());
|
||||
let zoom = self.zoom();
|
||||
self.render_state_mut().update_tile_view_pattern(view_region, &view_proj, zoom);
|
||||
|
||||
self.render_state.update_metadata();
|
||||
self.render_state_mut().update_metadata();
|
||||
}
|
||||
|
||||
// TODO: Could we draw inspiration from StagingBelt (https://docs.rs/wgpu/latest/wgpu/util/struct.StagingBelt.html)?
|
||||
@ -188,7 +187,7 @@ impl<W> MapState<W> {
|
||||
self.try_failed = self.request_tiles_in_view(view_region);
|
||||
}
|
||||
|
||||
self.render_state.update_globals(&view_proj, &self.camera);
|
||||
self.render_state().update_globals(&view_proj, &self.camera);
|
||||
}
|
||||
|
||||
self.camera.update_reference();
|
||||
@ -253,7 +252,7 @@ impl<W> MapState<W> {
|
||||
self.perspective.resize(width, height);
|
||||
self.camera.resize(width, height);
|
||||
|
||||
self.render_state.resize(width, height)
|
||||
self.render_state_mut().resize(width, height)
|
||||
}
|
||||
|
||||
pub fn scheduler(&self) -> &Scheduler {
|
||||
@ -289,19 +288,39 @@ impl<W> MapState<W> {
|
||||
}
|
||||
|
||||
pub fn suspend(&mut self) {
|
||||
self.render_state.suspend();
|
||||
self.render_state_mut().suspend();
|
||||
}
|
||||
|
||||
pub fn resume(&mut self) {
|
||||
self.render_state.resume();
|
||||
self.render_state_mut().resume();
|
||||
}
|
||||
|
||||
pub fn render_state(&self) -> &RenderState {
|
||||
self.render_state.as_ref().expect("render state not yet initialized. Call reinitialize().")
|
||||
}
|
||||
|
||||
pub fn render_state_mut(&mut self) -> &'_ mut RenderState {
|
||||
self.render_state.as_mut().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<W> MapState<W>
|
||||
where
|
||||
W: raw_window_handle::HasRawWindowHandle,
|
||||
where
|
||||
W: raw_window_handle::HasRawWindowHandle,
|
||||
{
|
||||
pub fn recreate_surface(&mut self) {
|
||||
self.render_state.recreate_surface(&self.window);
|
||||
self.render_state.as_mut().expect("render state not yet initialized. Call reinitialize().").recreate_surface(&self.window);
|
||||
}
|
||||
|
||||
pub fn is_initialized(&self) -> bool {
|
||||
self.render_state.is_some()
|
||||
}
|
||||
|
||||
pub async fn reinitialize(&mut self) {
|
||||
if self.render_state.is_none() {
|
||||
let window_size = WindowSize::new(100, 100).unwrap(); // TODO get size
|
||||
let render_state = RenderState::initialize(&self.window, window_size);
|
||||
self.render_state = Some(render_state.await.unwrap())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -68,11 +68,12 @@ impl RenderState {
|
||||
pub async fn initialize<W: raw_window_handle::HasRawWindowHandle>(
|
||||
window: &W,
|
||||
window_size: WindowSize,
|
||||
) -> Self {
|
||||
) -> Option<Self> {
|
||||
let sample_count = 4;
|
||||
|
||||
//let instance = wgpu::Instance::new(wgpu::Backends::GL);
|
||||
let instance = wgpu::Instance::new(wgpu::Backends::all());
|
||||
//let instance = wgpu::Instance::new(wgpu::Backends::all());
|
||||
let instance = wgpu::Instance::new(wgpu::Backends::VULKAN);
|
||||
|
||||
let surface = unsafe { instance.create_surface(&window) };
|
||||
let surface_config = wgpu::SurfaceConfiguration {
|
||||
@ -101,7 +102,13 @@ impl RenderState {
|
||||
} else if cfg!(target_os = "android") {
|
||||
Limits {
|
||||
max_storage_textures_per_shader_stage: 4,
|
||||
..wgpu::Limits::default()
|
||||
max_compute_workgroups_per_dimension: 0,
|
||||
max_compute_workgroup_size_z: 0,
|
||||
max_compute_workgroup_size_y: 0,
|
||||
max_compute_workgroup_size_x: 0,
|
||||
max_compute_workgroup_storage_size: 0,
|
||||
max_compute_invocations_per_workgroup: 0,
|
||||
..wgpu::Limits::downlevel_defaults()
|
||||
}
|
||||
} else {
|
||||
Limits {
|
||||
@ -125,8 +132,7 @@ impl RenderState {
|
||||
},
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
.await.ok()?;
|
||||
|
||||
surface.configure(&device, &surface_config);
|
||||
|
||||
@ -245,7 +251,7 @@ impl RenderState {
|
||||
None
|
||||
};
|
||||
|
||||
Self {
|
||||
Some(Self {
|
||||
instance,
|
||||
surface,
|
||||
device,
|
||||
@ -270,7 +276,7 @@ impl RenderState {
|
||||
tile_view_buffer,
|
||||
TILE_VIEW_BUFFER_SIZE,
|
||||
)),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn recreate_surface<W: raw_window_handle::HasRawWindowHandle>(&mut self, window: &W) {
|
||||
|
||||
@ -23,88 +23,102 @@ impl Runnable<winit::event_loop::EventLoop<()>> for MapState<winit::window::Wind
|
||||
let mut input_controller = InputController::new(0.2, 100.0, 0.1);
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
match event {
|
||||
Event::DeviceEvent {
|
||||
ref event,
|
||||
.. // We're not using device_id currently
|
||||
} => {
|
||||
input_controller.device_input(event);
|
||||
}
|
||||
|
||||
Event::WindowEvent {
|
||||
ref event,
|
||||
window_id,
|
||||
} if window_id == self.window().id() => {
|
||||
if !input_controller.window_input(event) {
|
||||
match event {
|
||||
WindowEvent::CloseRequested
|
||||
| WindowEvent::KeyboardInput {
|
||||
input:
|
||||
KeyboardInput {
|
||||
state: ElementState::Pressed,
|
||||
virtual_keycode: Some(VirtualKeyCode::Escape),
|
||||
..
|
||||
},
|
||||
..
|
||||
} => *control_flow = ControlFlow::Exit,
|
||||
WindowEvent::Resized(physical_size) => {
|
||||
self.resize(physical_size.width, physical_size.height);
|
||||
}
|
||||
WindowEvent::ScaleFactorChanged { new_inner_size, .. } => {
|
||||
self.resize(new_inner_size.width, new_inner_size.height);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
Event::RedrawRequested(_) => {
|
||||
let _span_ = tracing::span!(tracing::Level::TRACE, "redraw requested").entered();
|
||||
#[cfg(target_os = "android")]
|
||||
if !self.is_initialized() && event == Event::Resumed {
|
||||
use tokio::runtime::Handle;
|
||||
use tokio::task;
|
||||
|
||||
let now = Instant::now();
|
||||
let dt = now - last_render_time;
|
||||
last_render_time = now;
|
||||
let state = task::block_in_place(|| {
|
||||
Handle::current().block_on(async {
|
||||
self.reinitialize().await;
|
||||
})
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
input_controller.update_state(&mut self, dt);
|
||||
|
||||
match self.update_and_redraw() {
|
||||
Ok(_) => {}
|
||||
Err(wgpu::SurfaceError::Lost) => {
|
||||
log::error!("Surface Lost");
|
||||
},
|
||||
// The system is out of memory, we should probably quit
|
||||
Err(wgpu::SurfaceError::OutOfMemory) => {
|
||||
log::error!("Out of Memory");
|
||||
*control_flow = ControlFlow::Exit;
|
||||
},
|
||||
// All other errors (Outdated, Timeout) should be resolved by the next frame
|
||||
Err(e) => eprintln!("{:?}", e),
|
||||
};
|
||||
|
||||
if let Some(max_frames) = max_frames {
|
||||
if current_frame >= max_frames {
|
||||
log::info!("Exiting because maximum frames reached.");
|
||||
*control_flow = ControlFlow::Exit;
|
||||
}
|
||||
|
||||
current_frame += 1;
|
||||
}
|
||||
}
|
||||
Event::Suspended => {
|
||||
self.suspend();
|
||||
}
|
||||
Event::Resumed => {
|
||||
self.recreate_surface();
|
||||
let size = self.window().inner_size();
|
||||
self.resize(size.width, size.height);// FIXME: Resumed is also called when the app launches for the first time. Instead of first using a "fake" inner_size() in State::new we should initialize with a proper size from the beginning
|
||||
self.resume();
|
||||
}
|
||||
Event::MainEventsCleared => {
|
||||
// RedrawRequested will only trigger once, unless we manually
|
||||
// request it.
|
||||
self.window().request_redraw();
|
||||
}
|
||||
_ => {}
|
||||
match event {
|
||||
Event::DeviceEvent {
|
||||
ref event,
|
||||
.. // We're not using device_id currently
|
||||
} => {
|
||||
input_controller.device_input(event);
|
||||
}
|
||||
|
||||
Event::WindowEvent {
|
||||
ref event,
|
||||
window_id,
|
||||
} if window_id == self.window().id() => {
|
||||
if !input_controller.window_input(event) {
|
||||
match event {
|
||||
WindowEvent::CloseRequested
|
||||
| WindowEvent::KeyboardInput {
|
||||
input:
|
||||
KeyboardInput {
|
||||
state: ElementState::Pressed,
|
||||
virtual_keycode: Some(VirtualKeyCode::Escape),
|
||||
..
|
||||
},
|
||||
..
|
||||
} => *control_flow = ControlFlow::Exit,
|
||||
WindowEvent::Resized(physical_size) => {
|
||||
self.resize(physical_size.width, physical_size.height);
|
||||
}
|
||||
WindowEvent::ScaleFactorChanged { new_inner_size, .. } => {
|
||||
self.resize(new_inner_size.width, new_inner_size.height);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
Event::RedrawRequested(_) => {
|
||||
let _span_ = tracing::span!(tracing::Level::TRACE, "redraw requested").entered();
|
||||
|
||||
let now = Instant::now();
|
||||
let dt = now - last_render_time;
|
||||
last_render_time = now;
|
||||
|
||||
input_controller.update_state(&mut self, dt);
|
||||
|
||||
match self.update_and_redraw() {
|
||||
Ok(_) => {}
|
||||
Err(wgpu::SurfaceError::Lost) => {
|
||||
log::error!("Surface Lost");
|
||||
}
|
||||
// The system is out of memory, we should probably quit
|
||||
Err(wgpu::SurfaceError::OutOfMemory) => {
|
||||
log::error!("Out of Memory");
|
||||
*control_flow = ControlFlow::Exit;
|
||||
}
|
||||
// All other errors (Outdated, Timeout) should be resolved by the next frame
|
||||
Err(e) => eprintln!("{:?}", e),
|
||||
};
|
||||
|
||||
if let Some(max_frames) = max_frames {
|
||||
if current_frame >= max_frames {
|
||||
log::info!("Exiting because maximum frames reached.");
|
||||
*control_flow = ControlFlow::Exit;
|
||||
}
|
||||
|
||||
current_frame += 1;
|
||||
}
|
||||
}
|
||||
Event::Suspended => {
|
||||
self.suspend();
|
||||
}
|
||||
Event::Resumed => {
|
||||
self.recreate_surface();
|
||||
let size = self.window().inner_size();
|
||||
self.resize(size.width, size.height);// FIXME: Resumed is also called when the app launches for the first time. Instead of first using a "fake" inner_size() in State::new we should initialize with a proper size from the beginning
|
||||
self.resume();
|
||||
}
|
||||
Event::MainEventsCleared => {
|
||||
// RedrawRequested will only trigger once, unless we manually
|
||||
// request it.
|
||||
self.window().request_redraw();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -121,7 +135,7 @@ impl FromWindow for MapBuilder<winit::window::Window, winit::event_loop::EventLo
|
||||
let size = window.inner_size();
|
||||
(
|
||||
window,
|
||||
WindowSize::new(size.width, size.height).unwrap(),
|
||||
WindowSize::new(100, 100).unwrap(),
|
||||
event_loop,
|
||||
)
|
||||
}))
|
||||
@ -154,7 +168,7 @@ pub fn get_canvas(element_id: &'static str) -> web_sys::HtmlCanvasElement {
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
impl crate::window::FromCanvas
|
||||
for MapBuilder<winit::window::Window, winit::event_loop::EventLoop<()>>
|
||||
for MapBuilder<winit::window::Window, winit::event_loop::EventLoop<()>>
|
||||
{
|
||||
fn from_canvas(dom_id: &'static str) -> Self {
|
||||
let event_loop = EventLoop::new();
|
||||
@ -174,7 +188,7 @@ impl crate::window::FromCanvas
|
||||
size.width.try_into().unwrap(),
|
||||
size.height.try_into().unwrap(),
|
||||
)
|
||||
.unwrap(),
|
||||
.unwrap(),
|
||||
event_loop,
|
||||
)
|
||||
}))
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user