Skip to content

Commit

Permalink
Switch from android_glue to ndk-glue
Browse files Browse the repository at this point in the history
Additionally remove the Android lifecycle handling code, as winit no
longer provides the required callback meaning it does not compile.

This fixes compilation on Android. However, there is a caveat: Glutin
is no longer able to automatically destroy and create the Surface in
response to Android lifecycle events. The application must therefore
ensure they only create a Context following a winit Event::Resumed,
and they destroy the context when receiving an Event::Suspended.
  • Loading branch information
jamienicol committed May 30, 2022
1 parent 07c461e commit 008e5c2
Show file tree
Hide file tree
Showing 5 changed files with 10 additions and 104 deletions.
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@

- Fix crash when creating OpenGLES context without explicit version.
- Add `buffer_age` method on `WindowedContext`.
- On Android, switched from `StaticStructGenerator` to `StructGenerator`, fixing some compilation errors.
- Return an `Err` instead of panicking when surfaceless GLX context creation fails on Linux.
- Fix compilation on Android:
- Switch from `StaticStructGenerator` to `StructGenerator` to dynamically load symbols.
- Replace `android_glue` dependency with `ndk-glue`, and remove broken lifecycle event handling.
- Glutin can now be used on Android, however, the application must ensure it only creates the Context following a winit `Event::Resumed` event, and destroys the context in response to a `Event::Suspended` event.

# Version 0.28.0 (2021-12-02)

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ Glutin is only officially supported on the latest stable version of the Rust com

To compile the examples for android, you have to use the `cargo apk` utility.

See [the `android-rs-glue` repository](https://github.com/rust-windowing/android-rs-glue) for instructions.
See [`cargo-apk` in the `android-ndk-rs` repository](https://github.com/rust-windowing/android-ndk-rs/cargo-apk) for instructions.

### Emscripten with asmjs

Expand Down
2 changes: 1 addition & 1 deletion glutin/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ lazy_static = "1.3"
winit = { version = "0.26", default-features = false }

[target.'cfg(target_os = "android")'.dependencies]
android_glue = "0.2"
glutin_egl_sys = { version = "0.1.5", path = "../glutin_egl_sys" }
libloading = "0.7"
ndk-glue = "0.5" # Keep in sync with winit
parking_lot = "0.11"

[target.'cfg(target_os = "emscripten")'.dependencies]
Expand Down
55 changes: 4 additions & 51 deletions glutin/src/api/android/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ use crate::api::egl::{Context as EglContext, NativeDisplay, SurfaceType as EglSu
use crate::CreationError::{self, OsError};
use crate::{Api, ContextError, GlAttributes, PixelFormat, PixelFormatRequirements, Rect};

use crate::platform::android::EventLoopExtAndroid;
use glutin_egl_sys as ffi;
use parking_lot::Mutex;
use winit;
Expand All @@ -23,30 +22,6 @@ struct AndroidContext {
#[derive(Debug)]
pub struct Context(Arc<AndroidContext>);

#[derive(Debug)]
struct AndroidSyncEventHandler(Arc<AndroidContext>);

impl android_glue::SyncEventHandler for AndroidSyncEventHandler {
fn handle(&mut self, event: &android_glue::Event) {
match *event {
// 'on_surface_destroyed' Android event can arrive with some delay
// because multithreading communication. Because of
// that, swap_buffers can be called before processing
// 'on_surface_destroyed' event, with the native window
// surface already destroyed. EGL generates a BAD_SURFACE error in
// this situation. Set stop to true to prevent
// swap_buffer call race conditions.
android_glue::Event::TermWindow => {
let mut stopped = self.0.stopped.as_ref().unwrap().lock();
*stopped = true;
}
_ => {
return;
}
};
}
}

impl Context {
#[inline]
pub fn new_windowed<T>(
Expand All @@ -57,41 +32,19 @@ impl Context {
) -> Result<(winit::window::Window, Self), CreationError> {
let win = wb.build(el)?;
let gl_attr = gl_attr.clone().map_sharing(|c| &c.0.egl_context);
let nwin = unsafe { android_glue::get_native_window() };
if nwin.is_null() {
return Err(OsError("Android's native window is null".to_string()));
}
let nwin = ndk_glue::native_window();
let nwin =
nwin.as_ref().ok_or_else(|| OsError("Android's native window is null".to_string()))?;
let native_display = NativeDisplay::Android;
let egl_context =
EglContext::new(pf_reqs, &gl_attr, native_display, EglSurfaceType::Window, |c, _| {
Ok(c[0])
})
.and_then(|p| p.finish(nwin as *const _))?;
.and_then(|p| p.finish(nwin.ptr().as_ptr() as *const _))?;
let ctx = Arc::new(AndroidContext { egl_context, stopped: Some(Mutex::new(false)) });

let handler = Box::new(AndroidSyncEventHandler(ctx.clone()));
android_glue::add_sync_event_handler(handler);
let context = Context(ctx.clone());

el.set_suspend_callback(Some(Box::new(move |suspended| {
let mut stopped = ctx.stopped.as_ref().unwrap().lock();
*stopped = suspended;
if suspended {
// Android has stopped the activity or sent it to background.
// Release the EGL surface and stop the animation loop.
unsafe {
ctx.egl_context.on_surface_destroyed();
}
} else {
// Android has started the activity or sent it to foreground.
// Restore the EGL surface and animation loop.
unsafe {
let nwin = android_glue::get_native_window();
ctx.egl_context.on_surface_created(nwin as *const _);
}
}
})));

Ok((win, context))
}

Expand Down
50 changes: 0 additions & 50 deletions glutin/src/api/egl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,8 +161,6 @@ pub struct Context {
surface: Option<Mutex<ffi::egl::types::EGLSurface>>,
api: Api,
pixel_format: PixelFormat,
#[cfg(target_os = "android")]
config_id: ffi::egl::types::EGLConfig,
}

fn get_egl_version(
Expand Down Expand Up @@ -511,52 +509,6 @@ impl Context {
self.display
}

// Handle Android Life Cycle.
// Android has started the activity or sent it to foreground.
// Create a new surface and attach it to the recreated ANativeWindow.
// Restore the EGLContext.
#[cfg(target_os = "android")]
pub unsafe fn on_surface_created(&self, nwin: ffi::EGLNativeWindowType) {
let egl = EGL.as_ref().unwrap();
let mut surface = self.surface.as_ref().unwrap().lock();
if *surface != ffi::egl::NO_SURFACE {
return;
}
*surface = egl.CreateWindowSurface(self.display, self.config_id, nwin, std::ptr::null());
if surface.is_null() {
panic!("on_surface_created: eglCreateWindowSurface failed with 0x{:x}", egl.GetError())
}
let ret = egl.MakeCurrent(self.display, *surface, *surface, self.context);
if ret == 0 {
panic!("on_surface_created: eglMakeCurrent failed with 0x{:x}", egl.GetError())
}
}

// Handle Android Life Cycle.
// Android has stopped the activity or sent it to background.
// Release the surface attached to the destroyed ANativeWindow.
// The EGLContext is not destroyed so it can be restored later.
#[cfg(target_os = "android")]
pub unsafe fn on_surface_destroyed(&self) {
let egl = EGL.as_ref().unwrap();
let mut surface = self.surface.as_ref().unwrap().lock();
if *surface == ffi::egl::NO_SURFACE {
return;
}
let ret = egl.MakeCurrent(
self.display,
ffi::egl::NO_SURFACE,
ffi::egl::NO_SURFACE,
ffi::egl::NO_CONTEXT,
);
if ret == 0 {
panic!("on_surface_destroyed: eglMakeCurrent failed with 0x{:x}", egl.GetError())
}

egl.DestroySurface(self.display, *surface);
*surface = ffi::egl::NO_SURFACE;
}

#[inline]
pub fn get_proc_address(&self, addr: &str) -> *const core::ffi::c_void {
let egl = EGL.as_ref().unwrap();
Expand Down Expand Up @@ -991,8 +943,6 @@ impl<'a> ContextPrototype<'a> {
surface: surface.map(|s| Mutex::new(s)),
api: self.api,
pixel_format: self.pixel_format,
#[cfg(target_os = "android")]
config_id: self.config_id,
})
}
}
Expand Down

0 comments on commit 008e5c2

Please sign in to comment.