From e6b2cc7b2b8e5b47e6dc68895a63210513e2f81d Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Sat, 3 Apr 2021 08:14:43 -0700 Subject: [PATCH] Android test application Adds an example binary that can be run with `cargo apk`. One thing that will still need manual tuning (for now) is the size of the canvas. A good followup is to sense that from the window size. --- Cargo.lock | 62 ++++++++++++-- piet-gpu/Cargo.toml | 11 +++ piet-gpu/bin/android.rs | 176 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 244 insertions(+), 5 deletions(-) create mode 100644 piet-gpu/bin/android.rs diff --git a/Cargo.lock b/Cargo.lock index 30a5dc6..2a2d932 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -610,7 +610,19 @@ checksum = "5eb167c1febed0a496639034d0c76b3b74263636045db5489eee52143c246e73" dependencies = [ "jni-sys", "ndk-sys", - "num_enum", + "num_enum 0.4.3", + "thiserror", +] + +[[package]] +name = "ndk" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8794322172319b972f528bf90c6b467be0079f1fa82780ffb431088e741a73ab" +dependencies = [ + "jni-sys", + "ndk-sys", + "num_enum 0.5.1", "thiserror", ] @@ -623,7 +635,21 @@ dependencies = [ "lazy_static", "libc", "log", - "ndk", + "ndk 0.2.1", + "ndk-macro", + "ndk-sys", +] + +[[package]] +name = "ndk-glue" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5caf0c24d51ac1c905c27d4eda4fa0635bbe0de596b8f79235e0b17a4d29385" +dependencies = [ + "lazy_static", + "libc", + "log", + "ndk 0.3.0", "ndk-macro", "ndk-sys", ] @@ -687,7 +713,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca565a7df06f3d4b485494f25ba05da1435950f4dc263440eda7a6fa9b8e36e4" dependencies = [ "derivative", - "num_enum_derive", + "num_enum_derive 0.4.3", +] + +[[package]] +name = "num_enum" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "226b45a5c2ac4dd696ed30fa6b94b057ad909c7b7fc2e0d0808192bced894066" +dependencies = [ + "derivative", + "num_enum_derive 0.5.1", ] [[package]] @@ -702,6 +738,18 @@ dependencies = [ "syn", ] +[[package]] +name = "num_enum_derive" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c0fd9eba1d5db0994a239e09c1be402d35622277e35468ba891aa5e3188ce7e" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "objc" version = "0.2.7" @@ -773,11 +821,15 @@ name = "piet-gpu" version = "0.1.0" dependencies = [ "clap", + "ndk 0.3.0", + "ndk-glue 0.3.0", + "ndk-sys", "piet", "piet-gpu-hal", "piet-gpu-types", "png", "rand", + "raw-window-handle", "roxmltree", "winit", ] @@ -1298,8 +1350,8 @@ dependencies = [ "log", "mio", "mio-extras", - "ndk", - "ndk-glue", + "ndk 0.2.1", + "ndk-glue 0.2.1", "ndk-sys", "objc", "parking_lot", diff --git a/piet-gpu/Cargo.toml b/piet-gpu/Cargo.toml index 8057318..8334038 100644 --- a/piet-gpu/Cargo.toml +++ b/piet-gpu/Cargo.toml @@ -14,6 +14,11 @@ path = "bin/cli.rs" name = "winit" path = "bin/winit.rs" +[[example]] +name = "android" +path = "bin/android.rs" +crate-type = ["cdylib"] + [dependencies.piet-gpu-hal] path = "../piet-gpu-hal" @@ -27,3 +32,9 @@ rand = "0.7.3" roxmltree = "0.13" winit = "0.23" clap = "2.33" + +[target.'cfg(target_os = "android")'.dependencies] +ndk = "0.3" +ndk-sys = "0.2.0" +ndk-glue = "0.3" +raw-window-handle = "0.3" diff --git a/piet-gpu/bin/android.rs b/piet-gpu/bin/android.rs new file mode 100644 index 0000000..3bf3ca6 --- /dev/null +++ b/piet-gpu/bin/android.rs @@ -0,0 +1,176 @@ +//! Android example +//! +//! Run using `cargo apk run --example android` +//! +//! Requires the [cargo-apk] tool. +//! [cargo-apk]: https://crates.io/crates/cargo-apk + +use raw_window_handle::android::AndroidHandle; +use raw_window_handle::{HasRawWindowHandle, RawWindowHandle}; + +use ndk::native_window::NativeWindow; +use ndk_glue::Event; + +use piet_gpu_hal::hub; +use piet_gpu_hal::vulkan::{QueryPool, VkInstance, VkSurface, VkSwapchain}; +use piet_gpu_hal::{CmdBuf, Error, ImageLayout}; + +use piet_gpu::{render_scene, PietGpuRenderContext, Renderer}; + +#[cfg_attr(target_os = "android", ndk_glue::main(backtrace = "on"))] +fn main() { + my_main().unwrap(); +} + +struct MyHandle { + handle: AndroidHandle, +} + +// State required to render and present the contents +struct GfxState { + session: hub::Session, + renderer: Renderer, + swapchain: VkSwapchain, + current_frame: usize, + last_frame_idx: usize, + submitted: Option, + query_pools: Vec, + present_semaphores: Vec, +} + +const WIDTH: usize = 1080; +const HEIGHT: usize = 2280; +const NUM_FRAMES: usize = 2; + +fn my_main() -> Result<(), Error> { + let mut gfx_state = None; + loop { + for event in ndk_glue::poll_events() { + println!("got event {:?}", event); + match event { + Event::WindowCreated => { + let window = ndk_glue::native_window(); + if let Some(window) = &*window { + let handle = get_handle(window); + let (instance, surface) = VkInstance::new(Some(&handle))?; + gfx_state = Some(GfxState::new(&instance, surface.as_ref())?); + } else { + println!("native window is sadly none"); + } + } + Event::WindowRedrawNeeded => { + if let Some(gfx_state) = gfx_state.as_mut() { + for _ in 0..10 { + gfx_state.redraw(); + } + } + } + _ => (), + } + } + } +} + +fn get_handle(window: &NativeWindow) -> MyHandle { + println!( + "window = {:?}, {}x{}", + window.ptr(), + window.width(), + window.height() + ); + let mut handle = AndroidHandle::empty(); + handle.a_native_window = window.ptr().as_ptr() as *mut std::ffi::c_void; + MyHandle { handle } +} + +unsafe impl HasRawWindowHandle for MyHandle { + fn raw_window_handle(&self) -> RawWindowHandle { + RawWindowHandle::Android(self.handle) + } +} + +impl GfxState { + fn new(instance: &VkInstance, surface: Option<&VkSurface>) -> Result { + unsafe { + let device = instance.device(surface)?; + let mut swapchain = + instance.swapchain(WIDTH / 2, HEIGHT / 2, &device, surface.unwrap())?; + let session = hub::Session::new(device); + let mut current_frame = 0; + let present_semaphores = (0..NUM_FRAMES) + .map(|_| session.create_semaphore()) + .collect::, Error>>()?; + let query_pools = (0..NUM_FRAMES) + .map(|_| session.create_query_pool(8)) + .collect::, Error>>()?; + + let mut ctx = PietGpuRenderContext::new(); + render_scene(&mut ctx); + let n_paths = ctx.path_count(); + let n_pathseg = ctx.pathseg_count(); + let n_trans = ctx.pathseg_count(); + let scene = ctx.get_scene_buf(); + + let renderer = Renderer::new(&session, scene, n_paths, n_pathseg, n_trans)?; + + let submitted: Option = None; + let current_frame = 0; + let last_frame_idx = 0; + Ok(GfxState { + session, + renderer, + swapchain, + current_frame, + last_frame_idx, + submitted, + query_pools, + present_semaphores, + }) + } + } + + fn redraw(&mut self) { + println!("redraw"); + unsafe { + if let Some(submitted) = self.submitted.take() { + submitted.wait().unwrap(); + + let ts = self + .session + .fetch_query_pool(&self.query_pools[self.last_frame_idx]) + .unwrap(); + println!("render time: {:?}", ts); + } + let frame_idx = self.current_frame % NUM_FRAMES; + let (image_idx, acquisition_semaphore) = self.swapchain.next().unwrap(); + let swap_image = self.swapchain.image(image_idx); + let query_pool = &self.query_pools[frame_idx]; + let mut cmd_buf = self.session.cmd_buf().unwrap(); + cmd_buf.begin(); + self.renderer.record(&mut cmd_buf, &query_pool); + + // Image -> Swapchain + cmd_buf.image_barrier(&swap_image, ImageLayout::Undefined, ImageLayout::BlitDst); + cmd_buf.blit_image(self.renderer.image_dev.vk_image(), &swap_image); + cmd_buf.image_barrier(&swap_image, ImageLayout::BlitDst, ImageLayout::Present); + cmd_buf.finish(); + + self.submitted = Some( + self.session + .run_cmd_buf( + cmd_buf, + &[acquisition_semaphore], + &[self.present_semaphores[frame_idx]], + ) + .unwrap(), + ); + self.last_frame_idx = frame_idx; + + self.swapchain + .present(image_idx, &[self.present_semaphores[frame_idx]]) + .unwrap(); + + self.current_frame += 1; + } + } +}