Skip to content

Commit

Permalink
Upgrade winit to window resizing on macOS and upgrade Android setup (#…
Browse files Browse the repository at this point in the history
…287)

* Upgrade winit to window resizes on macOS

* Use native activity feature

* Update intellij file

* Configure winit correctly

* Fix android

* Fix android backend to GL

* Depend on SystemConfiguration in swift and example target

* Rename functions
  • Loading branch information
maxammann authored Oct 5, 2023
1 parent 04ff088 commit db2acb1
Show file tree
Hide file tree
Showing 22 changed files with 137 additions and 50 deletions.
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,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"

# TODO: Upgrade to 0.28+ and remove ndk-glue. See https://github.com/rust-windowing/winit#android
winit = { version = "0.27.5", default-features = false, features = [] }
winit = { version = "0.28.7", default-features = false }

#
# These dependencies should be updated to the latest version
Expand Down Expand Up @@ -68,7 +67,8 @@ js-sys = "0.3.64"
log = "0.4.20"
lyon = { version = "1.0.1", features = [] }
naga = { version = "0.13.0", features = ["wgsl-in"] }
ndk-glue = "0.7.0" # version is required by winit. This might for winit 0.28+, see https://github.com/rust-windowing/winit#Android
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
Expand Down
3 changes: 2 additions & 1 deletion android/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ maplibre = { path = "../maplibre", features = ["thread-safe-futures"] }
maplibre-winit = { path = "../maplibre-winit", version = "0.1.0" }
env_logger.workspace = true
log.workspace = true
ndk-glue.workspace = true
android_logger.workspace = true
android-activity.workspace = true
jni.workspace = true

[lib]
Expand Down
4 changes: 0 additions & 4 deletions android/gradle/.idea/gradle.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion android/gradle/.idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 17 additions & 12 deletions android/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,28 +1,33 @@
#![deny(unused_imports)]

use std::ffi::CString;

use jni::{objects::JClass, JNIEnv};
use log::Level;
use maplibre_winit::run_headed_map;
use maplibre::render::settings::WgpuSettings;
use maplibre_winit::{run_headed_map, WinitMapWindowConfig};

#[cfg(not(any(no_pendantic_os_check, target_os = "android")))]
compile_error!("android works only on android.");

#[cfg_attr(target_os = "android", ndk_glue::main(backtrace = "on"))]
pub fn android_main() {
env_logger::init_from_env(env_logger::Env::default().default_filter_or("info"));

// TODO: Maybe requires: Some(Backends::VULKAN)
run_headed_map(None);
#[no_mangle]
pub fn android_main(app: android_activity::AndroidApp) {
android_logger::init_once(
android_logger::Config::default().with_max_level(log::LevelFilter::Info),
);
log::log!(Level::Info, "maplibre starting");
run_headed_map(
None,
WinitMapWindowConfig::new("maplibre".to_string(), app),
WgpuSettings {
backends: Some(maplibre::render::settings::Backends::GL),
..WgpuSettings::default()
},
);
}

#[no_mangle]
pub extern "system" fn Java_org_maplibre_1rs_MapLibreRs_android_1main(
_env: JNIEnv,
_class: JClass,
) {
let tag = CString::new("maplibre").unwrap();
let message = CString::new("maplibre WOORKING").unwrap();
ndk_glue::android_log(Level::Warn, &tag, &message);
log::log!(Level::Warn, "maplibre WOORKING");
}
3 changes: 2 additions & 1 deletion apple/MapLibreRs/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ let package = Package(
// Products define the executables and libraries a package produces, and make them visible to other packages.
.library(
name: "MapLibreRs",
targets: ["MapLibreRs"]),
targets: ["MapLibreRs"]
),
],
dependencies: [
// Dependencies declare other packages that this package depends on.
Expand Down
1 change: 1 addition & 0 deletions apple/MapLibreRs/Sources/MapLibreRsExample/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import maplibre_rs
import Metal
import AppKit
import QuartzCore
import SystemConfiguration

maplibre_rs.MapLibre.start()

12 changes: 10 additions & 2 deletions apple/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#![deny(unused_imports)]

use maplibre_winit::run_headed_map;
use maplibre::render::settings::WgpuSettings;
use maplibre_winit::{run_headed_map, WinitMapWindowConfig};

#[cfg(not(any(no_pendantic_os_check, target_os = "macos", target_os = "ios")))]
compile_error!("apple works only on macOS and iOS.");
Expand All @@ -9,5 +10,12 @@ compile_error!("apple works only on macOS and iOS.");
pub fn maplibre_apple_main() {
env_logger::init_from_env(env_logger::Env::default().default_filter_or("info"));

run_headed_map(None);
run_headed_map(
None,
WinitMapWindowConfig::new("maplibre".to_string()),
WgpuSettings {
backends: Some(maplibre::render::settings::Backends::all()),
..WgpuSettings::default()
},
);
}
6 changes: 6 additions & 0 deletions apple/xcode/maplibre-rs.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
0B85D596281291A400906D21 /* maplibre_rs.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0B85D56B2812903700906D21 /* maplibre_rs.framework */; };
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 */; };
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
Expand All @@ -29,6 +30,8 @@
0B85D5942812913700906D21 /* maplibre.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = maplibre.swift; sourceTree = "<group>"; };
0B85D5A32812987B00906D21 /* libmaplibre_apple.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libmaplibre_apple.a; sourceTree = SOURCE_ROOT; };
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; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand All @@ -52,6 +55,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
5015FE4C2ACCB64600F8E4B6 /* SystemConfiguration.framework in Frameworks */,
0B85D599281291A700906D21 /* maplibre_rs.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down Expand Up @@ -93,6 +97,8 @@
0B85D573281290D400906D21 /* Frameworks */ = {
isa = PBXGroup;
children = (
5015FE472ACCA77900F8E4B6 /* SystemConfiguration.framework */,
5015FE492ACCA78300F8E4B6 /* SystemConfiguration.framework */,
0B85D5A32812987B00906D21 /* libmaplibre_apple.a */,
);
name = Frameworks;
Expand Down
2 changes: 2 additions & 0 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@
set shell := ["bash", "-c"]

# Keep this in sync with `android/gradle/lib/build.gradle`

export NIGHTLY_TOOLCHAIN := "nightly-2023-09-23"

# Keep this in sync with `rust-toolchain.toml` and `Cargo.toml`.
# Make sure the above is newer than this.

export STABLE_TOOLCHAIN := "1.72.1"
export CARGO_TERM_COLOR := "always"
export RUST_BACKTRACE := "1"
Expand Down
13 changes: 10 additions & 3 deletions maplibre-demo/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
use std::io::ErrorKind;

use clap::{Parser, Subcommand};
use maplibre::coords::LatLon;
use maplibre_winit::run_headed_map;
use maplibre::{coords::LatLon, render::settings::WgpuSettings};
use maplibre_winit::{run_headed_map, WinitMapWindowConfig};

#[cfg(feature = "headless")]
mod headless;
Expand Down Expand Up @@ -63,7 +63,14 @@ fn main() {
// You can check for the existence of subcommands, and if found use their
// matches just as you would the top level cmd
match &cli.command {
Commands::Headed {} => run_headed_map(None),
Commands::Headed {} => run_headed_map(
None,
WinitMapWindowConfig::new("maplibre".to_string()),
WgpuSettings {
backends: Some(maplibre::render::settings::Backends::all()),
..WgpuSettings::default()
},
),
#[cfg(feature = "headless")]
Commands::Headless {
tile_size,
Expand Down
3 changes: 3 additions & 0 deletions maplibre-winit/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ tokio.workspace = true
[target.'cfg(target_os = "linux")'.dependencies]
winit = { workspace = true, features = ["x11", "wayland"] }

[target.'cfg(target_os = "android")'.dependencies]
winit = { workspace = true, features = ["android-native-activity"] }

[target.'cfg(target_arch = "wasm32")'.dependencies]
web-sys = { workspace = true, features = ["Window"] }
wasm-bindgen = "0.2"
Expand Down
13 changes: 9 additions & 4 deletions maplibre-winit/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ impl<ET: 'static + PartialEq + Debug> EventLoop<ET> for WinitEventLoop<ET> {
self.event_loop
.run(move |event, _window_target, control_flow| {
#[cfg(target_os = "android")]
if !map.has_renderer() && event == Event::Resumed {
if !map.is_initialized() && event == Event::Resumed {
use tokio::{runtime::Handle, task};

task::block_in_place(|| {
Expand Down Expand Up @@ -131,6 +131,10 @@ impl<ET: 'static + PartialEq + Debug> EventLoop<ET> for WinitEventLoop<ET> {
}
}
Event::RedrawRequested(_) => {
if !map.is_initialized() {
return;
}

let now = Instant::now();
let dt = now - last_render_time;
last_render_time = now;
Expand All @@ -139,7 +143,7 @@ impl<ET: 'static + PartialEq + Debug> EventLoop<ET> for WinitEventLoop<ET> {
input_controller.update_state(map_context, dt);
}

// TODO: Maybe handle gracefully
// TODO: Handle gracefully
map.run_schedule().expect("Failed to run schedule!");

if let Some(max_frames) = max_frames {
Expand All @@ -152,7 +156,8 @@ impl<ET: 'static + PartialEq + Debug> EventLoop<ET> for WinitEventLoop<ET> {
}
}
Event::Suspended => {
// FIXME unimplemented!()
log::info!("Suspending and dropping render state.");
map.reset() // TODO: Instead of resetting the whole map (incl. the renderer) only reset the renderer
}
Event::Resumed => {
// FIXME unimplemented!()
Expand Down Expand Up @@ -204,7 +209,7 @@ impl<
HC: HttpClient,
K: OffscreenKernelEnvironment,
APC: AsyncProcedureCall<K>,
ET: 'static,
ET: 'static + Clone,
> Environment for WinitEnvironment<S, HC, K, APC, ET>
{
type MapWindowConfig = WinitMapWindowConfig<ET>;
Expand Down
42 changes: 34 additions & 8 deletions maplibre-winit/src/noweb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,27 @@ use winit::window::WindowBuilder;
use super::WinitMapWindow;
use crate::{WinitEnvironment, WinitEventLoop};

#[derive(Clone)]
pub struct WinitMapWindowConfig<ET> {
title: String,
#[cfg(target_os = "android")]
android_app: winit::platform::android::activity::AndroidApp,

phantom_et: PhantomData<ET>,
}

#[cfg(target_os = "android")]
impl<ET> WinitMapWindowConfig<ET> {
pub fn new(title: String, android_app: winit::platform::android::activity::AndroidApp) -> Self {
Self {
title,
android_app,
phantom_et: Default::default(),
}
}
}

#[cfg(not(target_os = "android"))]
impl<ET> WinitMapWindowConfig<ET> {
pub fn new(title: String) -> Self {
Self {
Expand All @@ -56,11 +71,21 @@ impl<ET> MapWindow for WinitMapWindow<ET> {
}
}

impl<ET: 'static> MapWindowConfig for 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();
let mut raw_event_loop_builder =
winit::event_loop::EventLoopBuilder::<ET>::with_user_event();

#[cfg(target_os = "android")]
use winit::platform::android::EventLoopBuilderExtAndroid;
#[cfg(target_os = "android")]
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 window = WindowBuilder::new()
.with_title(&self.title)
.build(&raw_event_loop)
Expand All @@ -75,24 +100,25 @@ impl<ET: 'static> MapWindowConfig for WinitMapWindowConfig<ET> {
}
}

pub fn run_headed_map(cache_path: Option<String>) {
pub fn run_headed_map(
cache_path: Option<String>,
window_config: WinitMapWindowConfig<()>,
wgpu_settings: WgpuSettings,
) {
run_multithreaded(async {
type Environment<S, HC, APC> =
WinitEnvironment<S, HC, ReqwestOffscreenKernelEnvironment, APC, ()>;

let client = ReqwestHttpClient::new(cache_path);

let kernel: Kernel<Environment<_, _, _>> = KernelBuilder::new()
.with_map_window_config(WinitMapWindowConfig::new("maplibre".to_string()))
.with_map_window_config(window_config)
.with_http_client(client.clone())
.with_apc(SchedulerAsyncProcedureCall::new(TokioScheduler::new()))
.with_scheduler(TokioScheduler::new())
.build();

let renderer_builder = RendererBuilder::new().with_wgpu_settings(WgpuSettings {
backends: Some(maplibre::render::settings::Backends::all()),
..WgpuSettings::default()
});
let renderer_builder = RendererBuilder::new().with_wgpu_settings(wgpu_settings);

let mut map = Map::new(
Style::default(),
Expand Down
3 changes: 2 additions & 1 deletion maplibre-winit/src/web.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use winit::{platform::web::WindowBuilderExtWebSys, window::WindowBuilder};
use super::WinitMapWindow;
use crate::WinitEventLoop;

#[derive(Clone)]
pub struct WinitMapWindowConfig<ET> {
canvas_id: String,
phantom_et: PhantomData<ET>,
Expand All @@ -20,7 +21,7 @@ impl<ET: 'static> WinitMapWindowConfig<ET> {
}
}

impl<ET: 'static> MapWindowConfig for WinitMapWindowConfig<ET> {
impl<ET: 'static + Clone> MapWindowConfig for WinitMapWindowConfig<ET> {
type MapWindow = WinitMapWindow<ET>;

fn create(&self) -> Self::MapWindow {
Expand Down
2 changes: 1 addition & 1 deletion maplibre/src/headless/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ pub async fn create_headless_renderer(
cache_path: Option<String>,
) -> (Kernel<HeadlessEnvironment>, Renderer) {
let client = ReqwestHttpClient::new(cache_path);
let kernel = KernelBuilder::new()
let mut kernel = KernelBuilder::new()
.with_map_window_config(HeadlessMapWindowConfig::new(
WindowSize::new(tile_size, tile_size).unwrap(),
))
Expand Down
1 change: 1 addition & 0 deletions maplibre/src/headless/window.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::window::{MapWindow, MapWindowConfig, WindowSize};

#[derive(Clone)]
pub struct HeadlessMapWindowConfig {
size: WindowSize,
}
Expand Down
Loading

0 comments on commit db2acb1

Please sign in to comment.