From 9cd0b2759f684687f09ed49205380c86bdb6e771 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Wed, 22 Mar 2023 18:29:05 +0200 Subject: [PATCH] example-runner-wgpu: transition from `ndk-glue` to `android-activity`. --- .github/workflows/ci.yaml | 2 +- Cargo.lock | 165 ++++++------------- android.nix | 34 ++++ examples/runners/wgpu/Cargo.toml | 10 +- examples/runners/wgpu/build.rs | 6 +- examples/runners/wgpu/src/bin/wgpu_runner.rs | 7 + examples/runners/wgpu/src/compute.rs | 16 +- examples/runners/wgpu/src/graphics.rs | 42 +++-- examples/runners/wgpu/src/lib.rs | 31 ++-- 9 files changed, 154 insertions(+), 159 deletions(-) create mode 100644 android.nix diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 2e66bdcdea..78314db9d6 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -116,7 +116,7 @@ jobs: echo "::endgroup::" echo "::group::Build WGPU example for Android" - cargo apk build -p example-runner-wgpu --features use-installed-tools --no-default-features + cargo apk build -p example-runner-wgpu --lib --features use-installed-tools --no-default-features echo "::endgroup::" lint: diff --git a/Cargo.lock b/Cargo.lock index 6a12026240..a963d46d33 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -77,6 +77,24 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04" +[[package]] +name = "android_log-sys" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85965b6739a430150bdd138e2374a98af0c3ee0d030b3bb7fc3bddff58d0102e" + +[[package]] +name = "android_logger" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8619b80c242aa7bd638b5c7ddd952addeecb71f69c75e33f1d47b2804f8f883a" +dependencies = [ + "android_log-sys", + "env_logger", + "log", + "once_cell", +] + [[package]] name = "android_system_properties" version = "0.1.5" @@ -157,7 +175,7 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", "winapi", ] @@ -310,7 +328,7 @@ dependencies = [ "ansi_term", "atty", "bitflags", - "strsim 0.8.0", + "strsim", "textwrap", "unicode-width", "vec_map", @@ -558,41 +576,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "darling" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim 0.10.0", - "syn", -] - -[[package]] -name = "darling_macro" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" -dependencies = [ - "darling_core", - "quote", - "syn", -] - [[package]] name = "derivative" version = "2.2.0" @@ -670,12 +653,12 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3" +checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" dependencies = [ - "atty", "humantime", + "is-terminal", "log", "regex", "termcolor", @@ -731,13 +714,13 @@ dependencies = [ name = "example-runner-wgpu" version = "0.0.0" dependencies = [ + "android_logger", "bytemuck", "cfg-if", "console_error_panic_hook", "console_log", "env_logger", "futures", - "ndk-glue", "shared", "spirv-builder", "structopt", @@ -794,12 +777,6 @@ dependencies = [ "miniz_oxide 0.4.4", ] -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - [[package]] name = "foreign-types" version = "0.3.2" @@ -1069,6 +1046,12 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + [[package]] name = "hexf-parse" version = "0.2.1" @@ -1081,12 +1064,6 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - [[package]] name = "indexmap" version = "1.9.2" @@ -1139,6 +1116,18 @@ dependencies = [ "windows-sys 0.45.0", ] +[[package]] +name = "is-terminal" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8687c819457e979cc940d09cb16e42a1bf70aa6b60a549de6d3a62a0ee90c69e" +dependencies = [ + "hermit-abi 0.3.1", + "io-lifetimes", + "rustix", + "windows-sys 0.45.0", +] + [[package]] name = "itertools" version = "0.10.5" @@ -1464,35 +1453,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" -[[package]] -name = "ndk-glue" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0434fabdd2c15e0aab768ca31d5b7b333717f03cf02037d5a0a3ff3c278ed67f" -dependencies = [ - "libc", - "log", - "ndk", - "ndk-context", - "ndk-macro", - "ndk-sys", - "once_cell", - "parking_lot 0.12.1", -] - -[[package]] -name = "ndk-macro" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0df7ac00c4672f9d5aece54ee3347520b7e20f158656c7db2e6de01902eb7a6c" -dependencies = [ - "darling", - "proc-macro-crate", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "ndk-sys" version = "0.4.1+23.1.7779620" @@ -1568,7 +1528,7 @@ version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", ] @@ -1688,17 +1648,6 @@ dependencies = [ "ttf-parser", ] -[[package]] -name = "parking_lot" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" -dependencies = [ - "instant", - "lock_api", - "parking_lot_core 0.8.5", -] - [[package]] name = "parking_lot" version = "0.12.1" @@ -1706,21 +1655,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", - "parking_lot_core 0.9.7", -] - -[[package]] -name = "parking_lot_core" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" -dependencies = [ - "cfg-if", - "instant", - "libc", - "redox_syscall 0.2.10", - "smallvec", - "winapi", + "parking_lot_core", ] [[package]] @@ -2387,12 +2322,6 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - [[package]] name = "structopt" version = "0.3.25" @@ -2793,7 +2722,7 @@ dependencies = [ "js-sys", "log", "naga", - "parking_lot 0.11.2", + "parking_lot", "profiling", "raw-window-handle 0.5.1", "smallvec", @@ -2817,7 +2746,7 @@ dependencies = [ "codespan-reporting", "log", "naga", - "parking_lot 0.11.2", + "parking_lot", "profiling", "raw-window-handle 0.5.1", "rustc-hash", @@ -2855,7 +2784,7 @@ dependencies = [ "metal", "naga", "objc", - "parking_lot 0.11.2", + "parking_lot", "profiling", "range-alloc", "raw-window-handle 0.5.1", diff --git a/android.nix b/android.nix new file mode 100644 index 0000000000..807d5ca7c3 --- /dev/null +++ b/android.nix @@ -0,0 +1,34 @@ +# HACK(eddyb) this is a minimal setup to allow testing the Android build on NixOS. +# +# Tested in `NIXPKGS_ACCEPT_ANDROID_SDK_LICENSE=1 nix-shell android.nix --pure`, +# by running the following commands (x64 target is for the Android Emulator): +# rustup target add aarch64-linux-android x86_64-linux-android +# cargo apk build -p example-runner-wgpu --target aarch64-linux-android +# cargo apk build -p example-runner-wgpu --target x86_64-linux-android +# +# (you can also replace `cargo apk build` with `cargo apk run` to launch it, +# via `adb`, into either the Android Emulator, or a physical Android device) + +let + pkgs = import {}; +in with pkgs; mkShell rec { + # Workaround for https://github.com/NixOS/nixpkgs/issues/60919. + # NOTE(eddyb) needed only in debug mode (warnings about needing optimizations + # turn into errors due to `-Werror`, for at least `spirv-tools-sys`). + hardeningDisable = [ "fortify" ]; + + # Allow cargo to download crates (even inside `nix-shell --pure`). + SSL_CERT_FILE = "${cacert}/etc/ssl/certs/ca-bundle.crt"; + + nativeBuildInputs = [ rustup cargo-apk jdk ]; + + ANDROID_SDK_ROOT = let + androidComposition = androidenv.composeAndroidPackages { + abiVersions = [ "arm64-v8a" "x86_64" ]; + includeNDK = true; + platformVersions = [ "30" ]; + }; + in "${androidComposition.androidsdk}/libexec/android-sdk"; + + ANDROID_NDK_ROOT = "${ANDROID_SDK_ROOT}/ndk-bundle"; +} diff --git a/examples/runners/wgpu/Cargo.toml b/examples/runners/wgpu/Cargo.toml index 5a02657795..149e0f6303 100644 --- a/examples/runners/wgpu/Cargo.toml +++ b/examples/runners/wgpu/Cargo.toml @@ -22,19 +22,19 @@ shared = { path = "../../shaders/shared" } futures = { version = "0.3", default-features = false, features = ["std", "executor"] } # Vulkan SDK or MoltenVK needs to be installed for `vulkan-portability` to work on macOS wgpu = { git = "https://github.com/gfx-rs/wgpu", features = ["spirv", "vulkan-portability"] } -winit = "0.28.3" +winit = { version = "0.28.3", features = ["android-native-activity"] } structopt = "0.3" strum = { version = "0.23.0", default_features = false, features = ["std", "derive"] } bytemuck = "1.6.3" [target.'cfg(not(any(target_os = "android", target_arch = "wasm32")))'.dependencies] +env_logger = "0.10.0" spirv-builder = { workspace = true, features = ["watch"] } [target.'cfg(target_os = "android")'.dependencies] -ndk-glue = "0.7.0" - -[target.'cfg(not(target_arch = "wasm32"))'.dependencies] -env_logger = "0.9.0" +android_logger = "0.11.0" +# NOTE(eddyb) `winit` feature `android-native-activity` is always enabled above, +# to avoid specifying the dependency twice, but only applies to android builds. [target.'cfg(target_arch = "wasm32")'.dependencies] web-sys = "0.3.60" diff --git a/examples/runners/wgpu/build.rs b/examples/runners/wgpu/build.rs index 4c79f896f8..55f708728d 100644 --- a/examples/runners/wgpu/build.rs +++ b/examples/runners/wgpu/build.rs @@ -25,7 +25,11 @@ fn main() -> Result<(), Box> { && dir.ends_with(profile) && dir.pop(); assert!(ok); - let dir = dir.join("spirv-builder"); + // NOTE(eddyb) this needs to be distinct from the `--target-dir` value that + // `spirv-builder` generates in a similar way from `$OUT_DIR` and `$PROFILE`, + // otherwise repeated `cargo build`s will cause build script reruns and the + // rebuilding of `rustc_codegen_spirv` (likely due to common proc macro deps). + let dir = dir.join("example-runner-wgpu-builder"); let status = std::process::Command::new("cargo") .args([ "run", diff --git a/examples/runners/wgpu/src/bin/wgpu_runner.rs b/examples/runners/wgpu/src/bin/wgpu_runner.rs index 120e8b9e0f..50d7c51f0c 100644 --- a/examples/runners/wgpu/src/bin/wgpu_runner.rs +++ b/examples/runners/wgpu/src/bin/wgpu_runner.rs @@ -1,3 +1,10 @@ +#[cfg(target_os = "android")] +const _: () = panic!( + "executable not applicable for Android targets, \ + make sure to pass `--lib` when building with `cargo-apk`" +); + fn main() { + #[cfg(not(target_os = "android"))] example_runner_wgpu::main(); } diff --git a/examples/runners/wgpu/src/compute.rs b/examples/runners/wgpu/src/compute.rs index 5968f43c8c..6736642003 100644 --- a/examples/runners/wgpu/src/compute.rs +++ b/examples/runners/wgpu/src/compute.rs @@ -1,22 +1,14 @@ use wgpu::util::DeviceExt; use super::Options; -use std::{convert::TryInto, future::Future, time::Duration}; - -fn block_on(future: impl Future) -> T { - cfg_if::cfg_if! { - if #[cfg(target_arch = "wasm32")] { - wasm_bindgen_futures::spawn_local(future) - } else { - futures::executor::block_on(future) - } - } -} +use std::{convert::TryInto, time::Duration}; pub fn start(options: &Options) { + env_logger::init(); + let shader_binary = crate::maybe_watch(options.shader, None); - block_on(start_internal(options, shader_binary)); + futures::executor::block_on(start_internal(options, shader_binary)); } pub async fn start_internal( diff --git a/examples/runners/wgpu/src/graphics.rs b/examples/runners/wgpu/src/graphics.rs index 8eab96b63a..39e5702870 100644 --- a/examples/runners/wgpu/src/graphics.rs +++ b/examples/runners/wgpu/src/graphics.rs @@ -369,17 +369,41 @@ fn create_pipeline( } #[allow(clippy::match_wild_err_arm)] -pub fn start(options: &Options) { +pub fn start( + #[cfg(target_os = "android")] android_app: winit::platform::android::activity::AndroidApp, + options: &Options, +) { + let mut event_loop_builder = EventLoopBuilder::with_user_event(); + cfg_if::cfg_if! { + if #[cfg(target_os = "android")] { + android_logger::init_once( + android_logger::Config::default() + .with_min_level("info".parse().unwrap()), + ); + + use winit::platform::android::EventLoopBuilderExtAndroid; + event_loop_builder.with_android_app(android_app); + } else if #[cfg(target_arch = "wasm32")] { + std::panic::set_hook(Box::new(console_error_panic_hook::hook)); + console_log::init().expect("could not initialize logger"); + } else { + env_logger::init(); + } + } + let event_loop = event_loop_builder.build(); + // Build the shader before we pop open a window, since it might take a while. - let event_loop = EventLoopBuilder::with_user_event().build(); - let proxy = event_loop.create_proxy(); let initial_shader = maybe_watch( options.shader, - Some(Box::new(move |res| match proxy.send_event(res) { - Ok(it) => it, - // ShaderModuleDescriptor is not `Debug`, so can't use unwrap/expect - Err(_err) => panic!("Event loop dead"), - })), + #[cfg(not(any(target_os = "android", target_arch = "wasm32")))] + { + let proxy = event_loop.create_proxy(); + Some(Box::new(move |res| match proxy.send_event(res) { + Ok(it) => it, + // ShaderModuleDescriptor is not `Debug`, so can't use unwrap/expect + Err(_err) => panic!("Event loop dead"), + })) + }, ); let window = winit::window::WindowBuilder::new() @@ -390,8 +414,6 @@ pub fn start(options: &Options) { cfg_if::cfg_if! { if #[cfg(target_arch = "wasm32")] { - std::panic::set_hook(Box::new(console_error_panic_hook::hook)); - console_log::init().expect("could not initialize logger"); use winit::platform::web::WindowExtWebSys; // On wasm, append the canvas to the document body web_sys::window() diff --git a/examples/runners/wgpu/src/lib.rs b/examples/runners/wgpu/src/lib.rs index 3ef1da0a86..4bdb7cd9cc 100644 --- a/examples/runners/wgpu/src/lib.rs +++ b/examples/runners/wgpu/src/lib.rs @@ -73,7 +73,10 @@ use structopt::StructOpt; use strum::{Display, EnumString}; +// NOTE(eddyb) while this could theoretically work on the web, it needs more work. +#[cfg(not(any(target_os = "android", target_arch = "wasm32")))] mod compute; + mod graphics; #[derive(EnumString, Display, PartialEq, Eq, Copy, Clone)] @@ -86,7 +89,9 @@ pub enum RustGPUShader { fn maybe_watch( shader: RustGPUShader, - on_watch: Option) + Send + 'static>>, + #[cfg(not(any(target_os = "android", target_arch = "wasm32")))] on_watch: Option< + Box) + Send + 'static>, + >, ) -> wgpu::ShaderModuleDescriptor<'static> { #[cfg(not(any(target_os = "android", target_arch = "wasm32")))] { @@ -145,10 +150,6 @@ fn maybe_watch( } } -fn is_compute_shader(shader: RustGPUShader) -> bool { - shader == RustGPUShader::Compute -} - #[derive(StructOpt)] #[structopt(name = "example-runner-wgpu")] pub struct Options { @@ -156,14 +157,20 @@ pub struct Options { shader: RustGPUShader, } -#[cfg_attr(target_os = "android", ndk_glue::main(backtrace = "on"))] -pub fn main() { - env_logger::init(); +#[cfg_attr(target_os = "android", export_name = "android_main")] +pub fn main( + #[cfg(target_os = "android")] android_app: winit::platform::android::activity::AndroidApp, +) { let options: Options = Options::from_args(); - if is_compute_shader(options.shader) { - compute::start(&options); - } else { - graphics::start(&options); + #[cfg(not(any(target_os = "android", target_arch = "wasm32")))] + if options.shader == RustGPUShader::Compute { + return compute::start(&options); } + + graphics::start( + #[cfg(target_os = "android")] + android_app, + &options, + ); }