diff --git a/Cargo.lock b/Cargo.lock index 5bf9c2c0a..630a7a635 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1278,6 +1278,7 @@ dependencies = [ "egui_glow", "glow 0.11.2", "glutin", + "glutin-winit", "image", "js-sys", "percent-encoding", @@ -1404,6 +1405,7 @@ dependencies = [ "egui-winit", "glow 0.11.2", "glutin", + "glutin-winit", "memoffset", "puffin", "raw-window-handle", @@ -1817,6 +1819,18 @@ dependencies = [ "x11-dl", ] +[[package]] +name = "glutin-winit" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "629a873fc04062830bfe8f97c03773bcd7b371e23bcc465d0a61448cd1588fa4" +dependencies = [ + "cfg_aliases", + "glutin", + "raw-window-handle", + "winit", +] + [[package]] name = "glutin_egl_sys" version = "0.3.1" diff --git a/crates/eframe/Cargo.toml b/crates/eframe/Cargo.toml index 3be55018d..48464a2c2 100644 --- a/crates/eframe/Cargo.toml +++ b/crates/eframe/Cargo.toml @@ -36,7 +36,7 @@ dark-light = ["dep:dark-light"] default_fonts = ["egui/default_fonts"] ## Use [`glow`](https://github.com/grovesNL/glow) for painting, via [`egui_glow`](https://github.com/emilk/egui/tree/master/crates/egui_glow). -glow = ["dep:glow", "dep:egui_glow", "dep:glutin"] +glow = ["dep:glow", "dep:egui_glow", "dep:glutin", "dep:glutin-winit"] ## Enable saving app state to disk. persistence = [ @@ -104,14 +104,8 @@ pollster = { version = "0.2", optional = true } # needed for wgpu # we can expose these to user so that they can select which backends they want to enable to avoid compiling useless deps. # this can be done at the same time we expose x11/wayland features of winit crate. -glutin = { version = "0.30.0", optional = true, es = [ - "egl", - "glx", - "x11", - "wayland", - "wgl", -] } - +glutin = { version = "0.30", optional = true } +glutin-winit = { version = "0.3.0", optional = true } image = { version = "0.24", optional = true, default-features = false, features = [ "png", ] } diff --git a/crates/eframe/src/lib.rs b/crates/eframe/src/lib.rs index 4c4da674c..de031d2af 100644 --- a/crates/eframe/src/lib.rs +++ b/crates/eframe/src/lib.rs @@ -221,8 +221,8 @@ pub enum Error { Glutin(#[from] glutin::error::Error), #[cfg(all(feature = "glow", not(target_arch = "wasm32")))] - #[error("Found no glutin configs matching the template: {0:?}")] - NoGlutinConfigs(glutin::config::ConfigTemplate), + #[error("Found no glutin configs matching the template: {0:?}. error: {1:?}")] + NoGlutinConfigs(glutin::config::ConfigTemplate, Box), #[cfg(feature = "wgpu")] #[error("WGPU error: {0}")] diff --git a/crates/eframe/src/native/epi_integration.rs b/crates/eframe/src/native/epi_integration.rs index 9a844e3cb..26c912ead 100644 --- a/crates/eframe/src/native/epi_integration.rs +++ b/crates/eframe/src/native/epi_integration.rs @@ -64,15 +64,13 @@ pub fn read_window_info( monitor_size, } } - -pub fn build_window( +pub fn window_builder( event_loop: &EventLoopWindowTarget, title: &str, native_options: &epi::NativeOptions, window_settings: Option, -) -> Result { +) -> winit::window::WindowBuilder { let epi::NativeOptions { - always_on_top, maximized, decorated, fullscreen, @@ -159,11 +157,19 @@ pub fn build_window( } } } - + window_builder +} +pub fn build_window( + event_loop: &EventLoopWindowTarget, + title: &str, + native_options: &epi::NativeOptions, + window_settings: Option, +) -> Result { + let window_builder = window_builder(event_loop, title, native_options, window_settings); let window = window_builder.build(event_loop)?; use winit::window::WindowLevel; - window.set_window_level(if *always_on_top { + window.set_window_level(if native_options.always_on_top { WindowLevel::AlwaysOnTop } else { WindowLevel::Normal diff --git a/crates/eframe/src/native/run.rs b/crates/eframe/src/native/run.rs index a68fa3c9f..933a193ef 100644 --- a/crates/eframe/src/native/run.rs +++ b/crates/eframe/src/native/run.rs @@ -152,8 +152,8 @@ fn run_and_return( event => match winit_app.on_event(event_loop, event) { Ok(event_result) => event_result, Err(err) => { + tracing::error!("Exiting because of error: {err:?} on event {event:?}"); returned_result = Err(err); - tracing::debug!("Exiting because of an error"); EventResult::Exit } }, @@ -297,6 +297,12 @@ mod glow_integration { use std::sync::Arc; use egui::NumExt as _; + use glutin::{ + display::GetGlDisplay, + prelude::{GlDisplay, NotCurrentGlContextSurfaceAccessor, PossiblyCurrentGlContext}, + surface::GlSurface, + }; + use raw_window_handle::HasRawWindowHandle; use super::*; @@ -321,132 +327,257 @@ mod glow_integration { painter: egui_glow::Painter, integration: epi_integration::EpiIntegration, app: Box, - // Conceptually this will be split out eventually so that the rest of the state // can be persistent. gl_window: GlutinWindowContext, } + + /// This struct will contain both persistent and temporary glutin state. + /// + /// Platform Quirks: + /// * Microsoft Windows: requires that we create a window before opengl context. + /// * Android: window and surface should be destroyed when we receive a suspend event. recreate on resume event. + /// + /// winit guarantees that we will get a Resumed event on startup on all platforms. + /// * Before Resumed event: `gl_config`, `gl_context` can be created at any time. on windows, a window must be created to get `gl_context`. + /// * Resumed: `gl_surface` will be created here. `window` will be re-created here for android. + /// * Suspended: on android, we drop window + surface. on other platforms, we don't get Suspended event. + /// + /// The setup is divided between the `new` fn and `on_resume` fn. we can just assume that `on_resume` is a continuation of + /// `new` fn on all platforms. only on android, do we get multiple resumed events because app can be suspended. struct GlutinWindowContext { - window: winit::window::Window, - gl_context: glutin::context::PossiblyCurrentContext, - gl_display: glutin::display::Display, - gl_surface: glutin::surface::Surface, + builder: winit::window::WindowBuilder, + swap_interval: glutin::surface::SwapInterval, + gl_config: glutin::config::Config, + current_gl_context: Option, + gl_surface: Option>, + not_current_gl_context: Option, + window: Option, } impl GlutinWindowContext { - // refactor this function to use `glutin-winit` crate eventually. - // preferably add android support at the same time. + /// There is a lot of complexity with opengl creation, so prefer extensivve logging to get all the help we can to debug issues. + /// #[allow(unsafe_code)] unsafe fn new( - winit_window: winit::window::Window, + winit_window_builder: winit::window::WindowBuilder, native_options: &epi::NativeOptions, + event_loop: &EventLoopWindowTarget, ) -> Result { use glutin::prelude::*; - use raw_window_handle::*; + // convert native options to glutin options let hardware_acceleration = match native_options.hardware_acceleration { crate::HardwareAcceleration::Required => Some(true), crate::HardwareAcceleration::Preferred => None, crate::HardwareAcceleration::Off => Some(false), }; - - 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. - // TODO: check whether we can expose these options as "features", so that users can select the relevant backend they want. - - // 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(raw_window_handle)); - // try egl and fallback to x11 glx - #[cfg(target_os = "linux")] - let preference = glutin::display::DisplayApiPreference::EglThenGlx(Box::new( - winit::platform::x11::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)?; let swap_interval = if native_options.vsync { glutin::surface::SwapInterval::Wait(std::num::NonZeroU32::new(1).unwrap()) } else { glutin::surface::SwapInterval::DontWait }; - - let config_template = glutin::config::ConfigTemplateBuilder::new() + /* opengl setup flow goes like this: + 1. we create a configuration for opengl "Display" / "Config" creation + 2. choose between special extensions like glx or egl or wgl and use them to create config/display + 3. opengl context configuration + 4. opengl context creation + */ + // start building config for gl display + let config_template_builder = glutin::config::ConfigTemplateBuilder::new() .prefer_hardware_accelerated(hardware_acceleration) - .with_depth_size(native_options.depth_buffer); + .with_depth_size(native_options.depth_buffer) + .with_stencil_size(native_options.stencil_buffer) + .with_transparency(native_options.transparent); // we don't know if multi sampling option is set. so, check if its more than 0. - let config_template = if native_options.multisampling > 0 { - config_template.with_multisampling( + let config_template_builder = if native_options.multisampling > 0 { + config_template_builder.with_multisampling( native_options .multisampling .try_into() - .expect("failed to fit multisamples into u8"), + .expect("failed to fit multisamples option of native_options into u8"), ) } else { - config_template + config_template_builder }; - let config_template = config_template - .with_stencil_size(native_options.stencil_buffer) - .with_transparency(native_options.transparent) - .compatible_with_native_window(raw_window_handle) - .build(); - // finds all valid configurations supported by this display that match the config_template - // this is where we will try to get a "fallback" config if we are okay with ignoring some native - // options required by user like multi sampling, srgb, transparency etc.. - // TODO: need to figure out a good fallback config template - let config = gl_display - .find_configs(config_template.clone())? - .next() - .ok_or(crate::Error::NoGlutinConfigs(config_template))?; + tracing::debug!( + "trying to create glutin Display with config: {:?}", + &config_template_builder + ); + // create gl display. this may probably create a window too on most platforms. definitely on `MS windows`. never on android. + let (window, gl_config) = glutin_winit::DisplayBuilder::new() + // we might want to expose this option to users in the future. maybe using an env var or using native_options. + .with_preference(glutin_winit::ApiPrefence::FallbackEgl) // https://github.com/emilk/egui/issues/2520#issuecomment-1367841150 + .with_window_builder(Some(winit_window_builder.clone())) + .build( + event_loop, + config_template_builder.clone(), + |mut config_iterator| { + let config = config_iterator.next().expect( + "failed to find a matching configuration for creating glutin config", + ); + tracing::debug!( + "using the first config from config picker closure. config: {:?}", + &config + ); + config + }, + ) + .map_err(|e| crate::Error::NoGlutinConfigs(config_template_builder.build(), e))?; + + let gl_display = gl_config.display(); + tracing::debug!( + "successfully created GL Display with version: {} and supported features: {:?}", + gl_display.version_string(), + gl_display.supported_features() + ); + let raw_window_handle = window.as_ref().map(|w| w.raw_window_handle()); + tracing::debug!( + "creating gl context using raw window handle: {:?}", + raw_window_handle + ); + + // create gl context. if core context cannot be created, try gl es context as fallback. 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(); + glutin::context::ContextAttributesBuilder::new().build(raw_window_handle); + let fallback_context_attributes = glutin::context::ContextAttributesBuilder::new() + .with_context_api(glutin::context::ContextApi::Gles(None)) + .build(raw_window_handle); + let gl_context = match gl_config + .display() + .create_context(&gl_config, &context_attributes) + { + Ok(it) => it, + Err(err) => { + tracing::warn!("failed to create context using default context attributes {context_attributes:?} due to error: {err}"); + tracing::debug!("retrying with fallback context attributes: {fallback_context_attributes:?}"); + gl_config + .display() + .create_context(&gl_config, &fallback_context_attributes)? + } + }; + let not_current_gl_context = Some(gl_context); + // the fun part with opengl gl is that we never know whether there is an error. the context creation might have failed, but + // it could keep working until we try to make surface current or swap buffers or something else. future glutin improvements might + // help us start from scratch again if we fail context creation and go back to preferEgl or try with different config etc.. + // https://github.com/emilk/egui/pull/2541#issuecomment-1370767582 + Ok(GlutinWindowContext { + builder: winit_window_builder, + swap_interval, + gl_config, + current_gl_context: None, + window, + gl_surface: None, + not_current_gl_context, + }) + } + /// This will be run after `new`. on android, it might be called multiple times over the course of the app's lifetime. + /// roughly, + /// 1. check if window already exists. otherwise, create one now. + /// 2. create attributes for surface creation. + /// 3. create surface. + /// 4. make surface and context current. + /// + /// we presently assume that we will + #[allow(unsafe_code)] + fn on_resume(&mut self, event_loop: &EventLoopWindowTarget) -> Result<()> { + if self.gl_surface.is_some() { + tracing::warn!( + "on_resume called even thought we already have a surface. early return" + ); + return Ok(()); + } + tracing::debug!("running on_resume fn."); + // make sure we have a window or create one. + let window = self.window.take().unwrap_or_else(|| { + tracing::debug!("window doesn't exist yet. creating one now with finalize_window"); + glutin_winit::finalize_window(event_loop, self.builder.clone(), &self.gl_config) + .expect("failed to finalize glutin window") + }); + // surface attributes + let (width, height): (u32, u32) = window.inner_size().into(); let width = std::num::NonZeroU32::new(width.at_least(1)).unwrap(); let height = std::num::NonZeroU32::new(height.at_least(1)).unwrap(); let surface_attributes = glutin::surface::SurfaceAttributesBuilder::::new() - .build(raw_window_handle, width, height); - // start creating the gl objects - let gl_context = gl_display.create_context(&config, &context_attributes)?; - - let gl_surface = gl_display.create_window_surface(&config, &surface_attributes)?; - let gl_context = gl_context.make_current(&gl_surface)?; - gl_surface.set_swap_interval(&gl_context, swap_interval)?; - Ok(GlutinWindowContext { - window: winit_window, - gl_context, - gl_display, - gl_surface, - }) + .build(window.raw_window_handle(), width, height); + tracing::debug!( + "creating surface with attributes: {:?}", + &surface_attributes + ); + // create surface + let gl_surface = unsafe { + self.gl_config + .display() + .create_window_surface(&self.gl_config, &surface_attributes)? + }; + tracing::debug!("surface created successfully: {gl_surface:?}.making context current"); + // make surface and context current. + let not_current_gl_context = self + .not_current_gl_context + .take() + .expect("failed to get not current context after resume event. impossible!"); + let current_gl_context = not_current_gl_context.make_current(&gl_surface)?; + // try setting swap interval. but its not absolutely necessary, so don't panic on failure. + tracing::debug!("made context current. setting swap interval for surface"); + if let Err(e) = gl_surface.set_swap_interval(¤t_gl_context, self.swap_interval) { + tracing::error!("failed to set swap interval due to error: {e:?}"); + } + // we will reach this point only once in most platforms except android. + // create window/surface/make context current once and just use them forever. + self.gl_surface = Some(gl_surface); + self.current_gl_context = Some(current_gl_context); + self.window = Some(window); + Ok(()) } + /// only applies for android. but we basically drop surface + window and make context not current + fn on_suspend(&mut self) -> Result<()> { + tracing::debug!("received suspend event. dropping window and surface"); + self.gl_surface.take(); + self.window.take(); + if let Some(current) = self.current_gl_context.take() { + tracing::debug!("context is current, so making it non-current"); + self.not_current_gl_context = Some(current.make_not_current()?); + } else { + tracing::debug!( + "context is already not current??? could be duplicate suspend event" + ); + } + Ok(()) + } fn window(&self) -> &winit::window::Window { - &self.window + self.window.as_ref().expect("winit window doesn't exist") } fn resize(&self, physical_size: winit::dpi::PhysicalSize) { - use glutin::surface::GlSurface; let width = std::num::NonZeroU32::new(physical_size.width.at_least(1)).unwrap(); let height = std::num::NonZeroU32::new(physical_size.height.at_least(1)).unwrap(); - self.gl_surface.resize(&self.gl_context, width, height); + self.gl_surface + .as_ref() + .expect("failed to get surface to resize") + .resize( + self.current_gl_context + .as_ref() + .expect("failed to get current context to resize surface"), + width, + height, + ); } fn swap_buffers(&self) -> glutin::error::Result<()> { - use glutin::surface::GlSurface; - self.gl_surface.swap_buffers(&self.gl_context) + self.gl_surface + .as_ref() + .expect("failed to get surface to swap buffers") + .swap_buffers( + self.current_gl_context + .as_ref() + .expect("failed to get current context to swap buffers"), + ) } 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) + self.gl_config.display().get_proc_address(addr) } } @@ -494,11 +625,12 @@ mod glow_integration { let window_settings = epi_integration::load_window_settings(storage); - let winit_window = - epi_integration::build_window(event_loop, title, native_options, window_settings)?; - // a lot of the code below has been lifted from glutin example in their repo. - let glutin_window_context = - unsafe { GlutinWindowContext::new(winit_window, native_options)? }; + let winit_window_builder = + epi_integration::window_builder(event_loop, title, native_options, window_settings); + let mut glutin_window_context = unsafe { + GlutinWindowContext::new(winit_window_builder, native_options, event_loop)? + }; + glutin_window_context.on_resume(event_loop)?; let gl = unsafe { glow::Context::from_loader_function(|s| { let s = std::ffi::CString::new(s) @@ -727,26 +859,24 @@ mod glow_integration { ) -> Result { Ok(match event { winit::event::Event::Resumed => { + // first resume event. + // we can actually move this outside of event loop. + // and just run the on_resume fn of gl_window if self.running.is_none() { self.init_run_state(event_loop)?; + } else { + // not the first resume event. create whatever you need. + self.running + .as_mut() + .unwrap() + .gl_window + .on_resume(event_loop)?; } EventResult::RepaintNow } winit::event::Event::Suspended => { - #[cfg(target_os = "android")] - { - tracing::error!("Suspended app can't destroy Window surface state with current Egui Glow backend (undefined behaviour)"); - // Instead of destroying everything which we _know_ we can't re-create - // we instead currently just try our luck with not destroying anything. - // - // When the application resumes then it will get a new `SurfaceView` but - // we have no practical way currently of creating a new EGL surface - // via the Glutin API while keeping the GL context and the rest of - // our app state. This will likely result in a black screen or - // frozen screen. - // - //self.running = None; - } + self.running.as_mut().unwrap().gl_window.on_suspend()?; + EventResult::Wait } diff --git a/crates/egui_glow/Cargo.toml b/crates/egui_glow/Cargo.toml index f8ae3656c..e7f065b29 100644 --- a/crates/egui_glow/Cargo.toml +++ b/crates/egui_glow/Cargo.toml @@ -69,8 +69,9 @@ wasm-bindgen = { version = "0.2" } [dev-dependencies] -glutin = "0.30.2" # examples/pure_glow +glutin = "0.30" # examples/pure_glow raw-window-handle = "0.5.0" +glutin-winit = "0.3.0" [[example]] diff --git a/crates/egui_glow/examples/pure_glow.rs b/crates/egui_glow/examples/pure_glow.rs index dad626efa..574e237d2 100644 --- a/crates/egui_glow/examples/pure_glow.rs +++ b/crates/egui_glow/examples/pure_glow.rs @@ -17,66 +17,91 @@ 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(raw_window_handle)); - // try egl and fallback to x11 glx - #[cfg(target_os = "linux")] - let preference = glutin::display::DisplayApiPreference::EglThenGlx(Box::new( - winit::platform::x11::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() + unsafe fn new(event_loop: &winit::event_loop::EventLoopWindowTarget<()>) -> Self { + use egui::NumExt; + use glutin::context::NotCurrentGlContextSurfaceAccessor; + use glutin::display::GetGlDisplay; + use glutin::display::GlDisplay; + use glutin::prelude::GlSurface; + use raw_window_handle::HasRawWindowHandle; + let winit_window_builder = winit::window::WindowBuilder::new() + .with_resizable(true) + .with_inner_size(winit::dpi::LogicalSize { + width: 800.0, + height: 600.0, + }) + .with_title("egui_glow example") // Keep hidden until we've painted something. See https://github.com/emilk/egui/pull/2279 + .with_visible(false); + + let config_template_builder = 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(); + .with_transparency(false); + tracing::debug!("trying to get gl_config"); + let (mut window, gl_config) = + glutin_winit::DisplayBuilder::new() // let glutin-winit helper crate handle the complex parts of opengl context creation + .with_preference(glutin_winit::ApiPrefence::FallbackEgl) // https://github.com/emilk/egui/issues/2520#issuecomment-1367841150 + .with_window_builder(Some(winit_window_builder.clone())) + .build( + event_loop, + config_template_builder, + |mut config_iterator| { + config_iterator.next().expect( + "failed to find a matching configuration for creating glutin config", + ) + }, + ) + .expect("failed to create gl_config"); + let gl_display = gl_config.display(); + tracing::debug!("found gl_config: {:?}", &gl_config); + + let raw_window_handle = window.as_ref().map(|w| w.raw_window_handle()); + tracing::debug!("raw window handle: {:?}", raw_window_handle); 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(); + glutin::context::ContextAttributesBuilder::new().build(raw_window_handle); + // by default, glutin will try to create a core opengl context. but, if it is not available, try to create a gl-es context using this fallback attributes + let fallback_context_attributes = glutin::context::ContextAttributesBuilder::new() + .with_context_api(glutin::context::ContextApi::Gles(None)) + .build(raw_window_handle); + let not_current_gl_context = unsafe { + gl_display + .create_context(&gl_config, &context_attributes) + .unwrap_or_else(|_| { + tracing::debug!("failed to create gl_context with attributes: {:?}. retrying with fallback context attributes: {:?}", + &context_attributes, + &fallback_context_attributes); + gl_config + .display() + .create_context(&gl_config, &fallback_context_attributes) + .expect("failed to create context even with fallback attributes") + }) + }; + + // this is where the window is created, if it has not been created while searching for suitable gl_config + let window = window.take().unwrap_or_else(|| { + tracing::debug!("window doesn't exist yet. creating one now with finalize_window"); + glutin_winit::finalize_window(event_loop, winit_window_builder.clone(), &gl_config) + .expect("failed to finalize glutin window") + }); + let (width, height): (u32, u32) = window.inner_size().into(); + let width = std::num::NonZeroU32::new(width.at_least(1)).unwrap(); + let height = std::num::NonZeroU32::new(height.at_least(1)).unwrap(); let surface_attributes = glutin::surface::SurfaceAttributesBuilder::::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(); + .build(window.raw_window_handle(), width, height); + tracing::debug!( + "creating surface with attributes: {:?}", + &surface_attributes + ); + let gl_surface = unsafe { + gl_display + .create_window_surface(&gl_config, &surface_attributes) + .unwrap() + }; + tracing::debug!("surface created successfully: {gl_surface:?}.making context current"); + let gl_context = not_current_gl_context.make_current(&gl_surface).unwrap(); gl_surface .set_swap_interval( @@ -86,7 +111,7 @@ impl GlutinWindowContext { .unwrap(); GlutinWindowContext { - window: winit_window, + window, gl_context, gl_display, gl_surface, @@ -216,19 +241,7 @@ fn main() { fn create_display( event_loop: &winit::event_loop::EventLoopWindowTarget<()>, ) -> (GlutinWindowContext, glow::Context) { - let winit_window = winit::window::WindowBuilder::new() - .with_resizable(true) - .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 - .build(event_loop) - .unwrap(); - - // 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 glutin_window_context = unsafe { GlutinWindowContext::new(event_loop) }; let gl = unsafe { glow::Context::from_loader_function(|s| { let s = std::ffi::CString::new(s) diff --git a/crates/egui_glow/src/painter.rs b/crates/egui_glow/src/painter.rs index 574ff1e77..0eb87a73f 100644 --- a/crates/egui_glow/src/painter.rs +++ b/crates/egui_glow/src/painter.rs @@ -106,6 +106,23 @@ impl Painter { crate::profile_function!(); crate::check_for_gl_error_even_in_release!(&gl, "before Painter::new"); + // some useful debug info. all three of them are present in gl 1.1. + unsafe { + let version = gl.get_parameter_string(glow::VERSION); + let renderer = gl.get_parameter_string(glow::RENDERER); + let vendor = gl.get_parameter_string(glow::VENDOR); + tracing::debug!( + "\nopengl version: {version}\nopengl renderer: {renderer}\nopengl vendor: {vendor}" + ); + } + + #[cfg(not(target_arch = "wasm32"))] + if gl.version().major < 2 { + // this checks on desktop that we are not using opengl 1.1 microsoft sw rendering context. + // ShaderVersion::get fn will segfault due to SHADING_LANGUAGE_VERSION (added in gl2.0) + return Err("egui_glow requires opengl 2.0+. ".to_owned()); + } + let max_texture_side = unsafe { gl.get_parameter_i32(glow::MAX_TEXTURE_SIZE) } as usize; let shader_version = shader_version.unwrap_or_else(|| ShaderVersion::get(&gl)); let is_webgl_1 = shader_version == ShaderVersion::Es100;