Skip to content

Commit

Permalink
egui_glow: update example to latest glutin (emilk#2396)
Browse files Browse the repository at this point in the history
This lifts the context handling from commit hash 8eb687c as this does
all the required handling for us that the older glutin once did.
  • Loading branch information
flukejones authored Dec 6, 2022
1 parent 32144d3 commit be4a5be
Show file tree
Hide file tree
Showing 3 changed files with 160 additions and 48 deletions.
21 changes: 11 additions & 10 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion crates/egui_glow/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ wasm-bindgen = { version = "0.2" }


[dev-dependencies]
glutin = "0.29.0" # examples/pure_glow
glutin = "0.30.2" # examples/pure_glow
raw-window-handle = "0.5.0"


[[example]]
Expand Down
184 changes: 147 additions & 37 deletions crates/egui_glow/examples/pure_glow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,124 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release
#![allow(unsafe_code)]

use egui_winit::winit;

/// The majority of `GlutinWindowContext` is taken from `eframe`
struct GlutinWindowContext {
window: winit::window::Window,
gl_context: glutin::context::PossiblyCurrentContext,
gl_display: glutin::display::Display,
gl_surface: glutin::surface::Surface<glutin::surface::WindowSurface>,
}

impl GlutinWindowContext {
// refactor this function to use `glutin-winit` crate eventually.
// preferably add android support at the same time.
#[allow(unsafe_code)]
unsafe fn new(winit_window: winit::window::Window) -> Self {
use glutin::prelude::*;
use raw_window_handle::*;

let raw_display_handle = winit_window.raw_display_handle();
let raw_window_handle = winit_window.raw_window_handle();

// EGL is crossplatform and the official khronos way
// but sometimes platforms/drivers may not have it, so we use back up options where possible.

// try egl and fallback to windows wgl. Windows is the only platform that *requires* window handle to create display.
#[cfg(target_os = "windows")]
let preference = glutin::display::DisplayApiPreference::EglThenWgl(Some(window_handle));
// try egl and fallback to x11 glx
#[cfg(target_os = "linux")]
let preference = glutin::display::DisplayApiPreference::EglThenGlx(Box::new(
winit::platform::unix::register_xlib_error_hook,
));
#[cfg(target_os = "macos")]
let preference = glutin::display::DisplayApiPreference::Cgl;
#[cfg(target_os = "android")]
let preference = glutin::display::DisplayApiPreference::Egl;

let gl_display = glutin::display::Display::new(raw_display_handle, preference).unwrap();

let config_template = glutin::config::ConfigTemplateBuilder::new()
.prefer_hardware_accelerated(None)
.with_depth_size(0)
.with_stencil_size(0)
.with_transparency(false)
.compatible_with_native_window(raw_window_handle)
.build();

let config = gl_display
.find_configs(config_template)
.unwrap()
.next()
.unwrap();

let context_attributes =
glutin::context::ContextAttributesBuilder::new().build(Some(raw_window_handle));
// for surface creation.
let (width, height): (u32, u32) = winit_window.inner_size().into();
let surface_attributes =
glutin::surface::SurfaceAttributesBuilder::<glutin::surface::WindowSurface>::new()
.build(
raw_window_handle,
std::num::NonZeroU32::new(width).unwrap(),
std::num::NonZeroU32::new(height).unwrap(),
);
// start creating the gl objects
let gl_context = gl_display
.create_context(&config, &context_attributes)
.unwrap();

let gl_surface = gl_display
.create_window_surface(&config, &surface_attributes)
.unwrap();

let gl_context = gl_context.make_current(&gl_surface).unwrap();

gl_surface
.set_swap_interval(
&gl_context,
glutin::surface::SwapInterval::Wait(std::num::NonZeroU32::new(1).unwrap()),
)
.unwrap();

GlutinWindowContext {
window: winit_window,
gl_context,
gl_display,
gl_surface,
}
}

fn window(&self) -> &winit::window::Window {
&self.window
}

fn resize(&self, physical_size: winit::dpi::PhysicalSize<u32>) {
use glutin::surface::GlSurface;
self.gl_surface.resize(
&self.gl_context,
physical_size.width.try_into().unwrap(),
physical_size.height.try_into().unwrap(),
);
}

fn swap_buffers(&self) -> glutin::error::Result<()> {
use glutin::surface::GlSurface;
self.gl_surface.swap_buffers(&self.gl_context)
}

fn get_proc_address(&self, addr: &std::ffi::CStr) -> *const std::ffi::c_void {
use glutin::display::GlDisplay;
self.gl_display.get_proc_address(addr)
}
}

fn main() {
let mut clear_color = [0.1, 0.1, 0.1];

let event_loop = glutin::event_loop::EventLoopBuilder::with_user_event().build();
let event_loop = winit::event_loop::EventLoopBuilder::with_user_event().build();
let (gl_window, gl) = create_display(&event_loop);
let gl = std::sync::Arc::new(gl);

Expand All @@ -27,16 +141,16 @@ fn main() {
});

*control_flow = if quit {
glutin::event_loop::ControlFlow::Exit
winit::event_loop::ControlFlow::Exit
} else if repaint_after.is_zero() {
gl_window.window().request_redraw();
glutin::event_loop::ControlFlow::Poll
winit::event_loop::ControlFlow::Poll
} else if let Some(repaint_after_instant) =
std::time::Instant::now().checked_add(repaint_after)
{
glutin::event_loop::ControlFlow::WaitUntil(repaint_after_instant)
winit::event_loop::ControlFlow::WaitUntil(repaint_after_instant)
} else {
glutin::event_loop::ControlFlow::Wait
winit::event_loop::ControlFlow::Wait
};

{
Expand All @@ -61,20 +175,19 @@ fn main() {
// Platform-dependent event handlers to workaround a winit bug
// See: https://github.com/rust-windowing/winit/issues/987
// See: https://github.com/rust-windowing/winit/issues/1619
glutin::event::Event::RedrawEventsCleared if cfg!(windows) => redraw(),
glutin::event::Event::RedrawRequested(_) if !cfg!(windows) => redraw(),
winit::event::Event::RedrawEventsCleared if cfg!(windows) => redraw(),
winit::event::Event::RedrawRequested(_) if !cfg!(windows) => redraw(),

glutin::event::Event::WindowEvent { event, .. } => {
use glutin::event::WindowEvent;
winit::event::Event::WindowEvent { event, .. } => {
use winit::event::WindowEvent;
if matches!(event, WindowEvent::CloseRequested | WindowEvent::Destroyed) {
*control_flow = glutin::event_loop::ControlFlow::Exit;
*control_flow = winit::event_loop::ControlFlow::Exit;
}

if let glutin::event::WindowEvent::Resized(physical_size) = &event {
if let winit::event::WindowEvent::Resized(physical_size) = &event {
gl_window.resize(*physical_size);
} else if let glutin::event::WindowEvent::ScaleFactorChanged {
new_inner_size,
..
} else if let winit::event::WindowEvent::ScaleFactorChanged {
new_inner_size, ..
} = &event
{
gl_window.resize(**new_inner_size);
Expand All @@ -86,10 +199,10 @@ fn main() {
gl_window.window().request_redraw();
}
}
glutin::event::Event::LoopDestroyed => {
winit::event::Event::LoopDestroyed => {
egui_glow.destroy();
}
glutin::event::Event::NewEvents(glutin::event::StartCause::ResumeTimeReached {
winit::event::Event::NewEvents(winit::event::StartCause::ResumeTimeReached {
..
}) => {
gl_window.window().request_redraw();
Expand All @@ -101,32 +214,29 @@ fn main() {
}

fn create_display(
event_loop: &glutin::event_loop::EventLoop<()>,
) -> (
glutin::WindowedContext<glutin::PossiblyCurrent>,
glow::Context,
) {
let window_builder = glutin::window::WindowBuilder::new()
event_loop: &winit::event_loop::EventLoopWindowTarget<()>,
) -> (GlutinWindowContext, glow::Context) {
let winit_window = winit::window::WindowBuilder::new()
.with_resizable(true)
.with_inner_size(glutin::dpi::LogicalSize {
.with_inner_size(winit::dpi::LogicalSize {
width: 800.0,
height: 600.0,
})
.with_title("egui_glow example")
.with_visible(false); // Keep hidden until we've painted something. See https://github.com/emilk/egui/pull/2279

let gl_window = unsafe {
glutin::ContextBuilder::new()
.with_depth_buffer(0)
.with_stencil_buffer(0)
.with_vsync(true)
.build_windowed(window_builder, event_loop)
.unwrap()
.make_current()
.unwrap()
.with_visible(false)
.build(event_loop)
.unwrap(); // Keep hidden until we've painted something. See https://github.com/emilk/egui/pull/2279

// a lot of the code below has been lifted from glutin example in their repo.
let glutin_window_context = unsafe { GlutinWindowContext::new(winit_window) };
let gl = unsafe {
glow::Context::from_loader_function(|s| {
let s = std::ffi::CString::new(s)
.expect("failed to construct C string from string for gl proc address");

glutin_window_context.get_proc_address(&s)
})
};

let gl = unsafe { glow::Context::from_loader_function(|s| gl_window.get_proc_address(s)) };

(gl_window, gl)
(glutin_window_context, gl)
}

0 comments on commit be4a5be

Please sign in to comment.