-
-
Notifications
You must be signed in to change notification settings - Fork 485
Add Android example #1417
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Add Android example #1417
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| #![cfg(target_os = "android")] | ||
|
|
||
| #[ndk_glue::main(backtrace = "on")] | ||
| fn main() { | ||
| glutin_examples::main() | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,14 +10,18 @@ use raw_window_handle::{ | |
| }; | ||
|
|
||
| use winit::event::{Event, WindowEvent}; | ||
| use winit::event_loop::{ControlFlow, EventLoop}; | ||
| use winit::event_loop::EventLoop; | ||
| #[cfg(glx_backend)] | ||
| use winit::platform::unix; | ||
| #[cfg(x11_platform)] | ||
| use winit::platform::unix::WindowBuilderExtUnix; | ||
| use winit::window::{Window, WindowBuilder}; | ||
|
|
||
| use glutin::config::{Config, ConfigSurfaceTypes, ConfigTemplate, ConfigTemplateBuilder}; | ||
| use glutin::context::{ContextApi, ContextAttributesBuilder}; | ||
| use glutin::display::{Display, DisplayApiPreference}; | ||
| #[cfg(x11_platform)] | ||
| use glutin::platform::x11::X11GlConfigExt; | ||
| use glutin::prelude::*; | ||
| use glutin::surface::{ | ||
| Surface, SurfaceAttributes, SurfaceAttributesBuilder, SwapInterval, WindowSurface, | ||
|
|
@@ -28,11 +32,13 @@ pub fn main() { | |
|
|
||
| let raw_display = event_loop.raw_display_handle(); | ||
|
|
||
| // We create a window before the display to accomodate for WGL, since it | ||
| // requires creating HDC for properly loading the WGL and it should be taken | ||
| // from the window you'll be rendering into. | ||
| let window = WindowBuilder::new().with_transparent(true).build(&event_loop).unwrap(); | ||
| let raw_window_handle = window.raw_window_handle(); | ||
| let mut window = cfg!(wgl_backend).then(|| { | ||
| // We create a window before the display to accommodate for WGL, since it | ||
| // requires creating HDC for properly loading the WGL and it should be taken | ||
| // from the window you'll be rendering into. | ||
| WindowBuilder::new().with_transparent(true).build(&event_loop).unwrap() | ||
| }); | ||
| let raw_window_handle = window.as_ref().map(|w| w.raw_window_handle()); | ||
|
|
||
| // Create the GL display. This will create display automatically for the | ||
| // underlying GL platform. See support module on how it's being done. | ||
|
|
@@ -41,98 +47,166 @@ pub fn main() { | |
| // Create the config we'll be used for window. We'll use the native window | ||
| // raw-window-handle for it to get the right visual and use proper hdc. Note | ||
| // that you can likely use it for other windows using the same config. | ||
| let template = config_template(window.raw_window_handle()); | ||
| let config = unsafe { | ||
| gl_display | ||
| .find_configs(template) | ||
| .unwrap() | ||
| .reduce(|accum, config| { | ||
| // Find the config with the maximum number of samples. | ||
| // | ||
| // In general if you're not sure what you want in template you can request or | ||
| // don't want to require multisampling for example, you can search for a | ||
| // specific option you want afterwards. | ||
| // | ||
| // XXX however on macOS you can request only one config, so you should do | ||
| // a search with the help of `find_configs` and adjusting your template. | ||
| if config.num_samples() > accum.num_samples() { | ||
| config | ||
| } else { | ||
| accum | ||
| } | ||
| }) | ||
| .unwrap() | ||
| }; | ||
| let template = config_template(raw_window_handle); | ||
| let config = unsafe { gl_display.find_configs(template) } | ||
| .unwrap() | ||
| .reduce(|accum, config| { | ||
| // Find the config with the maximum number of samples. | ||
| // | ||
| // In general if you're not sure what you want in template you can request or | ||
| // don't want to require multisampling for example, you can search for a | ||
| // specific option you want afterwards. | ||
| // | ||
| // XXX however on macOS you can request only one config, so you should do | ||
| // a search with the help of `find_configs` and adjusting your template. | ||
|
|
||
| // Since we try to show off transparency try to pick the config that supports it | ||
| // on X11 over the ones without it. XXX Configs that support | ||
| // transparency on X11 tend to not have multisapmling, so be aware | ||
| // of that. | ||
|
|
||
| #[cfg(x11_platform)] | ||
| let transparency_check = | ||
| config.x11_visual().map(|v| v.supports_transparency()).unwrap_or(false) | ||
| & !accum.x11_visual().map(|v| v.supports_transparency()).unwrap_or(false); | ||
|
|
||
| #[cfg(not(x11_platform))] | ||
| let transparency_check = false; | ||
|
|
||
| if transparency_check || config.num_samples() > accum.num_samples() { | ||
| config | ||
| } else { | ||
| accum | ||
| } | ||
| }) | ||
| .unwrap(); | ||
|
|
||
| println!("Picked a config with {} samples", config.num_samples()); | ||
|
|
||
| // Create a wrapper for GL window and surface. | ||
| let gl_window = GlWindow::from_existing(&gl_display, window, &config); | ||
|
|
||
| // The context creation part. It can be created before surface and that's how | ||
| // it's expected in multithreaded + multiwindow operation mode, since you | ||
| // can send NotCurrentContext, but not Surface. | ||
| let context_attributes = ContextAttributesBuilder::new().build(Some(raw_window_handle)); | ||
| let context_attributes = ContextAttributesBuilder::new().build(raw_window_handle); | ||
|
|
||
| // Since glutin by default tries to create OpenGL core context, which may not be | ||
| // present we should try gles. | ||
| let fallback_context_attributes = ContextAttributesBuilder::new() | ||
| .with_context_api(ContextApi::Gles(None)) | ||
| .build(Some(raw_window_handle)); | ||
| let gl_context = unsafe { | ||
| .build(raw_window_handle); | ||
| let mut not_current_gl_context = Some(unsafe { | ||
| gl_display.create_context(&config, &context_attributes).unwrap_or_else(|_| { | ||
| gl_display | ||
| .create_context(&config, &fallback_context_attributes) | ||
| .expect("failed to create context") | ||
| }) | ||
| }; | ||
|
|
||
| // Make it current and load symbols. | ||
| let gl_context = gl_context.make_current(&gl_window.surface).unwrap(); | ||
|
Comment on lines
-90
to
-91
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @kchibisov If I understood things correctly |
||
|
|
||
| // WGL requires current context on the calling thread to load symbols properly, | ||
| // so the call here is for portability reasons. In case you don't target WGL | ||
| // you can call it right after display creation. | ||
| // | ||
| // The symbol loading is done by the renderer. | ||
| let renderer = Renderer::new(&gl_display); | ||
|
|
||
| // Try setting vsync. | ||
| if let Err(res) = gl_window | ||
| .surface | ||
| .set_swap_interval(&gl_context, SwapInterval::Wait(NonZeroU32::new(1).unwrap())) | ||
| { | ||
| eprintln!("Error setting vsync: {:?}", res); | ||
| } | ||
| }); | ||
|
|
||
| let mut state = None; | ||
| let mut renderer = None; | ||
|
|
||
| event_loop.run(move |event, _, control_flow| { | ||
| *control_flow = ControlFlow::Wait; | ||
| event_loop.run(move |event, event_loop_window_target, control_flow| { | ||
| control_flow.set_wait(); | ||
| match event { | ||
| Event::Resumed => { | ||
| // While this event is only relevant for Android, it is raised on all platforms | ||
| // to provide a consistent place to create windows | ||
|
|
||
| #[cfg(target_os = "android")] | ||
| println!("Android window available"); | ||
|
|
||
| // Take a possibly early created window, or create a new one | ||
| let window = window.take().unwrap_or_else(|| { | ||
| // On X11 opacity is controlled by the visual we pass to the window latter on, | ||
| // other platforms decide on that by what you draw, so there's no need to pass | ||
| // this information to the window. | ||
| #[cfg(not(cgl_backend))] | ||
| let window = WindowBuilder::new(); | ||
|
|
||
| // Request opacity for window on macOS explicitly. | ||
| #[cfg(cgl_backend)] | ||
| let window = WindowBuilder::new().with_transparent(true); | ||
|
|
||
| // We must pass the visual into the X11 window upon creation, otherwise we | ||
| // could have mismatch errors during context activation and swap buffers. | ||
| #[cfg(x11_platform)] | ||
| let window = if let Some(visual) = config.x11_visual() { | ||
| window.with_x11_visual(visual.into_raw()) | ||
| } else { | ||
| window | ||
| }; | ||
|
|
||
| window.build(event_loop_window_target).unwrap() | ||
| }); | ||
|
|
||
| // Create a wrapper for GL window and surface. | ||
| let gl_window = GlWindow::from_existing(&gl_display, window, &config); | ||
|
|
||
| // Make it current. | ||
| let gl_context = not_current_gl_context | ||
| .take() | ||
| .unwrap() | ||
| .make_current(&gl_window.surface) | ||
| .unwrap(); | ||
|
|
||
| // The context needs to be current for the Renderer to set up shaders and | ||
| // buffers. It also performs function loading, which needs a current context on | ||
| // WGL. | ||
| renderer.get_or_insert_with(|| Renderer::new(&gl_display)); | ||
|
|
||
| // Try setting vsync. | ||
| if let Err(res) = gl_window | ||
| .surface | ||
| .set_swap_interval(&gl_context, SwapInterval::Wait(NonZeroU32::new(1).unwrap())) | ||
| { | ||
| eprintln!("Error setting vsync: {:?}", res); | ||
| } | ||
|
|
||
| assert!(state.replace((gl_context, gl_window)).is_none()); | ||
| }, | ||
| Event::Suspended => { | ||
| // This event is only raised on Android, where the backing NativeWindow for a GL | ||
| // Surface can appear and disappear at any moment. | ||
| println!("Android window removed"); | ||
|
|
||
| // Destroy the GL Surface and un-current the GL Context before ndk-glue releases | ||
| // the window back to the system. | ||
| let (gl_context, _) = state.take().unwrap(); | ||
| assert!(not_current_gl_context | ||
| .replace(gl_context.make_not_current().unwrap()) | ||
| .is_none()); | ||
| }, | ||
|
|
||
| Event::WindowEvent { event, .. } => match event { | ||
| WindowEvent::Resized(size) => { | ||
| if size.width != 0 && size.height != 0 { | ||
| // Some platforms like EGL require resizing GL surface to update the size | ||
| // Notable platforms here are Wayland and macOS, other don't require it | ||
| // and the function is no-op, but it's wise to resize it for portability | ||
| // reasons. | ||
| gl_window.surface.resize( | ||
| &gl_context, | ||
| NonZeroU32::new(size.width).unwrap(), | ||
| NonZeroU32::new(size.height).unwrap(), | ||
| ); | ||
| renderer.resize(size.width as i32, size.height as i32); | ||
| if let Some((gl_context, gl_window)) = &state { | ||
| gl_window.surface.resize( | ||
| gl_context, | ||
| NonZeroU32::new(size.width).unwrap(), | ||
| NonZeroU32::new(size.height).unwrap(), | ||
| ); | ||
| let renderer = renderer.as_ref().unwrap(); | ||
| renderer.resize(size.width as i32, size.height as i32); | ||
| } | ||
| } | ||
| }, | ||
| WindowEvent::CloseRequested => { | ||
| *control_flow = ControlFlow::Exit; | ||
| control_flow.set_exit(); | ||
| }, | ||
| _ => (), | ||
| }, | ||
| Event::RedrawEventsCleared => { | ||
| renderer.draw(); | ||
| gl_window.window.request_redraw(); | ||
| if let Some((gl_context, gl_window)) = &state { | ||
| let renderer = renderer.as_ref().unwrap(); | ||
| renderer.draw(); | ||
| gl_window.window.request_redraw(); | ||
|
|
||
| gl_window.surface.swap_buffers(&gl_context).unwrap(); | ||
| gl_window.surface.swap_buffers(gl_context).unwrap(); | ||
| } | ||
| }, | ||
| _ => (), | ||
| } | ||
|
|
@@ -168,11 +242,14 @@ impl GlWindow { | |
| } | ||
|
|
||
| /// Create template to find OpenGL config. | ||
| pub fn config_template(raw_window_handle: RawWindowHandle) -> ConfigTemplate { | ||
| let builder = ConfigTemplateBuilder::new() | ||
| .with_alpha_size(8) | ||
| .compatible_with_native_window(raw_window_handle) | ||
| .with_surface_type(ConfigSurfaceTypes::WINDOW); | ||
| pub fn config_template(raw_window_handle: Option<RawWindowHandle>) -> ConfigTemplate { | ||
| let mut builder = ConfigTemplateBuilder::new().with_alpha_size(8); | ||
|
|
||
| if let Some(raw_window_handle) = raw_window_handle { | ||
| builder = builder | ||
| .compatible_with_native_window(raw_window_handle) | ||
| .with_surface_type(ConfigSurfaceTypes::WINDOW); | ||
| } | ||
|
|
||
| #[cfg(cgl_backend)] | ||
| let builder = builder.with_transparency(true).with_multisampling(8); | ||
|
|
@@ -194,7 +271,7 @@ pub fn surface_attributes(window: &Window) -> SurfaceAttributes<WindowSurface> { | |
| /// Create the display. | ||
| pub fn create_display( | ||
| raw_display: RawDisplayHandle, | ||
| raw_window_handle: RawWindowHandle, | ||
| raw_window_handle: Option<RawWindowHandle>, | ||
| ) -> Display { | ||
| #[cfg(egl_backend)] | ||
| let preference = DisplayApiPreference::Egl; | ||
|
|
@@ -206,10 +283,10 @@ pub fn create_display( | |
| let preference = DisplayApiPreference::Cgl; | ||
|
|
||
| #[cfg(wgl_backend)] | ||
| let preference = DisplayApiPreference::Wgl(Some(raw_window_handle)); | ||
| let preference = DisplayApiPreference::Wgl(Some(raw_window_handle.unwrap())); | ||
|
|
||
| #[cfg(all(egl_backend, wgl_backend))] | ||
| let preference = DisplayApiPreference::WglThenEgl(Some(raw_window_handle)); | ||
| let preference = DisplayApiPreference::WglThenEgl(Some(raw_window_handle.unwrap())); | ||
MarijnS95 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| #[cfg(all(egl_backend, glx_backend))] | ||
| let preference = DisplayApiPreference::GlxThenEgl(Box::new(unix::register_xlib_error_hook)); | ||
|
|
||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.