2019-12-15 08:43:05 +03:00
|
|
|
use anyhow::{anyhow, bail, ensure, Error};
|
2019-10-25 01:54:41 +03:00
|
|
|
use std::ffi::c_void;
|
2020-08-16 20:40:36 +03:00
|
|
|
use std::rc::Rc;
|
2019-10-25 01:54:41 +03:00
|
|
|
|
2019-10-26 23:12:01 +03:00
|
|
|
#[allow(non_camel_case_types, clippy::unreadable_literal)]
|
2019-10-25 01:54:41 +03:00
|
|
|
pub mod ffi {
|
|
|
|
// gl_generator emits these weird cyclical and redundant type references;
|
|
|
|
// the types appear to have to be in a module and need to reference super,
|
|
|
|
// with some of the types specified in both scopes :-/
|
|
|
|
pub mod generated {
|
|
|
|
pub type khronos_utime_nanoseconds_t = super::khronos_utime_nanoseconds_t;
|
|
|
|
pub type khronos_uint64_t = super::khronos_uint64_t;
|
|
|
|
pub type khronos_ssize_t = super::khronos_ssize_t;
|
|
|
|
pub type EGLNativeDisplayType = super::EGLNativeDisplayType;
|
|
|
|
pub type EGLNativePixmapType = super::EGLNativePixmapType;
|
|
|
|
pub type EGLNativeWindowType = super::EGLNativeWindowType;
|
|
|
|
pub type EGLint = super::EGLint;
|
|
|
|
pub type NativeDisplayType = super::EGLNativeDisplayType;
|
|
|
|
pub type NativePixmapType = super::EGLNativePixmapType;
|
|
|
|
pub type NativeWindowType = super::EGLNativeWindowType;
|
|
|
|
|
|
|
|
include!(concat!(env!("OUT_DIR"), "/egl_bindings.rs"));
|
|
|
|
}
|
|
|
|
|
|
|
|
pub use generated::*;
|
|
|
|
|
|
|
|
use std::os::raw;
|
|
|
|
|
|
|
|
pub type EGLint = i32;
|
|
|
|
pub type khronos_ssize_t = raw::c_long;
|
|
|
|
pub type khronos_utime_nanoseconds_t = khronos_uint64_t;
|
|
|
|
pub type khronos_uint64_t = u64;
|
|
|
|
pub type EGLNativeDisplayType = *const raw::c_void;
|
|
|
|
pub type EGLNativePixmapType = *const raw::c_void;
|
|
|
|
|
|
|
|
#[cfg(target_os = "windows")]
|
|
|
|
pub type EGLNativeWindowType = winapi::shared::windef::HWND;
|
|
|
|
#[cfg(not(target_os = "windows"))]
|
|
|
|
pub type EGLNativeWindowType = *const raw::c_void;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct EglWrapper {
|
|
|
|
_lib: libloading::Library,
|
|
|
|
egl: ffi::Egl,
|
|
|
|
}
|
|
|
|
|
2020-08-16 20:40:36 +03:00
|
|
|
pub struct GlConnection {
|
2019-10-25 01:54:41 +03:00
|
|
|
egl: EglWrapper,
|
2019-10-25 03:42:18 +03:00
|
|
|
display: ffi::types::EGLDisplay,
|
2020-08-16 20:40:36 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
impl std::ops::Deref for GlConnection {
|
|
|
|
type Target = ffi::Egl;
|
|
|
|
|
|
|
|
fn deref(&self) -> &ffi::Egl {
|
|
|
|
&self.egl.egl
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Drop for GlConnection {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
unsafe {
|
|
|
|
self.egl.egl.Terminate(self.display);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct GlState {
|
|
|
|
connection: Rc<GlConnection>,
|
2019-10-25 03:42:18 +03:00
|
|
|
surface: ffi::types::EGLSurface,
|
|
|
|
context: ffi::types::EGLContext,
|
2019-10-25 01:54:41 +03:00
|
|
|
}
|
|
|
|
|
2020-07-13 19:36:37 +03:00
|
|
|
impl Drop for GlState {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
unsafe {
|
2020-08-16 20:40:36 +03:00
|
|
|
self.connection.MakeCurrent(
|
|
|
|
self.connection.display,
|
2020-07-13 19:36:37 +03:00
|
|
|
ffi::NO_SURFACE,
|
|
|
|
ffi::NO_SURFACE,
|
|
|
|
ffi::NO_CONTEXT,
|
|
|
|
);
|
2020-08-16 20:40:36 +03:00
|
|
|
self.connection
|
|
|
|
.DestroySurface(self.connection.display, self.surface);
|
|
|
|
self.connection
|
|
|
|
.DestroyContext(self.connection.display, self.context);
|
2020-07-13 19:36:37 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-25 01:54:41 +03:00
|
|
|
type GetProcAddressFunc =
|
|
|
|
unsafe extern "C" fn(*const std::os::raw::c_char) -> *const std::os::raw::c_void;
|
|
|
|
|
|
|
|
impl EglWrapper {
|
2019-12-15 08:43:05 +03:00
|
|
|
pub fn load_egl(lib: libloading::Library) -> anyhow::Result<Self> {
|
2019-10-25 01:54:41 +03:00
|
|
|
let get_proc_address: libloading::Symbol<GetProcAddressFunc> =
|
|
|
|
unsafe { lib.get(b"eglGetProcAddress\0")? };
|
|
|
|
let egl = ffi::Egl::load_with(|s: &'static str| {
|
|
|
|
let sym_name = std::ffi::CString::new(s).expect("symbol to be cstring compatible");
|
|
|
|
if let Ok(sym) = unsafe { lib.get(sym_name.as_bytes_with_nul()) } {
|
|
|
|
return *sym;
|
|
|
|
}
|
|
|
|
unsafe { get_proc_address(sym_name.as_ptr()) }
|
|
|
|
});
|
2020-12-21 09:01:06 +03:00
|
|
|
log::trace!("load_egl: {:?}", lib);
|
2019-10-25 01:54:41 +03:00
|
|
|
Ok(Self { _lib: lib, egl })
|
|
|
|
}
|
|
|
|
|
2020-07-13 19:36:37 +03:00
|
|
|
fn get_display(
|
2019-10-25 01:54:41 +03:00
|
|
|
&self,
|
|
|
|
display: Option<ffi::EGLNativeDisplayType>,
|
2019-12-15 08:43:05 +03:00
|
|
|
) -> anyhow::Result<ffi::types::EGLDisplay> {
|
2019-10-25 01:54:41 +03:00
|
|
|
let display = unsafe { self.egl.GetDisplay(display.unwrap_or(ffi::DEFAULT_DISPLAY)) };
|
|
|
|
if display.is_null() {
|
|
|
|
Err(self.error("egl GetDisplay"))
|
|
|
|
} else {
|
|
|
|
Ok(display)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-15 08:43:05 +03:00
|
|
|
pub fn error(&self, context: &str) -> Error {
|
2019-10-25 01:54:41 +03:00
|
|
|
let label = match unsafe { self.egl.GetError() } as u32 {
|
|
|
|
ffi::NOT_INITIALIZED => "NOT_INITIALIZED".into(),
|
|
|
|
ffi::BAD_ACCESS => "BAD_ACCESS".into(),
|
|
|
|
ffi::BAD_ALLOC => "BAD_ALLOC".into(),
|
|
|
|
ffi::BAD_ATTRIBUTE => "BAD_ATTRIBUTE".into(),
|
|
|
|
ffi::BAD_CONTEXT => "BAD_CONTEXT".into(),
|
|
|
|
ffi::BAD_CURRENT_SURFACE => "BAD_CURRENT_SURFACE".into(),
|
|
|
|
ffi::BAD_DISPLAY => "BAD_DISPLAY".into(),
|
|
|
|
ffi::BAD_SURFACE => "BAD_SURFACE".into(),
|
|
|
|
ffi::BAD_MATCH => "BAD_MATCH".into(),
|
|
|
|
ffi::BAD_PARAMETER => "BAD_PARAMETER".into(),
|
|
|
|
ffi::BAD_NATIVE_PIXMAP => "BAD_NATIVE_PIXMAP".into(),
|
|
|
|
ffi::BAD_NATIVE_WINDOW => "BAD_NATIVE_WINDOW".into(),
|
|
|
|
ffi::CONTEXT_LOST => "CONTEXT_LOST".into(),
|
|
|
|
ffi::SUCCESS => "Failed but with error code: SUCCESS".into(),
|
|
|
|
err => format!("EGL Error code: {}", err),
|
|
|
|
};
|
2019-12-15 08:43:05 +03:00
|
|
|
anyhow!("{}: {}", context, label)
|
2019-10-25 01:54:41 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn initialize_and_get_version(
|
|
|
|
&self,
|
|
|
|
display: ffi::types::EGLDisplay,
|
2019-12-15 08:43:05 +03:00
|
|
|
) -> anyhow::Result<(ffi::EGLint, ffi::EGLint)> {
|
2019-10-25 01:54:41 +03:00
|
|
|
let mut major = 0;
|
|
|
|
let mut minor = 0;
|
|
|
|
unsafe {
|
|
|
|
if self.egl.Initialize(display, &mut major, &mut minor) != 0 {
|
|
|
|
Ok((major, minor))
|
|
|
|
} else {
|
|
|
|
Err(self.error("egl Initialize"))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-10-25 03:42:18 +03:00
|
|
|
|
2020-07-07 18:57:17 +03:00
|
|
|
fn config_attrib(
|
|
|
|
&self,
|
|
|
|
display: ffi::types::EGLDisplay,
|
|
|
|
config: ffi::types::EGLConfig,
|
|
|
|
attribute: u32,
|
|
|
|
) -> Option<ffi::EGLint> {
|
|
|
|
let mut value = 0;
|
|
|
|
let res = unsafe {
|
|
|
|
self.egl
|
|
|
|
.GetConfigAttrib(display, config, attribute as ffi::EGLint, &mut value)
|
|
|
|
};
|
|
|
|
if res == 1 {
|
|
|
|
Some(value)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn log_config_info(&self, display: ffi::types::EGLDisplay, config: ffi::types::EGLConfig) {
|
|
|
|
#[derive(Debug)]
|
|
|
|
struct ConfigInfo {
|
|
|
|
config: ffi::types::EGLConfig,
|
|
|
|
alpha_size: Option<ffi::EGLint>,
|
|
|
|
red_size: Option<ffi::EGLint>,
|
|
|
|
green_size: Option<ffi::EGLint>,
|
|
|
|
blue_size: Option<ffi::EGLint>,
|
|
|
|
depth_size: Option<ffi::EGLint>,
|
|
|
|
conformant: Option<String>,
|
|
|
|
renderable_type: Option<String>,
|
|
|
|
native_visual_id: Option<ffi::EGLint>,
|
|
|
|
surface_type: Option<String>,
|
|
|
|
}
|
|
|
|
|
|
|
|
fn conformant_bits(bits: ffi::EGLint) -> String {
|
|
|
|
let bits = bits as ffi::types::EGLenum;
|
|
|
|
let mut s = String::new();
|
|
|
|
if bits & ffi::OPENGL_BIT != 0 {
|
|
|
|
s.push_str("OPENGL ");
|
|
|
|
}
|
|
|
|
if bits & ffi::OPENGL_ES2_BIT != 0 {
|
|
|
|
s.push_str("OPENGL_ES2 ");
|
|
|
|
}
|
|
|
|
if bits & ffi::OPENGL_ES3_BIT != 0 {
|
|
|
|
s.push_str("OPENGL_ES3 ");
|
|
|
|
}
|
|
|
|
if bits & ffi::OPENVG_BIT != 0 {
|
|
|
|
s.push_str("OPENVG_BIT ");
|
|
|
|
}
|
|
|
|
s
|
|
|
|
}
|
|
|
|
|
|
|
|
fn surface_bits(bits: ffi::EGLint) -> String {
|
|
|
|
let bits = bits as ffi::types::EGLenum;
|
|
|
|
let mut s = String::new();
|
|
|
|
if bits & ffi::PBUFFER_BIT != 0 {
|
|
|
|
s.push_str("PBUFFER ");
|
|
|
|
}
|
|
|
|
if bits & ffi::PIXMAP_BIT != 0 {
|
|
|
|
s.push_str("PIXMAP ");
|
|
|
|
}
|
|
|
|
if bits & ffi::WINDOW_BIT != 0 {
|
|
|
|
s.push_str("WINDOW ");
|
|
|
|
}
|
|
|
|
s
|
|
|
|
}
|
|
|
|
|
|
|
|
let info = ConfigInfo {
|
|
|
|
config,
|
|
|
|
alpha_size: self.config_attrib(display, config, ffi::ALPHA_SIZE),
|
|
|
|
red_size: self.config_attrib(display, config, ffi::RED_SIZE),
|
|
|
|
green_size: self.config_attrib(display, config, ffi::GREEN_SIZE),
|
|
|
|
blue_size: self.config_attrib(display, config, ffi::BLUE_SIZE),
|
|
|
|
depth_size: self.config_attrib(display, config, ffi::DEPTH_SIZE),
|
|
|
|
conformant: self
|
|
|
|
.config_attrib(display, config, ffi::CONFORMANT)
|
|
|
|
.map(conformant_bits),
|
|
|
|
renderable_type: self
|
|
|
|
.config_attrib(display, config, ffi::RENDERABLE_TYPE)
|
|
|
|
.map(conformant_bits),
|
|
|
|
native_visual_id: self.config_attrib(display, config, ffi::NATIVE_VISUAL_ID),
|
|
|
|
surface_type: self
|
|
|
|
.config_attrib(display, config, ffi::SURFACE_TYPE)
|
|
|
|
.map(surface_bits),
|
|
|
|
};
|
|
|
|
|
2020-12-21 09:01:06 +03:00
|
|
|
log::trace!("{:x?}", info);
|
2020-07-07 18:57:17 +03:00
|
|
|
}
|
|
|
|
|
2019-10-25 03:42:18 +03:00
|
|
|
pub fn choose_config(
|
|
|
|
&self,
|
|
|
|
display: ffi::types::EGLDisplay,
|
|
|
|
attributes: &[u32],
|
2019-12-15 08:43:05 +03:00
|
|
|
) -> anyhow::Result<Vec<ffi::types::EGLConfig>> {
|
|
|
|
ensure!(
|
2019-10-26 23:12:01 +03:00
|
|
|
!attributes.is_empty() && attributes[attributes.len() - 1] == ffi::NONE,
|
2019-10-25 03:42:18 +03:00
|
|
|
"attributes list must be terminated with ffi::NONE"
|
|
|
|
);
|
|
|
|
|
|
|
|
let mut num_configs = 0;
|
|
|
|
if unsafe {
|
|
|
|
self.egl
|
|
|
|
.GetConfigs(display, std::ptr::null_mut(), 0, &mut num_configs)
|
|
|
|
} != 1
|
|
|
|
{
|
|
|
|
return Err(self.error("egl GetConfigs to count possible number of configurations"));
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut configs = vec![std::ptr::null(); num_configs as usize];
|
|
|
|
|
2020-07-07 18:57:17 +03:00
|
|
|
if unsafe {
|
|
|
|
self.egl
|
|
|
|
.GetConfigs(display, configs.as_mut_ptr(), num_configs, &mut num_configs)
|
|
|
|
} != 1
|
|
|
|
{
|
|
|
|
return Err(self.error("egl GetConfigs to enumerate configurations"));
|
|
|
|
}
|
|
|
|
|
2020-12-21 09:01:06 +03:00
|
|
|
log::trace!("Available Configuration(s):");
|
2020-07-07 18:57:17 +03:00
|
|
|
for c in &configs {
|
|
|
|
self.log_config_info(display, *c);
|
|
|
|
}
|
|
|
|
|
2019-10-25 03:42:18 +03:00
|
|
|
if unsafe {
|
|
|
|
self.egl.ChooseConfig(
|
|
|
|
display,
|
|
|
|
attributes.as_ptr() as *const ffi::EGLint,
|
|
|
|
configs.as_mut_ptr(),
|
|
|
|
configs.len() as ffi::EGLint,
|
|
|
|
&mut num_configs,
|
|
|
|
)
|
|
|
|
} != 1
|
|
|
|
{
|
|
|
|
return Err(self.error("egl ChooseConfig to select configurations"));
|
|
|
|
}
|
|
|
|
|
|
|
|
configs.resize(num_configs as usize, std::ptr::null());
|
|
|
|
|
2020-12-21 09:01:06 +03:00
|
|
|
log::trace!("Matching Configuration(s):");
|
2020-07-07 18:57:17 +03:00
|
|
|
for c in &configs {
|
|
|
|
self.log_config_info(display, *c);
|
|
|
|
}
|
|
|
|
|
2020-07-11 23:23:30 +03:00
|
|
|
// If we're running on a system with 30bpp color depth then ChooseConfig
|
|
|
|
// will bias towards putting 10bpc matches first, but we want 8-bit.
|
|
|
|
// Let's filter out matches that are too deep
|
|
|
|
configs.retain(|config| {
|
|
|
|
self.config_attrib(display, *config, ffi::RED_SIZE) == Some(8)
|
|
|
|
&& self.config_attrib(display, *config, ffi::GREEN_SIZE) == Some(8)
|
|
|
|
&& self.config_attrib(display, *config, ffi::BLUE_SIZE) == Some(8)
|
|
|
|
});
|
|
|
|
|
2020-08-03 08:41:32 +03:00
|
|
|
// Sort by descending alpha size, otherwise we can end up selecting
|
|
|
|
// alpha size 0 under XWayland, even though a superior config with
|
|
|
|
// 32bpp 8bpc is available. For whatever reason (probably a Wayland/mutter
|
|
|
|
// weirdness) that renders with a transparent background on my pixelbook.
|
|
|
|
configs.sort_by(|a, b| {
|
|
|
|
self.config_attrib(display, *a, ffi::ALPHA_SIZE)
|
|
|
|
.cmp(&self.config_attrib(display, *b, ffi::ALPHA_SIZE))
|
|
|
|
.reverse()
|
|
|
|
});
|
|
|
|
|
2020-12-21 09:01:06 +03:00
|
|
|
log::trace!("Filtered down to these configuration(s):");
|
2020-07-11 23:23:30 +03:00
|
|
|
for c in &configs {
|
|
|
|
self.log_config_info(display, *c);
|
|
|
|
}
|
|
|
|
|
2019-10-25 03:42:18 +03:00
|
|
|
Ok(configs)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn create_window_surface(
|
|
|
|
&self,
|
|
|
|
display: ffi::types::EGLDisplay,
|
|
|
|
config: ffi::types::EGLConfig,
|
|
|
|
window: ffi::EGLNativeWindowType,
|
2019-12-15 08:43:05 +03:00
|
|
|
) -> anyhow::Result<ffi::types::EGLSurface> {
|
2019-10-25 03:42:18 +03:00
|
|
|
let surface = unsafe {
|
|
|
|
self.egl
|
|
|
|
.CreateWindowSurface(display, config, window, std::ptr::null())
|
|
|
|
};
|
|
|
|
if surface.is_null() {
|
|
|
|
Err(self.error("EGL CreateWindowSurface"))
|
|
|
|
} else {
|
|
|
|
Ok(surface)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn create_context(
|
|
|
|
&self,
|
|
|
|
display: ffi::types::EGLDisplay,
|
|
|
|
config: ffi::types::EGLConfig,
|
|
|
|
share_context: ffi::types::EGLContext,
|
|
|
|
attributes: &[u32],
|
2019-12-15 08:43:05 +03:00
|
|
|
) -> anyhow::Result<ffi::types::EGLConfig> {
|
|
|
|
ensure!(
|
2019-10-26 23:12:01 +03:00
|
|
|
!attributes.is_empty() && attributes[attributes.len() - 1] == ffi::NONE,
|
2019-10-25 03:42:18 +03:00
|
|
|
"attributes list must be terminated with ffi::NONE"
|
|
|
|
);
|
|
|
|
let context = unsafe {
|
|
|
|
self.egl.CreateContext(
|
|
|
|
display,
|
|
|
|
config,
|
|
|
|
share_context,
|
|
|
|
attributes.as_ptr() as *const i32,
|
|
|
|
)
|
|
|
|
};
|
|
|
|
if context.is_null() {
|
|
|
|
Err(self.error("EGL CreateContext"))
|
|
|
|
} else {
|
|
|
|
Ok(context)
|
|
|
|
}
|
|
|
|
}
|
2019-10-25 01:54:41 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
impl GlState {
|
2020-10-18 05:08:16 +03:00
|
|
|
#[cfg_attr(any(windows, target_os = "macos"), allow(unused))]
|
2020-08-16 20:40:36 +03:00
|
|
|
pub fn get_connection(&self) -> &Rc<GlConnection> {
|
|
|
|
&self.connection
|
|
|
|
}
|
|
|
|
|
2019-12-15 08:43:05 +03:00
|
|
|
fn with_egl_lib<F: FnMut(EglWrapper) -> anyhow::Result<Self>>(
|
|
|
|
mut func: F,
|
|
|
|
) -> anyhow::Result<Self> {
|
2020-10-17 19:22:34 +03:00
|
|
|
let mut paths: Vec<std::path::PathBuf> = vec![
|
2019-10-25 01:54:41 +03:00
|
|
|
#[cfg(target_os = "windows")]
|
2020-10-17 19:22:34 +03:00
|
|
|
"libEGL.dll".into(),
|
2019-10-25 01:54:41 +03:00
|
|
|
#[cfg(target_os = "windows")]
|
2020-10-17 19:22:34 +03:00
|
|
|
"atioglxx.dll".into(),
|
|
|
|
#[cfg(all(not(target_os = "macos"), not(target_os = "windows")))]
|
|
|
|
"libEGL.so.1".into(),
|
|
|
|
#[cfg(all(not(target_os = "macos"), not(target_os = "windows")))]
|
|
|
|
"libEGL.so".into(),
|
2019-10-25 01:54:41 +03:00
|
|
|
];
|
2020-07-12 00:19:18 +03:00
|
|
|
|
2020-10-17 19:22:34 +03:00
|
|
|
if cfg!(target_os = "macos") {
|
|
|
|
// On macOS, let's look in the application directory to see
|
|
|
|
// if we've deployed libEGL.dylib alongside; if so, we want
|
|
|
|
// to try loading that.
|
|
|
|
paths.push(
|
|
|
|
std::env::current_exe()?
|
|
|
|
.parent()
|
|
|
|
.ok_or_else(|| anyhow!("current_exe isn't in a directory!?"))?
|
|
|
|
.join("libEGL.dylib"),
|
|
|
|
);
|
|
|
|
|
|
|
|
// And just in case, let's also allow loading via
|
|
|
|
// DYLD_LIBRARY_PATH
|
|
|
|
paths.push("libEGL.dylib".into());
|
|
|
|
}
|
|
|
|
|
2020-07-12 00:19:18 +03:00
|
|
|
let mut errors = vec![];
|
2021-02-28 10:59:04 +03:00
|
|
|
let mut prefer_swrast = crate::configuration::config().prefer_swrast();
|
2020-07-12 00:19:18 +03:00
|
|
|
|
2020-10-01 08:37:37 +03:00
|
|
|
for _ in 0..2 {
|
2021-02-28 10:59:04 +03:00
|
|
|
if prefer_swrast {
|
2020-10-02 06:01:09 +03:00
|
|
|
// Assuming that we're using Mesa, set an environment
|
|
|
|
// variable that should select CPU based rendering.
|
|
|
|
std::env::set_var("LIBGL_ALWAYS_SOFTWARE", "true");
|
|
|
|
}
|
2020-10-01 08:37:37 +03:00
|
|
|
for path in &paths {
|
2020-10-18 05:08:16 +03:00
|
|
|
match libloading::Library::new(path) {
|
|
|
|
Ok(lib) => match EglWrapper::load_egl(lib) {
|
2020-10-01 08:37:37 +03:00
|
|
|
Ok(egl) => match func(egl) {
|
|
|
|
Ok(result) => {
|
|
|
|
return Ok(result);
|
|
|
|
}
|
|
|
|
Err(e) => {
|
2020-10-17 19:22:34 +03:00
|
|
|
errors.push(format!(
|
|
|
|
"with_egl_lib({}) failed: {}",
|
|
|
|
path.display(),
|
|
|
|
e
|
|
|
|
));
|
2020-10-01 08:37:37 +03:00
|
|
|
}
|
|
|
|
},
|
2019-11-30 05:05:09 +03:00
|
|
|
Err(e) => {
|
2020-10-17 19:22:34 +03:00
|
|
|
errors.push(format!("load_egl {} failed: {}", path.display(), e));
|
2019-11-30 05:05:09 +03:00
|
|
|
}
|
2020-10-18 05:08:16 +03:00
|
|
|
},
|
|
|
|
Err(e) => {
|
|
|
|
errors.push(format!("{}: {}", path.display(), e));
|
2019-11-30 05:05:09 +03:00
|
|
|
}
|
2019-10-25 01:54:41 +03:00
|
|
|
}
|
|
|
|
}
|
2020-10-18 05:08:16 +03:00
|
|
|
// Since we didn't yet succeed, try enabling software rasterization.
|
|
|
|
// However, don't do this on Windows; the EGL implementation on
|
|
|
|
// Windows isn't MESA so there's no point trying a second pass
|
|
|
|
// with the mesa environment set, and if we did, it would just
|
|
|
|
// cause us to try software mode instead of the native opengl
|
|
|
|
// drivers we'd pick up from the WGL fallback.
|
|
|
|
if cfg!(windows) {
|
|
|
|
break;
|
|
|
|
}
|
2021-02-28 10:59:04 +03:00
|
|
|
if prefer_swrast {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
prefer_swrast = true;
|
2019-10-25 01:54:41 +03:00
|
|
|
}
|
2020-07-12 00:19:18 +03:00
|
|
|
bail!("with_egl_lib failed: {}", errors.join(", "))
|
2019-10-25 01:54:41 +03:00
|
|
|
}
|
2019-11-30 05:05:09 +03:00
|
|
|
|
|
|
|
#[cfg(all(unix, feature = "wayland", not(target_os = "macos")))]
|
|
|
|
pub fn create_wayland(
|
|
|
|
display: Option<ffi::EGLNativeDisplayType>,
|
2020-05-03 01:53:32 +03:00
|
|
|
wegl_surface: &wayland_egl::WlEglSurface,
|
2019-12-15 08:43:05 +03:00
|
|
|
) -> anyhow::Result<Self> {
|
2020-07-06 17:45:25 +03:00
|
|
|
Self::create(display, wegl_surface.ptr())
|
2019-11-30 05:05:09 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn create(
|
|
|
|
display: Option<ffi::EGLNativeDisplayType>,
|
|
|
|
window: ffi::EGLNativeWindowType,
|
2019-12-15 08:43:05 +03:00
|
|
|
) -> anyhow::Result<Self> {
|
2019-11-30 05:05:09 +03:00
|
|
|
Self::with_egl_lib(|egl| {
|
|
|
|
let egl_display = egl.get_display(display)?;
|
|
|
|
|
|
|
|
let (major, minor) = egl.initialize_and_get_version(egl_display)?;
|
2020-12-21 09:01:06 +03:00
|
|
|
log::trace!("initialized EGL version {}.{}", major, minor);
|
2019-11-30 05:05:09 +03:00
|
|
|
|
2020-08-16 20:40:36 +03:00
|
|
|
let connection = Rc::new(GlConnection {
|
2019-11-30 05:05:09 +03:00
|
|
|
display: egl_display,
|
2020-08-16 20:40:36 +03:00
|
|
|
egl,
|
|
|
|
});
|
|
|
|
|
|
|
|
Self::create_with_existing_connection(&connection, window)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(all(unix, feature = "wayland", not(target_os = "macos")))]
|
|
|
|
pub fn create_wayland_with_existing_connection(
|
|
|
|
connection: &Rc<GlConnection>,
|
|
|
|
wegl_surface: &wayland_egl::WlEglSurface,
|
|
|
|
) -> anyhow::Result<Self> {
|
|
|
|
Self::create_with_existing_connection(connection, wegl_surface.ptr())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn create_with_existing_connection(
|
|
|
|
connection: &Rc<GlConnection>,
|
|
|
|
window: ffi::EGLNativeWindowType,
|
|
|
|
) -> anyhow::Result<GlState> {
|
|
|
|
let configs = connection.egl.choose_config(
|
|
|
|
connection.display,
|
|
|
|
&[
|
|
|
|
// We're explicitly asking for any alpha size; this is
|
|
|
|
// the default behavior but we're making it explicit here
|
|
|
|
// for the sake of documenting our intent.
|
|
|
|
// In general we want 32bpp with 8bpc, but for displays
|
|
|
|
// that are natively 10bpc we should be fine with relaxing
|
|
|
|
// this to 0 alpha bits, so by asking for 0 here we effectively
|
|
|
|
// indicate that we don't care.
|
|
|
|
// In our implementation of choose_config we will return
|
|
|
|
// only entries with 8bpc for red/green/blue so we should
|
|
|
|
// end up with either 24bpp/8bpc with no alpha, or 32bpp/8bpc
|
|
|
|
// with 8bpc alpha.
|
|
|
|
ffi::ALPHA_SIZE,
|
|
|
|
0,
|
|
|
|
// Request at least 8bpc, 24bpp. The implementation may
|
|
|
|
// return a context capable of more than this.
|
|
|
|
ffi::RED_SIZE,
|
|
|
|
8,
|
|
|
|
ffi::GREEN_SIZE,
|
|
|
|
8,
|
|
|
|
ffi::BLUE_SIZE,
|
|
|
|
8,
|
|
|
|
ffi::DEPTH_SIZE,
|
|
|
|
24,
|
|
|
|
ffi::CONFORMANT,
|
|
|
|
ffi::OPENGL_ES3_BIT,
|
|
|
|
ffi::RENDERABLE_TYPE,
|
|
|
|
ffi::OPENGL_ES3_BIT,
|
|
|
|
// Wayland EGL doesn't give us a working context if we request
|
|
|
|
// PBUFFER|PIXMAP. We don't appear to require these for X11,
|
|
|
|
// so we're just asking for a WINDOW capable context
|
|
|
|
ffi::SURFACE_TYPE,
|
|
|
|
ffi::WINDOW_BIT, //| ffi::PBUFFER_BIT | ffi::PIXMAP_BIT,
|
|
|
|
ffi::NONE,
|
|
|
|
],
|
|
|
|
)?;
|
|
|
|
|
2020-10-14 05:18:29 +03:00
|
|
|
if configs.is_empty() {
|
|
|
|
anyhow::bail!("no compatible EGL configuration was found");
|
|
|
|
}
|
|
|
|
let mut errors = String::new();
|
|
|
|
|
|
|
|
for config in configs {
|
|
|
|
let surface =
|
|
|
|
match connection
|
|
|
|
.egl
|
|
|
|
.create_window_surface(connection.display, config, window)
|
|
|
|
{
|
|
|
|
Ok(s) => s,
|
|
|
|
Err(e) => {
|
|
|
|
errors.push_str(&format!("{:#} {:x?}\n", e, config));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
};
|
2020-08-16 20:40:36 +03:00
|
|
|
|
2020-10-18 05:08:16 +03:00
|
|
|
let mut attributes = vec![ffi::CONTEXT_MAJOR_VERSION, 3];
|
|
|
|
if cfg!(windows) {
|
|
|
|
// On Windows, where drivers may be dynamically unloaded,
|
|
|
|
// let's make an effort to try to survive that event.
|
|
|
|
for &a in &[
|
|
|
|
ffi::CONTEXT_OPENGL_ROBUST_ACCESS_EXT,
|
|
|
|
ffi::TRUE,
|
|
|
|
ffi::CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT,
|
|
|
|
ffi::LOSE_CONTEXT_ON_RESET_EXT,
|
|
|
|
] {
|
|
|
|
attributes.push(a);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
attributes.push(ffi::NONE);
|
|
|
|
|
2020-10-14 05:18:29 +03:00
|
|
|
let context = match connection.egl.create_context(
|
|
|
|
connection.display,
|
|
|
|
config,
|
|
|
|
std::ptr::null(),
|
2020-10-18 05:08:16 +03:00
|
|
|
&attributes,
|
2020-10-14 05:18:29 +03:00
|
|
|
) {
|
|
|
|
Ok(c) => c,
|
|
|
|
Err(e) => {
|
|
|
|
errors.push_str(&format!("{:#} {:x?}\n", e, config));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
};
|
2020-08-16 20:40:36 +03:00
|
|
|
|
2020-12-21 09:01:06 +03:00
|
|
|
log::trace!("Successfully created a surface using this configuration");
|
2020-10-14 05:45:43 +03:00
|
|
|
connection.egl.log_config_info(connection.display, config);
|
2020-10-14 05:18:29 +03:00
|
|
|
return Ok(Self {
|
|
|
|
connection: Rc::clone(connection),
|
|
|
|
context,
|
|
|
|
surface,
|
|
|
|
});
|
|
|
|
}
|
2020-08-16 20:40:36 +03:00
|
|
|
|
2020-10-14 05:18:29 +03:00
|
|
|
Err(anyhow!(errors))
|
2019-11-30 05:05:09 +03:00
|
|
|
}
|
2019-10-25 01:54:41 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
unsafe impl glium::backend::Backend for GlState {
|
|
|
|
fn swap_buffers(&self) -> Result<(), glium::SwapBuffersError> {
|
2020-08-16 20:40:36 +03:00
|
|
|
let res = unsafe {
|
|
|
|
self.connection
|
|
|
|
.SwapBuffers(self.connection.display, self.surface)
|
|
|
|
};
|
2019-10-25 03:42:18 +03:00
|
|
|
if res != 1 {
|
2020-10-18 05:08:16 +03:00
|
|
|
Err(match unsafe { self.connection.GetError() } as u32 {
|
|
|
|
ffi::CONTEXT_LOST => glium::SwapBuffersError::ContextLost,
|
|
|
|
_ => glium::SwapBuffersError::AlreadySwapped,
|
|
|
|
})
|
2019-10-25 03:42:18 +03:00
|
|
|
} else {
|
|
|
|
Ok(())
|
|
|
|
}
|
2019-10-25 01:54:41 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void {
|
|
|
|
let sym_name = std::ffi::CString::new(symbol).expect("symbol to be cstring compatible");
|
2020-08-16 20:40:36 +03:00
|
|
|
self.connection.GetProcAddress(sym_name.as_ptr()) as *const c_void
|
2019-10-25 01:54:41 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
fn get_framebuffer_dimensions(&self) -> (u32, u32) {
|
2019-10-25 03:42:18 +03:00
|
|
|
let mut width = 0;
|
|
|
|
let mut height = 0;
|
|
|
|
|
|
|
|
unsafe {
|
2020-08-16 20:40:36 +03:00
|
|
|
self.connection.QuerySurface(
|
|
|
|
self.connection.display,
|
|
|
|
self.surface,
|
|
|
|
ffi::WIDTH as i32,
|
|
|
|
&mut width,
|
|
|
|
);
|
2019-10-25 03:42:18 +03:00
|
|
|
}
|
|
|
|
unsafe {
|
2020-08-16 20:40:36 +03:00
|
|
|
self.connection.QuerySurface(
|
|
|
|
self.connection.display,
|
|
|
|
self.surface,
|
|
|
|
ffi::HEIGHT as i32,
|
|
|
|
&mut height,
|
|
|
|
);
|
2019-10-25 03:42:18 +03:00
|
|
|
}
|
|
|
|
(width as u32, height as u32)
|
2019-10-25 01:54:41 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
fn is_current(&self) -> bool {
|
2020-08-16 20:40:36 +03:00
|
|
|
unsafe { self.connection.GetCurrentContext() == self.context }
|
2019-10-25 01:54:41 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
unsafe fn make_current(&self) {
|
2020-08-16 20:40:36 +03:00
|
|
|
self.connection.MakeCurrent(
|
|
|
|
self.connection.display,
|
|
|
|
self.surface,
|
|
|
|
self.surface,
|
|
|
|
self.context,
|
|
|
|
);
|
2019-10-25 01:54:41 +03:00
|
|
|
}
|
|
|
|
}
|