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 27, 2022
1 parent 7e33024 commit 232c69e
Show file tree
Hide file tree
Showing 4 changed files with 5 additions and 102 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
- 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.
- On Android, replace `android_glue` dependency with `ndk-glue`, and remove broken lifecycle event handling.

# Version 0.28.0 (2021-12-02)

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"
parking_lot = "0.11"

[target.'cfg(target_os = "emscripten")'.dependencies]
Expand Down
54 changes: 3 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,18 @@ 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 232c69e

Please sign in to comment.