From 21320bea24ee0af1ff188cde39cdc12d83d881d4 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Tue, 22 Jul 2025 23:17:32 +0200 Subject: [PATCH 1/2] Allow optional `RawDisplayHandle` in `InstanceDescriptor` This allows platforms like GLES with EGL context backend to properly initialize such that their `EGLDisplay` and `EGLContext` are compatible with an incoming `Surface`, which is otherwise impossible if not for hotpatching the context (and loosing all resources created on it) when a surface comes in and that compositor connection becomes known. This hotpatching is known to break quite a few systems and initialization ordering (i.e. the alternative is forcing users to create surfaces before querying adapters, which isn't feasible on platform like Android), and is better avoided or at least made possible by using the `(Raw)DisplayHandle` abstraction provided by `raw_display_handle` in the way it's intended to be used (additionally, removing it from the surface would be a sensible decision). There may however be cases where users want to initialize multiple `Instance`s or perhaps `Adapter`s for independent `RawDisplayHandle`s, and certain backends may be designed specifically to cope with this (i.e. Vulkan). Note that this PR does nothing to address many other soundness problems inside the EGL backend. --- Cargo.lock | 2 + Cargo.toml | 2 +- deno_webgpu/lib.rs | 1 + examples/features/src/framework.rs | 7 +- tests/src/init.rs | 1 + tests/tests/wgpu-validation/api/instance.rs | 1 + wgpu-core/src/instance.rs | 5 + wgpu-hal/Cargo.toml | 4 + wgpu-hal/examples/halmark/main.rs | 7 +- wgpu-hal/examples/ray-traced-triangle/main.rs | 8 +- wgpu-hal/src/gles/egl.rs | 363 ++++++------------ wgpu-hal/src/lib.rs | 2 + wgpu-hal/src/noop/mod.rs | 1 + wgpu-types/Cargo.toml | 1 + wgpu-types/src/instance.rs | 7 + 15 files changed, 149 insertions(+), 263 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8a67c3562eb..0ba45d19fc4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5109,6 +5109,7 @@ dependencies = [ "smallvec", "thiserror 2.0.12", "wasm-bindgen", + "wayland-sys", "web-sys", "wgpu-types", "windows 0.58.0", @@ -5188,6 +5189,7 @@ dependencies = [ "bytemuck", "js-sys", "log", + "raw-window-handle 0.6.2", "serde", "serde_json", "thiserror 2.0.12", diff --git a/Cargo.toml b/Cargo.toml index e8af139f6fd..83b2151dddd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -165,7 +165,7 @@ pp-rs = "0.2.1" profiling = { version = "1.0.1", default-features = false } quote = "1.0.38" raw-window-handle = { version = "0.6.2", default-features = false } -rwh_05 = { version = "0.5.2", package = "raw-window-handle" } # temporary compatibility for glutin-winit +rwh_05 = { version = "0.5.2", package = "raw-window-handle" } # temporary compatibility for glutin-winit rayon = "1.3" regex-lite = "0.1" renderdoc-sys = "1" diff --git a/deno_webgpu/lib.rs b/deno_webgpu/lib.rs index 6ce31a29c52..3d37f9a5ec2 100644 --- a/deno_webgpu/lib.rs +++ b/deno_webgpu/lib.rs @@ -162,6 +162,7 @@ impl GPU { gl: wgpu_types::GlBackendOptions::default(), noop: wgpu_types::NoopBackendOptions::default(), }, + display: None, }, ))); state.borrow::() diff --git a/examples/features/src/framework.rs b/examples/features/src/framework.rs index 7a3017848fa..0cdd6694c0c 100644 --- a/examples/features/src/framework.rs +++ b/examples/features/src/framework.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use wgpu::{Instance, Surface}; +use wgpu::{rwh::HasDisplayHandle, Instance, Surface}; use winit::{ dpi::PhysicalSize, event::{Event, KeyEvent, StartCause, WindowEvent}, @@ -268,7 +268,10 @@ impl ExampleContext { async fn init_async(surface: &mut SurfaceWrapper, window: Arc) -> Self { log::info!("Initializing wgpu..."); - let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor::from_env_or_default()); + let mut desc = wgpu::InstanceDescriptor::from_env_or_default(); + // XXX: Could also come from EventLoop before Window is created + desc.display = Some(window.display_handle().unwrap().as_raw()); + let instance = wgpu::Instance::new(&desc); surface.pre_adapter(&instance, window); let adapter = get_adapter_with_capabilities_or_from_env( diff --git a/tests/src/init.rs b/tests/src/init.rs index d2aac6f14e1..f448dbc1e67 100644 --- a/tests/src/init.rs +++ b/tests/src/init.rs @@ -68,6 +68,7 @@ pub fn initialize_instance(backends: wgpu::Backends, params: &TestParameters) -> // TODO(https://github.com/gfx-rs/wgpu/issues/7119): Enable noop backend? noop: wgpu::NoopBackendOptions::default(), }, + display: None, }) } diff --git a/tests/tests/wgpu-validation/api/instance.rs b/tests/tests/wgpu-validation/api/instance.rs index 4f30e90293f..bfea3b5fdb6 100644 --- a/tests/tests/wgpu-validation/api/instance.rs +++ b/tests/tests/wgpu-validation/api/instance.rs @@ -41,6 +41,7 @@ mod request_adapter_error { flags: wgpu::InstanceFlags::default(), memory_budget_thresholds: wgpu::MemoryBudgetThresholds::default(), backend_options: wgpu::BackendOptions::default(), + display: None, } } diff --git a/wgpu-core/src/instance.rs b/wgpu-core/src/instance.rs index 99f967f033b..5d25b378d62 100644 --- a/wgpu-core/src/instance.rs +++ b/wgpu-core/src/instance.rs @@ -8,6 +8,7 @@ use alloc::{ }; use hashbrown::HashMap; +use raw_window_handle::DisplayHandle; use thiserror::Error; use wgt::error::{ErrorType, WebGpuError}; @@ -131,6 +132,10 @@ impl Instance { flags: self.flags, memory_budget_thresholds: instance_desc.memory_budget_thresholds, backend_options: instance_desc.backend_options.clone(), + display: instance_desc + .display + // XXX: This assigns a bogus lifetime. hal::InstanceDescriptor should also take Raw? + .map(|r| unsafe { DisplayHandle::borrow_raw(r) }), }; use hal::Instance as _; diff --git a/wgpu-hal/Cargo.toml b/wgpu-hal/Cargo.toml index 94dc0e07e0c..44f8aeefa59 100644 --- a/wgpu-hal/Cargo.toml +++ b/wgpu-hal/Cargo.toml @@ -112,6 +112,7 @@ gles = [ "dep:hashbrown", "dep:js-sys", "dep:khronos-egl", + "dep:wayland-sys", "dep:libloading", "dep:log", "dep:ndk-sys", @@ -245,6 +246,9 @@ renderdoc-sys = { workspace = true, optional = true } [target.'cfg(unix)'.dependencies] # Backend: Vulkan libc = { workspace = true, optional = true } +# backend: GLES +# XXX: Using crate to copy from glutin. Better yet, use wayland-client or replace entire backend with glutin entirely. +wayland-sys = { version = "0.31", features = ["client", "dlopen", "egl"], optional = true } ######################### ### Platform: Windows ### diff --git a/wgpu-hal/examples/halmark/main.rs b/wgpu-hal/examples/halmark/main.rs index 22f211c909b..ccb34d3edf0 100644 --- a/wgpu-hal/examples/halmark/main.rs +++ b/wgpu-hal/examples/halmark/main.rs @@ -93,21 +93,24 @@ struct Example { impl Example { fn init(window: &winit::window::Window) -> Result> { + // XXX: Could be from the EventLoop as well + let raw_display_handle = window.display_handle()?; + let instance_desc = hal::InstanceDescriptor { name: "example", flags: wgpu_types::InstanceFlags::from_build_config().with_env(), memory_budget_thresholds: wgpu_types::MemoryBudgetThresholds::default(), // Can't rely on having DXC available, so use FXC instead backend_options: wgpu_types::BackendOptions::default(), + display: Some(raw_display_handle), }; let instance = unsafe { A::Instance::init(&instance_desc)? }; let surface = { let raw_window_handle = window.window_handle()?.as_raw(); - let raw_display_handle = window.display_handle()?.as_raw(); unsafe { instance - .create_surface(raw_display_handle, raw_window_handle) + .create_surface(raw_display_handle.as_raw(), raw_window_handle) .unwrap() } }; diff --git a/wgpu-hal/examples/ray-traced-triangle/main.rs b/wgpu-hal/examples/ray-traced-triangle/main.rs index 2947d1a6014..a182bcc11d9 100644 --- a/wgpu-hal/examples/ray-traced-triangle/main.rs +++ b/wgpu-hal/examples/ray-traced-triangle/main.rs @@ -235,6 +235,9 @@ impl Example { log::info!("using index buffer") } + // XXX: Could be from the EventLoop as well + let raw_display_handle = window.display_handle()?; + let instance_desc = hal::InstanceDescriptor { name: "example", flags: wgpu_types::InstanceFlags::default(), @@ -245,15 +248,16 @@ impl Example { }, ..Default::default() }, + // XXX: Coudla + display: Some(raw_display_handle), }; let instance = unsafe { A::Instance::init(&instance_desc)? }; let surface = { let raw_window_handle = window.window_handle()?.as_raw(); - let raw_display_handle = window.display_handle()?.as_raw(); unsafe { instance - .create_surface(raw_display_handle, raw_window_handle) + .create_surface(raw_display_handle.as_raw(), raw_window_handle) .unwrap() } }; diff --git a/wgpu-hal/src/gles/egl.rs b/wgpu-hal/src/gles/egl.rs index ff4d77a3357..c4ccf59b27c 100644 --- a/wgpu-hal/src/gles/egl.rs +++ b/wgpu-hal/src/gles/egl.rs @@ -1,5 +1,6 @@ -use alloc::{rc::Rc, string::String, sync::Arc, vec::Vec}; +use alloc::{string::String, sync::Arc, vec::Vec}; use core::{ffi, mem::ManuallyDrop, ptr, time::Duration}; +use raw_window_handle::RawDisplayHandle; use std::sync::LazyLock; use glow::HasContext; @@ -14,45 +15,19 @@ const EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR: i32 = 0x0001; const EGL_CONTEXT_OPENGL_ROBUST_ACCESS_EXT: i32 = 0x30BF; const EGL_PLATFORM_WAYLAND_KHR: u32 = 0x31D8; const EGL_PLATFORM_X11_KHR: u32 = 0x31D5; -const EGL_PLATFORM_ANGLE_ANGLE: u32 = 0x3202; -const EGL_PLATFORM_ANGLE_NATIVE_PLATFORM_TYPE_ANGLE: u32 = 0x348F; -const EGL_PLATFORM_ANGLE_DEBUG_LAYERS_ENABLED: u32 = 0x3451; +// const EGL_PLATFORM_ANGLE_ANGLE: u32 = 0x3202; +// const EGL_PLATFORM_ANGLE_NATIVE_PLATFORM_TYPE_ANGLE: u32 = 0x348F; +// const EGL_PLATFORM_ANGLE_DEBUG_LAYERS_ENABLED: u32 = 0x3451; const EGL_PLATFORM_SURFACELESS_MESA: u32 = 0x31DD; const EGL_GL_COLORSPACE_KHR: u32 = 0x309D; const EGL_GL_COLORSPACE_SRGB_KHR: u32 = 0x3089; -type XOpenDisplayFun = - unsafe extern "system" fn(display_name: *const ffi::c_char) -> *mut ffi::c_void; - -type XCloseDisplayFun = unsafe extern "system" fn(display: *mut ffi::c_void) -> ffi::c_int; - -type WlDisplayConnectFun = - unsafe extern "system" fn(display_name: *const ffi::c_char) -> *mut ffi::c_void; - -type WlDisplayDisconnectFun = unsafe extern "system" fn(display: *const ffi::c_void); - #[cfg(not(Emscripten))] type EglInstance = khronos_egl::DynamicInstance; #[cfg(Emscripten)] type EglInstance = khronos_egl::Instance; -type WlEglWindowCreateFun = unsafe extern "system" fn( - surface: *const ffi::c_void, - width: ffi::c_int, - height: ffi::c_int, -) -> *mut ffi::c_void; - -type WlEglWindowResizeFun = unsafe extern "system" fn( - window: *const ffi::c_void, - width: ffi::c_int, - height: ffi::c_int, - dx: ffi::c_int, - dy: ffi::c_int, -); - -type WlEglWindowDestroyFun = unsafe extern "system" fn(window: *const ffi::c_void); - type EglLabel = *const ffi::c_void; #[allow(clippy::upper_case_acronyms)] @@ -101,97 +76,6 @@ unsafe extern "system" fn egl_debug_proc( log::log!(log_severity, "EGL '{command}' code 0x{error:x}: {message}",); } -/// A simple wrapper around an X11 or Wayland display handle. -/// Since the logic in this file doesn't actually need to directly -/// persist a wayland connection handle, the only load-bearing -/// enum variant is the X11 variant -#[derive(Debug)] -enum DisplayRef { - X11(ptr::NonNull), - Wayland, -} - -impl DisplayRef { - /// Convenience for getting the underlying pointer - fn as_ptr(&self) -> *mut ffi::c_void { - match *self { - Self::X11(ptr) => ptr.as_ptr(), - Self::Wayland => unreachable!(), - } - } -} - -/// DisplayOwner ties the lifetime of the system display handle -/// to that of the loaded library. -/// It implements Drop to ensure that the display handle is closed -/// prior to unloading the library so that we don't leak the -/// associated file descriptors -#[derive(Debug)] -struct DisplayOwner { - library: libloading::Library, - display: DisplayRef, -} - -impl Drop for DisplayOwner { - fn drop(&mut self) { - match self.display { - DisplayRef::X11(ptr) => unsafe { - let func: libloading::Symbol = - self.library.get(c"XCloseDisplay".to_bytes()).unwrap(); - func(ptr.as_ptr()); - }, - DisplayRef::Wayland => {} - } - } -} - -fn open_x_display() -> Option { - log::debug!("Loading X11 library to get the current display"); - unsafe { - let library = find_library(&["libX11.so.6", "libX11.so"])?; - let func: libloading::Symbol = - library.get(c"XOpenDisplay".to_bytes()).unwrap(); - let result = func(ptr::null()); - ptr::NonNull::new(result).map(|ptr| DisplayOwner { - display: DisplayRef::X11(ptr), - library, - }) - } -} - -unsafe fn find_library(paths: &[&str]) -> Option { - for path in paths { - match unsafe { libloading::Library::new(path) } { - Ok(lib) => return Some(lib), - _ => continue, - }; - } - None -} - -fn test_wayland_display() -> Option { - /* We try to connect and disconnect here to simply ensure there - * is an active wayland display available. - */ - log::debug!("Loading Wayland library to get the current display"); - let library = unsafe { - let client_library = find_library(&["libwayland-client.so.0", "libwayland-client.so"])?; - let wl_display_connect: libloading::Symbol = client_library - .get(c"wl_display_connect".to_bytes()) - .unwrap(); - let wl_display_disconnect: libloading::Symbol = client_library - .get(c"wl_display_disconnect".to_bytes()) - .unwrap(); - let display = ptr::NonNull::new(wl_display_connect(ptr::null()))?; - wl_display_disconnect(display.as_ptr()); - find_library(&["libwayland-egl.so.1", "libwayland-egl.so"])? - }; - Some(DisplayOwner { - library, - display: DisplayRef::Wayland, - }) -} - #[derive(Clone, Copy, Debug)] enum SrgbFrameBufferKind { /// No support for SRGB surface @@ -453,10 +337,11 @@ struct Inner { version: (i32, i32), supports_native_window: bool, config: khronos_egl::Config, - #[cfg_attr(Emscripten, allow(dead_code))] - wl_display: Option<*mut ffi::c_void>, - #[cfg_attr(Emscripten, allow(dead_code))] - force_gles_minor_version: wgt::Gles3MinorVersion, + // XXX: Store RawDisplayHandle for validation in create_surface()? + // #[cfg_attr(Emscripten, allow(dead_code))] + // wl_display: Option<*mut ffi::c_void>, + // #[cfg_attr(Emscripten, allow(dead_code))] + // force_gles_minor_version: wgt::Gles3MinorVersion, /// Method by which the framebuffer should support srgb srgb_kind: SrgbFrameBufferKind, } @@ -751,15 +636,19 @@ impl Inner { version, supports_native_window, config, - wl_display: None, + // wl_display: None, + // raw_display, srgb_kind, - force_gles_minor_version, + // force_gles_minor_version, }) } } impl Drop for Inner { fn drop(&mut self) { + // ERROR: Since EglContext is erroneously Clone, these handles could be copied and + // accidentally used elsewhere outside of Inner, despite us assuming ownership and + // destroying the handles here. if let Err(e) = self .egl .instance @@ -784,7 +673,6 @@ enum WindowKind { #[derive(Clone, Debug)] struct WindowSystemInterface { - display_owner: Option>, kind: WindowKind, } @@ -869,74 +757,82 @@ impl crate::Instance for Instance { client_ext_str.split_whitespace().collect::>() ); - let wayland_library = if client_ext_str.contains("EGL_EXT_platform_wayland") { - test_wayland_display() - } else { - None - }; - let x11_display_library = if client_ext_str.contains("EGL_EXT_platform_x11") { - open_x_display() - } else { - None - }; - let angle_x11_display_library = if client_ext_str.contains("EGL_ANGLE_platform_angle") { - open_x_display() - } else { - None - }; - #[cfg(not(Emscripten))] let egl1_5 = egl.upcast::(); #[cfg(Emscripten)] let egl1_5: Option<&Arc> = Some(&egl); - let (display, display_owner, wsi_kind) = - if let (Some(library), Some(egl)) = (wayland_library, egl1_5) { + // TODO: Copypaste glutin... Or use glutin... + let (display, wsi_kind) = match (desc.display.map(|d| d.as_raw()), egl1_5) { + #[cfg(unix)] + (Some(RawDisplayHandle::Wayland(wayland_display_handle)), Some(egl)) => { log::info!("Using Wayland platform"); let display_attributes = [khronos_egl::ATTRIB_NONE]; let display = unsafe { egl.get_platform_display( EGL_PLATFORM_WAYLAND_KHR, - khronos_egl::DEFAULT_DISPLAY, + wayland_display_handle.display.as_ptr(), &display_attributes, ) } .unwrap(); - (display, Some(Rc::new(library)), WindowKind::Wayland) - } else if let (Some(display_owner), Some(egl)) = (x11_display_library, egl1_5) { + (display, WindowKind::Wayland) + } + // RawDisplayHandle::UiKit(ui_kit_display_handle) => todo!(), + // RawDisplayHandle::AppKit(app_kit_display_handle) => todo!(), + // RawDisplayHandle::Orbital(orbital_display_handle) => todo!(), + // RawDisplayHandle::Ohos(ohos_display_handle) => todo!(), + #[cfg(unix)] + (Some(RawDisplayHandle::Xlib(xlib_display_handle)), Some(egl)) => { log::info!("Using X11 platform"); let display_attributes = [khronos_egl::ATTRIB_NONE]; let display = unsafe { egl.get_platform_display( EGL_PLATFORM_X11_KHR, - display_owner.display.as_ptr(), - &display_attributes, - ) - } - .unwrap(); - (display, Some(Rc::new(display_owner)), WindowKind::X11) - } else if let (Some(display_owner), Some(egl)) = (angle_x11_display_library, egl1_5) { - log::info!("Using Angle platform with X11"); - let display_attributes = [ - EGL_PLATFORM_ANGLE_NATIVE_PLATFORM_TYPE_ANGLE as khronos_egl::Attrib, - EGL_PLATFORM_X11_KHR as khronos_egl::Attrib, - EGL_PLATFORM_ANGLE_DEBUG_LAYERS_ENABLED as khronos_egl::Attrib, - usize::from(desc.flags.contains(wgt::InstanceFlags::VALIDATION)), - khronos_egl::ATTRIB_NONE, - ]; - let display = unsafe { - egl.get_platform_display( - EGL_PLATFORM_ANGLE_ANGLE, - display_owner.display.as_ptr(), + xlib_display_handle + .display + .map_or(khronos_egl::DEFAULT_DISPLAY, ptr::NonNull::as_ptr), &display_attributes, ) } .unwrap(); - (display, Some(Rc::new(display_owner)), WindowKind::AngleX11) - } else if client_ext_str.contains("EGL_MESA_platform_surfaceless") { - log::warn!("No windowing system present. Using surfaceless platform"); - #[allow(clippy::unnecessary_literal_unwrap)] // This is only a literal on Emscripten + (display, WindowKind::X11) + } + // AngleX11 => { + // log::info!("Using Angle platform with X11"); + // let display_attributes = [ + // EGL_PLATFORM_ANGLE_NATIVE_PLATFORM_TYPE_ANGLE as khronos_egl::Attrib, + // EGL_PLATFORM_X11_KHR as khronos_egl::Attrib, + // EGL_PLATFORM_ANGLE_DEBUG_LAYERS_ENABLED as khronos_egl::Attrib, + // usize::from(desc.flags.contains(wgt::InstanceFlags::VALIDATION)), + // khronos_egl::ATTRIB_NONE, + // ]; + // let display = unsafe { + // egl.get_platform_display( + // EGL_PLATFORM_ANGLE_ANGLE, + // display_owner.display.as_ptr(), + // &display_attributes, + // ) + // } + // .unwrap(); + // (display, WindowKind::AngleX11) + // } + #[cfg(unix)] + (Some(RawDisplayHandle::Xcb(_xcb_display_handle)), Some(_egl)) => todo!("xcb"), + // RawDisplayHandle::Drm(drm_display_handle) => todo!(), + // RawDisplayHandle::Gbm(gbm_display_handle) => todo!(), + // RawDisplayHandle::Windows(windows_display_handle) => todo!(), + // RawDisplayHandle::Web(web_display_handle) => todo!(), + // RawDisplayHandle::Android(android_display_handle) => todo!(), + // RawDisplayHandle::Haiku(haiku_display_handle) => todo!(), + x if client_ext_str.contains("EGL_MESA_platform_surfaceless") => { + log::warn!( + "No (or unknown) windowing system ({x:?}) present. Using surfaceless platform" + ); + #[allow(clippy::unnecessary_literal_unwrap)] + // This is only a literal on Emscripten + // XXX: This extension is also supported on EGL 1.4 with EGL_EXT_platform_base: https://registry.khronos.org/EGL/extensions/MESA/EGL_MESA_platform_surfaceless.txt let egl = egl1_5.expect("Failed to get EGL 1.5 for surfaceless"); let display = unsafe { egl.get_platform_display( @@ -947,12 +843,16 @@ impl crate::Instance for Instance { } .unwrap(); - (display, None, WindowKind::Unknown) - } else { - log::warn!("EGL_MESA_platform_surfaceless not available. Using default platform"); + (display, WindowKind::Unknown) + } + x => { + log::warn!( + "No (or unknown) windowing system {x:?} and EGL_MESA_platform_surfaceless not available. Using default platform" + ); let display = unsafe { egl.get_display(khronos_egl::DEFAULT_DISPLAY) }.unwrap(); - (display, None, WindowKind::Unknown) - }; + (display, WindowKind::Unknown) + } + }; if desc.flags.contains(wgt::InstanceFlags::VALIDATION) && client_ext_str.contains("EGL_KHR_debug") @@ -984,10 +884,7 @@ impl crate::Instance for Instance { )?; Ok(Instance { - wsi: WindowSystemInterface { - display_owner, - kind: wsi_kind, - }, + wsi: WindowSystemInterface { kind: wsi_kind }, flags: desc.flags, options: desc.backend_options.gl.clone(), inner: Mutex::new(inner), @@ -997,13 +894,12 @@ impl crate::Instance for Instance { #[cfg_attr(target_os = "macos", allow(unused, unused_mut, unreachable_code))] unsafe fn create_surface( &self, - display_handle: raw_window_handle::RawDisplayHandle, + display_handle: RawDisplayHandle, window_handle: raw_window_handle::RawWindowHandle, ) -> Result { use raw_window_handle::RawWindowHandle as Rwh; - #[cfg_attr(any(target_os = "android", Emscripten), allow(unused_mut))] - let mut inner = self.inner.lock(); + let inner = self.inner.lock(); match (window_handle, display_handle) { (Rwh::Xlib(_), _) => {} @@ -1023,6 +919,7 @@ impl crate::Instance for Instance { ) .unwrap(); + // TODO: Now, all we need is to get rid of this weird/unnecessary thing. let ret = unsafe { ndk_sys::ANativeWindow_setBuffersGeometry( handle @@ -1041,51 +938,7 @@ impl crate::Instance for Instance { ))); } } - #[cfg(not(Emscripten))] - (Rwh::Wayland(_), raw_window_handle::RawDisplayHandle::Wayland(display_handle)) => { - if inner - .wl_display - .map(|ptr| ptr != display_handle.display.as_ptr()) - .unwrap_or(true) - { - /* Wayland displays are not sharable between surfaces so if the - * surface we receive from this handle is from a different - * display, we must re-initialize the context. - * - * See gfx-rs/gfx#3545 - */ - log::warn!("Re-initializing Gles context due to Wayland window"); - - use core::ops::DerefMut; - let display_attributes = [khronos_egl::ATTRIB_NONE]; - - let display = unsafe { - inner - .egl - .instance - .upcast::() - .unwrap() - .get_platform_display( - EGL_PLATFORM_WAYLAND_KHR, - display_handle.display.as_ptr(), - &display_attributes, - ) - } - .unwrap(); - - let new_inner = Inner::create( - self.flags, - Arc::clone(&inner.egl.instance), - display, - inner.force_gles_minor_version, - )?; - - let old_inner = core::mem::replace(inner.deref_mut(), new_inner); - inner.wl_display = Some(display_handle.display.as_ptr()); - - drop(old_inner); - } - } + (Rwh::Wayland(_), _) => {} #[cfg(Emscripten)] (Rwh::Web(_), _) => {} other => { @@ -1153,6 +1006,7 @@ impl crate::Instance for Instance { super::Adapter::expose( AdapterContext { glow: Mutex::new(gl), + // ERROR: Copying owned reference handles here, be careful to not drop them! egl: Some(inner.egl.clone()), }, self.options.clone(), @@ -1362,13 +1216,16 @@ impl crate::Surface for Surface { handle.a_native_window.as_ptr() } (WindowKind::Unknown, Rwh::OhosNdk(handle)) => handle.native_window.as_ptr(), + #[cfg(unix)] (WindowKind::Wayland, Rwh::Wayland(handle)) => { - let library = &self.wsi.display_owner.as_ref().unwrap().library; - let wl_egl_window_create: libloading::Symbol = - unsafe { library.get(c"wl_egl_window_create".to_bytes()) }.unwrap(); - let window = - unsafe { wl_egl_window_create(handle.surface.as_ptr(), 640, 480) } - .cast(); + let window = wayland_sys::ffi_dispatch!( + wayland_sys::egl::wayland_egl_handle(), + wl_egl_window_create, + handle.surface.as_ptr().cast(), + config.extent.width as i32, + config.extent.height as i32, + ); + let window = window.cast(); // XXX: Make code type-safe wl_window = Some(window); window } @@ -1474,19 +1331,17 @@ impl crate::Surface for Surface { } }; + // XXX: Skip if the window was created with a sane size? if let Some(window) = wl_window { - let library = &self.wsi.display_owner.as_ref().unwrap().library; - let wl_egl_window_resize: libloading::Symbol = - unsafe { library.get(c"wl_egl_window_resize".to_bytes()) }.unwrap(); - unsafe { - wl_egl_window_resize( - window, - config.extent.width as i32, - config.extent.height as i32, - 0, - 0, - ) - }; + wayland_sys::ffi_dispatch!( + wayland_sys::egl::wayland_egl_handle(), + wl_egl_window_resize, + window.cast(), // XXX: Make type-safe + config.extent.width as i32, + config.extent.height as i32, + 0, + 0, + ); } let format_desc = device.shared.describe_texture_format(config.format); @@ -1542,15 +1397,11 @@ impl crate::Surface for Surface { .destroy_surface(self.egl.display, surface) .unwrap(); if let Some(window) = wl_window { - let library = &self - .wsi - .display_owner - .as_ref() - .expect("unsupported window") - .library; - let wl_egl_window_destroy: libloading::Symbol = - unsafe { library.get(c"wl_egl_window_destroy".to_bytes()) }.unwrap(); - unsafe { wl_egl_window_destroy(window) }; + wayland_sys::ffi_dispatch!( + wayland_sys::egl::wayland_egl_handle(), + wl_egl_window_destroy, + window.cast(), // XXX: Make type-safe + ); } } } diff --git a/wgpu-hal/src/lib.rs b/wgpu-hal/src/lib.rs index e6b4e0b0f89..966579364c9 100644 --- a/wgpu-hal/src/lib.rs +++ b/wgpu-hal/src/lib.rs @@ -280,6 +280,7 @@ mod dynamic; #[cfg(feature = "validation_canary")] mod validation_canary; +use raw_window_handle::DisplayHandle; #[cfg(feature = "validation_canary")] pub use validation_canary::{ValidationCanary, VALIDATION_CANARY}; @@ -1764,6 +1765,7 @@ pub struct InstanceDescriptor<'a> { pub flags: wgt::InstanceFlags, pub memory_budget_thresholds: wgt::MemoryBudgetThresholds, pub backend_options: wgt::BackendOptions, + pub display: Option>, } #[derive(Clone, Debug)] diff --git a/wgpu-hal/src/noop/mod.rs b/wgpu-hal/src/noop/mod.rs index 55965a7e2fb..02d7881d645 100644 --- a/wgpu-hal/src/noop/mod.rs +++ b/wgpu-hal/src/noop/mod.rs @@ -98,6 +98,7 @@ impl crate::Instance for Context { name: _, flags: _, memory_budget_thresholds: _, + display: _, } = *desc; if enable { Ok(Context) diff --git a/wgpu-types/Cargo.toml b/wgpu-types/Cargo.toml index 921f319592a..a2cd84cbbcc 100644 --- a/wgpu-types/Cargo.toml +++ b/wgpu-types/Cargo.toml @@ -57,6 +57,7 @@ serde = { workspace = true, default-features = false, features = [ "alloc", "derive", ], optional = true } +raw-window-handle.workspace = true [target.'cfg(target_arch = "wasm32")'.dependencies] js-sys = { workspace = true, optional = true, default-features = false } diff --git a/wgpu-types/src/instance.rs b/wgpu-types/src/instance.rs index 79e3f7b2526..bd86c60e51e 100644 --- a/wgpu-types/src/instance.rs +++ b/wgpu-types/src/instance.rs @@ -37,6 +37,12 @@ pub struct InstanceDescriptor { pub memory_budget_thresholds: MemoryBudgetThresholds, /// Options the control the behavior of specific backends. pub backend_options: BackendOptions, + /// System platform or compositor connection to connect this `Instance` to. + /// + /// - On GLES, this is required when intending to present on the platform. + /// - On Vulkan, this could be used to skip extensions. + // Cannot have the safe borrow variant here... + pub display: Option, } impl InstanceDescriptor { @@ -61,6 +67,7 @@ impl InstanceDescriptor { flags, memory_budget_thresholds: MemoryBudgetThresholds::default(), backend_options, + display: None, } } } From e08b3f62e355f678e56ebe97b3eb924a99f8a81e Mon Sep 17 00:00:00 2001 From: Alex Klemenchuk Date: Sun, 27 Jul 2025 16:52:31 -0700 Subject: [PATCH 2/2] DRAFT: Add textureBindingViewDimension compatibility mode to resolve GLES texture view incompatibility --- examples/features/src/cube/mod.rs | 17 ++++++-- examples/features/src/cube/shader.wgsl | 4 +- examples/features/src/lib.rs | 57 +++++++++++++------------- examples/features/src/main.rs | 4 ++ wgpu-core/src/device/resource.rs | 1 + wgpu-core/src/present.rs | 1 + wgpu-hal/src/gles/adapter.rs | 1 + wgpu-hal/src/gles/mod.rs | 29 +++++++------ wgpu-hal/src/lib.rs | 1 + wgpu-types/src/lib.rs | 3 ++ wgpu/src/api/surface.rs | 1 + 11 files changed, 73 insertions(+), 46 deletions(-) diff --git a/examples/features/src/cube/mod.rs b/examples/features/src/cube/mod.rs index f3e9f768035..cdddc068b8a 100644 --- a/examples/features/src/cube/mod.rs +++ b/examples/features/src/cube/mod.rs @@ -1,6 +1,6 @@ use bytemuck::{Pod, Zeroable}; use std::f32::consts; -use wgpu::util::DeviceExt; +use wgpu::{util::DeviceExt, TextureViewDimension}; #[repr(C)] #[derive(Clone, Copy, Pod, Zeroable)] @@ -149,7 +149,7 @@ impl crate::framework::Example for Example { ty: wgpu::BindingType::Texture { multisampled: false, sample_type: wgpu::TextureSampleType::Uint, - view_dimension: wgpu::TextureViewDimension::D2, + view_dimension: wgpu::TextureViewDimension::D2Array, }, count: None, }, @@ -177,9 +177,20 @@ impl crate::framework::Example for Example { dimension: wgpu::TextureDimension::D2, format: wgpu::TextureFormat::R8Uint, usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, + texture_binding_view_dimension: Some(TextureViewDimension::D2Array), view_formats: &[], }); - let texture_view = texture.create_view(&wgpu::TextureViewDescriptor::default()); + let texture_view = texture.create_view(&wgpu::TextureViewDescriptor { + label: None, + format: None, + dimension: Some(wgpu::TextureViewDimension::D2Array), + usage: None, + aspect: wgpu::TextureAspect::All, + base_mip_level: 0, + mip_level_count: None, + base_array_layer: 0, + array_layer_count: Some(1), + }); queue.write_texture( texture.as_image_copy(), &texels, diff --git a/examples/features/src/cube/shader.wgsl b/examples/features/src/cube/shader.wgsl index 8e9fa6c4950..e0d20e5df99 100644 --- a/examples/features/src/cube/shader.wgsl +++ b/examples/features/src/cube/shader.wgsl @@ -20,11 +20,11 @@ fn vs_main( @group(0) @binding(1) -var r_color: texture_2d; +var r_color: texture_2d_array; @fragment fn fs_main(vertex: VertexOutput) -> @location(0) vec4 { - let tex = textureLoad(r_color, vec2(vertex.tex_coord * 256.0), 0); + let tex = textureLoad(r_color, vec2(vertex.tex_coord * 256.0), 0, 0); let v = f32(tex.x) / 255.0; return vec4(1.0 - (v * 5.0), 1.0 - (v * 15.0), 1.0 - (v * 50.0), 1.0); } diff --git a/examples/features/src/lib.rs b/examples/features/src/lib.rs index 271241f1ae0..1f23fa2b671 100644 --- a/examples/features/src/lib.rs +++ b/examples/features/src/lib.rs @@ -4,36 +4,35 @@ pub mod framework; pub mod utils; -pub mod big_compute_buffers; -pub mod boids; -pub mod bunnymark; -pub mod conservative_raster; +//pub mod big_compute_buffers; +//pub mod boids; +//pub mod bunnymark; +//pub mod conservative_raster; pub mod cube; -pub mod hello_synchronization; -pub mod hello_triangle; -pub mod hello_windows; -pub mod hello_workgroups; -pub mod mesh_shader; -pub mod mipmap; -pub mod msaa_line; -pub mod multiple_render_targets; -pub mod ray_cube_compute; -pub mod ray_cube_fragment; -pub mod ray_cube_normals; -pub mod ray_scene; -pub mod ray_shadows; -pub mod ray_traced_triangle; -pub mod render_to_texture; -pub mod repeated_compute; -pub mod shadow; -pub mod skybox; -pub mod srgb_blend; -pub mod stencil_triangles; -pub mod storage_texture; -pub mod texture_arrays; -pub mod timestamp_queries; -pub mod uniform_values; -pub mod water; +//pub mod hello_synchronization; +//pub mod hello_triangle; +//pub mod hello_windows; +//pub mod hello_workgroups; +//pub mod mipmap; +//pub mod msaa_line; +//pub mod multiple_render_targets; +//pub mod ray_cube_compute; +//pub mod ray_cube_fragment; +//pub mod ray_cube_normals; +//pub mod ray_scene; +//pub mod ray_shadows; +//pub mod ray_traced_triangle; +//pub mod render_to_texture; +//pub mod repeated_compute; +//pub mod shadow; +//pub mod skybox; +//pub mod srgb_blend; +//pub mod stencil_triangles; +//pub mod storage_texture; +//pub mod texture_arrays; +//pub mod timestamp_queries; +//pub mod uniform_values; +//pub mod water; #[cfg(test)] wgpu_test::gpu_test_main!(); diff --git a/examples/features/src/main.rs b/examples/features/src/main.rs index c9fc31259c9..e9452df9fa5 100644 --- a/examples/features/src/main.rs +++ b/examples/features/src/main.rs @@ -8,6 +8,7 @@ struct ExampleDesc { } const EXAMPLES: &[ExampleDesc] = &[ + /* ExampleDesc { name: "big_compute_buffers", function: wgpu_examples::big_compute_buffers::main, @@ -32,12 +33,14 @@ const EXAMPLES: &[ExampleDesc] = &[ webgl: false, // No conservative raster webgpu: false, // No conservative raster }, + */ ExampleDesc { name: "cube", function: wgpu_examples::cube::main, webgl: true, webgpu: true, }, + /* ExampleDesc { name: "hello_synchronization", function: wgpu_examples::hello_synchronization::main, @@ -188,6 +191,7 @@ const EXAMPLES: &[ExampleDesc] = &[ webgl: false, webgpu: false, }, + */ ]; fn get_example_name() -> Option { diff --git a/wgpu-core/src/device/resource.rs b/wgpu-core/src/device/resource.rs index ed03fce774b..a4731447d79 100644 --- a/wgpu-core/src/device/resource.rs +++ b/wgpu-core/src/device/resource.rs @@ -1263,6 +1263,7 @@ impl Device { sample_count: desc.sample_count, dimension: desc.dimension, format: desc.format, + texture_binding_view_dimension: desc.texture_binding_view_dimension, usage: hal_usage, memory_flags: hal::MemoryFlags::empty(), view_formats: hal_view_formats, diff --git a/wgpu-core/src/present.rs b/wgpu-core/src/present.rs index 4ad9a4c0f0b..a3ec02d56b3 100644 --- a/wgpu-core/src/present.rs +++ b/wgpu-core/src/present.rs @@ -190,6 +190,7 @@ impl Surface { mip_level_count: 1, format: config.format, dimension: wgt::TextureDimension::D2, + texture_binding_view_dimension: None, usage: config.usage, view_formats: config.view_formats, }; diff --git a/wgpu-hal/src/gles/adapter.rs b/wgpu-hal/src/gles/adapter.rs index 865f35013c7..80588f65792 100644 --- a/wgpu-hal/src/gles/adapter.rs +++ b/wgpu-hal/src/gles/adapter.rs @@ -410,6 +410,7 @@ impl super::Adapter { unsafe { gl.get_parameter_i32(glow::MAX_TEXTURE_MAX_ANISOTROPY_EXT) } as u32; downlevel_flags.set(wgt::DownlevelFlags::ANISOTROPIC_FILTERING, max_aniso >= 16); } + downlevel_flags.set( wgt::DownlevelFlags::BUFFER_BINDINGS_NOT_16_BYTE_ALIGNED, !(cfg!(any(webgl, Emscripten)) || is_angle), diff --git a/wgpu-hal/src/gles/mod.rs b/wgpu-hal/src/gles/mod.rs index 94416086d2e..05427120c1d 100644 --- a/wgpu-hal/src/gles/mod.rs +++ b/wgpu-hal/src/gles/mod.rs @@ -449,20 +449,24 @@ impl Texture { /// Returns the `target`, whether the image is 3d and whether the image is a cubemap. fn get_info_from_desc(desc: &TextureDescriptor) -> u32 { - match desc.dimension { - // WebGL (1 and 2) as well as some GLES versions do not have 1D textures, so we are - // doing `TEXTURE_2D` instead - wgt::TextureDimension::D1 => glow::TEXTURE_2D, - wgt::TextureDimension::D2 => { - // HACK: detect a cube map; forces cube compatible textures to be cube textures - match (desc.is_cube_compatible(), desc.size.depth_or_array_layers) { - (false, 1) => glow::TEXTURE_2D, - (false, _) => glow::TEXTURE_2D_ARRAY, - (true, 6) => glow::TEXTURE_CUBE_MAP, - (true, _) => glow::TEXTURE_CUBE_MAP_ARRAY, + if let Some(dim) = desc.texture_binding_view_dimension { + conv::_map_view_dimension(dim) + } else { + match desc.dimension { + // WebGL (1 and 2) as well as some GLES versions do not have 1D textures, so we are + // doing `TEXTURE_2D` instead + wgt::TextureDimension::D1 => glow::TEXTURE_2D, + wgt::TextureDimension::D2 => { + // HACK: detect a cube map; forces cube compatible textures to be cube textures + match (desc.is_cube_compatible(), desc.size.depth_or_array_layers) { + (false, 1) => glow::TEXTURE_2D, + (false, _) => glow::TEXTURE_2D_ARRAY, + (true, 6) => glow::TEXTURE_CUBE_MAP, + (true, _) => glow::TEXTURE_CUBE_MAP_ARRAY, + } } + wgt::TextureDimension::D3 => glow::TEXTURE_3D, } - wgt::TextureDimension::D3 => glow::TEXTURE_3D, } } @@ -510,6 +514,7 @@ impl Texture { "`D2` textures with ", "`depth_or_array_layers > 6 && depth_or_array_layers % 6 == 0` ", "are assumed to have view dimension `CubeArray`\n", + "Pass texture_binding_view_dimension to TextureDescriptor to avoid.", ), got, view_dimension, diff --git a/wgpu-hal/src/lib.rs b/wgpu-hal/src/lib.rs index 966579364c9..078089310be 100644 --- a/wgpu-hal/src/lib.rs +++ b/wgpu-hal/src/lib.rs @@ -1900,6 +1900,7 @@ pub struct TextureDescriptor<'a> { pub format: wgt::TextureFormat, pub usage: wgt::TextureUses, pub memory_flags: MemoryFlags, + pub texture_binding_view_dimension: Option, /// Allows views of this texture to have a different format /// than the texture does. pub view_formats: Vec, diff --git a/wgpu-types/src/lib.rs b/wgpu-types/src/lib.rs index 6252e5c6778..4ad8618a381 100644 --- a/wgpu-types/src/lib.rs +++ b/wgpu-types/src/lib.rs @@ -6152,6 +6152,7 @@ pub struct TextureDescriptor { pub sample_count: u32, /// Dimensions of the texture. pub dimension: TextureDimension, + pub texture_binding_view_dimension: Option, /// Format of the texture. pub format: TextureFormat, /// Allowed usages of the texture. If used in other ways, the operation will panic. @@ -6180,6 +6181,7 @@ impl TextureDescriptor { format: self.format, usage: self.usage, view_formats: self.view_formats.clone(), + texture_binding_view_dimension: self.texture_binding_view_dimension, } } @@ -6201,6 +6203,7 @@ impl TextureDescriptor { dimension: self.dimension, format: self.format, usage: self.usage, + texture_binding_view_dimension: self.texture_binding_view_dimension, view_formats: v_fun(self.view_formats.clone()), } } diff --git a/wgpu/src/api/surface.rs b/wgpu/src/api/surface.rs index 113f46639e6..d5a9df0696e 100644 --- a/wgpu/src/api/surface.rs +++ b/wgpu/src/api/surface.rs @@ -134,6 +134,7 @@ impl Surface<'_> { mip_level_count: 1, sample_count: 1, dimension: TextureDimension::D2, + texture_binding_view_dimension: None, view_formats: &[], };