//! 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::{ CmdBuf, Error, ImageLayout, Instance, QueryPool, Semaphore, Session, SubmittedCmdBuf, Surface, Swapchain, }; use piet::kurbo::Point; use piet::{RenderContext, Text, TextAttribute, TextLayoutBuilder}; use piet_gpu::{test_scenes, 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: Session, renderer: Renderer, swapchain: Swapchain, current_frame: usize, submitted: [Option; NUM_FRAMES], cmd_bufs: [Option; NUM_FRAMES], query_pools: Vec, present_semaphores: Vec, } 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 width = window.width() as usize; let height = window.height() as usize; let handle = get_handle(window); let (instance, surface) = Instance::new(Some(&handle), Default::default())?; gfx_state = Some(GfxState::new(&instance, surface.as_ref(), width, height)?); } else { println!("native window is sadly none"); } } Event::WindowRedrawNeeded => { if let Some(gfx_state) = gfx_state.as_mut() { for _ in 0..1000 { 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: &Instance, surface: Option<&Surface>, width: usize, height: usize, ) -> Result { unsafe { let device = instance.device(surface)?; let swapchain = instance.swapchain(width, height, &device, surface.unwrap())?; let session = Session::new(device); let 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 submitted = Default::default(); let cmd_bufs = Default::default(); let renderer = Renderer::new(&session, width, height, NUM_FRAMES)?; Ok(GfxState { session, renderer, swapchain, current_frame, submitted, cmd_bufs, query_pools, present_semaphores, }) } } fn redraw(&mut self) { println!("redraw"); unsafe { let frame_idx = self.current_frame % NUM_FRAMES; let mut info_string = String::new(); if let Some(submitted) = self.submitted[frame_idx].take() { self.cmd_bufs[frame_idx] = submitted.wait().unwrap(); let ts = self .session .fetch_query_pool(&self.query_pools[frame_idx]) .unwrap(); info_string = format!("{:.1}ms", ts.last().unwrap() * 1e3); println!("render time: {:?}", ts); } let mut ctx = PietGpuRenderContext::new(); test_scenes::render_anim_frame(&mut ctx, self.current_frame); //test_scenes::render_tiger(&mut ctx); render_info_string(&mut ctx, &info_string); if let Err(e) = self.renderer.upload_render_ctx(&mut ctx, frame_idx) { println!("error in uploading: {}", e); } 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.cmd_bufs[frame_idx] .take() .unwrap_or_else(|| self.session.cmd_buf().unwrap()); cmd_buf.begin(); self.renderer.record(&mut cmd_buf, &query_pool, frame_idx); // Image -> Swapchain cmd_buf.image_barrier(&swap_image, ImageLayout::Undefined, ImageLayout::BlitDst); cmd_buf.blit_image(&self.renderer.image_dev, &swap_image); cmd_buf.image_barrier(&swap_image, ImageLayout::BlitDst, ImageLayout::Present); cmd_buf.finish(); self.submitted[frame_idx] = Some( self.session .run_cmd_buf( cmd_buf, &[&acquisition_semaphore], &[&self.present_semaphores[frame_idx]], ) .unwrap(), ); self.swapchain .present(image_idx, &[&self.present_semaphores[frame_idx]]) .unwrap(); self.current_frame += 1; } } } fn render_info_string(rc: &mut impl RenderContext, info: &str) { let layout = rc .text() .new_text_layout(info.to_string()) .default_attribute(TextAttribute::FontSize(60.0)) .build() .unwrap(); rc.draw_text(&layout, Point::new(110.0, 120.0)); }