1
0
mirror of https://github.com/emilk/egui.git synced 2026-08-29 12:50:04 -04:00

Glutin Upgrade (#2187)

* working. but x11 blurry

* fixed x11 blurry. was just accidentally using multisampling even when user didnt request it

* allow dbg macro temporarily

* add windows WGL fallback support when EGL fails

* fmt

* glutin features explicitly added

* extract glutin context creation into a fn

* fix warnings
This commit is contained in:
Red Artist
2022-12-05 16:04:28 +05:30
committed by GitHub
parent 3ba39c3022
commit 8eb687cf04
3 changed files with 239 additions and 38 deletions

View File

@@ -87,7 +87,16 @@ egui-winit = { version = "0.19.0", path = "../egui-winit", default-features = fa
"clipboard",
"links",
] }
glutin = { version = "0.29.0" }
# 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", features = [
"egl",
"glx",
"x11",
"wayland",
"wgl",
] }
raw-window-handle = { version = "0.5.0" }
winit = "0.27.2"
# optional native:
@@ -95,7 +104,7 @@ dark-light = { version = "0.2.1", optional = true }
directories-next = { version = "2", optional = true }
egui-wgpu = { version = "0.19.0", path = "../egui-wgpu", optional = true, features = [
"winit",
] }
] } # if wgpu is used, use it with winit
image = { version = "0.24", optional = true, default-features = false, features = [
"png",
] }

View File

@@ -305,7 +305,145 @@ mod glow_integration {
// Conceptually this will be split out eventually so that the rest of the state
// can be persistent.
gl_window: glutin::WindowedContext<glutin::PossiblyCurrent>,
gl_window: GlutinWindowContext,
}
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,
native_options: &epi::NativeOptions,
) -> Self {
use glutin::prelude::*;
use raw_window_handle::*;
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(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)
.expect("failed to create glutin display");
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()
.prefer_hardware_accelerated(hardware_acceleration)
.with_depth_size(native_options.depth_buffer);
// 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(
native_options
.multisampling
.try_into()
.expect("failed to fit multisamples into u8"),
)
} else {
config_template
};
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)
.expect("failed to find even a single matching configuration")
.next()
.expect("failed to find a matching configuration for creating opengl context");
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)
.expect("failed to create opengl context");
let gl_surface = gl_display
.create_window_surface(&config, &surface_attributes)
.expect("failed to create glutin window surface");
let gl_context = gl_context
.make_current(&gl_surface)
.expect("failed to make gl context current");
gl_surface
.set_swap_interval(&gl_context, swap_interval)
.expect("failed to set vsync swap interval");
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()
.expect("physical size must not be zero"),
physical_size
.height
.try_into()
.expect("physical size must not be zero"),
);
}
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)
}
}
struct GlowWinitApp {
@@ -347,44 +485,33 @@ mod glow_integration {
storage: Option<&dyn epi::Storage>,
title: &String,
native_options: &NativeOptions,
) -> (
glutin::WindowedContext<glutin::PossiblyCurrent>,
glow::Context,
) {
) -> (GlutinWindowContext, glow::Context) {
crate::profile_function!();
use crate::HardwareAcceleration;
let hardware_acceleration = match native_options.hardware_acceleration {
HardwareAcceleration::Required => Some(true),
HardwareAcceleration::Preferred => None,
HardwareAcceleration::Off => Some(false),
};
let window_settings = epi_integration::load_window_settings(storage);
let window_builder = epi_integration::window_builder(native_options, &window_settings)
.with_title(title)
.with_transparent(native_options.transparent)
// Keep hidden until we've painted something. See https://github.com/emilk/egui/pull/2279
// We must also keep the window hidden until AccessKit is initialized.
.with_visible(false);
let winit_window = window_builder
.build(event_loop)
.expect("failed to create winit window");
// 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 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");
let gl_window = unsafe {
glutin::ContextBuilder::new()
.with_hardware_acceleration(hardware_acceleration)
.with_depth_buffer(native_options.depth_buffer)
.with_multisampling(native_options.multisampling)
.with_stencil_buffer(native_options.stencil_buffer)
.with_vsync(native_options.vsync)
.build_windowed(window_builder, event_loop)
.unwrap()
.make_current()
.unwrap()
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)
}
fn init_run_state(&mut self, event_loop: &EventLoopWindowTarget<UserEvent>) {