1
1
mirror of https://github.com/wez/wezterm.git synced 2024-12-24 13:52:55 +03:00
wezterm/window/src/egl.rs

662 lines
23 KiB
Rust
Raw Normal View History

2019-12-15 08:43:05 +03:00
use anyhow::{anyhow, bail, ensure, Error};
use std::ffi::c_void;
use std::rc::Rc;
2019-10-26 23:12:01 +03:00
#[allow(non_camel_case_types, clippy::unreadable_literal)]
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,
}
impl std::fmt::Debug for EglWrapper {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fmt.debug_struct("EglWrapper").finish()
}
}
#[derive(Debug)]
pub struct GlConnection {
egl: EglWrapper,
display: ffi::types::EGLDisplay,
}
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);
}
}
}
#[derive(Debug)]
pub struct GlState {
connection: Rc<GlConnection>,
surface: ffi::types::EGLSurface,
context: ffi::types::EGLContext,
}
impl Drop for GlState {
fn drop(&mut self) {
unsafe {
self.connection.MakeCurrent(
self.connection.display,
ffi::NO_SURFACE,
ffi::NO_SURFACE,
ffi::NO_CONTEXT,
);
self.connection
.DestroySurface(self.connection.display, self.surface);
self.connection
.DestroyContext(self.connection.display, self.context);
}
}
}
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> {
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()) }
});
log::trace!("load_egl: {:?}", lib);
Ok(Self { _lib: lib, egl })
}
fn get_display(
&self,
display: Option<ffi::EGLNativeDisplayType>,
2019-12-15 08:43:05 +03:00
) -> anyhow::Result<ffi::types::EGLDisplay> {
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 {
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)
}
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)> {
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"))
}
}
}
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),
};
log::trace!("{:x?}", info);
}
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,
"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];
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"));
}
log::trace!("Available Configuration(s):");
for c in &configs {
self.log_config_info(display, *c);
}
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());
log::trace!("Matching Configuration(s):");
for c in &configs {
self.log_config_info(display, *c);
}
// 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)
});
// 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()
});
log::trace!("Filtered down to these configuration(s):");
for c in &configs {
self.log_config_info(display, *c);
}
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> {
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,
"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)
}
}
}
impl GlState {
Windows: prefer to use Direct3D11 via ANGLE This is similar in spirit to the work in 4d71a7913a2ed6461b46a59e77d532ba5ae2a7a2 but for Windows. This commit adds ANGLE binaries built from https://chromium.googlesource.com/angle/angle/+/07ea804e620132517b6af0ef92fe85ea737d0c27 to the repo. The build and packaging will copy those into the same directory as wezterm.exe so that they can be resolved at runtime. By default, `prefer_egl = true`, which will cause the window crate to first try to load an EGL implementation. If that fails, or if `prefer_egl = false`, then the window crate will perform the usual WGL initialization. The practical effect of this change is that Direct3D11 is used for the underlying render, which avoids problematic OpenGL drivers and means that the process can survive graphics drivers being updated. It may also increase the chances that the GPU will really be used in an RDP session rather than the pessimised use of the software renderer. The one downside that I've noticed is that the resize behavior feels a little janky in comparison to WGL (frames can render with mismatched surface/window sizes which makes the window contents feel like they're zooming/rippling slightly as the window is live resized). I think this is specific to the ANGLE D3D implementation as EGL on other platforms feels more solid. I'm a little on the fence about making this the default; I think it makes sense to prefer something that won't quit unexpectedly while a software update is in progress, so that's a strong plus in favor of EGL as the default, but I'm not sure how much the resize wobble is going to set people off. If you prefer WGL and are fine with the risk of a drive update killing wezterm, then you can set this in your config: ```lua return { prefer_egl = false, } ``` refs: https://github.com/wez/wezterm/issues/265 closes: https://github.com/wez/wezterm/issues/156
2020-10-18 05:08:16 +03:00
#[cfg_attr(any(windows, target_os = "macos"), allow(unused))]
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> {
let mut paths: Vec<std::path::PathBuf> = vec![
#[cfg(target_os = "windows")]
"libEGL.dll".into(),
#[cfg(target_os = "windows")]
"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(),
];
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());
}
let mut errors = vec![];
let mut prefer_swrast = crate::configuration::config().prefer_swrast();
for _ in 0..2 {
if prefer_swrast {
// Assuming that we're using Mesa, set an environment
// variable that should select CPU based rendering.
std::env::set_var("LIBGL_ALWAYS_SOFTWARE", "true");
}
for path in &paths {
Windows: prefer to use Direct3D11 via ANGLE This is similar in spirit to the work in 4d71a7913a2ed6461b46a59e77d532ba5ae2a7a2 but for Windows. This commit adds ANGLE binaries built from https://chromium.googlesource.com/angle/angle/+/07ea804e620132517b6af0ef92fe85ea737d0c27 to the repo. The build and packaging will copy those into the same directory as wezterm.exe so that they can be resolved at runtime. By default, `prefer_egl = true`, which will cause the window crate to first try to load an EGL implementation. If that fails, or if `prefer_egl = false`, then the window crate will perform the usual WGL initialization. The practical effect of this change is that Direct3D11 is used for the underlying render, which avoids problematic OpenGL drivers and means that the process can survive graphics drivers being updated. It may also increase the chances that the GPU will really be used in an RDP session rather than the pessimised use of the software renderer. The one downside that I've noticed is that the resize behavior feels a little janky in comparison to WGL (frames can render with mismatched surface/window sizes which makes the window contents feel like they're zooming/rippling slightly as the window is live resized). I think this is specific to the ANGLE D3D implementation as EGL on other platforms feels more solid. I'm a little on the fence about making this the default; I think it makes sense to prefer something that won't quit unexpectedly while a software update is in progress, so that's a strong plus in favor of EGL as the default, but I'm not sure how much the resize wobble is going to set people off. If you prefer WGL and are fine with the risk of a drive update killing wezterm, then you can set this in your config: ```lua return { prefer_egl = false, } ``` refs: https://github.com/wez/wezterm/issues/265 closes: https://github.com/wez/wezterm/issues/156
2020-10-18 05:08:16 +03:00
match libloading::Library::new(path) {
Ok(lib) => match EglWrapper::load_egl(lib) {
Ok(egl) => match func(egl) {
Ok(result) => {
return Ok(result);
}
Err(e) => {
errors.push(format!(
"with_egl_lib({}) failed: {}",
path.display(),
e
));
}
},
2019-11-30 05:05:09 +03:00
Err(e) => {
errors.push(format!("load_egl {} failed: {}", path.display(), e));
2019-11-30 05:05:09 +03:00
}
Windows: prefer to use Direct3D11 via ANGLE This is similar in spirit to the work in 4d71a7913a2ed6461b46a59e77d532ba5ae2a7a2 but for Windows. This commit adds ANGLE binaries built from https://chromium.googlesource.com/angle/angle/+/07ea804e620132517b6af0ef92fe85ea737d0c27 to the repo. The build and packaging will copy those into the same directory as wezterm.exe so that they can be resolved at runtime. By default, `prefer_egl = true`, which will cause the window crate to first try to load an EGL implementation. If that fails, or if `prefer_egl = false`, then the window crate will perform the usual WGL initialization. The practical effect of this change is that Direct3D11 is used for the underlying render, which avoids problematic OpenGL drivers and means that the process can survive graphics drivers being updated. It may also increase the chances that the GPU will really be used in an RDP session rather than the pessimised use of the software renderer. The one downside that I've noticed is that the resize behavior feels a little janky in comparison to WGL (frames can render with mismatched surface/window sizes which makes the window contents feel like they're zooming/rippling slightly as the window is live resized). I think this is specific to the ANGLE D3D implementation as EGL on other platforms feels more solid. I'm a little on the fence about making this the default; I think it makes sense to prefer something that won't quit unexpectedly while a software update is in progress, so that's a strong plus in favor of EGL as the default, but I'm not sure how much the resize wobble is going to set people off. If you prefer WGL and are fine with the risk of a drive update killing wezterm, then you can set this in your config: ```lua return { prefer_egl = false, } ``` refs: https://github.com/wez/wezterm/issues/265 closes: https://github.com/wez/wezterm/issues/156
2020-10-18 05:08:16 +03:00
},
Err(e) => {
errors.push(format!("{}: {}", path.display(), e));
2019-11-30 05:05:09 +03:00
}
}
}
Windows: prefer to use Direct3D11 via ANGLE This is similar in spirit to the work in 4d71a7913a2ed6461b46a59e77d532ba5ae2a7a2 but for Windows. This commit adds ANGLE binaries built from https://chromium.googlesource.com/angle/angle/+/07ea804e620132517b6af0ef92fe85ea737d0c27 to the repo. The build and packaging will copy those into the same directory as wezterm.exe so that they can be resolved at runtime. By default, `prefer_egl = true`, which will cause the window crate to first try to load an EGL implementation. If that fails, or if `prefer_egl = false`, then the window crate will perform the usual WGL initialization. The practical effect of this change is that Direct3D11 is used for the underlying render, which avoids problematic OpenGL drivers and means that the process can survive graphics drivers being updated. It may also increase the chances that the GPU will really be used in an RDP session rather than the pessimised use of the software renderer. The one downside that I've noticed is that the resize behavior feels a little janky in comparison to WGL (frames can render with mismatched surface/window sizes which makes the window contents feel like they're zooming/rippling slightly as the window is live resized). I think this is specific to the ANGLE D3D implementation as EGL on other platforms feels more solid. I'm a little on the fence about making this the default; I think it makes sense to prefer something that won't quit unexpectedly while a software update is in progress, so that's a strong plus in favor of EGL as the default, but I'm not sure how much the resize wobble is going to set people off. If you prefer WGL and are fine with the risk of a drive update killing wezterm, then you can set this in your config: ```lua return { prefer_egl = false, } ``` refs: https://github.com/wez/wezterm/issues/265 closes: https://github.com/wez/wezterm/issues/156
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;
}
if prefer_swrast {
break;
}
prefer_swrast = true;
}
bail!("with_egl_lib failed: {}", errors.join(", "))
}
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>,
wegl_surface: &wayland_egl::WlEglSurface,
2019-12-15 08:43:05 +03:00
) -> anyhow::Result<Self> {
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)?;
log::trace!("initialized EGL version {}.{}", major, minor);
2019-11-30 05:05:09 +03:00
let connection = Rc::new(GlConnection {
2019-11-30 05:05:09 +03:00
display: egl_display,
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,
],
)?;
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;
}
};
Windows: prefer to use Direct3D11 via ANGLE This is similar in spirit to the work in 4d71a7913a2ed6461b46a59e77d532ba5ae2a7a2 but for Windows. This commit adds ANGLE binaries built from https://chromium.googlesource.com/angle/angle/+/07ea804e620132517b6af0ef92fe85ea737d0c27 to the repo. The build and packaging will copy those into the same directory as wezterm.exe so that they can be resolved at runtime. By default, `prefer_egl = true`, which will cause the window crate to first try to load an EGL implementation. If that fails, or if `prefer_egl = false`, then the window crate will perform the usual WGL initialization. The practical effect of this change is that Direct3D11 is used for the underlying render, which avoids problematic OpenGL drivers and means that the process can survive graphics drivers being updated. It may also increase the chances that the GPU will really be used in an RDP session rather than the pessimised use of the software renderer. The one downside that I've noticed is that the resize behavior feels a little janky in comparison to WGL (frames can render with mismatched surface/window sizes which makes the window contents feel like they're zooming/rippling slightly as the window is live resized). I think this is specific to the ANGLE D3D implementation as EGL on other platforms feels more solid. I'm a little on the fence about making this the default; I think it makes sense to prefer something that won't quit unexpectedly while a software update is in progress, so that's a strong plus in favor of EGL as the default, but I'm not sure how much the resize wobble is going to set people off. If you prefer WGL and are fine with the risk of a drive update killing wezterm, then you can set this in your config: ```lua return { prefer_egl = false, } ``` refs: https://github.com/wez/wezterm/issues/265 closes: https://github.com/wez/wezterm/issues/156
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);
let context = match connection.egl.create_context(
connection.display,
config,
std::ptr::null(),
Windows: prefer to use Direct3D11 via ANGLE This is similar in spirit to the work in 4d71a7913a2ed6461b46a59e77d532ba5ae2a7a2 but for Windows. This commit adds ANGLE binaries built from https://chromium.googlesource.com/angle/angle/+/07ea804e620132517b6af0ef92fe85ea737d0c27 to the repo. The build and packaging will copy those into the same directory as wezterm.exe so that they can be resolved at runtime. By default, `prefer_egl = true`, which will cause the window crate to first try to load an EGL implementation. If that fails, or if `prefer_egl = false`, then the window crate will perform the usual WGL initialization. The practical effect of this change is that Direct3D11 is used for the underlying render, which avoids problematic OpenGL drivers and means that the process can survive graphics drivers being updated. It may also increase the chances that the GPU will really be used in an RDP session rather than the pessimised use of the software renderer. The one downside that I've noticed is that the resize behavior feels a little janky in comparison to WGL (frames can render with mismatched surface/window sizes which makes the window contents feel like they're zooming/rippling slightly as the window is live resized). I think this is specific to the ANGLE D3D implementation as EGL on other platforms feels more solid. I'm a little on the fence about making this the default; I think it makes sense to prefer something that won't quit unexpectedly while a software update is in progress, so that's a strong plus in favor of EGL as the default, but I'm not sure how much the resize wobble is going to set people off. If you prefer WGL and are fine with the risk of a drive update killing wezterm, then you can set this in your config: ```lua return { prefer_egl = false, } ``` refs: https://github.com/wez/wezterm/issues/265 closes: https://github.com/wez/wezterm/issues/156
2020-10-18 05:08:16 +03:00
&attributes,
) {
Ok(c) => c,
Err(e) => {
errors.push_str(&format!("{:#} {:x?}\n", e, config));
continue;
}
};
log::trace!("Successfully created a surface using this configuration");
connection.egl.log_config_info(connection.display, config);
return Ok(Self {
connection: Rc::clone(connection),
context,
surface,
});
}
Err(anyhow!(errors))
2019-11-30 05:05:09 +03:00
}
}
unsafe impl glium::backend::Backend for GlState {
fn swap_buffers(&self) -> Result<(), glium::SwapBuffersError> {
let res = unsafe {
self.connection
.SwapBuffers(self.connection.display, self.surface)
};
if res != 1 {
Windows: prefer to use Direct3D11 via ANGLE This is similar in spirit to the work in 4d71a7913a2ed6461b46a59e77d532ba5ae2a7a2 but for Windows. This commit adds ANGLE binaries built from https://chromium.googlesource.com/angle/angle/+/07ea804e620132517b6af0ef92fe85ea737d0c27 to the repo. The build and packaging will copy those into the same directory as wezterm.exe so that they can be resolved at runtime. By default, `prefer_egl = true`, which will cause the window crate to first try to load an EGL implementation. If that fails, or if `prefer_egl = false`, then the window crate will perform the usual WGL initialization. The practical effect of this change is that Direct3D11 is used for the underlying render, which avoids problematic OpenGL drivers and means that the process can survive graphics drivers being updated. It may also increase the chances that the GPU will really be used in an RDP session rather than the pessimised use of the software renderer. The one downside that I've noticed is that the resize behavior feels a little janky in comparison to WGL (frames can render with mismatched surface/window sizes which makes the window contents feel like they're zooming/rippling slightly as the window is live resized). I think this is specific to the ANGLE D3D implementation as EGL on other platforms feels more solid. I'm a little on the fence about making this the default; I think it makes sense to prefer something that won't quit unexpectedly while a software update is in progress, so that's a strong plus in favor of EGL as the default, but I'm not sure how much the resize wobble is going to set people off. If you prefer WGL and are fine with the risk of a drive update killing wezterm, then you can set this in your config: ```lua return { prefer_egl = false, } ``` refs: https://github.com/wez/wezterm/issues/265 closes: https://github.com/wez/wezterm/issues/156
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,
})
} else {
Ok(())
}
}
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");
self.connection.GetProcAddress(sym_name.as_ptr()) as *const c_void
}
fn get_framebuffer_dimensions(&self) -> (u32, u32) {
let mut width = 0;
let mut height = 0;
unsafe {
self.connection.QuerySurface(
self.connection.display,
self.surface,
ffi::WIDTH as i32,
&mut width,
);
}
unsafe {
self.connection.QuerySurface(
self.connection.display,
self.surface,
ffi::HEIGHT as i32,
&mut height,
);
}
(width as u32, height as u32)
}
fn is_current(&self) -> bool {
unsafe { self.connection.GetCurrentContext() == self.context }
}
unsafe fn make_current(&self) {
if self.connection.MakeCurrent(
self.connection.display,
self.surface,
self.surface,
self.context,
) == 0
{
let err = self.connection.egl.error("MakeCurrent");
log::error!("make_current failed {:?} {:?}", self, err);
}
}
}