From aa8f147ca7d00b852cea88e5b4e85f15d134244b Mon Sep 17 00:00:00 2001 From: Martin Robinson Date: Fri, 2 Oct 2026 10:04:18 +0200 Subject: [PATCH] Convert `Surface` and `SurfaceTexture` into enums This continues the work started in #397 for the new API. This does add a large number of new conversion sites, but the hope is that once all types are enums this conversion can be hidden mostly behind a macro. Signed-off-by: Martin Robinson --- src/angle/context.rs | 7 +- src/angle/device.rs | 68 +++--- src/angle/surface.rs | 54 ++--- src/base/io_surface/surface.rs | 11 +- src/cgl/context.rs | 4 +- src/cgl/device.rs | 65 +++--- src/cgl/surface.rs | 54 ++--- src/chains.rs | 115 ++++------ src/device.rs | 41 ++-- src/hardware_buffer/context.rs | 4 +- src/hardware_buffer/device.rs | 69 ++++-- .../surface/android_surface.rs | 78 ++----- src/hardware_buffer/surface/mod.rs | 71 +++--- src/hardware_buffer/surface/ohos_surface.rs | 86 +++----- src/lib.rs | 5 +- src/macros.rs | 42 ++-- src/mesa_surfaceless/device.rs | 34 ++- src/mesa_surfaceless/surface.rs | 33 +-- src/multi/context.rs | 33 +-- src/multi/device.rs | 47 ++-- src/multi/surface.rs | 206 +++--------------- src/surface.rs | 97 ++++++++- src/surface_texture.rs | 109 +++++++++ src/tests.rs | 3 +- src/unix.rs | 37 ---- src/wayland/device.rs | 36 +-- src/wayland/surface.rs | 33 +-- src/wgl/context.rs | 4 +- src/wgl/device.rs | 89 ++++---- src/wgl/surface.rs | 57 ++--- src/x11/device.rs | 37 ++-- src/x11/surface.rs | 33 +-- 32 files changed, 762 insertions(+), 900 deletions(-) create mode 100644 src/surface_texture.rs diff --git a/src/angle/context.rs b/src/angle/context.rs index 0f9e51e5..520c0f93 100644 --- a/src/angle/context.rs +++ b/src/angle/context.rs @@ -1,20 +1,19 @@ //! Wrapper for EGL contexts managed by ANGLE using Direct3D 11 as a backend on Windows. -use super::surface::Surface; +use crate::angle::surface::AngleSurface; pub use crate::base::egl::context::NativeContext; use crate::base::egl::surface::ExternalEGLSurfaces; use crate::context::ContextID; -use crate::egl; use crate::egl::types::EGLContext; use crate::surface::Framebuffer; -use crate::Gl; +use crate::{egl, Gl}; use std::thread; /// An implementation of [`crate::Context`] for ANGLE on Windows. pub struct AngleContext { pub(crate) egl_context: EGLContext, pub(crate) id: ContextID, - pub(crate) framebuffer: Framebuffer, + pub(crate) framebuffer: Framebuffer, pub(crate) context_is_owned: bool, pub(crate) gl: Gl, } diff --git a/src/angle/device.rs b/src/angle/device.rs index 34c3ddaf..2423d9fa 100644 --- a/src/angle/device.rs +++ b/src/angle/device.rs @@ -4,7 +4,7 @@ use super::adapter::AngleAdapter; use crate::angle::connection::Connection; use crate::angle::context::{AngleContext, NativeContext}; use crate::angle::surface::{ - Surface, SurfaceDataGuard, SurfaceTexture, Synchronization, Win32Objects, + AngleSurface, AngleSurfaceTexture, SurfaceDataGuard, Synchronization, Win32Objects, }; use crate::base::egl::context::{self, CurrentContextGuard}; use crate::base::egl::device::EGL_FUNCTIONS; @@ -22,7 +22,7 @@ use crate::egl::types::{ use crate::surface::Framebuffer; use crate::{ egl, gl, Adapter, Context, ContextAttributes, ContextDescriptor, EglContextDescriptor, Error, - GLApi, Gl, SurfaceAccess, SurfaceInfo, SurfaceType, + GLApi, Gl, Surface, SurfaceAccess, SurfaceInfo, SurfaceTexture, SurfaceType, }; use euclid::default::Size2D; use glow::HasContext; @@ -487,14 +487,15 @@ impl Device { Ok(context) => context, Err(error) => return Err((error, surface)), }; + let surface: AngleSurface = surface.try_into()?; if context.id != surface.context_id { - return Err((Error::IncompatibleSurface, surface)); + return Err((Error::IncompatibleSurface, surface.into())); } match context.framebuffer { Framebuffer::None => {} - Framebuffer::External(_) => return Err((Error::ExternalRenderTarget, surface)), - Framebuffer::Surface(_) => return Err((Error::SurfaceAlreadyBound, surface)), + Framebuffer::External(_) => return Err((Error::ExternalRenderTarget, surface.into())), + Framebuffer::Surface(_) => return Err((Error::SurfaceAlreadyBound, surface.into())), } // If the surface is synchronized with GLFinish, then finish. @@ -561,7 +562,7 @@ impl Device { _ => {} } - Ok(Some(surface)) + Ok(Some(surface.into())) } /// Displays the contents of the currently bound surface to the screen, if @@ -608,7 +609,7 @@ impl Device { match context.framebuffer { Framebuffer::None => Ok(None), Framebuffer::External(_) => Err(Error::ExternalRenderTarget), - Framebuffer::Surface(ref surface) => Ok(Some(self.surface_info(surface))), + Framebuffer::Surface(ref surface) => Ok(Some(surface.info())), } } @@ -616,7 +617,7 @@ impl Device { pub fn native_context(&self, context: &Context) -> NativeContext { let context = context.angle().expect("Passed incorrect context type"); let (egl_draw_surface, egl_read_surface) = match context.framebuffer { - Framebuffer::Surface(Surface { egl_surface, .. }) => (egl_surface, egl_surface), + Framebuffer::Surface(AngleSurface { egl_surface, .. }) => (egl_surface, egl_surface), Framebuffer::External(ExternalEGLSurfaces { draw, read }) => (draw, read), Framebuffer::None => (egl::NO_SURFACE, egl::NO_SURFACE), }; @@ -639,7 +640,9 @@ impl Device { surface_type: SurfaceType<'_>, ) -> Result { match surface_type { - SurfaceType::Generic { ref size } => self.create_pbuffer_surface(context, size, None), + SurfaceType::Generic { ref size } => self + .create_pbuffer_surface(context, size, None) + .map(Into::into), SurfaceType::Widget { window_handle, .. } => { let RawWindowHandle::Win32(handle) = window_handle.as_raw() else { return Err(Error::IncompatibleSurfaceType); @@ -655,7 +658,7 @@ impl Device { context: &Context, size: &Size2D, texture: Option>, - ) -> Result { + ) -> Result { let context_descriptor = self.context_descriptor(context); let context_descriptor = context_descriptor.egl()?.clone(); let egl_config = self.context_descriptor_to_egl_config(&context_descriptor); @@ -730,7 +733,7 @@ impl Device { Synchronization::None }; - Ok(Surface { + Ok(AngleSurface { egl_surface, size: *size, context_id: context.id, @@ -754,6 +757,7 @@ impl Device { texture: ComPtr, ) -> Result { self.create_pbuffer_surface(context, size, Some(texture)) + .map(Into::into) } fn create_window_surface( @@ -793,13 +797,14 @@ impl Device { assert_ne!(width, 0); assert_ne!(height, 0); - Ok(Surface { + Ok(AngleSurface { egl_surface, size: Size2D::new(width, height), context_id: context.id, context_descriptor, win32_objects: Win32Objects::Window, - }) + } + .into()) }) } } @@ -820,8 +825,9 @@ impl Device { context: &mut Context, surface: Surface, ) -> Result { + let surface: AngleSurface = surface.try_into()?; let share_handle = match surface.win32_objects { - Win32Objects::Window => return Err((Error::WidgetAttached, surface)), + Win32Objects::Window => return Err((Error::WidgetAttached, surface.into())), Win32Objects::Pbuffer { share_handle, .. } => share_handle, }; @@ -853,7 +859,10 @@ impl Device { ); if local_egl_surface == egl::NO_SURFACE { let windowing_api_error = egl.GetError().to_windowing_api_error(); - return Err((Error::SurfaceImportFailed(windowing_api_error), surface)); + return Err(( + Error::SurfaceImportFailed(windowing_api_error), + surface.into(), + )); } let mut local_keyed_mutex: *mut IDXGIKeyedMutex = ptr::null_mut(); @@ -889,13 +898,13 @@ impl Device { fn create_surface_texture_from_local_surface( &self, context: &Context, - surface: Surface, + surface: AngleSurface, local_egl_surface: EGLSurface, local_keyed_mutex: Option>, ) -> Result { let context = match context.angle() { Ok(context) => context, - Err(error) => return Err((error, surface)), + Err(error) => return Err((error, surface.into())), }; EGL_FUNCTIONS.with(|egl| { @@ -913,7 +922,7 @@ impl Device { let windowing_api_error = egl.GetError().to_windowing_api_error(); return Err(( Error::SurfaceTextureCreationFailed(windowing_api_error), - surface, + surface.into(), )); } @@ -926,13 +935,14 @@ impl Device { gl.bind_texture(gl::TEXTURE_2D, None); debug_assert_eq!(gl.get_error(), gl::NO_ERROR); - Ok(SurfaceTexture { + Ok(AngleSurfaceTexture { surface, local_egl_surface, local_keyed_mutex, gl_texture: Some(texture), phantom: PhantomData, - }) + } + .into()) } }) } @@ -968,6 +978,7 @@ impl Device { surface: &mut Surface, ) -> Result<(), Error> { let context = context.angle()?; + let surface: &mut AngleSurface = surface.try_into()?; if context.id != surface.context_id { return Err(Error::IncompatibleSurface); } @@ -1004,12 +1015,13 @@ impl Device { pub fn destroy_surface_texture( &self, context: &mut Context, - mut surface_texture: SurfaceTexture, + surface_texture: SurfaceTexture, ) -> Result { let context = match context.angle() { Ok(context) => context, Err(error) => return Err((error, surface_texture)), }; + let mut surface_texture: AngleSurfaceTexture = surface_texture.try_into()?; unsafe { if let Some(texture) = surface_texture.gl_texture.take() { @@ -1031,7 +1043,7 @@ impl Device { }) } - Ok(surface_texture.surface) + Ok(surface_texture.surface.into()) } /// Returns the OpenGL texture target needed to read from this surface texture. @@ -1059,7 +1071,7 @@ impl Device { /// The supplied context must match the context the surface was created with, or an /// `IncompatibleSurface` error is returned. pub fn present_surface(&self, _: &Context, surface: &mut Surface) -> Result<(), Error> { - surface.present(self) + surface.angle()?.present(self) } /// Resizes a widget surface. @@ -1069,6 +1081,7 @@ impl Device { surface: &mut Surface, size: Size2D, ) -> Result<(), Error> { + let surface: &mut AngleSurface = surface.try_into()?; surface.resize(size); Ok(()) } @@ -1081,12 +1094,7 @@ impl Device { /// 0, the default framebuffer, depending on platform. #[inline] pub fn surface_info(&self, surface: &Surface) -> SurfaceInfo { - SurfaceInfo { - size: surface.size, - id: surface.id(), - context_id: surface.context_id, - framebuffer_object: None, - } + surface.angle().expect("Incompatible surface type").info() } /// Returns the OpenGL texture object containing the contents of this surface. @@ -1097,7 +1105,7 @@ impl Device { &self, surface_texture: &SurfaceTexture, ) -> Option { - surface_texture.gl_texture + surface_texture.angle().ok()?.gl_texture } } diff --git a/src/angle/surface.rs b/src/angle/surface.rs index 8ebe7f83..e2723363 100644 --- a/src/angle/surface.rs +++ b/src/angle/surface.rs @@ -5,7 +5,7 @@ use crate::base::egl::device::EGL_FUNCTIONS; use crate::context::ContextID; use crate::egl::types::EGLSurface; use crate::egl::{self}; -use crate::{EglContextDescriptor, Error, SurfaceID}; +use crate::{EglContextDescriptor, Error, SurfaceID, SurfaceInfo}; use euclid::default::Size2D; use std::fmt::{self, Debug, Formatter}; @@ -16,23 +16,8 @@ use winapi::um::d3d11; use winapi::um::winnt::HANDLE; use wio::com::ComPtr; -/// Represents a hardware buffer of pixels that can be rendered to via the CPU or GPU and either -/// displayed in a native widget or bound to a texture for reading. -/// -/// Surfaces come in two varieties: generic and widget surfaces. Generic surfaces can be bound to a -/// texture but cannot be displayed in a widget (without using other APIs such as Core Animation, -/// DirectComposition, or XPRESENT). Widget surfaces are the opposite: they can be displayed in a -/// widget but not bound to a texture. -/// -/// Surfaces are specific to a given context and cannot be rendered to from any context other than -/// the one they were created with. However, they can be *read* from any context on any thread (as -/// long as that context shares the same adapter and connection), by wrapping them in a -/// `SurfaceTexture`. -/// -/// Depending on the platform, each surface may be internally double-buffered. -/// -/// Surfaces must be destroyed with the `destroy_surface()` method, or a panic will occur. -pub struct Surface { +/// An implementation of [`crate::Surface`] for Angle on Windows. +pub struct AngleSurface { pub(crate) egl_surface: EGLSurface, pub(crate) size: Size2D, pub(crate) context_id: ContextID, @@ -40,32 +25,24 @@ pub struct Surface { pub(crate) win32_objects: Win32Objects, } -/// Represents an OpenGL texture that wraps a surface. -/// -/// Reading from the associated OpenGL texture reads from the surface. It is undefined behavior to -/// write to such a texture (e.g. by binding it to a framebuffer and rendering to that -/// framebuffer). -/// -/// Surface textures are local to a context, but that context does not have to be the same context -/// as that associated with the underlying surface. The texture must be destroyed with the -/// `destroy_surface_texture()` method, or a panic will occur. -pub struct SurfaceTexture { - pub(crate) surface: Surface, +/// An implementation of [`crate::SurfaceTexture`] for Angle on Windows. +pub struct AngleSurfaceTexture { + pub(crate) surface: AngleSurface, pub(crate) local_egl_surface: EGLSurface, pub(crate) local_keyed_mutex: Option>, pub(crate) gl_texture: Option, pub(crate) phantom: PhantomData<*const ()>, } -unsafe impl Send for Surface {} +unsafe impl Send for AngleSurface {} -impl Debug for Surface { +impl Debug for AngleSurface { fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { write!(f, "Surface({:x})", self.id().0) } } -impl Drop for Surface { +impl Drop for AngleSurface { fn drop(&mut self) { if self.egl_surface != egl::NO_SURFACE && !thread::panicking() { panic!("Should have destroyed the surface first with `destroy_surface()`!") @@ -73,7 +50,7 @@ impl Drop for Surface { } } -impl Debug for SurfaceTexture { +impl Debug for AngleSurfaceTexture { fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { write!(f, "SurfaceTexture({:?})", self.surface) } @@ -95,7 +72,7 @@ pub(crate) enum Synchronization { None, } -impl Surface { +impl AngleSurface { #[inline] pub(crate) fn id(&self) -> SurfaceID { SurfaceID(self.egl_surface as usize) @@ -137,6 +114,15 @@ impl Surface { pub(crate) fn resize(&mut self, size: Size2D) { self.size = size; } + + pub(crate) fn info(&self) -> SurfaceInfo { + SurfaceInfo { + size: self.size, + id: self.id(), + context_id: self.context_id, + framebuffer_object: None, + } + } } /// Represents the CPU view of the pixel data of this surface. diff --git a/src/base/io_surface/surface.rs b/src/base/io_surface/surface.rs index 5f949ee3..8dee6b17 100644 --- a/src/base/io_surface/surface.rs +++ b/src/base/io_surface/surface.rs @@ -2,7 +2,7 @@ use super::device::Device; use super::ffi::{kIOMapDefaultCache, kIOMapWriteCombineCache}; -use crate::{Error, SurfaceAccess, SurfaceID, SurfaceType, SystemSurfaceInfo}; +use crate::{Error, SurfaceAccess, SurfaceID, SurfaceType}; use euclid::default::Size2D; use libc::KERN_SUCCESS; @@ -380,15 +380,6 @@ impl Device { } } - /// Returns various information about the surface. - #[inline] - pub fn surface_info(&self, surface: &Surface) -> SystemSurfaceInfo { - SystemSurfaceInfo { - size: surface.size, - id: surface.id(), - } - } - /// Returns the native `IOSurfaceRef` corresponding to this surface. #[inline] pub fn native_surface(&self, surface: &Surface) -> NativeSurface { diff --git a/src/cgl/context.rs b/src/cgl/context.rs index d707881f..15ef1b09 100644 --- a/src/cgl/context.rs +++ b/src/cgl/context.rs @@ -1,7 +1,7 @@ //! Wrapper for Core OpenGL contexts. use super::ffi::{CGLReleaseContext, CGLRetainContext}; -use super::surface::Surface; +use crate::cgl::surface::CglSurface; use crate::context::ContextID; use crate::surface::Framebuffer; use crate::{Error, Gl}; @@ -17,7 +17,7 @@ use std::thread; pub struct CglContext { pub(crate) cgl_context: CGLContextObj, pub(crate) id: ContextID, - pub(crate) framebuffer: Framebuffer, + pub(crate) framebuffer: Framebuffer, pub(crate) gl: Rc, } diff --git a/src/cgl/device.rs b/src/cgl/device.rs index b0bcd300..219dcf31 100644 --- a/src/cgl/device.rs +++ b/src/cgl/device.rs @@ -6,7 +6,9 @@ use crate::base::io_surface::device::Device as SystemDevice; use crate::cgl::context::{CurrentContextGuard, NativeContext}; use crate::cgl::error::ToWindowingApiError; use crate::cgl::ffi::{CGLReleaseContext, CGLRetainContext}; -use crate::cgl::surface::{surface_bind_to_gl_texture, NativeSurface}; +use crate::cgl::surface::{ + surface_bind_to_gl_texture, CglSurface, CglSurfaceTexture, NativeSurface, +}; use crate::context::{ContextID, CREATE_CONTEXT_MUTEX}; use crate::renderbuffers::Renderbuffers; use crate::surface::Framebuffer; @@ -240,10 +242,10 @@ impl Device { return Ok(()); } - if let Framebuffer::Surface(mut surface) = + if let Framebuffer::Surface(ref mut surface) = mem::replace(&mut context.framebuffer, Framebuffer::None) { - self.destroy_surface_inner(context, &mut surface)?; + self.destroy_surface_inner(context, surface)?; } unsafe { @@ -316,15 +318,20 @@ impl Device { Ok(context) => context, Err(error) => return Err((error, new_surface)), }; + let new_surface: CglSurface = new_surface.try_into()?; match context.framebuffer { - Framebuffer::External(_) => return Err((Error::ExternalRenderTarget, new_surface)), - Framebuffer::Surface(_) => return Err((Error::SurfaceAlreadyBound, new_surface)), + Framebuffer::External(_) => { + return Err((Error::ExternalRenderTarget, new_surface.into())) + } + Framebuffer::Surface(_) => { + return Err((Error::SurfaceAlreadyBound, new_surface.into())) + } Framebuffer::None => {} } if new_surface.context_id != context.id { - return Err((Error::IncompatibleSurface, new_surface)); + return Err((Error::IncompatibleSurface, new_surface.into())); } context.framebuffer = Framebuffer::Surface(new_surface); @@ -367,7 +374,7 @@ impl Device { if let Some(framebuffer) = surface.framebuffer_object { gl_utils::unbind_framebuffer_if_necessary(gl, framebuffer); } - Ok(Some(surface)) + Ok(Some(surface.into())) } } } @@ -480,7 +487,7 @@ impl Device { match context.framebuffer { Framebuffer::None => Ok(None), Framebuffer::External(_) => Err(Error::ExternalRenderTarget), - Framebuffer::Surface(ref surface) => Ok(Some(self.surface_info(surface))), + Framebuffer::Surface(ref surface) => Ok(Some(surface.info())), } } @@ -557,13 +564,14 @@ impl Device { return Err(Error::SurfaceCreationFailed(WindowingApiError::Failed)); } - Ok(Surface { + Ok(CglSurface { system_surface, context_id: context.id, framebuffer_object: Some(framebuffer_object), texture_object: Some(texture_object), renderbuffers, - }) + } + .into()) } } @@ -582,15 +590,16 @@ impl Device { context: &mut Context, surface: Surface, ) -> Result { + let surface: CglSurface = surface.try_into()?; if surface.system_surface.view_info.is_some() { - return Err((Error::WidgetAttached, surface)); + return Err((Error::WidgetAttached, surface.into())); } let _guard = self.temporarily_make_context_current(context).unwrap(); let context = match context.cgl() { Ok(context) => context, - Err(error) => return Err((error, surface)), + Err(error) => return Err((error, surface.into())), }; let texture_object = self.bind_to_gl_texture( @@ -598,11 +607,12 @@ impl Device { &surface.system_surface.io_surface, &surface.system_surface.size, ); - Ok(SurfaceTexture { + Ok(CglSurfaceTexture { surface, texture_object: Some(texture_object), phantom: PhantomData, - }) + } + .into()) } fn bind_to_gl_texture( @@ -649,7 +659,7 @@ impl Device { fn destroy_surface_inner( &self, context: &mut CglContext, - surface: &mut Surface, + surface: &mut CglSurface, ) -> Result<(), Error> { let gl = &context.gl; if context.id != surface.context_id { @@ -682,8 +692,7 @@ impl Device { context: &mut Context, surface: &mut Surface, ) -> Result<(), Error> { - let context: &mut CglContext = context.try_into()?; - self.destroy_surface_inner(context, surface) + self.destroy_surface_inner(context.try_into()?, surface.try_into()?) } /// Destroys a surface texture and returns the underlying surface. @@ -696,12 +705,14 @@ impl Device { pub fn destroy_surface_texture( &self, context: &mut Context, - mut surface_texture: SurfaceTexture, + surface_texture: SurfaceTexture, ) -> Result { let concrete_context = match context.cgl() { Ok(concrete_context) => concrete_context, Err(error) => return Err((error, surface_texture)), }; + let mut surface_texture: CglSurfaceTexture = surface_texture.try_into()?; + let gl = &concrete_context.gl; if let Some(texture) = surface_texture.texture_object.take() { unsafe { @@ -709,7 +720,7 @@ impl Device { } } - Ok(surface_texture.surface) + Ok(surface_texture.surface.into()) } /// Returns the OpenGL texture object containing the contents of this surface. @@ -717,7 +728,7 @@ impl Device { /// It is only legal to read from, not write to, this texture object. #[inline] pub fn surface_texture_object(&self, surface_texture: &SurfaceTexture) -> Option { - surface_texture.texture_object + surface_texture.cgl().ok()?.texture_object } /// Returns the OpenGL texture target needed to read from this surface texture. @@ -737,6 +748,7 @@ impl Device { /// `IncompatibleSurface` error is returned. pub fn present_surface(&self, context: &Context, surface: &mut Surface) -> Result<(), Error> { let context = context.cgl()?; + let surface: &mut CglSurface = surface.try_into()?; self.0.present_surface(&mut surface.system_surface)?; surface.bind_to_texture(&context.gl); Ok(()) @@ -750,6 +762,7 @@ impl Device { size: Size2D, ) -> Result<(), Error> { let concrete_context = context.cgl()?; + let surface: &mut CglSurface = surface.try_into()?; if concrete_context.id != surface.context_id { return Err(Error::IncompatibleSurface); } @@ -762,7 +775,7 @@ impl Device { pub(crate) fn resize_inner( &self, - surface: &mut Surface, + surface: &mut CglSurface, size: Size2D, gl: &Rc, context_attributes: ContextAttributes, @@ -831,13 +844,8 @@ impl Device { /// 0, the default framebuffer, depending on platform. #[inline] pub fn surface_info(&self, surface: &Surface) -> SurfaceInfo { - let system_surface_info = self.0.surface_info(&surface.system_surface); - SurfaceInfo { - size: system_surface_info.size, - id: system_surface_info.id, - context_id: surface.context_id, - framebuffer_object: surface.framebuffer_object, - } + let surface = surface.cgl().expect("Incompatible surface type"); + surface.info() } /// Returns the native `IOSurface` corresponding to this surface. @@ -845,6 +853,7 @@ impl Device { /// The reference count is increased on the `IOSurface` before returning. #[inline] pub fn native_surface(&self, surface: &Surface) -> NativeSurface { + let surface = surface.cgl().expect("Incompatible surface type"); self.0.native_surface(&surface.system_surface) } } diff --git a/src/cgl/surface.rs b/src/cgl/surface.rs index 0e12c373..04d3f29c 100644 --- a/src/cgl/surface.rs +++ b/src/cgl/surface.rs @@ -3,7 +3,7 @@ use crate::base::io_surface::surface::Surface as SystemSurface; use crate::context::ContextID; use crate::renderbuffers::Renderbuffers; -use crate::{gl, SurfaceID}; +use crate::{gl, SurfaceID, SurfaceInfo}; use cgl::{kCGLNoError, CGLErrorString, CGLGetCurrentContext, CGLTexImageIOSurface2D, GLenum}; use glow::Context as Gl; @@ -16,23 +16,8 @@ use std::rc::Rc; pub use crate::base::io_surface::surface::NativeSurface; -/// Represents a hardware buffer of pixels that can be rendered to via the CPU or GPU and either -/// displayed in a native widget or bound to a texture for reading. -/// -/// Surfaces come in two varieties: generic and widget surfaces. Generic surfaces can be bound to a -/// texture but cannot be displayed in a widget (without using other APIs such as Core Animation, -/// DirectComposition, or XPRESENT). Widget surfaces are the opposite: they can be displayed in a -/// widget but not bound to a texture. -/// -/// Surfaces are specific to a given context and cannot be rendered to from any context other than -/// the one they were created with. However, they can be *read* from any context on any thread (as -/// long as that context shares the same adapter and connection), by wrapping them in a -/// `SurfaceTexture`. -/// -/// Depending on the platform, each surface may be internally double-buffered. -/// -/// Surfaces must be destroyed with the `destroy_surface()` method, or a panic will occur. -pub struct Surface { +/// An implementation of [`crate::Surface`] for CGL platforms. +pub struct CglSurface { pub(crate) system_surface: SystemSurface, pub(crate) context_id: ContextID, pub(crate) framebuffer_object: Option, @@ -40,30 +25,22 @@ pub struct Surface { pub(crate) renderbuffers: Renderbuffers, } -/// Represents an OpenGL texture that wraps a surface. -/// -/// Reading from the associated OpenGL texture reads from the surface. It is undefined behavior to -/// write to such a texture (e.g. by binding it to a framebuffer and rendering to that -/// framebuffer). -/// -/// Surface textures are local to a context, but that context does not have to be the same context -/// as that associated with the underlying surface. The texture must be destroyed with the -/// `destroy_surface_texture()` method, or a panic will occur. -pub struct SurfaceTexture { - pub(crate) surface: Surface, +/// An implementation of [`crate::SurfaceTexture`] for CGL platforms. +pub struct CglSurfaceTexture { + pub(crate) surface: CglSurface, pub(crate) texture_object: Option, pub(crate) phantom: PhantomData<*const ()>, } -unsafe impl Send for Surface {} +unsafe impl Send for CglSurface {} -impl Debug for Surface { +impl Debug for CglSurface { fn fmt(&self, formatter: &mut Formatter) -> fmt::Result { write!(formatter, "Surface({:x})", self.id().0) } } -impl Debug for SurfaceTexture { +impl Debug for CglSurfaceTexture { fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { write!(f, "SurfaceTexture({:?})", self.surface) } @@ -106,7 +83,7 @@ pub(crate) fn surface_bind_to_gl_texture( } } -impl Surface { +impl CglSurface { #[inline] pub(crate) fn id(&self) -> SurfaceID { SurfaceID(&*self.system_surface.io_surface as *const IOSurfaceRef as usize) @@ -123,4 +100,15 @@ impl Surface { ); unsafe { gl.bind_texture(gl::TEXTURE_RECTANGLE, None) }; } + + /// Returns various information about the surface. + #[inline] + pub fn info(&self) -> SurfaceInfo { + SurfaceInfo { + size: self.system_surface.size, + id: self.id(), + context_id: self.context_id, + framebuffer_object: self.framebuffer_object, + } + } } diff --git a/src/chains.rs b/src/chains.rs index 00432778..1fbd1fd7 100644 --- a/src/chains.rs +++ b/src/chains.rs @@ -22,8 +22,10 @@ #![allow(missing_docs)] -use crate::device::Device as DeviceAPI; -use crate::{Context, ContextID, Error, SurfaceAccess, SurfaceInfo, SurfaceType}; +use crate::{ + Context, ContextID, Device, Error, Surface, SurfaceAccess, SurfaceInfo, SurfaceTexture, + SurfaceType, +}; use euclid::default::Size2D; use fnv::{FnvHashMap, FnvHashSet}; use glow as gl; @@ -37,7 +39,7 @@ use std::mem; use std::sync::{Arc, Mutex, MutexGuard, RwLock, RwLockReadGuard, RwLockWriteGuard}; // The data stored for each swap chain. -struct SwapChainData { +struct SwapChainData { // The size of the back buffer size: Size2D, // The id of the producer context @@ -45,11 +47,11 @@ struct SwapChainData { // The surface access mode for the context. surface_access: SurfaceAccess, // The back buffer of the swap chain. - back_buffer: BackBuffer, + back_buffer: BackBuffer, // Some if the producing context has finished drawing a new front buffer, ready to be displayed. - pending_surface: Option, + pending_surface: Option, // All of the surfaces that have already been displayed, ready to be recycled. - recycled_surfaces: Vec, + recycled_surfaces: Vec, } pub enum PreserveBuffer<'a> { @@ -57,19 +59,15 @@ pub enum PreserveBuffer<'a> { No, } -enum BackBuffer { +enum BackBuffer { Attached, - Detached(Device::Surface), + Detached(Surface), TakenAttached, TakenDetached, } -impl BackBuffer { - fn take_surface( - &mut self, - device: &Device, - context: &mut Context, - ) -> Result { +impl BackBuffer { + fn take_surface(&mut self, device: &Device, context: &mut Context) -> Result { let new_back_buffer = match self { BackBuffer::Attached => BackBuffer::TakenAttached, BackBuffer::Detached(_) => BackBuffer::TakenDetached, @@ -86,7 +84,7 @@ impl BackBuffer { &mut self, device: &Device, context: &mut Context, - ) -> Result { + ) -> Result { let surface = self.take_surface(device, context)?; device .create_surface_texture(context, surface) @@ -99,7 +97,7 @@ impl BackBuffer { &mut self, device: &Device, context: &mut Context, - surface: Device::Surface, + surface: Surface, ) -> Result<(), Error> { let new_back_buffer = match self { BackBuffer::TakenAttached => { @@ -120,7 +118,7 @@ impl BackBuffer { &mut self, device: &Device, context: &mut Context, - surface_texture: Device::SurfaceTexture, + surface_texture: SurfaceTexture, ) -> Result<(), Error> { let surface = device .destroy_surface_texture(context, surface_texture) @@ -129,7 +127,7 @@ impl BackBuffer { } } -impl SwapChainData { +impl SwapChainData { // Returns `Ok` if `context` is the producer context for this swap chain. fn validate_context(&self, device: &Device, context: &Context) -> Result<(), Error> { if self.context_id == device.context_id(context) { @@ -232,7 +230,7 @@ impl SwapChainData { &mut self, device: &Device, context: &mut Context, - other: &mut SwapChainData, + other: &mut SwapChainData, ) -> Result<(), Error> { self.validate_context(device, context)?; other.validate_context(device, context)?; @@ -293,7 +291,7 @@ impl SwapChainData { &mut self, device: &Device, context: &mut Context, - ) -> Result { + ) -> Result { self.validate_context(device, context)?; self.back_buffer.take_surface_texture(device, context) } @@ -304,7 +302,7 @@ impl SwapChainData { &mut self, device: &Device, context: &mut Context, - surface_texture: Device::SurfaceTexture, + surface_texture: SurfaceTexture, ) -> Result<(), Error> { self.validate_context(device, context)?; self.back_buffer @@ -314,7 +312,7 @@ impl SwapChainData { // Take the current front buffer. // Returns the most recent recycled surface if there is no current front buffer. // Called by a consumer. - fn take_surface(&mut self) -> Option { + fn take_surface(&mut self) -> Option { self.pending_surface .take() .or_else(|| self.recycled_surfaces.pop()) @@ -323,13 +321,13 @@ impl SwapChainData { // Take the current front buffer. // Returns `None` if there is no current front buffer. // Called by a consumer. - fn take_pending_surface(&mut self) -> Option { + fn take_pending_surface(&mut self) -> Option { self.pending_surface.take() } // Recycle the current front buffer. // Called by a consumer. - fn recycle_surface(&mut self, surface: Device::Surface) { + fn recycle_surface(&mut self, surface: Surface) { self.recycled_surfaces.push(surface) } @@ -468,18 +466,18 @@ impl SwapChainData { } /// A thread-safe swap chain. -pub struct SwapChain(Arc>>); +pub struct SwapChain(Arc>); // We can't derive Clone unfortunately -impl Clone for SwapChain { +impl Clone for SwapChain { fn clone(&self) -> Self { SwapChain(self.0.clone()) } } -impl SwapChain { +impl SwapChain { // Guarantee unique access to the swap chain data - fn lock(&self) -> MutexGuard<'_, SwapChainData> { + fn lock(&self) -> MutexGuard<'_, SwapChainData> { self.0.lock().unwrap_or_else(|err| err.into_inner()) } @@ -503,7 +501,7 @@ impl SwapChain { &self, device: &Device, context: &mut Context, - other: &SwapChain, + other: &SwapChain, ) -> Result<(), Error> { self.lock() .take_attachment_from(device, context, &mut *other.lock()) @@ -535,7 +533,7 @@ impl SwapChain { &self, device: &Device, context: &mut Context, - ) -> Result { + ) -> Result { self.lock().take_surface_texture(device, context) } @@ -545,7 +543,7 @@ impl SwapChain { &self, device: &Device, context: &mut Context, - surface_texture: Device::SurfaceTexture, + surface_texture: SurfaceTexture, ) -> Result<(), Error> { self.lock() .recycle_surface_texture(device, context, surface_texture) @@ -554,7 +552,7 @@ impl SwapChain { /// Take the current front buffer. /// Returns `None` if there is no current front buffer. /// Called by a consumer. - pub fn take_pending_surface(&self) -> Option { + pub fn take_pending_surface(&self) -> Option { self.lock().take_pending_surface() } @@ -588,7 +586,7 @@ impl SwapChain { device: &Device, context: &mut Context, surface_access: SurfaceAccess, - ) -> Result, Error> { + ) -> Result { let size = device.context_surface_info(context).unwrap().unwrap().size; Ok(SwapChain(Arc::new(Mutex::new(SwapChainData { size, @@ -606,7 +604,7 @@ impl SwapChain { context: &mut Context, surface_access: SurfaceAccess, size: Size2D, - ) -> Result, Error> { + ) -> Result { let surface_type = SurfaceType::Generic { size }; let surface = device.create_surface(context, surface_access, surface_type)?; Ok(SwapChain(Arc::new(Mutex::new(SwapChainData { @@ -620,38 +618,32 @@ impl SwapChain { } } -impl SwapChainAPI for SwapChain -where - Device: 'static + DeviceAPI, - Device::Surface: Send, -{ - type Surface = Device::Surface; - +impl SwapChainAPI for SwapChain { /// Take the current front buffer. /// Returns the most recent recycled surface if there is no current front buffer. /// Called by a consumer. - fn take_surface(&self) -> Option { + fn take_surface(&self) -> Option { self.lock().take_surface() } /// Recycle the current front buffer. /// Called by a consumer. - fn recycle_surface(&self, surface: Device::Surface) { + fn recycle_surface(&self, surface: Surface) { self.lock().recycle_surface(surface) } } /// A thread-safe collection of swap chains. #[derive(Default)] -pub struct SwapChains { +pub struct SwapChains { // The swap chain ids, indexed by context id ids: Arc>>>, // The swap chains, indexed by swap chain id - table: Arc>>>, + table: Arc>>, } // We can't derive Clone unfortunately -impl Clone for SwapChains { +impl Clone for SwapChains { fn clone(&self) -> Self { SwapChains { ids: self.ids.clone(), @@ -660,13 +652,12 @@ impl Clone for SwapChains SwapChains +impl SwapChains where SwapChainID: Clone + Eq + Hash + Debug, - Device: DeviceAPI, { /// Create a new collection. - pub fn new() -> SwapChains { + pub fn new() -> SwapChains { SwapChains { ids: Arc::new(Mutex::new(FnvHashMap::default())), table: Arc::new(RwLock::new(FnvHashMap::default())), @@ -679,12 +670,12 @@ where } // Lock the lookup table - fn table(&self) -> RwLockReadGuard<'_, FnvHashMap>> { + fn table(&self) -> RwLockReadGuard<'_, FnvHashMap> { self.table.read().unwrap_or_else(|err| err.into_inner()) } // Lock the lookup table for writing - fn table_mut(&self) -> RwLockWriteGuard<'_, FnvHashMap>> { + fn table_mut(&self) -> RwLockWriteGuard<'_, FnvHashMap> { self.table.write().unwrap_or_else(|err| err.into_inner()) } @@ -760,7 +751,7 @@ where &self, device: &Device, context: &mut Context, - ) -> impl Iterator)> { + ) -> impl Iterator { self.ids() .get(&device.context_id(context)) .iter() @@ -771,17 +762,12 @@ where } } -impl SwapChainsAPI for SwapChains +impl SwapChainsAPI for SwapChains where SwapChainID: 'static + Clone + Eq + Hash + Debug + Sync + Send, - Device: 'static + DeviceAPI, - Device::Surface: Send, { - type Surface = Device::Surface; - type SwapChain = SwapChain; - /// Get a swap chain - fn get(&self, id: SwapChainID) -> Option> { + fn get(&self, id: SwapChainID) -> Option { debug!("Getting swap chain {:?}", id); self.table().get(&id).cloned() } @@ -789,20 +775,15 @@ where /// The consumer's view of a swap chain pub trait SwapChainAPI: 'static + Clone + Send { - type Surface; - /// Take the current front buffer. - fn take_surface(&self) -> Option; + fn take_surface(&self) -> Option; /// Recycle the current front buffer. - fn recycle_surface(&self, surface: Self::Surface); + fn recycle_surface(&self, surface: Surface); } /// The consumer's view of a collection of swap chains pub trait SwapChainsAPI: 'static + Clone + Send { - type Surface; - type SwapChain: SwapChainAPI; - /// Get a swap chain - fn get(&self, id: SwapChainID) -> Option; + fn get(&self, id: SwapChainID) -> Option; } diff --git a/src/device.rs b/src/device.rs index 7f663bfa..674aee82 100644 --- a/src/device.rs +++ b/src/device.rs @@ -2,8 +2,8 @@ use super::connection::Connection as ConnectionInterface; use crate::{ - Adapter, Context, ContextAttributes, ContextDescriptor, ContextID, Error, GLApi, SurfaceAccess, - SurfaceInfo, SurfaceType, + Adapter, Context, ContextAttributes, ContextDescriptor, ContextID, Error, GLApi, Surface, + SurfaceAccess, SurfaceInfo, SurfaceTexture, SurfaceType, }; use euclid::default::Size2D; use glow::Texture; @@ -19,10 +19,6 @@ where { /// The connection type associated with this device. type Connection; - /// The surface type associated with this device. - type Surface; - /// The surface texture type associated with this device. - type SurfaceTexture; // device.rs @@ -102,17 +98,14 @@ where fn bind_surface_to_context( &self, context: &mut Context, - surface: Self::Surface, - ) -> Result<(), (Error, Self::Surface)>; + surface: Surface, + ) -> Result<(), (Error, Surface)>; /// Removes and returns any attached surface from this context. /// /// Any pending OpenGL commands targeting this surface will be automatically flushed, so the /// surface is safe to read from immediately when this function returns. - fn unbind_surface_from_context( - &self, - context: &mut Context, - ) -> Result, Error>; + fn unbind_surface_from_context(&self, context: &mut Context) -> Result, Error>; /// Returns a unique ID representing a context. /// @@ -136,7 +129,7 @@ where context: &Context, surface_access: SurfaceAccess, surface_type: SurfaceType<'_>, - ) -> Result; + ) -> Result; /// Creates a surface texture from an existing generic surface for use with the given context. /// @@ -151,8 +144,8 @@ where fn create_surface_texture( &self, context: &mut Context, - surface: Self::Surface, - ) -> Result; + surface: Surface, + ) -> Result; /// Destroys a surface. /// @@ -161,11 +154,7 @@ where /// /// You must explicitly call this method to dispose of a surface. Otherwise, a panic occurs in /// the `drop` method. - fn destroy_surface( - &self, - context: &mut Context, - surface: &mut Self::Surface, - ) -> Result<(), Error>; + fn destroy_surface(&self, context: &mut Context, surface: &mut Surface) -> Result<(), Error>; /// Destroys a surface texture and returns the underlying surface. /// @@ -177,8 +166,8 @@ where fn destroy_surface_texture( &self, context: &mut Context, - surface_texture: Self::SurfaceTexture, - ) -> Result; + surface_texture: SurfaceTexture, + ) -> Result; /// Returns the OpenGL texture target needed to read from this surface texture. /// @@ -199,7 +188,7 @@ where /// /// The supplied context must match the context the surface was created with, or an /// `IncompatibleSurface` error is returned. - fn present_surface(&self, context: &Context, surface: &mut Self::Surface) -> Result<(), Error>; + fn present_surface(&self, context: &Context, surface: &mut Surface) -> Result<(), Error>; /// If the currently bound surface is a widget surface, resize it, fn resize_bound_surface(&self, context: &mut Context, size: Size2D) -> Result<(), Error>; @@ -208,7 +197,7 @@ where fn resize_surface( &self, context: &Context, - surface: &mut Self::Surface, + surface: &mut Surface, size: Size2D, ) -> Result<(), Error>; @@ -218,10 +207,10 @@ where /// Before rendering to a surface attached to a context, you must call `glBindFramebuffer()` /// on the framebuffer object returned by this function. This framebuffer object may or not be /// 0, the default framebuffer, depending on platform. - fn surface_info(&self, surface: &Self::Surface) -> SurfaceInfo; + fn surface_info(&self, surface: &Surface) -> SurfaceInfo; /// Returns the OpenGL texture object containing the contents of this surface. /// /// It is only legal to read from, not write to, this texture object. - fn surface_texture_object(&self, surface_texture: &Self::SurfaceTexture) -> Option; + fn surface_texture_object(&self, surface_texture: &SurfaceTexture) -> Option; } diff --git a/src/hardware_buffer/context.rs b/src/hardware_buffer/context.rs index 0fb4bc57..f1f1e6ed 100644 --- a/src/hardware_buffer/context.rs +++ b/src/hardware_buffer/context.rs @@ -1,10 +1,10 @@ //! OpenGL rendering contexts. -use super::surface::Surface; use crate::base::egl::surface::ExternalEGLSurfaces; use crate::context::ContextID; use crate::egl; use crate::egl::types::{EGLContext, EGLSurface}; +use crate::hardware_buffer::surface::HardwareBufferSurface; use crate::surface::Framebuffer; use crate::Gl; use std::thread; @@ -17,7 +17,7 @@ pub struct HardwareBufferContext { pub(crate) id: ContextID, pub(crate) pbuffer: EGLSurface, pub(crate) gl: Gl, - pub(crate) framebuffer: Framebuffer, + pub(crate) framebuffer: Framebuffer, pub(crate) context_is_owned: bool, } diff --git a/src/hardware_buffer/device.rs b/src/hardware_buffer/device.rs index 9449b6a3..c38a1a6d 100644 --- a/src/hardware_buffer/device.rs +++ b/src/hardware_buffer/device.rs @@ -9,7 +9,7 @@ use crate::base::egl::error::ToWindowingApiError; use crate::base::egl::surface::ExternalEGLSurfaces; use crate::context::{ContextID, CREATE_CONTEXT_MUTEX}; use crate::egl::types::{EGLConfig, EGLDisplay, EGLint}; -use crate::hardware_buffer::surface::SurfaceObjects; +use crate::hardware_buffer::surface::{HardwareBufferSurface, SurfaceObjects}; use crate::surface::Framebuffer; use crate::Adapter; use crate::{egl, ContextDescriptor, EglContextDescriptor, Surface}; @@ -207,16 +207,14 @@ impl Device { /// The context must have been created on this device. pub fn destroy_context(&self, context: &mut Context) -> Result<(), Error> { { - let framebuffer = { - let context: &mut HardwareBufferContext = context.try_into()?; - if context.egl_context == egl::NO_CONTEXT { - return Ok(()); - } - mem::replace(&mut context.framebuffer, Framebuffer::None) - }; + let context: &mut HardwareBufferContext = context.try_into()?; + if context.egl_context == egl::NO_CONTEXT { + return Ok(()); + } + let framebuffer = mem::replace(&mut context.framebuffer, Framebuffer::None); if let Framebuffer::Surface(mut target) = framebuffer { - self.destroy_surface(context, &mut target)?; + self.destroy_surface_inner(context, &mut target)?; } }; @@ -271,12 +269,12 @@ impl Device { let egl_context = context.egl_context; let (egl_draw_surface, egl_read_surface) = match context.framebuffer { - Framebuffer::Surface(Surface { + Framebuffer::Surface(HardwareBufferSurface { objects: SurfaceObjects::Window { egl_surface }, .. }) => (egl_surface, egl_surface), Framebuffer::External(ExternalEGLSurfaces { draw, read }) => (draw, read), - Framebuffer::Surface(Surface { + Framebuffer::Surface(HardwareBufferSurface { objects: SurfaceObjects::HardwareBuffer { .. }, .. }) => (context.pbuffer, context.pbuffer), @@ -322,14 +320,19 @@ impl Device { Ok(context) => context, Err(error) => return Err((error, new_surface)), }; + let new_surface: HardwareBufferSurface = new_surface.try_into()?; if context.id != new_surface.context_id { - return Err((Error::IncompatibleSurface, new_surface)); + return Err((Error::IncompatibleSurface, new_surface.into())); } match context.framebuffer { - Framebuffer::External { .. } => return Err((Error::ExternalRenderTarget, new_surface)), - Framebuffer::Surface(_) => return Err((Error::SurfaceAlreadyBound, new_surface)), + Framebuffer::External { .. } => { + return Err((Error::ExternalRenderTarget, new_surface.into())) + } + Framebuffer::Surface(_) => { + return Err((Error::SurfaceAlreadyBound, new_surface.into())) + } Framebuffer::None => {} } @@ -361,7 +364,7 @@ impl Device { }; match mem::replace(&mut context.framebuffer, Framebuffer::None) { - Framebuffer::Surface(surface) => Ok(Some(surface)), + Framebuffer::Surface(surface) => Ok(Some(surface.into())), Framebuffer::External { .. } | Framebuffer::None => unreachable!(), } } @@ -392,6 +395,36 @@ impl Device { Ok(()) } + /// Destroys a surface. + /// + /// The supplied context must be the context the surface is associated with, or this returns + /// an `IncompatibleSurface` error. + /// + /// You must explicitly call this method to dispose of a surface. Otherwise, a panic occurs in + /// the `drop` method. + pub fn destroy_surface( + &self, + context: &mut Context, + surface: &mut Surface, + ) -> Result<(), Error> { + let context: &mut HardwareBufferContext = context.try_into()?; + let surface: &mut HardwareBufferSurface = surface.try_into()?; + self.destroy_surface_inner(context, surface) + } + + /// Returns various information about the surface, including the framebuffer object needed to + /// render to this surface. + /// + /// Before rendering to a surface attached to a context, you must call `glBindFramebuffer()` + /// on the framebuffer object returned by this function. This framebuffer object may or not be + /// 0, the default framebuffer, depending on platform. + pub fn surface_info(&self, surface: &Surface) -> SurfaceInfo { + surface + .hardware_buffer() + .expect("Incompatible surface type") + .info() + } + /// Returns the attributes that the context descriptor was created with. pub fn context_descriptor_attributes( &self, @@ -457,7 +490,7 @@ impl Device { match context.framebuffer { Framebuffer::None => Ok(None), Framebuffer::External { .. } => Err(Error::ExternalRenderTarget), - Framebuffer::Surface(ref surface) => Ok(Some(self.surface_info(surface))), + Framebuffer::Surface(ref surface) => Ok(Some(surface.info())), } } @@ -467,12 +500,12 @@ impl Device { .hardware_buffer() .expect("Passed incorrect context type"); let (egl_draw_surface, egl_read_surface) = match context.framebuffer { - Framebuffer::Surface(Surface { + Framebuffer::Surface(HardwareBufferSurface { objects: SurfaceObjects::Window { egl_surface }, .. }) => (egl_surface, egl_surface), Framebuffer::External(ExternalEGLSurfaces { draw, read }) => (draw, read), - Framebuffer::Surface(Surface { + Framebuffer::Surface(HardwareBufferSurface { objects: SurfaceObjects::HardwareBuffer { .. }, .. }) => (context.pbuffer, context.pbuffer), diff --git a/src/hardware_buffer/surface/android_surface.rs b/src/hardware_buffer/surface/android_surface.rs index 2cdf8857..7a54f532 100644 --- a/src/hardware_buffer/surface/android_surface.rs +++ b/src/hardware_buffer/surface/android_surface.rs @@ -12,7 +12,6 @@ use super::super::android_ffi::{ AHARDWAREBUFFER_USAGE_CPU_WRITE_NEVER, AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER, }; use super::super::device::Device; -use super::{Surface, SurfaceTexture}; use crate::base::egl::device::EGL_FUNCTIONS; use crate::base::egl::ffi::EGLImageKHR; use crate::base::egl::ffi::EGL_EXTENSION_FUNCTIONS; @@ -23,9 +22,11 @@ use crate::egl::types::{EGLSurface, EGLint}; use crate::gl; use crate::gl_utils; use crate::hardware_buffer::context::HardwareBufferContext; +use crate::hardware_buffer::surface::{HardwareBufferSurface, HardwareBufferSurfaceTexture}; use crate::renderbuffers::Renderbuffers; use crate::{egl, Context}; -use crate::{Error, SurfaceAccess, SurfaceID, SurfaceInfo, SurfaceType, WindowingApiError}; +use crate::{Error, SurfaceAccess, SurfaceType, WindowingApiError}; +use crate::{Surface, SurfaceTexture}; use euclid::default::Size2D; use glow::{HasContext, Texture}; @@ -125,7 +126,7 @@ impl Device { gl::FRAMEBUFFER_COMPLETE ); - Ok(Surface { + Ok(HardwareBufferSurface { size: *size, context_id: concrete_context.id, objects: SurfaceObjects::HardwareBuffer { @@ -136,7 +137,8 @@ impl Device { renderbuffers, }, destroyed: false, - }) + } + .into()) } } @@ -158,12 +160,13 @@ impl Device { ); assert_ne!(egl_surface, egl::NO_SURFACE); - Ok(Surface { + Ok(HardwareBufferSurface { context_id: context.id, size: Size2D::new(width, height), objects: SurfaceObjects::Window { egl_surface }, destroyed: false, - }) + } + .into()) }) } @@ -182,19 +185,20 @@ impl Device { context: &mut Context, surface: Surface, ) -> Result { + let surface: HardwareBufferSurface = surface.try_into()?; unsafe { match surface.objects { - SurfaceObjects::Window { .. } => Err((Error::WidgetAttached, surface)), + SurfaceObjects::Window { .. } => Err((Error::WidgetAttached, surface.into())), SurfaceObjects::HardwareBuffer { hardware_buffer, .. } => { let _guard = match self.temporarily_make_context_current(context) { Ok(guard) => guard, - Err(err) => return Err((err, surface)), + Err(err) => return Err((err, surface.into())), }; let context: &mut HardwareBufferContext = match context.try_into() { Ok(context) => context, - Err(error) => return Err((error, surface)), + Err(error) => return Err((error, surface.into())), }; let gl = &context.gl; @@ -204,12 +208,13 @@ impl Device { gl, local_egl_image, ); - Ok(SurfaceTexture { + Ok(HardwareBufferSurfaceTexture { surface, local_egl_image, texture_object: Some(texture_object), phantom: PhantomData, - }) + } + .into()) } } } @@ -249,19 +254,11 @@ impl Device { egl_image } - /// Destroys a surface. - /// - /// The supplied context must be the context the surface is associated with, or this returns - /// an `IncompatibleSurface` error. - /// - /// You must explicitly call this method to dispose of a surface. Otherwise, a panic occurs in - /// the `drop` method. - pub fn destroy_surface( + pub(crate) fn destroy_surface_inner( &self, - context: &mut Context, - surface: &mut Surface, + context: &mut HardwareBufferContext, + surface: &mut HardwareBufferSurface, ) -> Result<(), Error> { - let context: &mut HardwareBufferContext = context.try_into()?; if context.id != surface.context_id { return Err(Error::IncompatibleSurface); } @@ -318,14 +315,16 @@ impl Device { pub fn destroy_surface_texture( &self, context: &mut Context, - mut surface_texture: SurfaceTexture, + surface_texture: SurfaceTexture, ) -> Result { let _guard = self.temporarily_make_context_current(context); let context: &mut HardwareBufferContext = match context.try_into() { Ok(context) => context, Err(error) => return Err((error, surface_texture)), }; + let mut surface_texture: HardwareBufferSurfaceTexture = surface_texture.try_into()?; let gl = &context.gl; + unsafe { if let Some(texture) = surface_texture.texture_object.take() { gl.delete_texture(texture); @@ -340,7 +339,7 @@ impl Device { surface_texture.local_egl_image = EGL_NO_IMAGE_KHR; } - Ok(surface_texture.surface) + Ok(surface_texture.surface.into()) } /// Returns a pointer to the underlying surface data for reading or writing by the CPU. @@ -358,41 +357,12 @@ impl Device { SURFACE_GL_TEXTURE_TARGET } - /// Returns various information about the surface, including the framebuffer object needed to - /// render to this surface. - /// - /// Before rendering to a surface attached to a context, you must call `glBindFramebuffer()` - /// on the framebuffer object returned by this function. This framebuffer object may or not be - /// 0, the default framebuffer, depending on platform. - pub fn surface_info(&self, surface: &Surface) -> SurfaceInfo { - SurfaceInfo { - size: surface.size, - id: surface.id(), - context_id: surface.context_id, - framebuffer_object: match surface.objects { - SurfaceObjects::HardwareBuffer { - framebuffer_object, .. - } => framebuffer_object, - SurfaceObjects::Window { .. } => None, - }, - } - } - /// Returns the OpenGL texture object containing the contents of this surface. /// /// It is only legal to read from, not write to, this texture object. #[inline] pub fn surface_texture_object(&self, surface_texture: &SurfaceTexture) -> Option { - surface_texture.texture_object - } -} - -impl Surface { - pub(super) fn id(&self) -> SurfaceID { - match self.objects { - SurfaceObjects::HardwareBuffer { egl_image, .. } => SurfaceID(egl_image as usize), - SurfaceObjects::Window { egl_surface } => SurfaceID(egl_surface as usize), - } + surface_texture.hardware_buffer().ok()?.texture_object } } diff --git a/src/hardware_buffer/surface/mod.rs b/src/hardware_buffer/surface/mod.rs index b383f96e..9b6fc2e2 100644 --- a/src/hardware_buffer/surface/mod.rs +++ b/src/hardware_buffer/surface/mod.rs @@ -3,7 +3,7 @@ use crate::base::egl::ffi::EGLImageKHR; use crate::context::ContextID; use crate::hardware_buffer::context::HardwareBufferContext; -use crate::{Context, Device, Error}; +use crate::{Context, Device, Error, Surface, SurfaceID, SurfaceInfo}; use crate::base::egl::device::EGL_FUNCTIONS; use euclid::default::Size2D; @@ -24,54 +24,31 @@ mod ohos_surface; #[cfg(ohos_platform)] pub use ohos_surface::*; -/// Represents a hardware buffer of pixels that can be rendered to via the CPU or GPU and either -/// displayed in a native widget or bound to a texture for reading. -/// -/// Surfaces come in two varieties: generic and widget surfaces. Generic surfaces can be bound to a -/// texture but cannot be displayed in a widget (without using other APIs such as Core Animation, -/// DirectComposition, or XPRESENT). Widget surfaces are the opposite: they can be displayed in a -/// widget but not bound to a texture. -/// -/// Surfaces are specific to a given context and cannot be rendered to from any context other than -/// the one they were created with. However, they can be *read* from any context on any thread (as -/// long as that context shares the same adapter and connection), by wrapping them in a -/// `SurfaceTexture`. -/// -/// Depending on the platform, each surface may be internally double-buffered. -/// -/// Surfaces must be destroyed with the `destroy_surface()` method, or a panic will occur. -pub struct Surface { +/// An implementation of [`crate::Surface`] for Android and OHOS platforms. +pub struct HardwareBufferSurface { pub(crate) context_id: ContextID, pub(crate) size: Size2D, pub(crate) objects: SurfaceObjects, pub(crate) destroyed: bool, } -/// Represents an OpenGL texture that wraps a surface. -/// -/// Reading from the associated OpenGL texture reads from the surface. It is undefined behavior to -/// write to such a texture (e.g. by binding it to a framebuffer and rendering to that -/// framebuffer). -/// -/// Surface textures are local to a context, but that context does not have to be the same context -/// as that associated with the underlying surface. The texture must be destroyed with the -/// `destroy_surface_texture()` method, or a panic will occur. -pub struct SurfaceTexture { - pub(crate) surface: Surface, +/// An implementation of [`crate::SurfaceTexture`] for Android and OHOS platforms. +pub struct HardwareBufferSurfaceTexture { + pub(crate) surface: HardwareBufferSurface, pub(crate) local_egl_image: EGLImageKHR, pub(crate) texture_object: Option, pub(crate) phantom: PhantomData<*const ()>, } -unsafe impl Send for Surface {} +unsafe impl Send for HardwareBufferSurface {} -impl Debug for Surface { +impl Debug for HardwareBufferSurface { fn fmt(&self, formatter: &mut Formatter) -> fmt::Result { write!(formatter, "Surface({:x})", self.id().0) } } -impl Drop for Surface { +impl Drop for HardwareBufferSurface { fn drop(&mut self) { if !self.destroyed && !thread::panicking() { panic!("Should have destroyed the surface first with `destroy_surface()`!") @@ -79,13 +56,34 @@ impl Drop for Surface { } } -impl Surface { +impl HardwareBufferSurface { pub(crate) fn resize(&mut self, size: Size2D) { self.size = size; } + + pub(crate) fn info(&self) -> SurfaceInfo { + SurfaceInfo { + size: self.size, + id: self.id(), + context_id: self.context_id, + framebuffer_object: match self.objects { + SurfaceObjects::HardwareBuffer { + framebuffer_object, .. + } => framebuffer_object, + SurfaceObjects::Window { .. } => None, + }, + } + } + + pub(crate) fn id(&self) -> SurfaceID { + match self.objects { + SurfaceObjects::HardwareBuffer { egl_image, .. } => SurfaceID(egl_image as usize), + SurfaceObjects::Window { egl_surface } => SurfaceID(egl_surface as usize), + } + } } -impl Debug for SurfaceTexture { +impl Debug for HardwareBufferSurfaceTexture { fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { write!(f, "SurfaceTexture({:?})", self.surface) } @@ -100,13 +98,13 @@ impl Device { /// The supplied context must match the context the surface was created with, or an /// `IncompatibleSurface` error is returned. pub fn present_surface(&self, context: &Context, surface: &mut Surface) -> Result<(), Error> { - self.present_surface_inner(context.hardware_buffer()?, surface) + self.present_surface_inner(context.hardware_buffer()?, surface.hardware_buffer()?) } pub(crate) fn present_surface_inner( &self, context: &HardwareBufferContext, - surface: &Surface, + surface: &HardwareBufferSurface, ) -> Result<(), Error> { if context.id != surface.context_id { return Err(Error::IncompatibleSurface); @@ -130,6 +128,7 @@ impl Device { surface: &mut Surface, size: Size2D, ) -> Result<(), Error> { + let surface: &mut HardwareBufferSurface = surface.try_into()?; surface.resize(size); Ok(()) } diff --git a/src/hardware_buffer/surface/ohos_surface.rs b/src/hardware_buffer/surface/ohos_surface.rs index 4a020cd6..15f9de80 100644 --- a/src/hardware_buffer/surface/ohos_surface.rs +++ b/src/hardware_buffer/surface/ohos_surface.rs @@ -13,14 +13,14 @@ use crate::base::egl::ffi::EGLImageKHR; use crate::base::egl::ffi::EGL_EXTENSION_FUNCTIONS; use crate::base::egl::ffi::EGL_IMAGE_PRESERVED_KHR; use crate::base::egl::ffi::EGL_NO_IMAGE_KHR; -use crate::egl; use crate::egl::types::{EGLSurface, EGLint}; -use crate::gl; -use crate::gl_utils; use crate::hardware_buffer::context::HardwareBufferContext; +use crate::hardware_buffer::surface::HardwareBufferSurface; +use crate::hardware_buffer::surface::HardwareBufferSurfaceTexture; use crate::renderbuffers::Renderbuffers; -use crate::Context; -use crate::{Error, SurfaceAccess, SurfaceID, SurfaceInfo, SurfaceType}; +use crate::{ + egl, gl, gl_utils, Context, Error, Surface, SurfaceAccess, SurfaceTexture, SurfaceType, +}; use super::super::device::Device; use super::super::ohos_ffi::{ @@ -29,7 +29,6 @@ use super::super::ohos_ffi::{ OH_NativeBuffer_Unreference, OH_NativeBuffer_Usage, OH_NativeWindow_NativeWindowHandleOpt, EGL_NATIVE_BUFFER_OHOS, }; -use super::{Surface, SurfaceTexture}; use raw_window_handle::RawWindowHandle; const SURFACE_GL_TEXTURE_TARGET: u32 = gl::TEXTURE_2D; @@ -118,7 +117,7 @@ impl Device { gl::FRAMEBUFFER_COMPLETE ); - Ok(Surface { + Ok(HardwareBufferSurface { size: *size, context_id: concrete_context.id, objects: SurfaceObjects::HardwareBuffer { @@ -129,7 +128,8 @@ impl Device { renderbuffers, }, destroyed: false, - }) + } + .into()) } } @@ -162,12 +162,13 @@ impl Device { ); assert_ne!(egl_surface, egl::NO_SURFACE); - Ok(Surface { + Ok(HardwareBufferSurface { context_id: context.id, size: Size2D::new(width, height), objects: SurfaceObjects::Window { egl_surface }, destroyed: false, - }) + } + .into()) }) } @@ -186,20 +187,23 @@ impl Device { context: &mut Context, surface: Surface, ) -> Result { + let surface: HardwareBufferSurface = surface.try_into()?; unsafe { match surface.objects { - SurfaceObjects::Window { .. } => return Err((Error::WidgetAttached, surface)), + SurfaceObjects::Window { .. } => { + return Err((Error::WidgetAttached, surface.into())) + } SurfaceObjects::HardwareBuffer { hardware_buffer, .. } => { let _guard = match self.temporarily_make_context_current(context) { Ok(guard) => guard, - Err(err) => return Err((err, surface)), + Err(err) => return Err((err, surface.into())), }; let context: &mut HardwareBufferContext = match context.try_into() { Ok(context) => context, - Err(error) => return Err((error, surface)), + Err(error) => return Err((error, surface.into())), }; let gl = &context.gl; @@ -208,12 +212,13 @@ impl Device { gl, local_egl_image, ); - Ok(SurfaceTexture { + Ok(HardwareBufferSurfaceTexture { surface, local_egl_image, texture_object: Some(texture_object), phantom: PhantomData, - }) + } + .into()) } } } @@ -247,19 +252,11 @@ impl Device { egl_image } - /// Destroys a surface. - /// - /// The supplied context must be the context the surface is associated with, or this returns - /// an `IncompatibleSurface` error. - /// - /// You must explicitly call this method to dispose of a surface. Otherwise, a panic occurs in - /// the `drop` method. - pub fn destroy_surface( + pub(crate) fn destroy_surface_inner( &self, - context: &mut Context, - surface: &mut Surface, + context: &mut HardwareBufferContext, + surface: &mut HardwareBufferSurface, ) -> Result<(), Error> { - let context: &mut HardwareBufferContext = context.try_into()?; if context.id != surface.context_id { return Err(Error::IncompatibleSurface); } @@ -317,7 +314,7 @@ impl Device { pub fn destroy_surface_texture( &self, context: &mut Context, - mut surface_texture: SurfaceTexture, + surface_texture: SurfaceTexture, ) -> Result { let _guard = self.temporarily_make_context_current(context); @@ -325,7 +322,9 @@ impl Device { Ok(context) => context, Err(error) => return Err((error, surface_texture)), }; + let mut surface_texture: HardwareBufferSurfaceTexture = surface_texture.try_into()?; let gl = &context.gl; + unsafe { if let Some(texture) = surface_texture.texture_object.take() { gl.delete_texture(texture); @@ -340,7 +339,7 @@ impl Device { surface_texture.local_egl_image = EGL_NO_IMAGE_KHR; } - Ok(surface_texture.surface) + Ok(surface_texture.surface.into()) } /// Returns a pointer to the underlying surface data for reading or writing by the CPU. @@ -358,41 +357,12 @@ impl Device { SURFACE_GL_TEXTURE_TARGET } - /// Returns various information about the surface, including the framebuffer object needed to - /// render to this surface. - /// - /// Before rendering to a surface attached to a context, you must call `glBindFramebuffer()` - /// on the framebuffer object returned by this function. This framebuffer object may or not be - /// 0, the default framebuffer, depending on platform. - pub fn surface_info(&self, surface: &Surface) -> SurfaceInfo { - SurfaceInfo { - size: surface.size, - id: surface.id(), - context_id: surface.context_id, - framebuffer_object: match surface.objects { - SurfaceObjects::HardwareBuffer { - framebuffer_object, .. - } => framebuffer_object, - SurfaceObjects::Window { .. } => None, - }, - } - } - /// Returns the OpenGL texture object containing the contents of this surface. /// /// It is only legal to read from, not write to, this texture object. #[inline] pub fn surface_texture_object(&self, surface_texture: &SurfaceTexture) -> Option { - surface_texture.texture_object - } -} - -impl Surface { - pub(super) fn id(&self) -> SurfaceID { - match self.objects { - SurfaceObjects::HardwareBuffer { egl_image, .. } => SurfaceID(egl_image as usize), - SurfaceObjects::Window { egl_surface } => SurfaceID(egl_surface as usize), - } + surface_texture.hardware_buffer().ok()?.texture_object } } diff --git a/src/lib.rs b/src/lib.rs index dfa8b0f7..bafcb2c2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -38,6 +38,7 @@ pub mod mesa_surfaceless; pub mod multi; mod renderbuffers; mod surface; +mod surface_texture; #[cfg(all(x11_platform, not(wayland_default)))] pub mod unix; #[cfg(wayland_platform)] @@ -64,11 +65,11 @@ pub use crate::context::{Context, ContextAttributeFlags, ContextAttributes, Cont pub use crate::context_descriptor::ContextDescriptor; pub use crate::error::{Error, WindowingApiError}; pub use crate::info::{GLApi, GLVersion}; -pub use crate::surface::{SurfaceAccess, SurfaceID, SurfaceInfo, SurfaceType, SystemSurfaceInfo}; +pub use crate::surface::{Surface, SurfaceAccess, SurfaceID, SurfaceInfo, SurfaceType}; +pub use crate::surface_texture::SurfaceTexture; pub use adapter::{Adapter, AdapterPreferences, PowerPreference, RenderingPreference}; pub use default::connection::Connection; pub use default::device::Device; -pub use default::surface::{Surface, SurfaceTexture}; pub(crate) use glow::{self as gl, Context as Gl}; pub(crate) use macros::implement_interfaces; diff --git a/src/macros.rs b/src/macros.rs index c23cf7f7..cae1e72f 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -36,7 +36,6 @@ macro_rules! implement_interfaces { mod implementation { use super::connection::Connection; use super::device::Device; - use super::surface::{Surface, SurfaceTexture}; use euclid::default::Size2D; use glow::Texture; use std::os::raw::c_void; @@ -46,8 +45,8 @@ macro_rules! implement_interfaces { use $crate::info::GLApi; use $crate::Error; use $crate::{ - Context, ContextAttributes, ContextDescriptor, ContextID, SurfaceAccess, - SurfaceInfo, SurfaceType, + Context, ContextAttributes, ContextDescriptor, ContextID, Surface, SurfaceAccess, + SurfaceInfo, SurfaceTexture, SurfaceType, }; impl ConnectionInterface for Connection { @@ -86,8 +85,6 @@ macro_rules! implement_interfaces { impl DeviceInterface for Device { type Connection = Connection; - type Surface = Surface; - type SurfaceTexture = SurfaceTexture; // device.rs @@ -162,8 +159,8 @@ macro_rules! implement_interfaces { fn bind_surface_to_context( &self, context: &mut Context, - surface: Self::Surface, - ) -> Result<(), (Error, Self::Surface)> { + surface: Surface, + ) -> Result<(), (Error, Surface)> { Device::bind_surface_to_context(self, context, surface) } @@ -171,7 +168,7 @@ macro_rules! implement_interfaces { fn unbind_surface_from_context( &self, context: &mut Context, - ) -> Result, Error> { + ) -> Result, Error> { Device::unbind_surface_from_context(self, context) } @@ -196,7 +193,7 @@ macro_rules! implement_interfaces { context: &Context, surface_access: SurfaceAccess, surface_type: SurfaceType<'_>, - ) -> Result { + ) -> Result { Device::create_surface(self, context, surface_access, surface_type) } @@ -204,8 +201,8 @@ macro_rules! implement_interfaces { fn create_surface_texture( &self, context: &mut Context, - surface: Self::Surface, - ) -> Result { + surface: Surface, + ) -> Result { Device::create_surface_texture(self, context, surface) } @@ -213,7 +210,7 @@ macro_rules! implement_interfaces { fn destroy_surface( &self, context: &mut Context, - surface: &mut Self::Surface, + surface: &mut Surface, ) -> Result<(), Error> { Device::destroy_surface(self, context, surface) } @@ -222,8 +219,8 @@ macro_rules! implement_interfaces { fn destroy_surface_texture( &self, context: &mut Context, - surface_texture: Self::SurfaceTexture, - ) -> Result { + surface_texture: SurfaceTexture, + ) -> Result { Device::destroy_surface_texture(self, context, surface_texture) } @@ -241,7 +238,7 @@ macro_rules! implement_interfaces { fn present_surface( &self, context: &Context, - surface: &mut Self::Surface, + surface: &mut Surface, ) -> Result<(), Error> { Device::present_surface(self, context, surface) } @@ -266,14 +263,14 @@ macro_rules! implement_interfaces { } #[inline] - fn surface_info(&self, surface: &Self::Surface) -> SurfaceInfo { + fn surface_info(&self, surface: &Surface) -> SurfaceInfo { Device::surface_info(self, surface) } #[inline] fn surface_texture_object( &self, - surface_texture: &Self::SurfaceTexture, + surface_texture: &SurfaceTexture, ) -> Option { Device::surface_texture_object(self, surface_texture) } @@ -303,6 +300,17 @@ macro_rules! enum_conversion { } } + impl TryFrom<$enum> for $type { + type Error = (crate::Error, $enum); + fn try_from(value: $enum) -> Result<$type, Self::Error> { + #[allow(unreachable_patterns)] + match value { + $enum::$variant(inner) => Ok(inner), + _ => Err((crate::Error::$error, value)), + } + } + } + impl $enum { #[doc = concat!("Try to convert this generic [`", stringify!($enum), "`] into a [`", stringify!($type), "`].")] pub fn $name(&self) -> Result<&$type, crate::Error> { diff --git a/src/mesa_surfaceless/device.rs b/src/mesa_surfaceless/device.rs index c4fbf5a3..2f09cec8 100644 --- a/src/mesa_surfaceless/device.rs +++ b/src/mesa_surfaceless/device.rs @@ -8,8 +8,8 @@ use crate::context::ContextID; use crate::egl::types::EGLint; use crate::free_unix::adapter::FreeUnixAdapter; use crate::mesa_surfaceless::context::{NativeContext, SurfacelessMesaContext}; -use crate::mesa_surfaceless::surface::{Surface, SurfaceTexture}; -use crate::{egl, Adapter, Context, EglContextDescriptor}; +use crate::mesa_surfaceless::surface::{SurfacelessMesaSurface, SurfacelessMesaSurfaceTexture}; +use crate::{egl, Adapter, Context, EglContextDescriptor, Surface, SurfaceTexture}; use crate::{gl, ContextDescriptor}; use crate::{ContextAttributes, Gl, SurfaceInfo}; use crate::{Error, GLApi, SurfaceAccess, SurfaceType}; @@ -264,11 +264,12 @@ impl Device { Ok(context) => context, Err(error) => return Err((error, surface)), }; + let surface: SurfacelessMesaSurface = surface.try_into()?; unsafe { context .0 .bind_surface(self.native_connection.egl_display, surface.0) - .map_err(|(err, surface)| (err, Surface(surface))) + .map_err(|(err, surface)| (err, SurfacelessMesaSurface(surface).into())) } } @@ -285,7 +286,7 @@ impl Device { context .0 .unbind_surface(&context.1, self.native_connection.egl_display) - .map(|maybe_surface| maybe_surface.map(Surface)) + .map(|maybe_surface| maybe_surface.map(SurfacelessMesaSurface).map(Into::into)) } } @@ -358,14 +359,15 @@ impl Device { let context_attributes = self.context_descriptor_attributes(&context_descriptor); let context = context.surfaceless_mesa()?; - Ok(Surface(EGLBackedSurface::new_generic( + Ok(SurfacelessMesaSurface(EGLBackedSurface::new_generic( &context.1, self.native_connection.egl_display, context.0.egl_context, context.0.id, &context_attributes, size, - ))) + )) + .into()) } /// Creates a surface texture from an existing generic surface for use with the given context. @@ -392,10 +394,11 @@ impl Device { Ok(context) => context, Err(error) => return Err((error, surface)), }; + let surface: SurfacelessMesaSurface = surface.try_into()?; match surface.0.to_surface_texture(&context.1) { - Ok(surface_texture) => Ok(SurfaceTexture(surface_texture)), - Err((err, surface)) => Err((err, Surface(surface))), + Ok(surface_texture) => Ok(SurfacelessMesaSurfaceTexture(surface_texture).into()), + Err((err, surface)) => Err((err, SurfacelessMesaSurface(surface).into())), } } @@ -413,6 +416,7 @@ impl Device { ) -> Result<(), Error> { let egl_display = self.native_connection.egl_display; let context: &mut SurfacelessMesaContext = context.try_into()?; + let surface: &mut SurfacelessMesaSurface = surface.try_into()?; let window = surface.0.destroy(&context.1, egl_display, context.0.id)?; debug_assert!(window.is_none()); Ok(()) @@ -434,9 +438,12 @@ impl Device { Ok(concrete_context) => concrete_context, Err(error) => return Err((error, surface_texture)), }; + let surface_texture: SurfacelessMesaSurfaceTexture = surface_texture.try_into()?; match self.temporarily_make_context_current(context) { - Ok(_guard) => Ok(Surface(surface_texture.0.destroy(&concrete_context.1))), - Err(err) => Err((err, surface_texture)), + Ok(_guard) => { + Ok(SurfacelessMesaSurface(surface_texture.0.destroy(&concrete_context.1)).into()) + } + Err(err) => Err((err, surface_texture.into())), } } @@ -449,6 +456,7 @@ impl Device { /// `IncompatibleSurface` error is returned. pub fn present_surface(&self, context: &Context, surface: &mut Surface) -> Result<(), Error> { let context = context.surfaceless_mesa()?; + let surface: &mut SurfacelessMesaSurface = surface.try_into()?; surface .0 .present(self.native_connection.egl_display, context.0.egl_context) @@ -461,6 +469,7 @@ impl Device { surface: &mut Surface, size: Size2D, ) -> Result<(), Error> { + let surface: &mut SurfacelessMesaSurface = surface.try_into()?; surface.0.size = size; Ok(()) } @@ -486,6 +495,9 @@ impl Device { /// on the framebuffer object returned by this function. This framebuffer object may or not be /// 0, the default framebuffer, depending on platform. pub fn surface_info(&self, surface: &Surface) -> SurfaceInfo { + let surface = surface + .surfaceless_mesa() + .expect("Incompatible surface type"); surface.0.info() } @@ -494,6 +506,6 @@ impl Device { /// It is only legal to read from, not write to, this texture object. #[inline] pub fn surface_texture_object(&self, surface_texture: &SurfaceTexture) -> Option { - surface_texture.0.texture_object + surface_texture.surfaceless_mesa().ok()?.0.texture_object } } diff --git a/src/mesa_surfaceless/surface.rs b/src/mesa_surfaceless/surface.rs index f5c529ef..3b48177b 100644 --- a/src/mesa_surfaceless/surface.rs +++ b/src/mesa_surfaceless/surface.rs @@ -6,38 +6,15 @@ use crate::base::egl::surface::{EGLBackedSurface, EGLSurfaceTexture}; use std::marker::PhantomData; -/// Represents a hardware buffer of pixels that can be rendered to via the CPU or GPU and either -/// displayed in a native widget or bound to a texture for reading. -/// -/// Surfaces come in two varieties: generic and widget surfaces. Generic surfaces can be bound to a -/// texture but cannot be displayed in a widget (without using other APIs such as Core Animation, -/// DirectComposition, or XPRESENT). Widget surfaces are the opposite: they can be displayed in a -/// widget but not bound to a texture. -/// -/// Surfaces are specific to a given context and cannot be rendered to from any context other than -/// the one they were created with. However, they can be *read* from any context on any thread (as -/// long as that context shares the same adapter and connection), by wrapping them in a -/// `SurfaceTexture`. -/// -/// Depending on the platform, each surface may be internally double-buffered. -/// -/// Surfaces must be destroyed with the `destroy_surface()` method, or a panic will occur. +/// An implementation of [`crate::Surface`] for Surfaceless Mesa. #[derive(Debug)] -pub struct Surface(pub(crate) EGLBackedSurface); +pub struct SurfacelessMesaSurface(pub(crate) EGLBackedSurface); -/// Represents an OpenGL texture that wraps a surface. -/// -/// Reading from the associated OpenGL texture reads from the surface. It is undefined behavior to -/// write to such a texture (e.g. by binding it to a framebuffer and rendering to that -/// framebuffer). -/// -/// Surface textures are local to a context, but that context does not have to be the same context -/// as that associated with the underlying surface. The texture must be destroyed with the -/// `destroy_surface_texture()` method, or a panic will occur. +/// An implementation of [`crate::SurfaceTexture`] for Surfaceless Mesa. #[derive(Debug)] -pub struct SurfaceTexture(pub(crate) EGLSurfaceTexture); +pub struct SurfacelessMesaSurfaceTexture(pub(crate) EGLSurfaceTexture); -unsafe impl Send for Surface {} +unsafe impl Send for SurfacelessMesaSurface {} /// Represents the CPU view of the pixel data of this surface. pub struct SurfaceDataGuard<'a> { diff --git a/src/multi/context.rs b/src/multi/context.rs index 898ecc84..ae2ae5e8 100644 --- a/src/multi/context.rs +++ b/src/multi/context.rs @@ -3,9 +3,10 @@ use euclid::default::Size2D; use super::device::Device; -use super::surface::Surface; use crate::device::Device as DeviceInterface; -use crate::{Context, ContextAttributes, ContextDescriptor, ContextID, Error, SurfaceInfo}; +use crate::{ + Context, ContextAttributes, ContextDescriptor, ContextID, Error, Surface, SurfaceInfo, +}; use std::os::raw::c_void; @@ -94,21 +95,11 @@ where pub fn bind_surface_to_context( &self, context: &mut Context, - surface: Surface, - ) -> Result<(), (Error, Surface)> { + surface: Surface, + ) -> Result<(), (Error, Surface)> { match self { - Device::Default(device) => match surface { - Surface::Default(surface) => device - .bind_surface_to_context(context, surface) - .map_err(|(err, surface)| (err, Surface::Default(surface))), - _ => Err((Error::IncompatibleSurface, surface)), - }, - Device::Alternate(device) => match surface { - Surface::Alternate(surface) => device - .bind_surface_to_context(context, surface) - .map_err(|(err, surface)| (err, Surface::Alternate(surface))), - _ => Err((Error::IncompatibleSurface, surface)), - }, + Device::Default(device) => device.bind_surface_to_context(context, surface), + Device::Alternate(device) => device.bind_surface_to_context(context, surface), } } @@ -119,14 +110,10 @@ where pub fn unbind_surface_from_context( &self, context: &mut Context, - ) -> Result>, Error> { + ) -> Result, Error> { match self { - Device::Default(device) => device - .unbind_surface_from_context(context) - .map(|surface| surface.map(Surface::Default)), - Device::Alternate(device) => device - .unbind_surface_from_context(context) - .map(|surface| surface.map(Surface::Alternate)), + Device::Default(device) => device.unbind_surface_from_context(context), + Device::Alternate(device) => device.unbind_surface_from_context(context), } } diff --git a/src/multi/device.rs b/src/multi/device.rs index 88899dbc..8f302066 100644 --- a/src/multi/device.rs +++ b/src/multi/device.rs @@ -1,13 +1,12 @@ //! A device abstraction that allows the choice of backends dynamically. use super::connection::Connection; -use super::surface::{Surface, SurfaceTexture}; use crate::connection::Connection as ConnectionInterface; use crate::context::ContextAttributes; use crate::device::Device as DeviceInterface; use crate::{ - Adapter, Context, ContextDescriptor, ContextID, Error, GLApi, SurfaceAccess, SurfaceInfo, - SurfaceType, + Adapter, Context, ContextDescriptor, ContextID, Error, GLApi, Surface, SurfaceAccess, + SurfaceInfo, SurfaceTexture, SurfaceType, }; use euclid::default::Size2D; use glow::Texture; @@ -68,8 +67,6 @@ where Alt::Connection: ConnectionInterface, { type Connection = Connection; - type Surface = Surface; - type SurfaceTexture = SurfaceTexture; // device.rs @@ -144,16 +141,13 @@ where fn bind_surface_to_context( &self, context: &mut Context, - surface: Surface, - ) -> Result<(), (Error, Surface)> { + surface: Surface, + ) -> Result<(), (Error, Surface)> { Device::bind_surface_to_context(self, context, surface) } #[inline] - fn unbind_surface_from_context( - &self, - context: &mut Context, - ) -> Result>, Error> { + fn unbind_surface_from_context(&self, context: &mut Context) -> Result, Error> { Device::unbind_surface_from_context(self, context) } @@ -175,7 +169,7 @@ where context: &Context, surface_access: SurfaceAccess, surface_type: SurfaceType<'_>, - ) -> Result, Error> { + ) -> Result { Device::create_surface(self, context, surface_access, surface_type) } @@ -183,17 +177,13 @@ where fn create_surface_texture( &self, context: &mut Context, - surface: Surface, - ) -> Result, (Error, Surface)> { + surface: Surface, + ) -> Result { Device::create_surface_texture(self, context, surface) } #[inline] - fn destroy_surface( - &self, - context: &mut Context, - surface: &mut Surface, - ) -> Result<(), Error> { + fn destroy_surface(&self, context: &mut Context, surface: &mut Surface) -> Result<(), Error> { Device::destroy_surface(self, context, surface) } @@ -201,8 +191,8 @@ where fn destroy_surface_texture( &self, context: &mut Context, - surface_texture: SurfaceTexture, - ) -> Result, (Error, SurfaceTexture)> { + surface_texture: SurfaceTexture, + ) -> Result { Device::destroy_surface_texture(self, context, surface_texture) } @@ -222,11 +212,7 @@ where } #[inline] - fn present_surface( - &self, - context: &Context, - surface: &mut Surface, - ) -> Result<(), Error> { + fn present_surface(&self, context: &Context, surface: &mut Surface) -> Result<(), Error> { Device::present_surface(self, context, surface) } @@ -234,22 +220,19 @@ where fn resize_surface( &self, context: &Context, - surface: &mut Surface, + surface: &mut Surface, size: Size2D, ) -> Result<(), Error> { Device::resize_surface(self, context, surface, size) } #[inline] - fn surface_info(&self, surface: &Surface) -> SurfaceInfo { + fn surface_info(&self, surface: &Surface) -> SurfaceInfo { Device::surface_info(self, surface) } #[inline] - fn surface_texture_object( - &self, - surface_texture: &SurfaceTexture, - ) -> Option { + fn surface_texture_object(&self, surface_texture: &SurfaceTexture) -> Option { Device::surface_texture_object(self, surface_texture) } } diff --git a/src/multi/surface.rs b/src/multi/surface.rs index fa20f0c0..c3f3914f 100644 --- a/src/multi/surface.rs +++ b/src/multi/surface.rs @@ -2,79 +2,10 @@ use super::device::Device; use crate::device::Device as DeviceInterface; -use crate::{Context, Error, SurfaceAccess, SurfaceInfo, SurfaceType}; +use crate::{Context, Error, Surface, SurfaceAccess, SurfaceInfo, SurfaceTexture, SurfaceType}; use euclid::default::Size2D; use glow::Texture; -use std::fmt::{self, Debug, Formatter}; - -/// Represents a hardware buffer of pixels that can be rendered to via the CPU or GPU and either -/// displayed in a native widget or bound to a texture for reading. -/// -/// Surfaces come in two varieties: generic and widget surfaces. Generic surfaces can be bound to a -/// texture but cannot be displayed in a widget (without using other APIs such as Core Animation, -/// DirectComposition, or XPRESENT). Widget surfaces are the opposite: they can be displayed in a -/// widget but not bound to a texture. -/// -/// Surfaces are specific to a given context and cannot be rendered to from any context other than -/// the one they were created with. However, they can be *read* from any context on any thread (as -/// long as that context shares the same adapter and connection), by wrapping them in a -/// `SurfaceTexture`. -/// -/// Depending on the platform, each surface may be internally double-buffered. -/// -/// Surfaces must be destroyed with the `destroy_surface()` method, or a panic will occur. -pub enum Surface -where - Def: DeviceInterface, - Alt: DeviceInterface, -{ - /// The default surface type. - Default(Def::Surface), - /// The alternate surface type. - Alternate(Alt::Surface), -} - -/// Represents an OpenGL texture that wraps a surface. -/// -/// Reading from the associated OpenGL texture reads from the surface. It is undefined behavior to -/// write to such a texture (e.g. by binding it to a framebuffer and rendering to that -/// framebuffer). -/// -/// Surface textures are local to a context, but that context does not have to be the same context -/// as that associated with the underlying surface. The texture must be destroyed with the -/// `destroy_surface_texture()` method, or a panic will occur. -pub enum SurfaceTexture -where - Def: DeviceInterface, - Alt: DeviceInterface, -{ - /// The default surface texture type. - Default(Def::SurfaceTexture), - /// The alternate surface texture type. - Alternate(Alt::SurfaceTexture), -} - -impl Debug for Surface -where - Def: DeviceInterface, - Alt: DeviceInterface, -{ - fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { - write!(f, "Surface") - } -} - -impl Debug for SurfaceTexture -where - Def: DeviceInterface, - Alt: DeviceInterface, -{ - fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { - write!(f, "SurfaceTexture") - } -} - impl Device where Def: DeviceInterface, @@ -89,14 +20,12 @@ where context: &Context, surface_access: SurfaceAccess, surface_type: SurfaceType<'_>, - ) -> Result, Error> { + ) -> Result { match self { - Device::Default(device) => device - .create_surface(context, surface_access, surface_type) - .map(Surface::Default), - Device::Alternate(device) => device - .create_surface(context, surface_access, surface_type) - .map(Surface::Alternate), + Device::Default(device) => device.create_surface(context, surface_access, surface_type), + Device::Alternate(device) => { + device.create_surface(context, surface_access, surface_type) + } } } @@ -113,27 +42,11 @@ where pub fn create_surface_texture( &self, context: &mut Context, - surface: Surface, - ) -> Result, (Error, Surface)> { + surface: Surface, + ) -> Result { match self { - Device::Default(device) => match surface { - Surface::Default(surface) => { - match device.create_surface_texture(context, surface) { - Ok(surface_texture) => Ok(SurfaceTexture::Default(surface_texture)), - Err((err, surface)) => Err((err, Surface::Default(surface))), - } - } - _ => Err((Error::IncompatibleSurface, surface)), - }, - Device::Alternate(device) => match surface { - Surface::Alternate(surface) => { - match device.create_surface_texture(context, surface) { - Ok(surface_texture) => Ok(SurfaceTexture::Alternate(surface_texture)), - Err((err, surface)) => Err((err, Surface::Alternate(surface))), - } - } - _ => Err((Error::IncompatibleSurface, surface)), - }, + Device::Default(device) => device.create_surface_texture(context, surface), + Device::Alternate(device) => device.create_surface_texture(context, surface), } } @@ -147,17 +60,11 @@ where pub fn destroy_surface( &self, context: &mut Context, - surface: &mut Surface, + surface: &mut Surface, ) -> Result<(), Error> { match self { - Device::Default(device) => match *surface { - Surface::Default(ref mut surface) => device.destroy_surface(context, surface), - _ => Err(Error::IncompatibleSurface), - }, - Device::Alternate(device) => match *surface { - Surface::Alternate(ref mut surface) => device.destroy_surface(context, surface), - _ => Err(Error::IncompatibleSurface), - }, + Device::Default(device) => device.destroy_surface(context, surface), + Device::Alternate(device) => device.destroy_surface(context, surface), } } @@ -171,31 +78,11 @@ where pub fn destroy_surface_texture( &self, context: &mut Context, - surface_texture: SurfaceTexture, - ) -> Result, (Error, SurfaceTexture)> { + surface_texture: SurfaceTexture, + ) -> Result { match self { - Device::Default(device) => match surface_texture { - SurfaceTexture::Default(surface_texture) => { - match device.destroy_surface_texture(context, surface_texture) { - Ok(surface) => Ok(Surface::Default(surface)), - Err((err, surface_texture)) => { - Err((err, SurfaceTexture::Default(surface_texture))) - } - } - } - _ => Err((Error::IncompatibleSurfaceTexture, surface_texture)), - }, - Device::Alternate(device) => match surface_texture { - SurfaceTexture::Alternate(surface_texture) => { - match device.destroy_surface_texture(context, surface_texture) { - Ok(surface) => Ok(Surface::Alternate(surface)), - Err((err, surface_texture)) => { - Err((err, SurfaceTexture::Alternate(surface_texture))) - } - } - } - _ => Err((Error::IncompatibleSurfaceTexture, surface_texture)), - }, + Device::Default(device) => device.destroy_surface_texture(context, surface_texture), + Device::Alternate(device) => device.destroy_surface_texture(context, surface_texture), } } @@ -206,20 +93,10 @@ where /// /// The supplied context must match the context the surface was created with, or an /// `IncompatibleSurface` error is returned. - pub fn present_surface( - &self, - context: &Context, - surface: &mut Surface, - ) -> Result<(), Error> { + pub fn present_surface(&self, context: &Context, surface: &mut Surface) -> Result<(), Error> { match self { - Device::Default(device) => match *surface { - Surface::Default(ref mut surface) => device.present_surface(context, surface), - _ => Err(Error::IncompatibleSurface), - }, - Device::Alternate(device) => match *surface { - Surface::Alternate(ref mut surface) => device.present_surface(context, surface), - _ => Err(Error::IncompatibleSurface), - }, + Device::Default(device) => device.present_surface(context, surface), + Device::Alternate(device) => device.present_surface(context, surface), } } @@ -227,20 +104,12 @@ where pub fn resize_surface( &self, context: &Context, - surface: &mut Surface, + surface: &mut Surface, size: Size2D, ) -> Result<(), Error> { match self { - Device::Default(device) => match *surface { - Surface::Default(ref mut surface) => device.resize_surface(context, surface, size), - _ => Err(Error::IncompatibleSurface), - }, - Device::Alternate(device) => match *surface { - Surface::Alternate(ref mut surface) => { - device.resize_surface(context, surface, size) - } - _ => Err(Error::IncompatibleSurface), - }, + Device::Default(device) => device.resize_surface(context, surface, size), + Device::Alternate(device) => device.resize_surface(context, surface, size), } } @@ -261,33 +130,20 @@ where /// Before rendering to a surface attached to a context, you must call `glBindFramebuffer()` /// on the framebuffer object returned by this function. This framebuffer object may or not be /// 0, the default framebuffer, depending on platform. - pub fn surface_info(&self, surface: &Surface) -> SurfaceInfo { - match (self, surface) { - (Device::Default(device), Surface::Default(ref surface)) => { - device.surface_info(surface) - } - (Device::Alternate(device), Surface::Alternate(ref surface)) => { - device.surface_info(surface) - } - _ => panic!("Incompatible context!"), + pub fn surface_info(&self, surface: &Surface) -> SurfaceInfo { + match self { + Device::Default(device) => device.surface_info(surface), + Device::Alternate(device) => device.surface_info(surface), } } /// Returns the OpenGL texture object containing the contents of this surface. /// /// It is only legal to read from, not write to, this texture object. - pub fn surface_texture_object( - &self, - surface_texture: &SurfaceTexture, - ) -> Option { - match (self, surface_texture) { - (Device::Default(device), SurfaceTexture::Default(ref surface_texture)) => { - device.surface_texture_object(surface_texture) - } - (Device::Alternate(device), SurfaceTexture::Alternate(ref surface_texture)) => { - device.surface_texture_object(surface_texture) - } - _ => panic!("Incompatible context!"), + pub fn surface_texture_object(&self, surface_texture: &SurfaceTexture) -> Option { + match self { + Device::Default(device) => device.surface_texture_object(surface_texture), + Device::Alternate(device) => device.surface_texture_object(surface_texture), } } } diff --git a/src/surface.rs b/src/surface.rs index 29974dad..694a1dce 100644 --- a/src/surface.rs +++ b/src/surface.rs @@ -1,18 +1,25 @@ //! Information related to hardware surfaces. use crate::context::ContextID; - +use crate::macros::enum_conversion; use euclid::default::Size2D; use raw_window_handle::WindowHandle; use std::fmt::{self, Display, Formatter}; -/// Various data about the surface. -pub struct SystemSurfaceInfo { - /// The surface's size, in device pixels. - pub size: Size2D, - /// The ID of the surface. This should be globally unique for each currently-allocated surface. - pub id: SurfaceID, -} +#[cfg(all(windows_platform, feature = "sm-angle"))] +use crate::angle::surface::AngleSurface; +#[cfg(macos_platform)] +use crate::cgl::surface::CglSurface; +#[cfg(any(android_platform, ohos_platform))] +use crate::hardware_buffer::surface::HardwareBufferSurface; +#[cfg(free_unix)] +use crate::mesa_surfaceless::surface::SurfacelessMesaSurface; +#[cfg(wayland_platform)] +use crate::wayland::surface::WaylandSurface; +#[cfg(all(windows_platform, not(feature = "sm-no-wgl")))] +use crate::wgl::surface::WglSurface; +#[cfg(x11_platform)] +use crate::x11::surface::X11Surface; /// Various data about the surface. pub struct SurfaceInfo { @@ -126,3 +133,77 @@ impl SurfaceAccess { } } } + +/// Represents a hardware buffer of pixels that can be rendered to via the CPU or GPU and either +/// displayed in a native widget or bound to a texture for reading. +/// +/// Surfaces come in two varieties: generic and widget surfaces. Generic surfaces can be bound to a +/// texture but cannot be displayed in a widget (without using other APIs such as Core Animation, +/// DirectComposition, or XPRESENT). Widget surfaces are the opposite: they can be displayed in a +/// widget but not bound to a texture. +/// +/// Surfaces are specific to a given context and cannot be rendered to from any context other than +/// the one they were created with. However, they can be *read* from any context on any thread (as +/// long as that context shares the same adapter and connection), by wrapping them in a +/// `SurfaceTexture`. +/// +/// Depending on the platform, each surface may be internally double-buffered. +/// +/// Surfaces must be destroyed with the `destroy_surface()` method, or a panic will occur. +#[derive(Debug)] +pub enum Surface { + /// An ANGLE surface for Windows systems. + #[cfg(all(windows_platform, feature = "sm-angle"))] + Angle(AngleSurface), + /// A surface for Apple systems. + #[cfg(macos_platform)] + Cgl(CglSurface), + /// A hardware buffer surface for OHOS and Android systems. + #[cfg(any(android_platform, ohos_platform))] + HardwareBuffer(HardwareBufferSurface), + /// A surfaceless Mesa surface for X11 / Wayland systems. + #[cfg(free_unix)] + SurfacelessMesa(SurfacelessMesaSurface), + /// A surface for Wayland systems. + #[cfg(wayland_platform)] + Wayland(WaylandSurface), + /// A WGL surface for Windows systems. + #[cfg(all(windows_platform, not(feature = "sm-no-wgl")))] + Wgl(WglSurface), + /// A surface for X11 systems. + #[cfg(x11_platform)] + X11(X11Surface), +} + +#[cfg(all(windows_platform, feature = "sm-angle"))] +enum_conversion!(Surface, Angle, AngleSurface, angle, IncompatibleSurface); +#[cfg(macos_platform)] +enum_conversion!(Surface, Cgl, CglSurface, cgl, IncompatibleSurface); +#[cfg(any(android_platform, ohos_platform))] +enum_conversion!( + Surface, + HardwareBuffer, + HardwareBufferSurface, + hardware_buffer, + IncompatibleSurface +); +#[cfg(free_unix)] +enum_conversion!( + Surface, + SurfacelessMesa, + SurfacelessMesaSurface, + surfaceless_mesa, + IncompatibleSurface +); +#[cfg(wayland_platform)] +enum_conversion!( + Surface, + Wayland, + WaylandSurface, + wayland, + IncompatibleSurface +); +#[cfg(all(windows_platform, not(feature = "sm-no-wgl")))] +enum_conversion!(Surface, Wgl, WglSurface, wgl, IncompatibleSurface); +#[cfg(x11_platform)] +enum_conversion!(Surface, X11, X11Surface, x11, IncompatibleSurface); diff --git a/src/surface_texture.rs b/src/surface_texture.rs new file mode 100644 index 00000000..2ec7a122 --- /dev/null +++ b/src/surface_texture.rs @@ -0,0 +1,109 @@ +//! Information related to hardware surface textures. + +use crate::macros::enum_conversion; + +#[cfg(all(windows_platform, feature = "sm-angle"))] +use crate::angle::surface::AngleSurfaceTexture; +#[cfg(macos_platform)] +use crate::cgl::surface::CglSurfaceTexture; +#[cfg(any(android_platform, ohos_platform))] +use crate::hardware_buffer::surface::HardwareBufferSurfaceTexture; +#[cfg(free_unix)] +use crate::mesa_surfaceless::surface::SurfacelessMesaSurfaceTexture; +#[cfg(wayland_platform)] +use crate::wayland::surface::WaylandSurfaceTexture; +#[cfg(all(windows_platform, not(feature = "sm-no-wgl")))] +use crate::wgl::surface::WglSurfaceTexture; +#[cfg(x11_platform)] +use crate::x11::surface::X11SurfaceTexture; + +/// Represents an OpenGL texture that wraps a surface. +/// +/// Reading from the associated OpenGL texture reads from the surface. It is undefined behavior to +/// write to such a texture (e.g. by binding it to a framebuffer and rendering to that +/// framebuffer). +/// +/// Surface textures are local to a context, but that context does not have to be the same context +/// as that associated with the underlying surface. The texture must be destroyed with the +/// `destroy_surface_texture()` method, or a panic will occur. +#[derive(Debug)] +pub enum SurfaceTexture { + /// An ANGLE surface texture for Windows systems. + #[cfg(all(windows_platform, feature = "sm-angle"))] + Angle(AngleSurfaceTexture), + /// A surface texture for Apple systems. + #[cfg(macos_platform)] + Cgl(CglSurfaceTexture), + /// A hardware buffer surface texture for OHOS and Android systems. + #[cfg(any(android_platform, ohos_platform))] + HardwareBuffer(HardwareBufferSurfaceTexture), + /// A surfaceless Mesa surface texture for X11 / Wayland systems. + #[cfg(free_unix)] + SurfacelessMesa(SurfacelessMesaSurfaceTexture), + /// A surface texture for Wayland systems. + #[cfg(wayland_platform)] + Wayland(WaylandSurfaceTexture), + /// A WGL surface texture for Windows systems. + #[cfg(all(windows_platform, not(feature = "sm-no-wgl")))] + Wgl(WglSurfaceTexture), + /// A surface texture for X11 systems. + #[cfg(x11_platform)] + X11(X11SurfaceTexture), +} + +#[cfg(all(windows_platform, feature = "sm-angle"))] +enum_conversion!( + SurfaceTexture, + Angle, + AngleSurfaceTexture, + angle, + IncompatibleSurfaceTexture +); +#[cfg(macos_platform)] +enum_conversion!( + SurfaceTexture, + Cgl, + CglSurfaceTexture, + cgl, + IncompatibleSurfaceTexture +); +#[cfg(any(android_platform, ohos_platform))] +enum_conversion!( + SurfaceTexture, + HardwareBuffer, + HardwareBufferSurfaceTexture, + hardware_buffer, + IncompatibleSurfaceTexture +); +#[cfg(free_unix)] +enum_conversion!( + SurfaceTexture, + SurfacelessMesa, + SurfacelessMesaSurfaceTexture, + surfaceless_mesa, + IncompatibleSurfaceTexture +); +#[cfg(wayland_platform)] +enum_conversion!( + SurfaceTexture, + Wayland, + WaylandSurfaceTexture, + wayland, + IncompatibleSurfaceTexture +); +#[cfg(all(windows_platform, not(feature = "sm-no-wgl")))] +enum_conversion!( + SurfaceTexture, + Wgl, + WglSurfaceTexture, + wgl, + IncompatibleSurfaceTexture +); +#[cfg(x11_platform)] +enum_conversion!( + SurfaceTexture, + X11, + X11SurfaceTexture, + x11, + IncompatibleSurfaceTexture +); diff --git a/src/tests.rs b/src/tests.rs index da7e41c4..ba59e8c1 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -9,10 +9,9 @@ use super::connection::Connection; use super::context::NativeContext; use super::device::Device; -use super::surface::Surface; use crate::{ gl, Adapter, AdapterPreferences, Context, ContextAttributeFlags, ContextAttributes, - ContextDescriptor, Error, GLApi, GLVersion, Gl, PowerPreference, RenderingPreference, + ContextDescriptor, Error, GLApi, GLVersion, Gl, PowerPreference, RenderingPreference, Surface, SurfaceAccess, SurfaceType, WindowingApiError, }; diff --git a/src/unix.rs b/src/unix.rs index c4dfc53d..e21692f8 100644 --- a/src/unix.rs +++ b/src/unix.rs @@ -32,43 +32,6 @@ pub mod device { /// Hardware buffers of pixels. pub mod surface { - use crate::mesa_surfaceless::device::Device as SWDevice; - use crate::multi::device::Device as MultiDevice; - use crate::multi::surface::Surface as MultiSurface; - use crate::multi::surface::SurfaceTexture as MultiSurfaceTexture; - use crate::wayland::device::Device as WaylandDevice; - use crate::x11::device::Device as X11Device; - type HWDevice = MultiDevice; - - /// Represents a hardware buffer of pixels that can be rendered to via the CPU or GPU and - /// either displayed in a native widget or bound to a texture for reading. - /// - /// Surfaces come in two varieties: generic and widget surfaces. Generic surfaces can be bound - /// to a texture but cannot be displayed in a widget (without using other APIs such as Core - /// Animation, DirectComposition, or XPRESENT). Widget surfaces are the opposite: they can be - /// displayed in a widget but not bound to a texture. - /// - /// Surfaces are specific to a given context and cannot be rendered to from any context other - /// than the one they were created with. However, they can be *read* from any context on any - /// thread (as long as that context shares the same adapter and connection), by wrapping them - /// in a `SurfaceTexture`. - /// - /// Depending on the platform, each surface may be internally double-buffered. - /// - /// Surfaces must be destroyed with the `destroy_surface()` method, or a panic will occur. - pub type Surface = MultiSurface; - - /// Represents an OpenGL texture that wraps a surface. - /// - /// Reading from the associated OpenGL texture reads from the surface. It is undefined behavior - /// to write to such a texture (e.g. by binding it to a framebuffer and rendering to that - /// framebuffer). - /// - /// Surface textures are local to a context, but that context does not have to be the same - /// context as that associated with the underlying surface. The texture must be destroyed with - /// the `destroy_surface_texture()` method, or a panic will occur. - pub type SurfaceTexture = MultiSurfaceTexture; - // FIXME(pcwalton): Revamp how this works. #[doc(hidden)] pub struct SurfaceDataGuard {} diff --git a/src/wayland/device.rs b/src/wayland/device.rs index 3bc0d8c1..762a2f95 100644 --- a/src/wayland/device.rs +++ b/src/wayland/device.rs @@ -2,16 +2,17 @@ use super::connection::{Connection, NativeConnectionWrapper}; use super::context::{NativeContext, WaylandContext}; -use super::surface::{Surface, SurfaceDataGuard, SurfaceTexture}; +use super::surface::SurfaceDataGuard; use crate::base::egl::context::{self, CurrentContextGuard, EGLBackedContext}; use crate::base::egl::surface::EGLBackedSurface; use crate::context::ContextID; use crate::egl::types::EGLint; use crate::free_unix::adapter::FreeUnixAdapter; use crate::surface::Framebuffer; +use crate::wayland::surface::{WaylandSurface, WaylandSurfaceTexture}; use crate::{ egl, gl, Adapter, Context, ContextAttributes, ContextDescriptor, EglContextDescriptor, Error, - GLApi, Gl, SurfaceAccess, SurfaceInfo, SurfaceType, + GLApi, Gl, Surface, SurfaceAccess, SurfaceInfo, SurfaceTexture, SurfaceType, }; use euclid::default::Size2D; use glow::Texture; @@ -259,12 +260,13 @@ impl Device { Ok(context) => context, Err(error) => return Err((error, surface)), }; + let surface: WaylandSurface = surface.try_into()?; unsafe { context .0 .bind_surface(self.native_connection.egl_display, surface.0) - .map_err(|(err, surface)| (err, Surface(surface))) + .map_err(|(err, surface)| (err, WaylandSurface(surface).into())) } } @@ -281,7 +283,7 @@ impl Device { context .0 .unbind_surface(&context.1, self.native_connection.egl_display) - .map(|maybe_surface| maybe_surface.map(Surface)) + .map(|maybe_surface| maybe_surface.map(WaylandSurface).map(Into::into)) } } @@ -369,14 +371,15 @@ impl Device { let context_attributes = self.context_descriptor_attributes(&context_descriptor); let context = context.wayland()?; - Ok(Surface(EGLBackedSurface::new_generic( + Ok(WaylandSurface(EGLBackedSurface::new_generic( &context.1, self.native_connection.egl_display, context.0.egl_context, context.0.id, &context_attributes, size, - ))) + )) + .into()) } unsafe fn create_window_surface( @@ -397,13 +400,14 @@ impl Device { ); let context = context.wayland()?; - Ok(Surface(EGLBackedSurface::new_window( + Ok(WaylandSurface(EGLBackedSurface::new_window( self.native_connection.egl_display, egl_config, egl_window as *mut c_void, context.0.id, size, - ))) + )) + .into()) } /// Creates a surface texture from an existing generic surface for use with the given context. @@ -430,10 +434,11 @@ impl Device { Ok(context) => context, Err(error) => return Err((error, surface)), }; + let surface: WaylandSurface = surface.try_into()?; match surface.0.to_surface_texture(&context.1) { - Ok(surface_texture) => Ok(SurfaceTexture(surface_texture)), - Err((err, surface)) => Err((err, Surface(surface))), + Ok(surface_texture) => Ok(WaylandSurfaceTexture(surface_texture).into()), + Err((err, surface)) => Err((err, WaylandSurface(surface).into())), } } @@ -451,6 +456,7 @@ impl Device { ) -> Result<(), Error> { let egl_display = self.native_connection.egl_display; let context = context.wayland()?; + let surface: &mut WaylandSurface = surface.try_into()?; if let Some(wayland_egl_window) = surface.0.destroy(&context.1, egl_display, context.0.id)? { @@ -478,10 +484,11 @@ impl Device { Ok(concrete_context) => concrete_context, Err(error) => return Err((error, surface_texture)), }; + let surface_texture: WaylandSurfaceTexture = surface_texture.try_into()?; match self.temporarily_make_context_current(context) { - Ok(_guard) => Ok(Surface(surface_texture.0.destroy(&concrete_context.1))), - Err(err) => Err((err, surface_texture)), + Ok(_guard) => Ok(WaylandSurface(surface_texture.0.destroy(&concrete_context.1)).into()), + Err(err) => Err((err, surface_texture.into())), } } @@ -494,6 +501,7 @@ impl Device { /// `IncompatibleSurface` error is returned. pub fn present_surface(&self, context: &Context, surface: &mut Surface) -> Result<(), Error> { let context = context.wayland()?; + let surface: &mut WaylandSurface = surface.try_into()?; surface .0 .present(self.native_connection.egl_display, context.0.egl_context) @@ -506,6 +514,7 @@ impl Device { surface: &mut Surface, size: Size2D, ) -> Result<(), Error> { + let surface: &mut WaylandSurface = surface.try_into()?; surface.0.resize_for_wayland(size) } @@ -530,6 +539,7 @@ impl Device { /// on the framebuffer object returned by this function. This framebuffer object may or not be /// 0, the default framebuffer, depending on platform. pub fn surface_info(&self, surface: &Surface) -> SurfaceInfo { + let surface = surface.wayland().expect("Incompatible surface type"); surface.0.info() } @@ -538,6 +548,6 @@ impl Device { /// It is only legal to read from, not write to, this texture object. #[inline] pub fn surface_texture_object(&self, surface_texture: &SurfaceTexture) -> Option { - surface_texture.0.texture_object + surface_texture.wayland().ok()?.0.texture_object } } diff --git a/src/wayland/surface.rs b/src/wayland/surface.rs index 51ff633a..2812040a 100644 --- a/src/wayland/surface.rs +++ b/src/wayland/surface.rs @@ -8,38 +8,15 @@ use std::marker::PhantomData; use std::os::raw::c_void; use wayland_sys::egl::{wayland_egl_handle, wl_egl_window}; -/// Represents a hardware buffer of pixels that can be rendered to via the CPU or GPU and either -/// displayed in a native widget or bound to a texture for reading. -/// -/// Surfaces come in two varieties: generic and widget surfaces. Generic surfaces can be bound to a -/// texture but cannot be displayed in a widget (without using other APIs such as Core Animation, -/// DirectComposition, or XPRESENT). Widget surfaces are the opposite: they can be displayed in a -/// widget but not bound to a texture. -/// -/// Surfaces are specific to a given context and cannot be rendered to from any context other than -/// the one they were created with. However, they can be *read* from any context on any thread (as -/// long as that context shares the same adapter and connection), by wrapping them in a -/// `SurfaceTexture`. -/// -/// Depending on the platform, each surface may be internally double-buffered. -/// -/// Surfaces must be destroyed with the `destroy_surface()` method, or a panic will occur. +/// An implementation of [`crate::Surface`] for Wayland platforms. #[derive(Debug)] -pub struct Surface(pub(crate) EGLBackedSurface); +pub struct WaylandSurface(pub(crate) EGLBackedSurface); -/// Represents an OpenGL texture that wraps a surface. -/// -/// Reading from the associated OpenGL texture reads from the surface. It is undefined behavior to -/// write to such a texture (e.g. by binding it to a framebuffer and rendering to that -/// framebuffer). -/// -/// Surface textures are local to a context, but that context does not have to be the same context -/// as that associated with the underlying surface. The texture must be destroyed with the -/// `destroy_surface_texture()` method, or a panic will occur. +/// An implementation of [`crate::SurfaceTexture`] for Wayland platforms. #[derive(Debug)] -pub struct SurfaceTexture(pub(crate) EGLSurfaceTexture); +pub struct WaylandSurfaceTexture(pub(crate) EGLSurfaceTexture); -unsafe impl Send for Surface {} +unsafe impl Send for WaylandSurface {} /// Represents the CPU view of the pixel data of this surface. pub struct SurfaceDataGuard<'a> { diff --git a/src/wgl/context.rs b/src/wgl/context.rs index aa121277..9d429a1a 100644 --- a/src/wgl/context.rs +++ b/src/wgl/context.rs @@ -3,9 +3,9 @@ //! Wrapper for WGL contexts on Windows. use super::device::HiddenWindow; -use super::surface::Surface; use crate::gl; use crate::surface::Framebuffer; +use crate::wgl::surface::WglSurface; use crate::Gl; use crate::{ContextID, Error, GLVersion}; use glow::HasContext; @@ -99,7 +99,7 @@ pub struct WglContext { pub(crate) id: ContextID, pub(crate) gl: Gl, pub(crate) hidden_window: Option, - pub(crate) framebuffer: Framebuffer, + pub(crate) framebuffer: Framebuffer, pub(crate) status: ContextStatus, } diff --git a/src/wgl/device.rs b/src/wgl/device.rs index 36c95bdc..7d32e18f 100644 --- a/src/wgl/device.rs +++ b/src/wgl/device.rs @@ -12,11 +12,11 @@ use crate::wgl::context::{ ContextStatus, CurrentContextGuard, FramebufferGuard, NativeContext, WglContext, WglContextDescriptor, OPENGL_LIBRARY, WGL_EXTENSION_FUNCTIONS, }; -use crate::wgl::surface::{Surface, SurfaceDataGuard, SurfaceTexture, Win32Objects}; +use crate::wgl::surface::{SurfaceDataGuard, WglSurface, WglSurfaceTexture, Win32Objects}; use crate::{ gl, gl_utils, Adapter, AdapterPreferences, Context, ContextAttributeFlags, ContextAttributes, - ContextDescriptor, Error, GLApi, GLVersion, Gl, PowerPreference, SurfaceAccess, SurfaceInfo, - SurfaceType, + ContextDescriptor, Error, GLApi, GLVersion, Gl, PowerPreference, Surface, SurfaceAccess, + SurfaceInfo, SurfaceTexture, SurfaceType, }; use euclid::default::Size2D; use glow::HasContext; @@ -632,15 +632,16 @@ impl Device { Ok(context) => context, Err(error) => return Err((error, surface)), }; + let surface: WglSurface = surface.try_into()?; if context.id != surface.context_id { - return Err((Error::IncompatibleSurface, surface)); + return Err((Error::IncompatibleSurface, surface.into())); } match context.framebuffer { Framebuffer::None => {} - Framebuffer::External(()) => return Err((Error::ExternalRenderTarget, surface)), - Framebuffer::Surface(_) => return Err((Error::SurfaceAlreadyBound, surface)), + Framebuffer::External(()) => return Err((Error::ExternalRenderTarget, surface.into())), + Framebuffer::Surface(_) => return Err((Error::SurfaceAlreadyBound, surface.into())), } let is_current = self.context_is_current(context); @@ -668,7 +669,7 @@ impl Device { match mem::replace(&mut context.framebuffer, Framebuffer::None) { Framebuffer::Surface(surface) => { self.unlock_surface(&surface); - Ok(Some(surface)) + Ok(Some(surface.into())) } Framebuffer::External(()) => Err(Error::ExternalRenderTarget), Framebuffer::None => Ok(None), @@ -703,17 +704,18 @@ impl Device { pub(crate) fn get_context_dc<'a>(&self, context: &'a WglContext) -> DCGuard<'a> { unsafe { - match context.framebuffer { - Framebuffer::Surface(Surface { - win32_objects: Win32Objects::Widget { window_handle }, - .. - }) => DCGuard::new(winuser::GetDC(window_handle), Some(window_handle)), - Framebuffer::Surface(Surface { - win32_objects: Win32Objects::Texture { .. }, - .. - }) - | Framebuffer::External(()) - | Framebuffer::None => context.hidden_window.as_ref().unwrap().get_dc(), + match &context.framebuffer { + Framebuffer::Surface(surface) => match surface.win32_objects { + Win32Objects::Texture { .. } => { + context.hidden_window.as_ref().unwrap().get_dc() + } + Win32Objects::Widget { window_handle } => { + DCGuard::new(winuser::GetDC(window_handle), Some(window_handle)) + } + }, + Framebuffer::External(()) | Framebuffer::None => { + context.hidden_window.as_ref().unwrap().get_dc() + } } } } @@ -736,7 +738,7 @@ impl Device { match context.framebuffer { Framebuffer::None => Ok(None), Framebuffer::External(()) => Err(Error::ExternalRenderTarget), - Framebuffer::Surface(ref surface) => Ok(Some(self.surface_info(surface))), + Framebuffer::Surface(ref surface) => Ok(Some(surface.info())), } } @@ -877,7 +879,7 @@ impl Device { // FIXME(pcwalton): Do we need to acquire the keyed mutex, or does the GL driver do // that? - Ok(Surface { + Ok(WglSurface { size: *size, context_id: concrete_context.id, win32_objects: Win32Objects::Texture { @@ -889,7 +891,8 @@ impl Device { renderbuffers, }, destroyed: false, - }) + } + .into()) } } @@ -915,7 +918,7 @@ impl Device { set_dc_pixel_format(window_dc, pixel_format); } - Ok(Surface { + Ok(WglSurface { size: Size2D::new( widget_rect.right - widget_rect.left, widget_rect.bottom - widget_rect.top, @@ -923,7 +926,8 @@ impl Device { context_id: context.id, win32_objects: Win32Objects::Widget { window_handle }, destroyed: false, - }) + } + .into()) } } @@ -943,6 +947,7 @@ impl Device { .dx_interop_functions .as_ref() .expect("How did you make a surface without DX interop?"); + let surface: &mut WglSurface = surface.try_into()?; if context.wgl()?.id != surface.context_id { return Err(Error::IncompatibleSurface); @@ -1002,8 +1007,9 @@ impl Device { context: &mut Context, surface: Surface, ) -> Result { + let surface: WglSurface = surface.try_into()?; let dxgi_share_handle = match surface.win32_objects { - Win32Objects::Widget { .. } => return Err((Error::WidgetAttached, surface)), + Win32Objects::Widget { .. } => return Err((Error::WidgetAttached, surface.into())), Win32Objects::Texture { dxgi_share_handle, .. } => dxgi_share_handle, @@ -1016,11 +1022,11 @@ impl Device { let _guard = match self.temporarily_make_context_current(context) { Ok(guard) => guard, - Err(err) => return Err((err, surface)), + Err(err) => return Err((err, surface.into())), }; let context = match context.wgl() { Ok(context) => context, - Err(error) => return Err((error, surface)), + Err(error) => return Err((error, surface.into())), }; unsafe { @@ -1034,7 +1040,7 @@ impl Device { if !winerror::SUCCEEDED(result) || local_d3d11_texture.is_null() { return Err(( Error::SurfaceImportFailed(WindowingApiError::Failed), - surface, + surface.into(), )); } let local_d3d11_texture = ComPtr::from_raw(local_d3d11_texture as *mut ID3D11Texture2D); @@ -1091,13 +1097,14 @@ impl Device { ); // Finish up. - Ok(SurfaceTexture { + Ok(WglSurfaceTexture { surface, local_d3d11_texture, local_gl_dx_interop_object, gl_texture: Some(gl_texture), phantom: PhantomData, - }) + } + .into()) } } @@ -1111,7 +1118,7 @@ impl Device { pub fn destroy_surface_texture( &self, context: &mut Context, - mut surface_texture: SurfaceTexture, + surface_texture: SurfaceTexture, ) -> Result { let dx_interop_functions = WGL_EXTENSION_FUNCTIONS .dx_interop_functions @@ -1126,6 +1133,7 @@ impl Device { Ok(context) => context, Err(error) => return Err((error, surface_texture)), }; + let mut surface_texture: WglSurfaceTexture = surface_texture.try_into()?; unsafe { // Unlock the texture. @@ -1150,10 +1158,10 @@ impl Device { } } - Ok(surface_texture.surface) + Ok(surface_texture.surface.into()) } - pub(crate) fn lock_surface(&self, surface: &Surface) { + pub(crate) fn lock_surface(&self, surface: &WglSurface) { let mut gl_dx_interop_object = match surface.win32_objects { Win32Objects::Widget { .. } => return, Win32Objects::Texture { @@ -1177,7 +1185,7 @@ impl Device { } } - pub(crate) fn unlock_surface(&self, surface: &Surface) { + pub(crate) fn unlock_surface(&self, surface: &WglSurface) { let mut gl_dx_interop_object = match surface.win32_objects { Win32Objects::Widget { .. } => return, Win32Objects::Texture { @@ -1226,7 +1234,7 @@ impl Device { /// The supplied context must match the context the surface was created with, or an /// `IncompatibleSurface` error is returned. pub fn present_surface(&self, _: &Context, surface: &mut Surface) -> Result<(), Error> { - surface.present() + surface.wgl()?.present() } /// Resizes a widget surface. @@ -1236,6 +1244,7 @@ impl Device { surface: &mut Surface, size: Size2D, ) -> Result<(), Error> { + let surface: &mut WglSurface = surface.try_into()?; surface.resize(size); Ok(()) } @@ -1248,15 +1257,7 @@ impl Device { /// 0, the default framebuffer, depending on platform. #[inline] pub fn surface_info(&self, surface: &Surface) -> SurfaceInfo { - SurfaceInfo { - size: surface.size, - id: surface.id(), - context_id: surface.context_id, - framebuffer_object: match surface.win32_objects { - Win32Objects::Texture { gl_framebuffer, .. } => gl_framebuffer, - Win32Objects::Widget { .. } => None, - }, - } + surface.wgl().expect("Incompatible surface type").info() } /// Returns the OpenGL texture object containing the contents of this surface. @@ -1267,7 +1268,7 @@ impl Device { &self, surface_texture: &SurfaceTexture, ) -> Option { - surface_texture.gl_texture + surface_texture.wgl().ok()?.gl_texture } } diff --git a/src/wgl/surface.rs b/src/wgl/surface.rs index 870e67ab..6baad280 100644 --- a/src/wgl/surface.rs +++ b/src/wgl/surface.rs @@ -3,7 +3,7 @@ //! An implementation of the GPU device for Windows using WGL/Direct3D interoperability. use crate::renderbuffers::Renderbuffers; -use crate::{ContextID, Error, SurfaceID}; +use crate::{ContextID, Error, SurfaceID, SurfaceInfo}; use euclid::default::Size2D; use std::fmt::{self, Debug, Formatter}; @@ -17,23 +17,8 @@ use winapi::um::wingdi; use winapi::um::winuser; use wio::com::ComPtr; -/// Represents a hardware buffer of pixels that can be rendered to via the CPU or GPU and either -/// displayed in a native widget or bound to a texture for reading. -/// -/// Surfaces come in two varieties: generic and widget surfaces. Generic surfaces can be bound to a -/// texture but cannot be displayed in a widget (without using other APIs such as Core Animation, -/// DirectComposition, or XPRESENT). Widget surfaces are the opposite: they can be displayed in a -/// widget but not bound to a texture. -/// -/// Surfaces are specific to a given context and cannot be rendered to from any context other than -/// the one they were created with. However, they can be *read* from any context on any thread (as -/// long as that context shares the same adapter and connection), by wrapping them in a -/// `SurfaceTexture`. -/// -/// Depending on the platform, each surface may be internally double-buffered. -/// -/// Surfaces must be destroyed with the `destroy_surface()` method, or a panic will occur. -pub struct Surface { +/// An implementation of [`crate::Surface`] for WGL platforms. +pub struct WglSurface { pub(crate) size: Size2D, pub(crate) context_id: ContextID, pub(crate) win32_objects: Win32Objects, @@ -54,17 +39,9 @@ pub(crate) enum Win32Objects { }, } -/// Represents an OpenGL texture that wraps a surface. -/// -/// Reading from the associated OpenGL texture reads from the surface. It is undefined behavior to -/// write to such a texture (e.g. by binding it to a framebuffer and rendering to that -/// framebuffer). -/// -/// Surface textures are local to a context, but that context does not have to be the same context -/// as that associated with the underlying surface. The texture must be destroyed with the -/// `destroy_surface_texture()` method, or a panic will occur. -pub struct SurfaceTexture { - pub(crate) surface: Surface, +/// An implementation of [`crate::SurfaceTexture`] for WGL platforms. +pub struct WglSurfaceTexture { + pub(crate) surface: WglSurface, #[allow(dead_code)] pub(crate) local_d3d11_texture: ComPtr, pub(crate) local_gl_dx_interop_object: HANDLE, @@ -72,15 +49,15 @@ pub struct SurfaceTexture { pub(crate) phantom: PhantomData<*const ()>, } -unsafe impl Send for Surface {} +unsafe impl Send for WglSurface {} -impl Debug for Surface { +impl Debug for WglSurface { fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { write!(f, "Surface({:x})", self.id().0) } } -impl Drop for Surface { +impl Drop for WglSurface { fn drop(&mut self) { if !self.destroyed && !thread::panicking() { panic!("Should have destroyed the surface first with `destroy_surface()`!") @@ -88,13 +65,13 @@ impl Drop for Surface { } } -impl Debug for SurfaceTexture { +impl Debug for WglSurfaceTexture { fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { write!(f, "SurfaceTexture({:?})", self.surface) } } -impl Surface { +impl WglSurface { pub(crate) fn id(&self) -> SurfaceID { match self.win32_objects { Win32Objects::Texture { @@ -133,6 +110,18 @@ impl Surface { pub(crate) fn resize(&mut self, size: Size2D) { self.size = size; } + + pub(crate) fn info(&self) -> SurfaceInfo { + SurfaceInfo { + size: self.size, + id: self.id(), + context_id: self.context_id, + framebuffer_object: match self.win32_objects { + Win32Objects::Texture { gl_framebuffer, .. } => gl_framebuffer, + Win32Objects::Widget { .. } => None, + }, + } + } } /// Represents the CPU view of the pixel data of this surface. diff --git a/src/x11/device.rs b/src/x11/device.rs index 27537fa6..9a71b553 100644 --- a/src/x11/device.rs +++ b/src/x11/device.rs @@ -2,7 +2,6 @@ use super::connection::{Connection, NativeConnectionWrapper}; use super::context::NativeContext; -use super::surface::Surface; use crate::base::egl::{ context::{self, CurrentContextGuard, EGLBackedContext}, surface::EGLBackedSurface, @@ -11,10 +10,10 @@ use crate::context::ContextID; use crate::egl::types::EGLint; use crate::free_unix::adapter::FreeUnixAdapter; use crate::x11::context::X11Context; -use crate::x11::surface::{SurfaceDataGuard, SurfaceTexture}; +use crate::x11::surface::{SurfaceDataGuard, X11Surface, X11SurfaceTexture}; use crate::{ egl, gl, Adapter, Context, ContextAttributes, ContextDescriptor, EglContextDescriptor, Error, - GLApi, Gl, SurfaceAccess, SurfaceInfo, SurfaceType, + GLApi, Gl, Surface, SurfaceAccess, SurfaceInfo, SurfaceTexture, SurfaceType, }; use euclid::default::Size2D; use glow::Texture; @@ -257,12 +256,13 @@ impl Device { Ok(context) => context, Err(error) => return Err((error, surface)), }; + let surface: X11Surface = surface.try_into()?; unsafe { context .0 .bind_surface(self.native_connection.egl_display, surface.0) - .map_err(|(err, surface)| (err, Surface(surface))) + .map_err(|(err, surface)| (err, X11Surface(surface).into())) } } @@ -279,7 +279,7 @@ impl Device { context .0 .unbind_surface(&context.1, self.native_connection.egl_display) - .map(|maybe_surface| maybe_surface.map(Surface)) + .map(|maybe_surface| maybe_surface.map(X11Surface).map(Into::into)) } } @@ -355,14 +355,15 @@ impl Device { let context_attributes = self.context_descriptor_attributes(&context_descriptor); let context = context.x11()?; - Ok(Surface(EGLBackedSurface::new_generic( + Ok(X11Surface(EGLBackedSurface::new_generic( &context.1, self.native_connection.egl_display, context.0.egl_context, context.0.id, &context_attributes, size, - ))) + )) + .into()) } unsafe fn create_window_surface( @@ -395,13 +396,14 @@ impl Device { ); let size = Size2D::new(width as i32, height as i32); - Ok(Surface(EGLBackedSurface::new_window( + Ok(X11Surface(EGLBackedSurface::new_window( self.native_connection.egl_display, egl_config, &mut x11_window as *mut Window as *mut c_void, context.0.id, &size, - ))) + )) + .into()) } /// Creates a surface texture from an existing generic surface for use with the given context. @@ -428,10 +430,11 @@ impl Device { Ok(context) => context, Err(error) => return Err((error, surface)), }; + let surface: X11Surface = surface.try_into()?; match surface.0.to_surface_texture(&context.1) { - Ok(surface_texture) => Ok(SurfaceTexture(surface_texture)), - Err((err, surface)) => Err((err, Surface(surface))), + Ok(surface_texture) => Ok(X11SurfaceTexture(surface_texture).into()), + Err((err, surface)) => Err((err, X11Surface(surface).into())), } } @@ -449,6 +452,7 @@ impl Device { ) -> Result<(), Error> { let egl_display = self.native_connection.egl_display; let context = context.x11()?; + let surface: &mut X11Surface = surface.try_into()?; surface.0.destroy(&context.1, egl_display, context.0.id)?; Ok(()) } @@ -469,9 +473,11 @@ impl Device { Ok(concrete_context) => concrete_context, Err(error) => return Err((error, surface_texture)), }; + let surface_texture: X11SurfaceTexture = surface_texture.try_into()?; + match self.temporarily_make_context_current(context) { - Ok(_guard) => Ok(Surface(surface_texture.0.destroy(&concrete_context.1))), - Err(err) => Err((err, surface_texture)), + Ok(_guard) => Ok(X11Surface(surface_texture.0.destroy(&concrete_context.1)).into()), + Err(err) => Err((err, surface_texture.into())), } } @@ -484,6 +490,7 @@ impl Device { /// `IncompatibleSurface` error is returned. pub fn present_surface(&self, context: &Context, surface: &mut Surface) -> Result<(), Error> { let context = context.x11()?; + let surface: &mut X11Surface = surface.try_into()?; surface .0 .present(self.native_connection.egl_display, context.0.egl_context) @@ -496,6 +503,7 @@ impl Device { surface: &mut Surface, size: Size2D, ) -> Result<(), Error> { + let surface: &mut X11Surface = surface.try_into()?; surface.0.resize(size); Ok(()) } @@ -521,6 +529,7 @@ impl Device { /// on the framebuffer object returned by this function. This framebuffer object may or not be /// 0, the default framebuffer, depending on platform. pub fn surface_info(&self, surface: &Surface) -> SurfaceInfo { + let surface = surface.x11().expect("Incompatible surface type"); surface.0.info() } @@ -529,6 +538,6 @@ impl Device { /// It is only legal to read from, not write to, this texture object. #[inline] pub fn surface_texture_object(&self, surface_texture: &SurfaceTexture) -> Option { - surface_texture.0.texture_object + surface_texture.x11().ok()?.0.texture_object } } diff --git a/src/x11/surface.rs b/src/x11/surface.rs index 68510e5f..66ef02ca 100644 --- a/src/x11/surface.rs +++ b/src/x11/surface.rs @@ -5,38 +5,15 @@ use crate::base::egl::surface::{EGLBackedSurface, EGLSurfaceTexture}; use std::marker::PhantomData; -/// Represents a hardware buffer of pixels that can be rendered to via the CPU or GPU and either -/// displayed in a native widget or bound to a texture for reading. -/// -/// Surfaces come in two varieties: generic and widget surfaces. Generic surfaces can be bound to a -/// texture but cannot be displayed in a widget (without using other APIs such as Core Animation, -/// DirectComposition, or XPRESENT). Widget surfaces are the opposite: they can be displayed in a -/// widget but not bound to a texture. -/// -/// Surfaces are specific to a given context and cannot be rendered to from any context other than -/// the one they were created with. However, they can be *read* from any context on any thread (as -/// long as that context shares the same adapter and connection), by wrapping them in a -/// `SurfaceTexture`. -/// -/// Depending on the platform, each surface may be internally double-buffered. -/// -/// Surfaces must be destroyed with the `destroy_surface()` method, or a panic will occur. +/// An implementation of [`crate::Surface`] for X11 platforms. #[derive(Debug)] -pub struct Surface(pub(crate) EGLBackedSurface); +pub struct X11Surface(pub(crate) EGLBackedSurface); -/// Represents an OpenGL texture that wraps a surface. -/// -/// Reading from the associated OpenGL texture reads from the surface. It is undefined behavior to -/// write to such a texture (e.g. by binding it to a framebuffer and rendering to that -/// framebuffer). -/// -/// Surface textures are local to a context, but that context does not have to be the same context -/// as that associated with the underlying surface. The texture must be destroyed with the -/// `destroy_surface_texture()` method, or a panic will occur. +/// An implementation of [`crate::SurfaceTexture`] for X11 platforms. #[derive(Debug)] -pub struct SurfaceTexture(pub(crate) EGLSurfaceTexture); +pub struct X11SurfaceTexture(pub(crate) EGLSurfaceTexture); -unsafe impl Send for Surface {} +unsafe impl Send for X11Surface {} /// Represents the CPU view of the pixel data of this surface. pub struct SurfaceDataGuard<'a> {