summaryrefslogtreecommitdiffstats
path: root/third_party/rust/wgpu-hal/src/gles/egl.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/rust/wgpu-hal/src/gles/egl.rs
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/wgpu-hal/src/gles/egl.rs')
-rw-r--r--third_party/rust/wgpu-hal/src/gles/egl.rs1310
1 files changed, 1310 insertions, 0 deletions
diff --git a/third_party/rust/wgpu-hal/src/gles/egl.rs b/third_party/rust/wgpu-hal/src/gles/egl.rs
new file mode 100644
index 0000000000..13e217f9d9
--- /dev/null
+++ b/third_party/rust/wgpu-hal/src/gles/egl.rs
@@ -0,0 +1,1310 @@
+use glow::HasContext;
+use parking_lot::{Mutex, MutexGuard};
+
+use std::{ffi, os::raw, ptr, sync::Arc, time::Duration};
+
+/// The amount of time to wait while trying to obtain a lock to the adapter context
+const CONTEXT_LOCK_TIMEOUT_SECS: u64 = 1;
+
+const EGL_CONTEXT_FLAGS_KHR: i32 = 0x30FC;
+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_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 raw::c_char) -> *mut raw::c_void;
+
+type WlDisplayConnectFun =
+ unsafe extern "system" fn(display_name: *const raw::c_char) -> *mut raw::c_void;
+
+type WlDisplayDisconnectFun = unsafe extern "system" fn(display: *const raw::c_void);
+
+#[cfg(not(target_os = "emscripten"))]
+type EglInstance = khronos_egl::DynamicInstance<khronos_egl::EGL1_4>;
+
+#[cfg(target_os = "emscripten")]
+type EglInstance = khronos_egl::Instance<khronos_egl::Static>;
+
+type WlEglWindowCreateFun = unsafe extern "system" fn(
+ surface: *const raw::c_void,
+ width: raw::c_int,
+ height: raw::c_int,
+) -> *mut raw::c_void;
+
+type WlEglWindowResizeFun = unsafe extern "system" fn(
+ window: *const raw::c_void,
+ width: raw::c_int,
+ height: raw::c_int,
+ dx: raw::c_int,
+ dy: raw::c_int,
+);
+
+type WlEglWindowDestroyFun = unsafe extern "system" fn(window: *const raw::c_void);
+
+#[cfg(target_os = "android")]
+extern "C" {
+ pub fn ANativeWindow_setBuffersGeometry(
+ window: *mut raw::c_void,
+ width: i32,
+ height: i32,
+ format: i32,
+ ) -> i32;
+}
+
+type EglLabel = *const raw::c_void;
+
+#[allow(clippy::upper_case_acronyms)]
+type EGLDEBUGPROCKHR = Option<
+ unsafe extern "system" fn(
+ error: khronos_egl::Enum,
+ command: *const raw::c_char,
+ message_type: u32,
+ thread_label: EglLabel,
+ object_label: EglLabel,
+ message: *const raw::c_char,
+ ),
+>;
+
+const EGL_DEBUG_MSG_CRITICAL_KHR: u32 = 0x33B9;
+const EGL_DEBUG_MSG_ERROR_KHR: u32 = 0x33BA;
+const EGL_DEBUG_MSG_WARN_KHR: u32 = 0x33BB;
+const EGL_DEBUG_MSG_INFO_KHR: u32 = 0x33BC;
+
+type EglDebugMessageControlFun = unsafe extern "system" fn(
+ proc: EGLDEBUGPROCKHR,
+ attrib_list: *const khronos_egl::Attrib,
+) -> raw::c_int;
+
+unsafe extern "system" fn egl_debug_proc(
+ error: khronos_egl::Enum,
+ command_raw: *const raw::c_char,
+ message_type: u32,
+ _thread_label: EglLabel,
+ _object_label: EglLabel,
+ message_raw: *const raw::c_char,
+) {
+ let log_severity = match message_type {
+ EGL_DEBUG_MSG_CRITICAL_KHR | EGL_DEBUG_MSG_ERROR_KHR => log::Level::Error,
+ EGL_DEBUG_MSG_WARN_KHR => log::Level::Warn,
+ EGL_DEBUG_MSG_INFO_KHR => log::Level::Info,
+ _ => log::Level::Debug,
+ };
+ let command = unsafe { ffi::CStr::from_ptr(command_raw) }.to_string_lossy();
+ let message = if message_raw.is_null() {
+ "".into()
+ } else {
+ unsafe { ffi::CStr::from_ptr(message_raw) }.to_string_lossy()
+ };
+
+ log::log!(
+ log_severity,
+ "EGL '{}' code 0x{:x}: {}",
+ command,
+ error,
+ message,
+ );
+}
+
+fn open_x_display() -> Option<(ptr::NonNull<raw::c_void>, libloading::Library)> {
+ log::info!("Loading X11 library to get the current display");
+ unsafe {
+ let library = libloading::Library::new("libX11.so").ok()?;
+ let func: libloading::Symbol<XOpenDisplayFun> = library.get(b"XOpenDisplay").unwrap();
+ let result = func(ptr::null());
+ ptr::NonNull::new(result).map(|ptr| (ptr, library))
+ }
+}
+
+unsafe fn find_library(paths: &[&str]) -> Option<libloading::Library> {
+ for path in paths {
+ match unsafe { libloading::Library::new(path) } {
+ Ok(lib) => return Some(lib),
+ _ => continue,
+ };
+ }
+ None
+}
+
+fn test_wayland_display() -> Option<libloading::Library> {
+ /* We try to connect and disconnect here to simply ensure there
+ * is an active wayland display available.
+ */
+ log::info!("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<WlDisplayConnectFun> =
+ client_library.get(b"wl_display_connect").unwrap();
+ let wl_display_disconnect: libloading::Symbol<WlDisplayDisconnectFun> =
+ client_library.get(b"wl_display_disconnect").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(library)
+}
+
+#[derive(Clone, Copy, Debug)]
+enum SrgbFrameBufferKind {
+ /// No support for SRGB surface
+ None,
+ /// Using EGL 1.5's support for colorspaces
+ Core,
+ /// Using EGL_KHR_gl_colorspace
+ Khr,
+}
+
+/// Choose GLES framebuffer configuration.
+fn choose_config(
+ egl: &EglInstance,
+ display: khronos_egl::Display,
+ srgb_kind: SrgbFrameBufferKind,
+) -> Result<(khronos_egl::Config, bool), crate::InstanceError> {
+ //TODO: EGL_SLOW_CONFIG
+ let tiers = [
+ (
+ "off-screen",
+ &[
+ khronos_egl::SURFACE_TYPE,
+ khronos_egl::PBUFFER_BIT,
+ khronos_egl::RENDERABLE_TYPE,
+ khronos_egl::OPENGL_ES2_BIT,
+ ][..],
+ ),
+ (
+ "presentation",
+ &[khronos_egl::SURFACE_TYPE, khronos_egl::WINDOW_BIT][..],
+ ),
+ #[cfg(not(target_os = "android"))]
+ (
+ "native-render",
+ &[khronos_egl::NATIVE_RENDERABLE, khronos_egl::TRUE as _][..],
+ ),
+ ];
+
+ let mut attributes = Vec::with_capacity(9);
+ for tier_max in (0..tiers.len()).rev() {
+ let name = tiers[tier_max].0;
+ log::info!("\tTrying {}", name);
+
+ attributes.clear();
+ for &(_, tier_attr) in tiers[..=tier_max].iter() {
+ attributes.extend_from_slice(tier_attr);
+ }
+ // make sure the Alpha is enough to support sRGB
+ match srgb_kind {
+ SrgbFrameBufferKind::None => {}
+ _ => {
+ attributes.push(khronos_egl::ALPHA_SIZE);
+ attributes.push(8);
+ }
+ }
+ attributes.push(khronos_egl::NONE);
+
+ match egl.choose_first_config(display, &attributes) {
+ Ok(Some(config)) => {
+ if tier_max == 1 {
+ //Note: this has been confirmed to malfunction on Intel+NV laptops,
+ // but also on Angle.
+ log::warn!("EGL says it can present to the window but not natively",);
+ }
+ // Android emulator can't natively present either.
+ let tier_threshold = if cfg!(target_os = "android") || cfg!(windows) {
+ 1
+ } else {
+ 2
+ };
+ return Ok((config, tier_max >= tier_threshold));
+ }
+ Ok(None) => {
+ log::warn!("No config found!");
+ }
+ Err(e) => {
+ log::error!("error in choose_first_config: {:?}", e);
+ }
+ }
+ }
+
+ Err(crate::InstanceError)
+}
+
+fn gl_debug_message_callback(source: u32, gltype: u32, id: u32, severity: u32, message: &str) {
+ let source_str = match source {
+ glow::DEBUG_SOURCE_API => "API",
+ glow::DEBUG_SOURCE_WINDOW_SYSTEM => "Window System",
+ glow::DEBUG_SOURCE_SHADER_COMPILER => "ShaderCompiler",
+ glow::DEBUG_SOURCE_THIRD_PARTY => "Third Party",
+ glow::DEBUG_SOURCE_APPLICATION => "Application",
+ glow::DEBUG_SOURCE_OTHER => "Other",
+ _ => unreachable!(),
+ };
+
+ let log_severity = match severity {
+ glow::DEBUG_SEVERITY_HIGH => log::Level::Error,
+ glow::DEBUG_SEVERITY_MEDIUM => log::Level::Warn,
+ glow::DEBUG_SEVERITY_LOW => log::Level::Info,
+ glow::DEBUG_SEVERITY_NOTIFICATION => log::Level::Trace,
+ _ => unreachable!(),
+ };
+
+ let type_str = match gltype {
+ glow::DEBUG_TYPE_DEPRECATED_BEHAVIOR => "Deprecated Behavior",
+ glow::DEBUG_TYPE_ERROR => "Error",
+ glow::DEBUG_TYPE_MARKER => "Marker",
+ glow::DEBUG_TYPE_OTHER => "Other",
+ glow::DEBUG_TYPE_PERFORMANCE => "Performance",
+ glow::DEBUG_TYPE_POP_GROUP => "Pop Group",
+ glow::DEBUG_TYPE_PORTABILITY => "Portability",
+ glow::DEBUG_TYPE_PUSH_GROUP => "Push Group",
+ glow::DEBUG_TYPE_UNDEFINED_BEHAVIOR => "Undefined Behavior",
+ _ => unreachable!(),
+ };
+
+ let _ = std::panic::catch_unwind(|| {
+ log::log!(
+ log_severity,
+ "GLES: [{}/{}] ID {} : {}",
+ source_str,
+ type_str,
+ id,
+ message
+ );
+ });
+
+ if cfg!(debug_assertions) && log_severity == log::Level::Error {
+ // Set canary and continue
+ crate::VALIDATION_CANARY.set();
+ }
+}
+
+#[derive(Clone, Debug)]
+struct EglContext {
+ instance: Arc<EglInstance>,
+ version: (i32, i32),
+ display: khronos_egl::Display,
+ raw: khronos_egl::Context,
+ pbuffer: Option<khronos_egl::Surface>,
+}
+
+impl EglContext {
+ fn make_current(&self) {
+ self.instance
+ .make_current(self.display, self.pbuffer, self.pbuffer, Some(self.raw))
+ .unwrap();
+ }
+ fn unmake_current(&self) {
+ self.instance
+ .make_current(self.display, None, None, None)
+ .unwrap();
+ }
+}
+
+/// A wrapper around a [`glow::Context`] and the required EGL context that uses locking to guarantee
+/// exclusive access when shared with multiple threads.
+pub struct AdapterContext {
+ glow: Mutex<glow::Context>,
+ egl: Option<EglContext>,
+}
+
+unsafe impl Sync for AdapterContext {}
+unsafe impl Send for AdapterContext {}
+
+impl AdapterContext {
+ pub fn is_owned(&self) -> bool {
+ self.egl.is_some()
+ }
+
+ /// Returns the EGL instance.
+ ///
+ /// This provides access to EGL functions and the ability to load GL and EGL extension functions.
+ pub fn egl_instance(&self) -> Option<&EglInstance> {
+ self.egl.as_ref().map(|egl| &*egl.instance)
+ }
+
+ /// Returns the EGLDisplay corresponding to the adapter context.
+ ///
+ /// Returns [`None`] if the adapter was externally created.
+ pub fn raw_display(&self) -> Option<&khronos_egl::Display> {
+ self.egl.as_ref().map(|egl| &egl.display)
+ }
+
+ /// Returns the EGL version the adapter context was created with.
+ ///
+ /// Returns [`None`] if the adapter was externally created.
+ pub fn egl_version(&self) -> Option<(i32, i32)> {
+ self.egl.as_ref().map(|egl| egl.version)
+ }
+
+ pub fn raw_context(&self) -> *mut raw::c_void {
+ match self.egl {
+ Some(ref egl) => egl.raw.as_ptr(),
+ None => ptr::null_mut(),
+ }
+ }
+}
+
+struct EglContextLock<'a> {
+ instance: &'a Arc<EglInstance>,
+ display: khronos_egl::Display,
+}
+
+/// A guard containing a lock to an [`AdapterContext`]
+pub struct AdapterContextLock<'a> {
+ glow: MutexGuard<'a, glow::Context>,
+ egl: Option<EglContextLock<'a>>,
+}
+
+impl<'a> std::ops::Deref for AdapterContextLock<'a> {
+ type Target = glow::Context;
+
+ fn deref(&self) -> &Self::Target {
+ &self.glow
+ }
+}
+
+impl<'a> Drop for AdapterContextLock<'a> {
+ fn drop(&mut self) {
+ if let Some(egl) = self.egl.take() {
+ egl.instance
+ .make_current(egl.display, None, None, None)
+ .unwrap();
+ }
+ }
+}
+
+impl AdapterContext {
+ /// Get's the [`glow::Context`] without waiting for a lock
+ ///
+ /// # Safety
+ ///
+ /// This should only be called when you have manually made sure that the current thread has made
+ /// the EGL context current and that no other thread also has the EGL context current.
+ /// Additionally, you must manually make the EGL context **not** current after you are done with
+ /// it, so that future calls to `lock()` will not fail.
+ ///
+ /// > **Note:** Calling this function **will** still lock the [`glow::Context`] which adds an
+ /// > extra safe-guard against accidental concurrent access to the context.
+ pub unsafe fn get_without_egl_lock(&self) -> MutexGuard<glow::Context> {
+ self.glow
+ .try_lock_for(Duration::from_secs(CONTEXT_LOCK_TIMEOUT_SECS))
+ .expect("Could not lock adapter context. This is most-likely a deadlock.")
+ }
+
+ /// Obtain a lock to the EGL context and get handle to the [`glow::Context`] that can be used to
+ /// do rendering.
+ #[track_caller]
+ pub fn lock<'a>(&'a self) -> AdapterContextLock<'a> {
+ let glow = self
+ .glow
+ // Don't lock forever. If it takes longer than 1 second to get the lock we've got a
+ // deadlock and should panic to show where we got stuck
+ .try_lock_for(Duration::from_secs(CONTEXT_LOCK_TIMEOUT_SECS))
+ .expect("Could not lock adapter context. This is most-likely a deadlock.");
+
+ let egl = self.egl.as_ref().map(|egl| {
+ egl.make_current();
+ EglContextLock {
+ instance: &egl.instance,
+ display: egl.display,
+ }
+ });
+
+ AdapterContextLock { glow, egl }
+ }
+}
+
+#[derive(Debug)]
+struct Inner {
+ /// Note: the context contains a dummy pbuffer (1x1).
+ /// Required for `eglMakeCurrent` on platforms that doesn't supports `EGL_KHR_surfaceless_context`.
+ egl: EglContext,
+ #[allow(unused)]
+ version: (i32, i32),
+ supports_native_window: bool,
+ config: khronos_egl::Config,
+ #[cfg_attr(target_os = "emscripten", allow(dead_code))]
+ wl_display: Option<*mut raw::c_void>,
+ /// Method by which the framebuffer should support srgb
+ srgb_kind: SrgbFrameBufferKind,
+}
+
+impl Inner {
+ fn create(
+ flags: crate::InstanceFlags,
+ egl: Arc<EglInstance>,
+ display: khronos_egl::Display,
+ ) -> Result<Self, crate::InstanceError> {
+ let version = egl.initialize(display).map_err(|_| crate::InstanceError)?;
+ let vendor = egl
+ .query_string(Some(display), khronos_egl::VENDOR)
+ .unwrap();
+ let display_extensions = egl
+ .query_string(Some(display), khronos_egl::EXTENSIONS)
+ .unwrap()
+ .to_string_lossy();
+ log::info!("Display vendor {:?}, version {:?}", vendor, version,);
+ log::debug!(
+ "Display extensions: {:#?}",
+ display_extensions.split_whitespace().collect::<Vec<_>>()
+ );
+
+ let srgb_kind = if version >= (1, 5) {
+ log::info!("\tEGL surface: +srgb");
+ SrgbFrameBufferKind::Core
+ } else if display_extensions.contains("EGL_KHR_gl_colorspace") {
+ log::info!("\tEGL surface: +srgb khr");
+ SrgbFrameBufferKind::Khr
+ } else {
+ log::warn!("\tEGL surface: -srgb");
+ SrgbFrameBufferKind::None
+ };
+
+ if log::max_level() >= log::LevelFilter::Trace {
+ log::trace!("Configurations:");
+ let config_count = egl.get_config_count(display).unwrap();
+ let mut configurations = Vec::with_capacity(config_count);
+ egl.get_configs(display, &mut configurations).unwrap();
+ for &config in configurations.iter() {
+ log::trace!("\tCONFORMANT=0x{:X}, RENDERABLE=0x{:X}, NATIVE_RENDERABLE=0x{:X}, SURFACE_TYPE=0x{:X}, ALPHA_SIZE={}",
+ egl.get_config_attrib(display, config, khronos_egl::CONFORMANT).unwrap(),
+ egl.get_config_attrib(display, config, khronos_egl::RENDERABLE_TYPE).unwrap(),
+ egl.get_config_attrib(display, config, khronos_egl::NATIVE_RENDERABLE).unwrap(),
+ egl.get_config_attrib(display, config, khronos_egl::SURFACE_TYPE).unwrap(),
+ egl.get_config_attrib(display, config, khronos_egl::ALPHA_SIZE).unwrap(),
+ );
+ }
+ }
+
+ let (config, supports_native_window) = choose_config(&egl, display, srgb_kind)?;
+ egl.bind_api(khronos_egl::OPENGL_ES_API).unwrap();
+
+ let needs_robustness = true;
+ let mut khr_context_flags = 0;
+ let supports_khr_context = display_extensions.contains("EGL_KHR_create_context");
+
+ //TODO: make it so `Device` == EGL Context
+ let mut context_attributes = vec![
+ khronos_egl::CONTEXT_CLIENT_VERSION,
+ 3, // Request GLES 3.0 or higher
+ ];
+ if flags.contains(crate::InstanceFlags::DEBUG) {
+ if version >= (1, 5) {
+ log::info!("\tEGL context: +debug");
+ context_attributes.push(khronos_egl::CONTEXT_OPENGL_DEBUG);
+ context_attributes.push(khronos_egl::TRUE as _);
+ } else if supports_khr_context {
+ log::info!("\tEGL context: +debug KHR");
+ khr_context_flags |= EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR;
+ } else {
+ log::info!("\tEGL context: -debug");
+ }
+ }
+ if needs_robustness {
+ //Note: the core version can fail if robustness is not supported
+ // (regardless of whether the extension is supported!).
+ // In fact, Angle does precisely that awful behavior, so we don't try it there.
+ if version >= (1, 5) && !display_extensions.contains("EGL_ANGLE_") {
+ log::info!("\tEGL context: +robust access");
+ context_attributes.push(khronos_egl::CONTEXT_OPENGL_ROBUST_ACCESS);
+ context_attributes.push(khronos_egl::TRUE as _);
+ } else if display_extensions.contains("EGL_EXT_create_context_robustness") {
+ log::info!("\tEGL context: +robust access EXT");
+ context_attributes.push(EGL_CONTEXT_OPENGL_ROBUST_ACCESS_EXT);
+ context_attributes.push(khronos_egl::TRUE as _);
+ } else {
+ //Note: we aren't trying `EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR`
+ // because it's for desktop GL only, not GLES.
+ log::warn!("\tEGL context: -robust access");
+ }
+
+ //TODO do we need `khronos_egl::CONTEXT_OPENGL_NOTIFICATION_STRATEGY_EXT`?
+ }
+ if khr_context_flags != 0 {
+ context_attributes.push(EGL_CONTEXT_FLAGS_KHR);
+ context_attributes.push(khr_context_flags);
+ }
+ context_attributes.push(khronos_egl::NONE);
+ let context = match egl.create_context(display, config, None, &context_attributes) {
+ Ok(context) => context,
+ Err(e) => {
+ log::warn!("unable to create GLES 3.x context: {:?}", e);
+ return Err(crate::InstanceError);
+ }
+ };
+
+ // Testing if context can be binded without surface
+ // and creating dummy pbuffer surface if not.
+ let pbuffer = if version >= (1, 5)
+ || display_extensions.contains("EGL_KHR_surfaceless_context")
+ || cfg!(target_os = "emscripten")
+ {
+ log::info!("\tEGL context: +surfaceless");
+ None
+ } else {
+ let attributes = [
+ khronos_egl::WIDTH,
+ 1,
+ khronos_egl::HEIGHT,
+ 1,
+ khronos_egl::NONE,
+ ];
+ egl.create_pbuffer_surface(display, config, &attributes)
+ .map(Some)
+ .map_err(|e| {
+ log::warn!("Error in create_pbuffer_surface: {:?}", e);
+ crate::InstanceError
+ })?
+ };
+
+ Ok(Self {
+ egl: EglContext {
+ instance: egl,
+ display,
+ raw: context,
+ pbuffer,
+ version,
+ },
+ version,
+ supports_native_window,
+ config,
+ wl_display: None,
+ srgb_kind,
+ })
+ }
+}
+
+impl Drop for Inner {
+ fn drop(&mut self) {
+ if let Err(e) = self
+ .egl
+ .instance
+ .destroy_context(self.egl.display, self.egl.raw)
+ {
+ log::warn!("Error in destroy_context: {:?}", e);
+ }
+ if let Err(e) = self.egl.instance.terminate(self.egl.display) {
+ log::warn!("Error in terminate: {:?}", e);
+ }
+ }
+}
+
+#[derive(Clone, Copy, Debug, PartialEq)]
+enum WindowKind {
+ Wayland,
+ X11,
+ AngleX11,
+ Unknown,
+}
+
+#[derive(Clone, Debug)]
+struct WindowSystemInterface {
+ library: Option<Arc<libloading::Library>>,
+ kind: WindowKind,
+}
+
+pub struct Instance {
+ wsi: WindowSystemInterface,
+ flags: crate::InstanceFlags,
+ inner: Mutex<Inner>,
+}
+
+impl Instance {
+ pub fn raw_display(&self) -> khronos_egl::Display {
+ self.inner
+ .try_lock()
+ .expect("Could not lock instance. This is most-likely a deadlock.")
+ .egl
+ .display
+ }
+
+ /// Returns the version of the EGL display.
+ pub fn egl_version(&self) -> (i32, i32) {
+ self.inner
+ .try_lock()
+ .expect("Could not lock instance. This is most-likely a deadlock.")
+ .version
+ }
+}
+
+unsafe impl Send for Instance {}
+unsafe impl Sync for Instance {}
+
+impl crate::Instance<super::Api> for Instance {
+ unsafe fn init(desc: &crate::InstanceDescriptor) -> Result<Self, crate::InstanceError> {
+ #[cfg(target_os = "emscripten")]
+ let egl_result: Result<EglInstance, khronos_egl::Error> =
+ Ok(khronos_egl::Instance::new(khronos_egl::Static));
+
+ #[cfg(not(target_os = "emscripten"))]
+ let egl_result = if cfg!(windows) {
+ unsafe {
+ khronos_egl::DynamicInstance::<khronos_egl::EGL1_4>::load_required_from_filename(
+ "libEGL.dll",
+ )
+ }
+ } else if cfg!(any(target_os = "macos", target_os = "ios")) {
+ unsafe {
+ khronos_egl::DynamicInstance::<khronos_egl::EGL1_4>::load_required_from_filename(
+ "libEGL.dylib",
+ )
+ }
+ } else {
+ unsafe { khronos_egl::DynamicInstance::<khronos_egl::EGL1_4>::load_required() }
+ };
+ let egl = match egl_result {
+ Ok(egl) => Arc::new(egl),
+ Err(e) => {
+ log::info!("Unable to open libEGL: {:?}", e);
+ return Err(crate::InstanceError);
+ }
+ };
+
+ let client_extensions = egl.query_string(None, khronos_egl::EXTENSIONS);
+
+ let client_ext_str = match client_extensions {
+ Ok(ext) => ext.to_string_lossy().into_owned(),
+ Err(_) => String::new(),
+ };
+ log::debug!(
+ "Client extensions: {:#?}",
+ client_ext_str.split_whitespace().collect::<Vec<_>>()
+ );
+
+ 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(target_os = "emscripten"))]
+ let egl1_5 = egl.upcast::<khronos_egl::EGL1_5>();
+
+ #[cfg(target_os = "emscripten")]
+ let egl1_5: Option<&Arc<EglInstance>> = Some(&egl);
+
+ let (display, wsi_library, wsi_kind) = if let (Some(library), Some(egl)) =
+ (wayland_library, egl1_5)
+ {
+ log::info!("Using Wayland platform");
+ let display_attributes = [khronos_egl::ATTRIB_NONE];
+ let display = egl
+ .get_platform_display(
+ EGL_PLATFORM_WAYLAND_KHR,
+ khronos_egl::DEFAULT_DISPLAY,
+ &display_attributes,
+ )
+ .unwrap();
+ (display, Some(Arc::new(library)), WindowKind::Wayland)
+ } else if let (Some((display, library)), Some(egl)) = (x11_display_library, egl1_5) {
+ log::info!("Using X11 platform");
+ let display_attributes = [khronos_egl::ATTRIB_NONE];
+ let display = egl
+ .get_platform_display(EGL_PLATFORM_X11_KHR, display.as_ptr(), &display_attributes)
+ .unwrap();
+ (display, Some(Arc::new(library)), WindowKind::X11)
+ } else if let (Some((display, library)), 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(crate::InstanceFlags::VALIDATION)),
+ khronos_egl::ATTRIB_NONE,
+ ];
+ let display = egl
+ .get_platform_display(
+ EGL_PLATFORM_ANGLE_ANGLE,
+ display.as_ptr(),
+ &display_attributes,
+ )
+ .unwrap();
+ (display, Some(Arc::new(library)), WindowKind::AngleX11)
+ } else if client_ext_str.contains("EGL_MESA_platform_surfaceless") {
+ log::info!("No windowing system present. Using surfaceless platform");
+ let egl = egl1_5.expect("Failed to get EGL 1.5 for surfaceless");
+ let display = egl
+ .get_platform_display(
+ EGL_PLATFORM_SURFACELESS_MESA,
+ std::ptr::null_mut(),
+ &[khronos_egl::ATTRIB_NONE],
+ )
+ .unwrap();
+ (display, None, WindowKind::Unknown)
+ } else {
+ log::info!("EGL_MESA_platform_surfaceless not available. Using default platform");
+ let display = egl.get_display(khronos_egl::DEFAULT_DISPLAY).unwrap();
+ (display, None, WindowKind::Unknown)
+ };
+
+ if desc.flags.contains(crate::InstanceFlags::VALIDATION)
+ && client_ext_str.contains("EGL_KHR_debug")
+ {
+ log::info!("Enabling EGL debug output");
+ let function: EglDebugMessageControlFun = {
+ let addr = egl.get_proc_address("eglDebugMessageControlKHR").unwrap();
+ unsafe { std::mem::transmute(addr) }
+ };
+ let attributes = [
+ EGL_DEBUG_MSG_CRITICAL_KHR as khronos_egl::Attrib,
+ 1,
+ EGL_DEBUG_MSG_ERROR_KHR as khronos_egl::Attrib,
+ 1,
+ EGL_DEBUG_MSG_WARN_KHR as khronos_egl::Attrib,
+ 1,
+ EGL_DEBUG_MSG_INFO_KHR as khronos_egl::Attrib,
+ 1,
+ khronos_egl::ATTRIB_NONE,
+ ];
+ unsafe { (function)(Some(egl_debug_proc), attributes.as_ptr()) };
+ }
+
+ let inner = Inner::create(desc.flags, egl, display)?;
+
+ Ok(Instance {
+ wsi: WindowSystemInterface {
+ library: wsi_library,
+ kind: wsi_kind,
+ },
+ flags: desc.flags,
+ inner: Mutex::new(inner),
+ })
+ }
+
+ #[cfg_attr(target_os = "macos", allow(unused, unused_mut, unreachable_code))]
+ unsafe fn create_surface(
+ &self,
+ display_handle: raw_window_handle::RawDisplayHandle,
+ window_handle: raw_window_handle::RawWindowHandle,
+ ) -> Result<Surface, crate::InstanceError> {
+ use raw_window_handle::RawWindowHandle as Rwh;
+
+ #[cfg_attr(
+ any(target_os = "android", target_os = "emscripten"),
+ allow(unused_mut)
+ )]
+ let mut inner = self.inner.lock();
+
+ match (window_handle, display_handle) {
+ (Rwh::Xlib(_), _) => {}
+ (Rwh::Xcb(_), _) => {}
+ (Rwh::Win32(_), _) => {}
+ (Rwh::AppKit(_), _) => {}
+ #[cfg(target_os = "android")]
+ (Rwh::AndroidNdk(handle), _) => {
+ let format = inner
+ .egl
+ .instance
+ .get_config_attrib(
+ inner.egl.display,
+ inner.config,
+ khronos_egl::NATIVE_VISUAL_ID,
+ )
+ .unwrap();
+
+ let ret = unsafe {
+ ANativeWindow_setBuffersGeometry(handle.a_native_window, 0, 0, format)
+ };
+
+ if ret != 0 {
+ log::error!("Error returned from ANativeWindow_setBuffersGeometry");
+ return Err(crate::InstanceError);
+ }
+ }
+ #[cfg(not(target_os = "emscripten"))]
+ (Rwh::Wayland(_), raw_window_handle::RawDisplayHandle::Wayland(display_handle)) => {
+ if inner
+ .wl_display
+ .map(|ptr| ptr != display_handle.display)
+ .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 std::ops::DerefMut;
+ let display_attributes = [khronos_egl::ATTRIB_NONE];
+
+ let display = inner
+ .egl
+ .instance
+ .upcast::<khronos_egl::EGL1_5>()
+ .unwrap()
+ .get_platform_display(
+ EGL_PLATFORM_WAYLAND_KHR,
+ display_handle.display,
+ &display_attributes,
+ )
+ .unwrap();
+
+ let new_inner =
+ Inner::create(self.flags, Arc::clone(&inner.egl.instance), display)
+ .map_err(|_| crate::InstanceError)?;
+
+ let old_inner = std::mem::replace(inner.deref_mut(), new_inner);
+ inner.wl_display = Some(display_handle.display);
+
+ drop(old_inner);
+ }
+ }
+ #[cfg(target_os = "emscripten")]
+ (Rwh::Web(_), _) => {}
+ other => {
+ log::error!("Unsupported window: {:?}", other);
+ return Err(crate::InstanceError);
+ }
+ };
+
+ inner.egl.unmake_current();
+
+ Ok(Surface {
+ egl: inner.egl.clone(),
+ wsi: self.wsi.clone(),
+ config: inner.config,
+ presentable: inner.supports_native_window,
+ raw_window_handle: window_handle,
+ swapchain: None,
+ srgb_kind: inner.srgb_kind,
+ })
+ }
+ unsafe fn destroy_surface(&self, _surface: Surface) {}
+
+ unsafe fn enumerate_adapters(&self) -> Vec<crate::ExposedAdapter<super::Api>> {
+ let inner = self.inner.lock();
+ inner.egl.make_current();
+
+ let gl = unsafe {
+ glow::Context::from_loader_function(|name| {
+ inner
+ .egl
+ .instance
+ .get_proc_address(name)
+ .map_or(ptr::null(), |p| p as *const _)
+ })
+ };
+
+ if self.flags.contains(crate::InstanceFlags::DEBUG) && gl.supports_debug() {
+ log::info!("Max label length: {}", unsafe {
+ gl.get_parameter_i32(glow::MAX_LABEL_LENGTH)
+ });
+ }
+
+ if self.flags.contains(crate::InstanceFlags::VALIDATION) && gl.supports_debug() {
+ log::info!("Enabling GLES debug output");
+ unsafe { gl.enable(glow::DEBUG_OUTPUT) };
+ unsafe { gl.debug_message_callback(gl_debug_message_callback) };
+ }
+
+ inner.egl.unmake_current();
+
+ unsafe {
+ super::Adapter::expose(AdapterContext {
+ glow: Mutex::new(gl),
+ egl: Some(inner.egl.clone()),
+ })
+ }
+ .into_iter()
+ .collect()
+ }
+}
+
+impl super::Adapter {
+ /// Creates a new external adapter using the specified loader function.
+ ///
+ /// # Safety
+ ///
+ /// - The underlying OpenGL ES context must be current.
+ /// - The underlying OpenGL ES context must be current when interfacing with any objects returned by
+ /// wgpu-hal from this adapter.
+ pub unsafe fn new_external(
+ fun: impl FnMut(&str) -> *const ffi::c_void,
+ ) -> Option<crate::ExposedAdapter<super::Api>> {
+ let context = unsafe { glow::Context::from_loader_function(fun) };
+ unsafe {
+ Self::expose(AdapterContext {
+ glow: Mutex::new(context),
+ egl: None,
+ })
+ }
+ }
+
+ pub fn adapter_context(&self) -> &AdapterContext {
+ &self.shared.context
+ }
+}
+
+impl super::Device {
+ /// Returns the underlying EGL context.
+ pub fn context(&self) -> &AdapterContext {
+ &self.shared.context
+ }
+}
+
+#[derive(Debug)]
+pub struct Swapchain {
+ surface: khronos_egl::Surface,
+ wl_window: Option<*mut raw::c_void>,
+ framebuffer: glow::Framebuffer,
+ renderbuffer: glow::Renderbuffer,
+ /// Extent because the window lies
+ extent: wgt::Extent3d,
+ format: wgt::TextureFormat,
+ format_desc: super::TextureFormatDesc,
+ #[allow(unused)]
+ sample_type: wgt::TextureSampleType,
+}
+
+#[derive(Debug)]
+pub struct Surface {
+ egl: EglContext,
+ wsi: WindowSystemInterface,
+ config: khronos_egl::Config,
+ pub(super) presentable: bool,
+ raw_window_handle: raw_window_handle::RawWindowHandle,
+ swapchain: Option<Swapchain>,
+ srgb_kind: SrgbFrameBufferKind,
+}
+
+unsafe impl Send for Surface {}
+unsafe impl Sync for Surface {}
+
+impl Surface {
+ pub(super) unsafe fn present(
+ &mut self,
+ _suf_texture: super::Texture,
+ gl: &glow::Context,
+ ) -> Result<(), crate::SurfaceError> {
+ let sc = self.swapchain.as_ref().unwrap();
+
+ self.egl
+ .instance
+ .make_current(
+ self.egl.display,
+ Some(sc.surface),
+ Some(sc.surface),
+ Some(self.egl.raw),
+ )
+ .map_err(|e| {
+ log::error!("make_current(surface) failed: {}", e);
+ crate::SurfaceError::Lost
+ })?;
+
+ unsafe { gl.disable(glow::SCISSOR_TEST) };
+ unsafe { gl.color_mask(true, true, true, true) };
+
+ unsafe { gl.bind_framebuffer(glow::DRAW_FRAMEBUFFER, None) };
+ unsafe { gl.bind_framebuffer(glow::READ_FRAMEBUFFER, Some(sc.framebuffer)) };
+ // Note the Y-flipping here. GL's presentation is not flipped,
+ // but main rendering is. Therefore, we Y-flip the output positions
+ // in the shader, and also this blit.
+ unsafe {
+ gl.blit_framebuffer(
+ 0,
+ sc.extent.height as i32,
+ sc.extent.width as i32,
+ 0,
+ 0,
+ 0,
+ sc.extent.width as i32,
+ sc.extent.height as i32,
+ glow::COLOR_BUFFER_BIT,
+ glow::NEAREST,
+ )
+ };
+ unsafe { gl.bind_framebuffer(glow::READ_FRAMEBUFFER, None) };
+
+ self.egl
+ .instance
+ .swap_buffers(self.egl.display, sc.surface)
+ .map_err(|e| {
+ log::error!("swap_buffers failed: {}", e);
+ crate::SurfaceError::Lost
+ })?;
+ self.egl
+ .instance
+ .make_current(self.egl.display, None, None, None)
+ .map_err(|e| {
+ log::error!("make_current(null) failed: {}", e);
+ crate::SurfaceError::Lost
+ })?;
+
+ Ok(())
+ }
+
+ unsafe fn unconfigure_impl(
+ &mut self,
+ device: &super::Device,
+ ) -> Option<(khronos_egl::Surface, Option<*mut raw::c_void>)> {
+ let gl = &device.shared.context.lock();
+ match self.swapchain.take() {
+ Some(sc) => {
+ unsafe { gl.delete_renderbuffer(sc.renderbuffer) };
+ unsafe { gl.delete_framebuffer(sc.framebuffer) };
+ Some((sc.surface, sc.wl_window))
+ }
+ None => None,
+ }
+ }
+
+ pub fn supports_srgb(&self) -> bool {
+ match self.srgb_kind {
+ SrgbFrameBufferKind::None => false,
+ _ => true,
+ }
+ }
+}
+
+impl crate::Surface<super::Api> for Surface {
+ unsafe fn configure(
+ &mut self,
+ device: &super::Device,
+ config: &crate::SurfaceConfiguration,
+ ) -> Result<(), crate::SurfaceError> {
+ use raw_window_handle::RawWindowHandle as Rwh;
+
+ let (surface, wl_window) = match unsafe { self.unconfigure_impl(device) } {
+ Some(pair) => pair,
+ None => {
+ let mut wl_window = None;
+ let (mut temp_xlib_handle, mut temp_xcb_handle);
+ #[allow(trivial_casts)]
+ let native_window_ptr = match (self.wsi.kind, self.raw_window_handle) {
+ (WindowKind::Unknown | WindowKind::X11, Rwh::Xlib(handle)) => {
+ temp_xlib_handle = handle.window;
+ &mut temp_xlib_handle as *mut _ as *mut std::ffi::c_void
+ }
+ (WindowKind::AngleX11, Rwh::Xlib(handle)) => {
+ handle.window as *mut std::ffi::c_void
+ }
+ (WindowKind::Unknown | WindowKind::X11, Rwh::Xcb(handle)) => {
+ temp_xcb_handle = handle.window;
+ &mut temp_xcb_handle as *mut _ as *mut std::ffi::c_void
+ }
+ (WindowKind::AngleX11, Rwh::Xcb(handle)) => {
+ handle.window as *mut std::ffi::c_void
+ }
+ (WindowKind::Unknown, Rwh::AndroidNdk(handle)) => handle.a_native_window,
+ (WindowKind::Wayland, Rwh::Wayland(handle)) => {
+ let library = self.wsi.library.as_ref().unwrap();
+ let wl_egl_window_create: libloading::Symbol<WlEglWindowCreateFun> =
+ unsafe { library.get(b"wl_egl_window_create") }.unwrap();
+ let window = unsafe { wl_egl_window_create(handle.surface, 640, 480) }
+ as *mut _ as *mut std::ffi::c_void;
+ wl_window = Some(window);
+ window
+ }
+ #[cfg(target_os = "emscripten")]
+ (WindowKind::Unknown, Rwh::Web(handle)) => handle.id as *mut std::ffi::c_void,
+ (WindowKind::Unknown, Rwh::Win32(handle)) => handle.hwnd,
+ (WindowKind::Unknown, Rwh::AppKit(handle)) => {
+ #[cfg(not(target_os = "macos"))]
+ let window_ptr = handle.ns_view;
+ #[cfg(target_os = "macos")]
+ let window_ptr = {
+ use objc::{msg_send, runtime::Object, sel, sel_impl};
+ // ns_view always have a layer and don't need to verify that it exists.
+ let layer: *mut Object =
+ msg_send![handle.ns_view as *mut Object, layer];
+ layer as *mut ffi::c_void
+ };
+ window_ptr
+ }
+ _ => {
+ log::warn!(
+ "Initialized platform {:?} doesn't work with window {:?}",
+ self.wsi.kind,
+ self.raw_window_handle
+ );
+ return Err(crate::SurfaceError::Other("incompatible window kind"));
+ }
+ };
+
+ let mut attributes = vec![
+ khronos_egl::RENDER_BUFFER,
+ // We don't want any of the buffering done by the driver, because we
+ // manage a swapchain on our side.
+ // Some drivers just fail on surface creation seeing `EGL_SINGLE_BUFFER`.
+ if cfg!(any(target_os = "android", target_os = "macos"))
+ || cfg!(windows)
+ || self.wsi.kind == WindowKind::AngleX11
+ {
+ khronos_egl::BACK_BUFFER
+ } else {
+ khronos_egl::SINGLE_BUFFER
+ },
+ ];
+ match self.srgb_kind {
+ SrgbFrameBufferKind::None => {}
+ SrgbFrameBufferKind::Core => {
+ attributes.push(khronos_egl::GL_COLORSPACE);
+ attributes.push(khronos_egl::GL_COLORSPACE_SRGB);
+ }
+ SrgbFrameBufferKind::Khr => {
+ attributes.push(EGL_GL_COLORSPACE_KHR as i32);
+ attributes.push(EGL_GL_COLORSPACE_SRGB_KHR as i32);
+ }
+ }
+ attributes.push(khronos_egl::ATTRIB_NONE as i32);
+
+ #[cfg(not(target_os = "emscripten"))]
+ let egl1_5 = self.egl.instance.upcast::<khronos_egl::EGL1_5>();
+
+ #[cfg(target_os = "emscripten")]
+ let egl1_5: Option<&Arc<EglInstance>> = Some(&self.egl.instance);
+
+ // Careful, we can still be in 1.4 version even if `upcast` succeeds
+ let raw_result = match egl1_5 {
+ Some(egl) if self.wsi.kind != WindowKind::Unknown => {
+ let attributes_usize = attributes
+ .into_iter()
+ .map(|v| v as usize)
+ .collect::<Vec<_>>();
+ egl.create_platform_window_surface(
+ self.egl.display,
+ self.config,
+ native_window_ptr,
+ &attributes_usize,
+ )
+ }
+ _ => unsafe {
+ self.egl.instance.create_window_surface(
+ self.egl.display,
+ self.config,
+ native_window_ptr,
+ Some(&attributes),
+ )
+ },
+ };
+
+ match raw_result {
+ Ok(raw) => (raw, wl_window),
+ Err(e) => {
+ log::warn!("Error in create_window_surface: {:?}", e);
+ return Err(crate::SurfaceError::Lost);
+ }
+ }
+ }
+ };
+
+ if let Some(window) = wl_window {
+ let library = self.wsi.library.as_ref().unwrap();
+ let wl_egl_window_resize: libloading::Symbol<WlEglWindowResizeFun> =
+ unsafe { library.get(b"wl_egl_window_resize") }.unwrap();
+ unsafe {
+ wl_egl_window_resize(
+ window,
+ config.extent.width as i32,
+ config.extent.height as i32,
+ 0,
+ 0,
+ )
+ };
+ }
+
+ let format_desc = device.shared.describe_texture_format(config.format);
+ let gl = &device.shared.context.lock();
+ let renderbuffer = unsafe { gl.create_renderbuffer() }.map_err(|error| {
+ log::error!("Internal swapchain renderbuffer creation failed: {error}");
+ crate::DeviceError::OutOfMemory
+ })?;
+ unsafe { gl.bind_renderbuffer(glow::RENDERBUFFER, Some(renderbuffer)) };
+ unsafe {
+ gl.renderbuffer_storage(
+ glow::RENDERBUFFER,
+ format_desc.internal,
+ config.extent.width as _,
+ config.extent.height as _,
+ )
+ };
+ let framebuffer = unsafe { gl.create_framebuffer() }.map_err(|error| {
+ log::error!("Internal swapchain framebuffer creation failed: {error}");
+ crate::DeviceError::OutOfMemory
+ })?;
+ unsafe { gl.bind_framebuffer(glow::READ_FRAMEBUFFER, Some(framebuffer)) };
+ unsafe {
+ gl.framebuffer_renderbuffer(
+ glow::READ_FRAMEBUFFER,
+ glow::COLOR_ATTACHMENT0,
+ glow::RENDERBUFFER,
+ Some(renderbuffer),
+ )
+ };
+ unsafe { gl.bind_renderbuffer(glow::RENDERBUFFER, None) };
+ unsafe { gl.bind_framebuffer(glow::READ_FRAMEBUFFER, None) };
+
+ self.swapchain = Some(Swapchain {
+ surface,
+ wl_window,
+ renderbuffer,
+ framebuffer,
+ extent: config.extent,
+ format: config.format,
+ format_desc,
+ sample_type: wgt::TextureSampleType::Float { filterable: false },
+ });
+
+ Ok(())
+ }
+
+ unsafe fn unconfigure(&mut self, device: &super::Device) {
+ if let Some((surface, wl_window)) = unsafe { self.unconfigure_impl(device) } {
+ self.egl
+ .instance
+ .destroy_surface(self.egl.display, surface)
+ .unwrap();
+ if let Some(window) = wl_window {
+ let library = self.wsi.library.as_ref().expect("unsupported window");
+ let wl_egl_window_destroy: libloading::Symbol<WlEglWindowDestroyFun> =
+ unsafe { library.get(b"wl_egl_window_destroy") }.unwrap();
+ unsafe { wl_egl_window_destroy(window) };
+ }
+ }
+ }
+
+ unsafe fn acquire_texture(
+ &mut self,
+ _timeout_ms: Option<Duration>, //TODO
+ ) -> Result<Option<crate::AcquiredSurfaceTexture<super::Api>>, crate::SurfaceError> {
+ let sc = self.swapchain.as_ref().unwrap();
+ let texture = super::Texture {
+ inner: super::TextureInner::Renderbuffer {
+ raw: sc.renderbuffer,
+ },
+ drop_guard: None,
+ array_layer_count: 1,
+ mip_level_count: 1,
+ format: sc.format,
+ format_desc: sc.format_desc.clone(),
+ copy_size: crate::CopyExtent {
+ width: sc.extent.width,
+ height: sc.extent.height,
+ depth: 1,
+ },
+ is_cubemap: false,
+ };
+ Ok(Some(crate::AcquiredSurfaceTexture {
+ texture,
+ suboptimal: false,
+ }))
+ }
+ unsafe fn discard_texture(&mut self, _texture: super::Texture) {}
+}