Skip to content
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

janitor: Upgrade to latest glutin crate #1942

Merged
merged 2 commits into from
Dec 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion internal/backends/winit/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,9 @@ send_wrapper = "0.6.0"

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
fontdb = { version = "0.10", optional = true, features = ["memmap", "fontconfig"] }
glutin = { version = "0.29", default-features = false }
glutin = { version = "0.30", default-features = false }
glutin-winit = { version = "0.2.1" }
raw-window-handle = { version = "0.5" }

# For the FemtoVG renderer
[target.'cfg(target_family = "windows")'.dependencies]
Expand Down
227 changes: 123 additions & 104 deletions internal/backends/winit/glcontext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,40 @@
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial

use std::cell::RefCell;
#[cfg(target_arch = "wasm32")]
use std::rc::Rc;

#[cfg(not(target_arch = "wasm32"))]
use glutin::{
context::{ContextApi, ContextAttributesBuilder},
display::GetGlDisplay,
prelude::*,
surface::{SurfaceAttributesBuilder, WindowSurface},
};

// glutin::WindowedContext tries to enforce being current or not. Since we need the WindowedContext's window() function
// in the GL renderer regardless whether we're current or not, we wrap the two states back into one type.
enum OpenGLContextState {
#[cfg(not(target_arch = "wasm32"))]
NotCurrent(glutin::WindowedContext<glutin::NotCurrent>),
NotCurrent(
(
glutin::context::NotCurrentContext,
glutin::surface::Surface<glutin::surface::WindowSurface>,
),
),
#[cfg(not(target_arch = "wasm32"))]
Current(glutin::WindowedContext<glutin::PossiblyCurrent>),
Current(
(
glutin::context::PossiblyCurrentContext,
glutin::surface::Surface<glutin::surface::WindowSurface>,
),
),
#[cfg(target_arch = "wasm32")]
Current { window: Rc<winit::window::Window>, canvas: web_sys::HtmlCanvasElement },
Current { canvas: web_sys::HtmlCanvasElement },
}

pub struct OpenGLContext(RefCell<Option<OpenGLContextState>>);

impl OpenGLContext {
pub fn window(&self) -> std::cell::Ref<winit::window::Window> {
std::cell::Ref::map(self.0.borrow(), |state| match state.as_ref().unwrap() {
#[cfg(not(target_arch = "wasm32"))]
OpenGLContextState::NotCurrent(context) => context.window(),
#[cfg(not(target_arch = "wasm32"))]
OpenGLContextState::Current(context) => context.window(),
#[cfg(target_arch = "wasm32")]
OpenGLContextState::Current { window, .. } => window.as_ref(),
})
}

#[cfg(target_arch = "wasm32")]
pub fn html_canvas_element(&self) -> std::cell::Ref<web_sys::HtmlCanvasElement> {
std::cell::Ref::map(self.0.borrow(), |state| match state.as_ref().unwrap() {
Expand All @@ -38,11 +44,9 @@ impl OpenGLContext {
}

#[cfg(skia_backend_opengl)]
pub fn glutin_context(
&self,
) -> std::cell::Ref<glutin::WindowedContext<glutin::PossiblyCurrent>> {
pub fn glutin_context(&self) -> std::cell::Ref<glutin::context::PossiblyCurrentContext> {
std::cell::Ref::map(self.0.borrow(), |state| match state.as_ref().unwrap() {
OpenGLContextState::Current(gl_context) => gl_context,
OpenGLContextState::Current((gl_context, ..)) => gl_context,
OpenGLContextState::NotCurrent(..) => {
panic!("internal error: glutin_context() called without current context")
}
Expand All @@ -53,9 +57,9 @@ impl OpenGLContext {
let mut ctx = self.0.borrow_mut();
*ctx = Some(match ctx.take().unwrap() {
#[cfg(not(target_arch = "wasm32"))]
OpenGLContextState::NotCurrent(not_current_ctx) => {
let current_ctx = unsafe { not_current_ctx.make_current().unwrap() };
OpenGLContextState::Current(current_ctx)
OpenGLContextState::NotCurrent((not_current_ctx, surface)) => {
let current_ctx = not_current_ctx.make_current(&surface).unwrap();
OpenGLContextState::Current((current_ctx, surface))
}
state @ OpenGLContextState::Current { .. } => state,
});
Expand All @@ -67,9 +71,9 @@ impl OpenGLContext {
let mut ctx = self.0.borrow_mut();
*ctx = Some(match ctx.take().unwrap() {
state @ OpenGLContextState::NotCurrent(_) => state,
OpenGLContextState::Current(current_ctx_rc) => {
OpenGLContextState::NotCurrent(unsafe {
current_ctx_rc.make_not_current().unwrap()
OpenGLContextState::Current((current_ctx_rc, surface)) => {
OpenGLContextState::NotCurrent({
(current_ctx_rc.make_not_current().unwrap(), surface)
})
}
});
Expand All @@ -92,28 +96,39 @@ impl OpenGLContext {
#[cfg(not(target_arch = "wasm32"))]
match &self.0.borrow().as_ref().unwrap() {
OpenGLContextState::NotCurrent(_) => {}
OpenGLContextState::Current(current_ctx) => {
current_ctx.swap_buffers().unwrap();
OpenGLContextState::Current((current_ctx, surface)) => {
surface.swap_buffers(current_ctx).unwrap();
}
}
}

pub fn ensure_resized(&self) {
pub fn ensure_resized(&self, _window: &winit::window::Window) {
#[cfg(not(target_arch = "wasm32"))]
{
let mut ctx = self.0.borrow_mut();
*ctx = Some(match ctx.take().unwrap() {
#[cfg(not(target_arch = "wasm32"))]
OpenGLContextState::NotCurrent(not_current_ctx) => {
let current_ctx = unsafe { not_current_ctx.make_current().unwrap() };
current_ctx.resize(current_ctx.window().inner_size());
OpenGLContextState::NotCurrent(unsafe {
current_ctx.make_not_current().unwrap()
})
OpenGLContextState::NotCurrent((not_current_ctx, surface)) => {
let current_ctx = not_current_ctx.make_current(&surface).unwrap();
let size = _window.inner_size();
surface.resize(
&current_ctx,
size.width.try_into().unwrap(),
size.height.try_into().unwrap(),
);
OpenGLContextState::NotCurrent((
current_ctx.make_not_current().unwrap(),
surface,
))
}
OpenGLContextState::Current(current) => {
current.resize(current.window().inner_size());
OpenGLContextState::Current(current)
OpenGLContextState::Current((current, surface)) => {
let size = _window.inner_size();
surface.resize(
&current,
size.width.try_into().unwrap(),
size.height.try_into().unwrap(),
);
OpenGLContextState::Current((current, surface))
}
});
}
Expand All @@ -122,86 +137,87 @@ impl OpenGLContext {
pub fn new_context(
window_builder: winit::window::WindowBuilder,
#[cfg(target_arch = "wasm32")] canvas_id: &str,
) -> Self {
) -> (Self, Rc<winit::window::Window>) {
#[cfg(not(target_arch = "wasm32"))]
{
use crate::event_loop::EventLoopInterface;
use glutin::ContextBuilder;
let windowed_context = crate::event_loop::with_window_target(|event_loop| {
// Try different strategies for creating an GL context. First request our "favorite", OpenGL ES 2.0,
// then try GlLatest (but with windows quirk) and finally try glutin's defaults.
// We might be able to just go back to requesting GlLatest if
// https://github.com/rust-windowing/glutin/issues/1371 is resolved
// in favor of falling back to creating a GLES context.
let context_factory_fns = [
|window_builder, event_loop: &dyn EventLoopInterface| {
let builder = ContextBuilder::new()
.with_vsync(true)
.with_gl(glutin::GlRequest::Specific(glutin::Api::OpenGlEs, (2, 0)));
#[cfg(target_os = "windows")]
let builder = builder.with_srgb(false);
builder
.build_windowed(window_builder, event_loop.event_loop_target())
.map_err(|creation_error| {
format!(
"could not create OpenGL ES 2.0 context: {}",
creation_error
)
})
},
|window_builder, event_loop: &dyn EventLoopInterface| {
let builder = ContextBuilder::new().with_vsync(true);
// With latest Windows 10 and VmWare glutin's default for srgb produces surfaces that are always rendered black :(
#[cfg(target_os = "windows")]
let builder = builder.with_srgb(false);
builder
.build_windowed(window_builder, event_loop.event_loop_target())
.map_err(|creation_error| {
format!(
"could not create GlLatest context (with windows quirk): {}",
creation_error
)
})
},
|window_builder, event_loop: &dyn EventLoopInterface| {
// Try again with glutin defaults
ContextBuilder::new()
.with_vsync(true)
.build_windowed(window_builder, event_loop.event_loop_target())
.map_err(|creation_error| {
format!("could not create GlLatest context : {}", creation_error)
})
},
];

let mut last_err = None;
for factory_fn in context_factory_fns {
match factory_fn(window_builder.clone(), event_loop) {
Ok(new_context) => {
return new_context;
}
Err(e) => {
last_err = Some(e);
}
}
}
let (maybe_window, config) = crate::event_loop::with_window_target(|event_loop| {
glutin_winit::DisplayBuilder::new()
.with_preference(glutin_winit::ApiPrefence::PreferEgl)
.with_window_builder(Some(window_builder))
.build(
event_loop.event_loop_target(),
glutin::config::ConfigTemplateBuilder::new(),
|configs| {
configs
.reduce(|accum, config| {
let transparency_check =
config.supports_transparency().unwrap_or(false)
& !accum.supports_transparency().unwrap_or(false);

panic!("Failed to create OpenGL context: {}", last_err.unwrap())
if transparency_check
|| config.num_samples() > accum.num_samples()
{
config
} else {
accum
}
})
.unwrap()
},
)
.unwrap()
});
let windowed_context = unsafe { windowed_context.make_current().unwrap() };

let window = maybe_window.unwrap();

let gl_display = config.display();

use raw_window_handle::HasRawWindowHandle;
let gles_context_attributes = ContextAttributesBuilder::new()
.with_context_api(ContextApi::Gles(Some(glutin::context::Version {
major: 2,
minor: 0,
})))
.build(Some(window.raw_window_handle()));

let fallback_context_attributes =
ContextAttributesBuilder::new().build(Some(window.raw_window_handle()));

let not_current_gl_context = unsafe {
gl_display
.create_context(&config, &gles_context_attributes)
.or_else(|_| gl_display.create_context(&config, &fallback_context_attributes))
.expect("failed to create context")
};

#[cfg(target_os = "macos")]
{
use cocoa::appkit::NSView;
use winit::platform::macos::WindowExtMacOS;
let ns_view = windowed_context.window().ns_view();
let ns_view = window.ns_view();
let view_id: cocoa::base::id = ns_view as *const _ as *mut _;
unsafe {
NSView::setLayerContentsPlacement(view_id, cocoa::appkit::NSViewLayerContentsPlacement::NSViewLayerContentsPlacementTopLeft)
}
}

Self(RefCell::new(Some(OpenGLContextState::Current(windowed_context))))
let (width, height): (u32, u32) = window.inner_size().into();
let attrs = SurfaceAttributesBuilder::<WindowSurface>::new().build(
window.raw_window_handle(),
width.try_into().unwrap(),
height.try_into().unwrap(),
);

let surface =
unsafe { config.display().create_window_surface(&config, &attrs).unwrap() };

(
Self(RefCell::new(Some(OpenGLContextState::Current((
not_current_gl_context.make_current(&surface).unwrap(),
surface,
))))),
Rc::new(window),
)
}

#[cfg(target_arch = "wasm32")]
Expand Down Expand Up @@ -281,15 +297,18 @@ impl OpenGLContext {
}
}

Self(RefCell::new(Some(OpenGLContextState::Current { window, canvas })))
(Self(RefCell::new(Some(OpenGLContextState::Current { canvas }))), window)
}
}

// TODO: fix this interface to also take a ffi::CStr so that we can avoid the allocation. Problem: It's in our public api.
#[cfg(not(target_arch = "wasm32"))]
pub fn get_proc_address(&self, name: &str) -> *const std::ffi::c_void {
match &self.0.borrow().as_ref().unwrap() {
OpenGLContextState::NotCurrent(_) => std::ptr::null(),
OpenGLContextState::Current(current_ctx) => current_ctx.get_proc_address(name),
OpenGLContextState::Current((current_ctx, _)) => {
current_ctx.display().get_proc_address(&std::ffi::CString::new(name).unwrap())
}
}
}
}
Loading