Update winit and wgpu (#303)

* Update winit and wgpu which both target rwh 0.6

* Upgrade to winit 0.30 to fix android crash https://github.com/bevyengine/bevy/issues/13331

* Add Carbon framework to desktop builds and improve build script to use nix if available

* Fix swift package and compilaiton with Nix

* Cleanup nix shell

* Avoid setting the size on the canvas, winit resizes now

* Handle fetch errors for web
This commit is contained in:
Max Ammann 2024-07-05 00:48:42 +02:00 committed by GitHub
parent c974e22aa2
commit de7d18103a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
29 changed files with 194 additions and 180 deletions

2
.gitignore vendored
View File

@ -30,3 +30,5 @@ perf.data*
frame_*.png
.direnv/
xcode/libs/*/**

3
.idea/maplibre-rs.iml generated
View File

@ -23,8 +23,9 @@
<excludeFolder url="file://$MODULE_DIR$/web/lib/src/wasm" />
<excludeFolder url="file://$MODULE_DIR$/web/lib/dist" />
<excludeFolder url="file://$MODULE_DIR$/maplibre-cache" />
<excludeFolder url="file://$MODULE_DIR$/apple/xcode/libs" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
</module>

View File

@ -2,6 +2,9 @@
<configuration default="false" name="Run demo (debug)" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
<option name="command" value="run -p maplibre-demo -- headed" />
<option name="workingDirectory" value="file://$PROJECT_DIR$" />
<envs>
<env name="RUST_LOG" value="maplibre=info" />
</envs>
<option name="emulateTerminal" value="false" />
<option name="channel" value="DEFAULT" />
<option name="requiredFeatures" value="true" />
@ -9,9 +12,6 @@
<option name="withSudo" value="false" />
<option name="buildTarget" value="REMOTE" />
<option name="backtrace" value="SHORT" />
<envs>
<env name="RUST_LOG" value="maplibre=trace" />
</envs>
<option name="isRedirectInput" value="false" />
<option name="redirectInputPath" value="" />
<method v="2">

View File

@ -7,11 +7,9 @@ members = [
"maplibre-winit",
"maplibre-build-tools",
"maplibre-demo",
"android",
"apple",
"web",
"benchmarks",
]
@ -36,7 +34,7 @@ rand = { version = "0.7.3", features = ["wasm-bindgen"] }
# FIXME: Untrusted dependency, 0.2.x doesn't compile with cache middleware
reqwest-middleware = "0.1.6"
winit = { version = "0.28.7", default-features = false }
winit = { version = "0.30", default-features = false, features = ["rwh_06"] }
#
# These dependencies should be updated to the latest version
@ -67,10 +65,8 @@ js-sys = "0.3"
log = "0.4.20"
lyon = { version = "1.0.1", features = [] }
naga = { version = "0.13.0", features = ["wgsl-in"] }
android-activity = "0.4.3"
android_logger = "0.13.3"
png = { version = "0.17.10" }
raw-window-handle = "0.5.2"
reqwest = { version = "0.11.20", default-features = false, features = ["rustls-tls", "gzip"] } # Use rusttls on android because cross compiling is difficult
reqwest-middleware-cache = "0.1.1" # FIXME: Untrusted dependency
rstar = "0.11.0"
@ -91,7 +87,7 @@ wasm-bindgen = "=0.2.92"
wasm-bindgen-futures = "0.4"
wasm-bindgen-test = "0.3"
web-sys = "0.3" # Individual features are customized in each crate
wgpu = "0.18.0"
wgpu = "0.19.4"
[profile.release]
lto = true

View File

@ -13,11 +13,10 @@ authors.workspace = true
[dependencies]
maplibre = { path = "../maplibre", features = ["thread-safe-futures"] }
maplibre-winit = { path = "../maplibre-winit", version = "0.1.0" }
maplibre-winit = { path = "../maplibre-winit", version = "0.1.0" }
env_logger.workspace = true
log.workspace = true
android_logger.workspace = true
android-activity.workspace = true
jni.workspace = true
[lib]

View File

@ -3,7 +3,7 @@
use jni::{objects::JClass, JNIEnv};
use log::Level;
use maplibre::render::settings::WgpuSettings;
use maplibre_winit::{run_headed_map, WinitMapWindowConfig};
use maplibre_winit::{android_activity, run_headed_map, WinitMapWindowConfig};
#[cfg(not(any(no_pendantic_os_check, target_os = "android")))]
compile_error!("android works only on android.");

View File

@ -1,8 +1,9 @@
import maplibre_rs
import Metal
import AppKit
import QuartzCore
import SystemConfiguration
import Metal // Required by wgpu?
import AppKit // Required by ??
import QuartzCore // Required by ??
import SystemConfiguration // Required by reqwest?
import Carbon // Required by winit
maplibre_rs.MapLibre.start()

View File

@ -17,6 +17,7 @@
0B85D599281291A700906D21 /* maplibre_rs.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0B85D56B2812903700906D21 /* maplibre_rs.framework */; };
0B85D5A42812991100906D21 /* libmaplibre_apple.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0B85D5A32812987B00906D21 /* libmaplibre_apple.a */; };
5015FE4C2ACCB64600F8E4B6 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5015FE492ACCA78300F8E4B6 /* SystemConfiguration.framework */; };
53BD1F062C34FB12008DAA23 /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 53BD1F052C34FB12008DAA23 /* Carbon.framework */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
@ -32,6 +33,7 @@
0BE452D62812EEA8003BD2A5 /* example--iOS--Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "example--iOS--Info.plist"; sourceTree = "<group>"; };
5015FE472ACCA77900F8E4B6 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS17.0.sdk/System/Library/Frameworks/SystemConfiguration.framework; sourceTree = DEVELOPER_DIR; };
5015FE492ACCA78300F8E4B6 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; };
53BD1F052C34FB12008DAA23 /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = System/Library/Frameworks/Carbon.framework; sourceTree = SDKROOT; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -56,6 +58,7 @@
buildActionMask = 2147483647;
files = (
5015FE4C2ACCB64600F8E4B6 /* SystemConfiguration.framework in Frameworks */,
53BD1F062C34FB12008DAA23 /* Carbon.framework in Frameworks */,
0B85D599281291A700906D21 /* maplibre_rs.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -97,6 +100,7 @@
0B85D573281290D400906D21 /* Frameworks */ = {
isa = PBXGroup;
children = (
53BD1F052C34FB12008DAA23 /* Carbon.framework */,
5015FE472ACCA77900F8E4B6 /* SystemConfiguration.framework */,
5015FE492ACCA78300F8E4B6 /* SystemConfiguration.framework */,
0B85D5A32812987B00906D21 /* libmaplibre_apple.a */,
@ -259,6 +263,7 @@
/* Begin PBXShellScriptBuildPhase section */
0BE452D72812EFC1003BD2A5 /* Cargo Build */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
@ -273,7 +278,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/bash;
shellScript = "arch=\"unknown\"\nvendor=\"apple\"\nos_type=\"unknown\"\nenvironment_type=\"\"\n\nmode=\"\"\n\necho \"ARCH: $ARCHS\"\n\nif [[ $CONFIGURATION == \"Release\" ]]\nthen\n mode=\"--release\"\nfi\n\nif [[ $ARCHS == \"x86_64\" ]]\nthen\n arch=\"x86_64\"\nelif [[ $ARCHS == \"arm64\" ]]\nthen\n arch=\"aarch64\"\nfi\n\nif [[ $SDK_NAME == *\"iphoneos\"* ]]\nthen\n os_type=\"ios\"\nelif [[ $SDK_NAME == *\"macos\"* ]]\nthen\n os_type=\"darwin\"\nelif [[ $SDK_NAME == *\"iphonesimulator\"* ]]\nthen\n os_type=\"ios\"\n if [[ $ARCHS == \"arm64\" ]]\n then\n environment_type=\"sim\"\n fi\nfi\n\n\ntriplet=\"$arch-$vendor-$os_type\"\n\nif [ -n \"$environment_type\" ]\nthen\n triplet=\"$triplet-$environment_type\"\nfi\n\necho \"Mode: $mode\"\necho \"Triplet: $triplet\"\necho \"Shell: $SHELL\"\n\ncmd=$(cat << END\n export HOME=$HOME\n . $HOME/.cargo/env\n \n if [ -f \"/opt/homebrew/bin/brew\" ];\n then\n echo \"Homebrew support activated\"\n eval \"\\$(/opt/homebrew/bin/brew shellenv)\"\n fi\n \n cargo build -p apple $mode --target $triplet --lib\nEND\n)\n\necho \"Command: $cmd\"\n\nenv -i /bin/bash -c \"$cmd\"\n";
shellScript = "arch=\"unknown\"\nvendor=\"apple\"\nos_type=\"unknown\"\nenvironment_type=\"\"\n\nmode=\"\"\n\necho \"ARCH: $ARCHS\"\n\nif [[ $CONFIGURATION == \"Release\" ]]\nthen\n mode=\"--release\"\nfi\n\nif [[ $ARCHS == \"x86_64\" ]]\nthen\n arch=\"x86_64\"\nelif [[ $ARCHS == \"arm64\" ]]\nthen\n arch=\"aarch64\"\nfi\n\nif [[ $SDK_NAME == *\"iphoneos\"* ]]\nthen\n os_type=\"ios\"\nelif [[ $SDK_NAME == *\"macos\"* ]]\nthen\n os_type=\"darwin\"\nelif [[ $SDK_NAME == *\"iphonesimulator\"* ]]\nthen\n os_type=\"ios\"\n if [[ $ARCHS == \"arm64\" ]]\n then\n environment_type=\"sim\"\n fi\nfi\n\n\ntriplet=\"$arch-$vendor-$os_type\"\n\nif [ -n \"$environment_type\" ]\nthen\n triplet=\"$triplet-$environment_type\"\nfi\n\necho \"Mode: $mode\"\necho \"Triplet: $triplet\"\necho \"Shell: $SHELL\"\n\ncmd=$(cat << END\n export HOME=$HOME # Set home as this env is clean\n export PATH=\"\\$PATH:/usr/bin\" # We have a clean shell, so make /usr/bin available\n . $HOME/.cargo/env\n \n if [ -f \"/opt/homebrew/bin/brew\" ];\n then\n echo \"Homebrew support activated\"\n eval \"\\$(/opt/homebrew/bin/brew shellenv)\"\n fi\n \n cargo build -p apple $mode --target $triplet --lib\nEND\n)\n\necho \"Command: $cmd\"\n\n# Potentially improve this check for other systems\nif test -f /run/current-system/sw/bin/nix-shell; then\n env -i /run/current-system/sw/bin/nix-shell \"$PROJECT_DIR/../../shell.nix\" --run \"$cmd\"\nelse\n env -i /bin/bash -c \"$cmd\"\nfi\n";
};
/* End PBXShellScriptBuildPhase section */

View File

@ -13,10 +13,6 @@ authors.workspace = true
[target.'cfg(any(target_os = "macos", target_os = "ios", target_os = "linux", target_os = "android", target_os = "windows"))'.dependencies]
tokio.workspace = true
# FIXME: is this section needed? Seems to be identical to the default
#[target.'cfg(target_os = "android")'.dependencies]
#winit.wokrspace = true
[target.'cfg(target_os = "linux")'.dependencies]
winit = { workspace = true, features = ["x11", "wayland"] }
@ -34,3 +30,4 @@ winit.workspace = true
cgmath.workspace = true
instant.workspace = true
log.workspace = true
thiserror.workspace = true

View File

@ -1,6 +1,7 @@
use std::time::Duration;
use maplibre::context::MapContext;
use winit::keyboard::Key;
use super::UpdateState;
@ -35,30 +36,27 @@ impl UpdateState for DebugHandler {
}
impl DebugHandler {
pub fn process_key_press(
&mut self,
key: winit::event::VirtualKeyCode,
state: winit::event::ElementState,
) -> bool {
pub fn process_key_press(&mut self, key: &Key, state: winit::event::ElementState) -> bool {
let amount = if state == winit::event::ElementState::Pressed {
100.0
} else {
0.0
};
match key {
winit::event::VirtualKeyCode::V => {
match key.as_ref() {
Key::Character("v") => {
self.left_delta += amount;
true
}
winit::event::VirtualKeyCode::B => {
Key::Character("b") => {
self.top_delta += amount;
true
}
winit::event::VirtualKeyCode::N => {
Key::Character("n") => {
self.bottom_delta += amount;
true
}
winit::event::VirtualKeyCode::M => {
Key::Character("m") => {
self.right_delta += amount;
true
}

View File

@ -4,7 +4,7 @@ use std::time::Duration;
use cgmath::Vector2;
use maplibre::context::MapContext;
use winit::event::{DeviceEvent, KeyboardInput, TouchPhase, WindowEvent};
use winit::event::{DeviceEvent, KeyEvent, TouchPhase, WindowEvent};
use crate::input::{
camera_handler::CameraHandler, debug_handler::DebugHandler, pan_handler::PanHandler,
@ -72,17 +72,14 @@ impl InputController {
true
}
WindowEvent::KeyboardInput {
input:
KeyboardInput {
state,
virtual_keycode: Some(key),
..
},
event: KeyEvent {
state, logical_key, ..
},
..
} => {
self.shift_handler.process_key_press(*key, *state)
|| self.debug_handler.process_key_press(*key, *state)
|| self.zoom_handler.process_key_press(*key, *state)
self.shift_handler.process_key_press(logical_key, *state)
|| self.debug_handler.process_key_press(logical_key, *state)
|| self.zoom_handler.process_key_press(logical_key, *state)
}
WindowEvent::Touch(touch) => match touch.phase {
TouchPhase::Started => {

View File

@ -2,6 +2,7 @@ use std::time::Duration;
use cgmath::{Vector2, Zero};
use maplibre::context::MapContext;
use winit::keyboard::{Key, NamedKey};
use super::UpdateState;
@ -41,30 +42,26 @@ impl ShiftHandler {
} * self.sensitivity;*/
}
pub fn process_key_press(
&mut self,
key: winit::event::VirtualKeyCode,
state: winit::event::ElementState,
) -> bool {
pub fn process_key_press(&mut self, key: &Key, state: winit::event::ElementState) -> bool {
let amount = if state == winit::event::ElementState::Pressed {
10.0 * self.sensitivity // left, right is the same as panning 10px
} else {
0.0
};
match key {
winit::event::VirtualKeyCode::W | winit::event::VirtualKeyCode::Up => {
match key.as_ref() {
Key::Character("w") | Key::Named(NamedKey::ArrowUp) => {
self.camera_translate.y -= amount;
true
}
winit::event::VirtualKeyCode::S | winit::event::VirtualKeyCode::Down => {
Key::Character("s") | Key::Named(NamedKey::ArrowDown) => {
self.camera_translate.y += amount;
true
}
winit::event::VirtualKeyCode::A | winit::event::VirtualKeyCode::Left => {
Key::Character("a") | Key::Named(NamedKey::ArrowLeft) => {
self.camera_translate.x -= amount;
true
}
winit::event::VirtualKeyCode::D | winit::event::VirtualKeyCode::Right => {
Key::Character("d") | Key::Named(NamedKey::ArrowRight) => {
self.camera_translate.x += amount;
true
}

View File

@ -2,6 +2,7 @@ use std::time::Duration;
use cgmath::Vector2;
use maplibre::{context::MapContext, coords::Zoom};
use winit::keyboard::Key;
use super::UpdateState;
@ -77,23 +78,19 @@ impl ZoomHandler {
);
}
pub fn process_key_press(
&mut self,
key: winit::event::VirtualKeyCode,
state: winit::event::ElementState,
) -> bool {
pub fn process_key_press(&mut self, key: &Key, state: winit::event::ElementState) -> bool {
let amount = if state == winit::event::ElementState::Pressed {
0.1
} else {
0.0
};
match key {
winit::event::VirtualKeyCode::Plus | winit::event::VirtualKeyCode::I => {
match key.as_ref() {
Key::Character("i") | Key::Character("+") => {
self.update_zoom(amount);
true
}
winit::event::VirtualKeyCode::Minus | winit::event::VirtualKeyCode::K => {
Key::Character("k") | Key::Character("-") => {
self.update_zoom(-amount);
true
}

View File

@ -11,14 +11,17 @@ use maplibre::{
window::{HeadedMapWindow, MapWindowConfig, PhysicalSize},
};
use winit::{
event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent},
event_loop::ControlFlow,
event::{ElementState, Event, KeyEvent, WindowEvent},
keyboard::{Key, NamedKey},
};
use crate::input::{InputController, UpdateState};
pub mod input;
#[cfg(target_os = "android")]
pub use winit::platform::android::activity as android_activity;
pub type RawWinitWindow = winit::window::Window;
pub type RawWinitEventLoop<ET> = winit::event_loop::EventLoop<ET>;
pub type RawEventLoopProxy<ET> = winit::event_loop::EventLoopProxy<ET>;
@ -46,9 +49,9 @@ impl<ET> WinitMapWindow<ET> {
}
impl<ET> HeadedMapWindow for WinitMapWindow<ET> {
type RawWindow = RawWinitWindow;
type WindowHandle = RawWinitWindow;
fn raw(&self) -> &Self::RawWindow {
fn handle(&self) -> &Self::WindowHandle {
&self.window
}
@ -84,7 +87,7 @@ impl<ET: 'static + PartialEq + Debug> EventLoop<ET> for WinitEventLoop<ET> {
let mut scale_factor = map.window().scale_factor();
self.event_loop
.run(move |event, _window_target, control_flow| {
.run(move |event, window_target| {
#[cfg(target_os = "android")]
if !map.is_initialized() && event == Event::Resumed {
use tokio::{runtime::Handle, task};
@ -109,61 +112,67 @@ impl<ET: 'static + PartialEq + Debug> EventLoop<ET> for WinitEventLoop<ET> {
ref event,
window_id,
} if window_id == map.window().id().into() => {
match event {
WindowEvent::RedrawRequested => {
if !map.is_initialized() {
return;
}
let now = Instant::now();
let dt = now - last_render_time;
last_render_time = now;
if let Ok(map_context) = map.context_mut() {
input_controller.update_state(map_context, dt);
}
// TODO: Handle gracefully
map.run_schedule().expect("Failed to run schedule!");
if let Some(max_frames) = max_frames {
if current_frame >= max_frames {
log::info!("Exiting because maximum frames reached.");
window_target.exit()
}
current_frame += 1;
}
map.window().request_redraw();
}
_ => {}
}
if !input_controller.window_input(event, scale_factor) {
match event {
WindowEvent::CloseRequested
| WindowEvent::KeyboardInput {
input:
KeyboardInput {
event: KeyEvent {
state: ElementState::Pressed,
virtual_keycode: Some(VirtualKeyCode::Escape),
logical_key: Key::Named(NamedKey::Exit),
..
},
..
} => *control_flow = ControlFlow::Exit,
} => window_target.exit(),
WindowEvent::Resized(winit::dpi::PhysicalSize { width, height}) => {
if let Ok(map_context) = map.context_mut() {
let size = PhysicalSize::new(*width, *height).expect("window values should not be zero");
map_context.resize(size, scale_factor);
map.window().request_redraw();
}
}
WindowEvent::ScaleFactorChanged { new_inner_size: winit::dpi::PhysicalSize { width, height}, scale_factor: new_scale_factor } => {
WindowEvent::ScaleFactorChanged { inner_size_writer, scale_factor: new_scale_factor } => {
if let Ok(map_context) = map.context_mut() {
log::info!("New scaling factor: {}", new_scale_factor);
scale_factor = *new_scale_factor;
let size = PhysicalSize::new(*width, *height).expect("window values should not be zero");
map_context.resize(size, scale_factor);
map_context.resize(map_context.renderer.resources.surface.size(), scale_factor);
}
}
_ => {}
}
}
}
Event::RedrawRequested(_) => {
if !map.is_initialized() {
return;
}
let now = Instant::now();
let dt = now - last_render_time;
last_render_time = now;
if let Ok(map_context) = map.context_mut() {
input_controller.update_state(map_context, dt);
}
// TODO: Handle gracefully
map.run_schedule().expect("Failed to run schedule!");
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 => {
log::info!("Suspending and dropping render state.");
map.reset() // TODO: Instead of resetting the whole map (incl. the renderer) only reset the renderer
@ -171,11 +180,6 @@ impl<ET: 'static + PartialEq + Debug> EventLoop<ET> for WinitEventLoop<ET> {
Event::Resumed => {
// FIXME unimplemented!()
}
Event::MainEventsCleared => {
// RedrawRequested will only trigger once, unless we manually
// request it.
map.window().request_redraw();
}
_ => {}
}
});

View File

@ -16,9 +16,9 @@ use maplibre::{
},
render::{builder::RendererBuilder, settings::WgpuSettings, RenderPlugin},
style::Style,
window::{MapWindow, MapWindowConfig, PhysicalSize},
window::{MapWindow, MapWindowConfig, PhysicalSize, WindowCreateError},
};
use winit::window::WindowBuilder;
use winit::window::WindowAttributes;
use super::WinitMapWindow;
use crate::{WinitEnvironment, WinitEventLoop};
@ -27,7 +27,7 @@ use crate::{WinitEnvironment, WinitEventLoop};
pub struct WinitMapWindowConfig<ET> {
title: String,
#[cfg(target_os = "android")]
android_app: winit::platform::android::activity::AndroidApp,
android_app: crate::android_activity::AndroidApp,
phantom_et: PhantomData<ET>,
}
@ -72,9 +72,8 @@ impl<ET> MapWindow for WinitMapWindow<ET> {
impl<ET: 'static + Clone> MapWindowConfig for WinitMapWindowConfig<ET> {
type MapWindow = WinitMapWindow<ET>;
fn create(&self) -> Self::MapWindow {
let mut raw_event_loop_builder =
winit::event_loop::EventLoopBuilder::<ET>::with_user_event();
fn create(&self) -> Result<Self::MapWindow, WindowCreateError> {
let mut raw_event_loop_builder = winit::event_loop::EventLoop::<ET>::with_user_event();
#[cfg(target_os = "android")]
use winit::platform::android::EventLoopBuilderExtAndroid;
@ -82,19 +81,20 @@ impl<ET: 'static + Clone> MapWindowConfig for WinitMapWindowConfig<ET> {
let mut raw_event_loop_builder =
raw_event_loop_builder.with_android_app(self.android_app.clone());
let raw_event_loop = raw_event_loop_builder.build();
let raw_event_loop = raw_event_loop_builder
.build()
.map_err(|_| WindowCreateError::EventLoop)?;
let window = WindowBuilder::new()
.with_title(&self.title)
.build(&raw_event_loop)
.unwrap();
let window = raw_event_loop
.create_window(WindowAttributes::new().with_title(&self.title))
.map_err(|_| WindowCreateError::Window)?;
Self::MapWindow {
Ok(Self::MapWindow {
window,
event_loop: Some(WinitEventLoop {
event_loop: raw_event_loop,
}),
}
})
}
}

View File

@ -1,7 +1,7 @@
use std::marker::PhantomData;
use maplibre::window::{MapWindow, MapWindowConfig, PhysicalSize};
use winit::{platform::web::WindowBuilderExtWebSys, window::WindowBuilder};
use maplibre::window::{MapWindow, MapWindowConfig, PhysicalSize, WindowCreateError};
use winit::{platform::web::WindowAttributesExtWebSys, window::WindowAttributes};
use super::WinitMapWindow;
use crate::WinitEventLoop;
@ -24,22 +24,23 @@ impl<ET: 'static> WinitMapWindowConfig<ET> {
impl<ET: 'static + Clone> MapWindowConfig for WinitMapWindowConfig<ET> {
type MapWindow = WinitMapWindow<ET>;
fn create(&self) -> Self::MapWindow {
let raw_event_loop = winit::event_loop::EventLoopBuilder::<ET>::with_user_event().build();
fn create(&self) -> Result<Self::MapWindow, WindowCreateError> {
let raw_event_loop = winit::event_loop::EventLoop::<ET>::with_user_event()
.build()
.map_err(|_| WindowCreateError::EventLoop)?;
let window: winit::window::Window = WindowBuilder::new()
.with_canvas(Some(get_canvas(&self.canvas_id)))
.build(&raw_event_loop)
.unwrap();
let window: winit::window::Window = raw_event_loop
.create_window(
WindowAttributes::default().with_canvas(Some(get_canvas(&self.canvas_id))),
)
.map_err(|_| WindowCreateError::Window)?;
let size = get_body_size().unwrap();
window.set_inner_size(size);
Self::MapWindow {
Ok(Self::MapWindow {
window,
event_loop: Some(WinitEventLoop {
event_loop: raw_event_loop,
}),
}
})
}
}
@ -47,20 +48,10 @@ impl<ET: 'static> MapWindow for WinitMapWindow<ET> {
fn size(&self) -> PhysicalSize {
let size = self.window.inner_size();
PhysicalSize::new(size.width, size.height).expect("failed to get window dimensions.")
PhysicalSize::new(size.width, size.height).unwrap_or(PhysicalSize::new(1, 1).unwrap())
}
}
pub fn get_body_size() -> Option<winit::dpi::LogicalSize<i32>> {
let web_window: web_sys::Window = web_sys::window().unwrap();
let document = web_window.document().unwrap();
let body = document.body().unwrap();
Some(winit::dpi::LogicalSize {
width: body.client_width(),
height: body.client_height(),
})
}
pub fn get_canvas(element_id: &str) -> web_sys::HtmlCanvasElement {
use wasm_bindgen::JsCast;

View File

@ -58,7 +58,6 @@ tile-grid.workspace = true
wgpu.workspace = true
#wgpu = { git = "https://github.com/gfx-rs/wgpu.git", rev = "" }
lyon.workspace = true
raw-window-handle.workspace = true
# cached = "0.32"

View File

@ -42,7 +42,7 @@ pub async fn create_headless_renderer(
.build();
let mwc: &HeadlessMapWindowConfig = kernel.map_window_config();
let window: HeadlessMapWindow = mwc.create();
let window: HeadlessMapWindow = mwc.create().expect("failed to create headless window");
let renderer = RendererBuilder::new()
.build()

View File

@ -1,4 +1,4 @@
use crate::window::{MapWindow, MapWindowConfig, PhysicalSize};
use crate::window::{MapWindow, MapWindowConfig, PhysicalSize, WindowCreateError};
#[derive(Clone)]
pub struct HeadlessMapWindowConfig {
@ -14,8 +14,8 @@ impl HeadlessMapWindowConfig {
impl MapWindowConfig for HeadlessMapWindowConfig {
type MapWindow = HeadlessMapWindow;
fn create(&self) -> Self::MapWindow {
Self::MapWindow { size: self.size }
fn create(&self) -> Result<Self::MapWindow, WindowCreateError> {
Ok(Self::MapWindow { size: self.size })
}
}

View File

@ -19,7 +19,7 @@ use crate::{
schedule::{Schedule, Stage},
style::Style,
tcs::world::World,
window::{HeadedMapWindow, MapWindow, MapWindowConfig},
window::{HeadedMapWindow, MapWindow, MapWindowConfig, WindowCreateError},
};
#[derive(Error, Debug)]
@ -33,6 +33,8 @@ pub enum MapError {
RenderGraphInit(RenderGraphError),
#[error("initializing device failed")]
DeviceInit(RenderError),
#[error("creating window failed")]
Window(#[from] WindowCreateError),
}
pub enum CurrentMapContext {
@ -58,13 +60,13 @@ where
{
pub fn new(
style: Style,
mut kernel: Kernel<E>,
kernel: Kernel<E>,
renderer_builder: RendererBuilder,
plugins: Vec<Box<dyn Plugin<E>>>,
) -> Result<Self, MapError> {
let schedule = Schedule::default();
let window = kernel.map_window_config().create();
let window = kernel.map_window_config().create()?;
let kernel = Rc::new(kernel);

View File

@ -6,6 +6,8 @@ use crate::render::graph::RenderGraphError;
pub enum RenderError {
#[error("error in surface")]
Surface(#[from] wgpu::SurfaceError),
#[error("error while getting window handle")]
Handle(#[from] wgpu::rwh::HandleError),
#[error("error during surface creation")]
CreateSurfaceError(#[from] wgpu::CreateSurfaceError),
#[error("error in render graph")]

View File

@ -175,7 +175,10 @@ impl Renderer {
gles_minor_version: Default::default(),
});
let surface: wgpu::Surface = unsafe { instance.create_surface(window.raw())? };
let surface: wgpu::Surface = unsafe {
instance
.create_surface_unsafe(wgpu::SurfaceTargetUnsafe::from_window(&window.handle())?)?
};
let (adapter, device, queue) = Self::request_device(
&instance,
@ -255,7 +258,7 @@ impl Renderer {
async fn request_device(
instance: &wgpu::Instance,
settings: &WgpuSettings,
request_adapter_options: &wgpu::RequestAdapterOptions<'_>,
request_adapter_options: &wgpu::RequestAdapterOptions<'_, '_>,
) -> Result<(wgpu::Adapter, wgpu::Device, wgpu::Queue), RenderError> {
let adapter = instance
.request_adapter(request_adapter_options)
@ -400,8 +403,8 @@ impl Renderer {
.request_device(
&wgpu::DeviceDescriptor {
label: settings.device_label.as_ref().map(|a| a.as_ref()),
features,
limits,
required_features: features,
required_limits: limits,
},
trace_path,
)
@ -431,7 +434,7 @@ impl Renderer {
mod tests {
use crate::{
tcs::world::World,
window::{MapWindow, MapWindowConfig, PhysicalSize},
window::{MapWindow, MapWindowConfig, PhysicalSize, WindowCreateError},
};
#[derive(Clone)]
@ -442,8 +445,8 @@ mod tests {
impl MapWindowConfig for HeadlessMapWindowConfig {
type MapWindow = HeadlessMapWindow;
fn create(&self) -> Self::MapWindow {
Self::MapWindow { size: self.size }
fn create(&self) -> Result<Self::MapWindow, WindowCreateError> {
Ok(Self::MapWindow { size: self.size })
}
}
@ -487,8 +490,8 @@ mod tests {
.request_device(
&wgpu::DeviceDescriptor {
label: None,
features: wgpu::Features::default(),
limits: wgpu::Limits::default(),
required_features: wgpu::Features::default(),
required_limits: wgpu::Limits::default(),
},
None,
)

View File

@ -40,7 +40,7 @@ impl BufferDimensions {
}
pub struct WindowHead {
surface: wgpu::Surface,
surface: wgpu::Surface<'static>,
size: PhysicalSize,
texture_format: wgpu::TextureFormat,
@ -63,6 +63,7 @@ impl WindowHead {
height: self.size.height(),
present_mode: self.present_mode,
view_formats: vec![self.texture_format],
desired_maximum_frame_latency: 2,
};
self.surface.configure(device, &surface_config);
@ -76,7 +77,10 @@ impl WindowHead {
where
MW: MapWindow + HeadedMapWindow,
{
self.surface = unsafe { instance.create_surface(window.raw())? };
self.surface = unsafe {
instance
.create_surface_unsafe(wgpu::SurfaceTargetUnsafe::from_window(&window.handle())?)?
};
Ok(())
}
@ -170,7 +174,7 @@ pub struct Surface {
impl Surface {
pub fn from_surface<MW>(
surface: wgpu::Surface,
surface: wgpu::Surface<'static>,
adapter: &wgpu::Adapter,
window: &MW,
settings: &RendererSettings,

View File

@ -1,4 +1,4 @@
use std::{collections::HashSet, marker::PhantomData};
use std::{borrow::Cow, collections::HashSet, marker::PhantomData};
use geozero::{
mvt::{tile, Message},
@ -24,9 +24,9 @@ pub enum ProcessVectorError {
/// Sending of results failed
#[error("sending data back through context failed")]
SendError(SendError),
/// Error during processing of the pipeline
#[error("processing data in pipeline failed")]
Processing(Box<dyn std::error::Error>),
/// Error when decoding e.g. the protobuf file
#[error("decoding failed")]
Decoding(Cow<'static, str>),
}
/// A request for a tile at the given coordinates and in the given layers.
@ -42,7 +42,8 @@ pub fn process_vector_tile<T: VectorTransferables, C: Context>(
) -> Result<(), ProcessVectorError> {
// Decode
let mut tile = geozero::mvt::Tile::decode(data).expect("failed to load tile");
let mut tile = geozero::mvt::Tile::decode(data)
.map_err(|e| ProcessVectorError::Decoding(e.to_string().into()))?;
// Available
@ -125,7 +126,7 @@ impl<T: VectorTransferables, C: Context> ProcessVectorContext<T, C> {
fn tile_finished(&mut self, coords: &WorldTileCoords) -> Result<(), ProcessVectorError> {
self.context
.send(T::TileTessellated::build_from(*coords))
.map_err(|e| ProcessVectorError::Processing(Box::new(e)))
.map_err(|e| ProcessVectorError::SendError(e))
}
fn layer_missing(
@ -135,7 +136,7 @@ impl<T: VectorTransferables, C: Context> ProcessVectorContext<T, C> {
) -> Result<(), ProcessVectorError> {
self.context
.send(T::LayerMissing::build_from(*coords, layer_name.to_owned()))
.map_err(|e| ProcessVectorError::Processing(Box::new(e)))
.map_err(|e| ProcessVectorError::SendError(e))
}
fn layer_tesselation_finished(
@ -152,7 +153,7 @@ impl<T: VectorTransferables, C: Context> ProcessVectorContext<T, C> {
feature_indices,
layer_data,
))
.map_err(|e| ProcessVectorError::Processing(Box::new(e)))
.map_err(|e| ProcessVectorError::SendError(e))
}
fn layer_indexing_finished(
@ -165,7 +166,7 @@ impl<T: VectorTransferables, C: Context> ProcessVectorContext<T, C> {
*coords,
TileIndex::Linear { list: geometries },
))
.map_err(|e| ProcessVectorError::Processing(Box::new(e)))
.map_err(|e| ProcessVectorError::SendError(e))
}
}

View File

@ -2,7 +2,8 @@
use std::num::NonZeroU32;
use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle};
use thiserror::Error;
use wgpu::rwh::{HasDisplayHandle, HasWindowHandle};
/// Window of a certain [`PhysicalSize`]. This can either be a proper window or a headless one.
pub trait MapWindow {
@ -12,9 +13,9 @@ pub trait MapWindow {
/// Window which references a physical `RawWindow`. This is only implemented by headed windows and
/// not by headless windows.
pub trait HeadedMapWindow: MapWindow {
type RawWindow: HasRawWindowHandle + HasRawDisplayHandle;
type WindowHandle: HasWindowHandle + HasDisplayHandle + Sync;
fn raw(&self) -> &Self::RawWindow;
fn handle(&self) -> &Self::WindowHandle;
// TODO: Can we avoid this?
fn request_redraw(&self);
@ -24,12 +25,20 @@ pub trait HeadedMapWindow: MapWindow {
fn id(&self) -> u64;
}
#[derive(Error, Debug)]
pub enum WindowCreateError {
#[error("unable to create event loop")]
EventLoop,
#[error("unable to create window")]
Window,
}
/// A configuration for a window which determines the corresponding implementation of a
/// [`MapWindow`] and is able to create it.
pub trait MapWindowConfig: 'static + Clone {
type MapWindow: MapWindow;
fn create(&self) -> Self::MapWindow;
fn create(&self) -> Result<Self::MapWindow, WindowCreateError>;
}
/// Window size with a width and an height in pixels.

View File

@ -11,20 +11,27 @@ let
})
{ };
in
pkgs.mkShell {
(pkgs.mkShell.override {
# Wew are using the host clang on macOS; the Nix clang adds a clag that breaks cross compilation here:
# https://github.com/NixOS/nixpkgs/blob/362cb82b75394680990cbe89f40fe65d35f66617/pkgs/build-support/cc-wrapper/default.nix#L490
# It caused this error during the compilation of ring: clang-15: error: invalid argument '-mmacos-version-min=11.0' not allowed with '-miphoneos-version-min=7.0'
stdenv = stdenvNoCC;
}) {
nativeBuildInputs = [
# Tools
unstable.rustup
unstable.just
unstable.nodejs
unstable.mdbook
unstable.wasm-bindgen-cli
# unstable.wasm-bindgen-cli # we need wasm-bindgen-cli@0.2.92, so pull it from cargo instead
unstable.tracy
unstable.nixpkgs-fmt # To format this file: nixpkgs-fmt *.nix
# System dependencies
unstable.flatbuffers
unstable.protobuf
pkgs.jdk17
unstable.xorg.libXrandr
unstable.xorg.libXi
unstable.xorg.libXcursor
@ -33,18 +40,12 @@ pkgs.mkShell {
unstable.sqlite
unstable.wayland
unstable.pkg-config
]
++ lib.optionals stdenv.isDarwin [
unstable.libiconv
pkgs.darwin.apple_sdk.frameworks.ApplicationServices
pkgs.darwin.apple_sdk.frameworks.CoreVideo
pkgs.darwin.apple_sdk.frameworks.AppKit
];
shellHook = ''
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:${ pkgs.lib.makeLibraryPath [ unstable.libxkbcommon ] }";
# Vulkan
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:${ pkgs.lib.makeLibraryPath [ pkgs.vulkan-loader ] }";
# EGL
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:${ pkgs.lib.makeLibraryPath [ pkgs.libglvnd ] }";
'';
}

View File

@ -4,6 +4,6 @@
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body style="margin: 0; padding: 0; width:100%; height: 100%; max-height: 100%;">
<canvas id="maplibre" style="position:absolute;"></canvas>
<canvas id="maplibre" style="position:absolute; width:100%; height: 100%;"></canvas>
</body>
</html>

View File

@ -20,6 +20,8 @@ pub enum WebError {
/// TypeError like it is defined in JS
#[error("TypeError from JS")]
TypeError(Cow<'static, str>),
#[error("fetching data failed")]
FetchError(Cow<'static, str>),
/// Any other Error
#[error("Error from JS")]
GenericError(Cow<'static, str>),

View File

@ -29,6 +29,12 @@ impl WHATWGFetchHttpClient {
.dyn_into()
.map_err(|_e| WebError::TypeError("Unable to cast to Response".into()))?;
if !response.ok() {
return Err(WebError::GenericError(
format!("failed to fetch {}", response.status()).into(),
));
}
// Get ArrayBuffer
let maybe_array_buffer = JsFuture::from(response.array_buffer()?).await?;
Ok(maybe_array_buffer)