Allow late initialization of render state

This commit is contained in:
Maximilian Ammann 2022-04-23 18:37:12 +02:00
parent c289c184d3
commit 630a1e4bf7
3 changed files with 143 additions and 104 deletions

View File

@ -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())
}
}
}

View File

@ -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) {

View File

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