From f17a1d0deaf73c07fe8c2444c4b2b5350275ecf8 Mon Sep 17 00:00:00 2001 From: Martin Robinson Date: Sun, 27 Sep 2026 16:53:00 +0200 Subject: [PATCH 1/2] Convert `Context` into an enum This requires a bit more error handling than before, but some of this will be cleaned up once all data structures are converted to enums. Signed-off-by: Martin Robinson --- src/angle/context.rs | 21 +- src/angle/device.rs | 107 ++++++---- src/cgl/context.rs | 21 +- src/cgl/device.rs | 97 ++++++--- src/chains.rs | 52 ++--- src/context.rs | 89 ++++++++ src/device.rs | 48 ++--- src/hardware_buffer/context.rs | 21 +- src/hardware_buffer/device.rs | 59 ++++-- .../surface/android_surface.rs | 24 ++- src/hardware_buffer/surface/mod.rs | 5 +- src/hardware_buffer/surface/ohos_surface.rs | 25 ++- src/lib.rs | 3 +- src/macros.rs | 55 ++--- src/mesa_surfaceless/context.rs | 19 +- src/mesa_surfaceless/device.rs | 58 +++++- src/multi/context.rs | 196 +++++------------- src/multi/device.rs | 48 ++--- src/multi/surface.rs | 104 ++++------ src/tests.rs | 8 +- src/unix.rs | 29 --- src/wayland/context.rs | 19 +- src/wayland/device.rs | 55 +++-- src/wgl/context.rs | 23 +- src/wgl/device.rs | 94 ++++++--- src/x11/context.rs | 19 +- src/x11/device.rs | 50 ++++- 27 files changed, 723 insertions(+), 626 deletions(-) diff --git a/src/angle/context.rs b/src/angle/context.rs index ef063fc3..0f9e51e5 100644 --- a/src/angle/context.rs +++ b/src/angle/context.rs @@ -10,23 +10,8 @@ use crate::surface::Framebuffer; use crate::Gl; use std::thread; -/// Represents an OpenGL rendering context. -/// -/// A context allows you to issue rendering commands to a surface. When initially created, a -/// context has no attached surface, so rendering commands will fail or be ignored. Typically, you -/// attach a surface to the context before rendering. -/// -/// Contexts take ownership of the surfaces attached to them. In order to mutate a surface in any -/// way other than rendering to it (e.g. presenting it to a window, which causes a buffer swap), it -/// must first be detached from its context. Each surface is associated with a single context upon -/// creation and may not be rendered to from any other context. However, you can wrap a surface in -/// a surface texture, which allows the surface to be read from another context. -/// -/// OpenGL objects may not be shared across contexts directly, but surface textures effectively -/// allow for sharing of texture data. Contexts are local to a single thread and device. -/// -/// A context must be explicitly destroyed with `destroy_context()`, or a panic will occur. -pub struct Context { +/// 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, @@ -34,7 +19,7 @@ pub struct Context { pub(crate) gl: Gl, } -impl Drop for Context { +impl Drop for AngleContext { #[inline] fn drop(&mut self) { if self.egl_context != egl::NO_CONTEXT && !thread::panicking() { diff --git a/src/angle/device.rs b/src/angle/device.rs index f6d3eb84..7ab4bb0e 100644 --- a/src/angle/device.rs +++ b/src/angle/device.rs @@ -2,7 +2,7 @@ use super::adapter::AngleAdapter; use crate::angle::connection::Connection; -use crate::angle::context::{Context, NativeContext}; +use crate::angle::context::{AngleContext, NativeContext}; use crate::angle::surface::{ Surface, SurfaceDataGuard, SurfaceTexture, Synchronization, Win32Objects, }; @@ -20,10 +20,9 @@ use crate::egl::types::{ EGLAttrib, EGLConfig, EGLDeviceEXT, EGLDisplay, EGLNativeWindowType, EGLSurface, EGLint, }; use crate::surface::Framebuffer; -use crate::EglContextDescriptor; use crate::{ - egl, gl, Adapter, ContextAttributes, ContextDescriptor, Error, GLApi, Gl, SurfaceAccess, - SurfaceInfo, SurfaceType, + egl, gl, Adapter, Context, ContextAttributes, ContextDescriptor, EglContextDescriptor, Error, + GLApi, Gl, SurfaceAccess, SurfaceInfo, SurfaceType, }; use euclid::default::Size2D; use glow::HasContext; @@ -271,6 +270,11 @@ impl Device { descriptor: &ContextDescriptor, share_with: Option<&Context>, ) -> Result { + let share_with = match share_with { + Some(share_with) => Some(share_with.angle()?), + None => None, + }; + let (egl_context, id) = { let mut next_context_id_lock = CREATE_CONTEXT_MUTEX.lock().unwrap(); let egl_context = unsafe { @@ -302,14 +306,14 @@ impl Device { })?; } - let context = Context { + Ok(AngleContext { egl_context, id, framebuffer: Framebuffer::None, context_is_owned: true, gl: unsafe { Gl::from_loader_function(context::get_proc_address) }, - }; - Ok(context) + } + .into()) } /// Wraps a native `EGLContext` in a context object. @@ -324,7 +328,7 @@ impl Device { let mut next_context_id = CREATE_CONTEXT_MUTEX.lock().unwrap(); // Create the context. - let context = Context { + let context = AngleContext { egl_context: native_context.egl_context, id: *next_context_id, framebuffer: Framebuffer::External(ExternalEGLSurfaces { @@ -336,21 +340,22 @@ impl Device { }; next_context_id.0 += 1; - Ok(context) + Ok(context.into()) } /// Destroys a context. /// /// The context must have been created on this device. pub fn destroy_context(&self, context: &mut Context) -> Result<(), Error> { - if context.egl_context == egl::NO_CONTEXT { - return Ok(()); - } - if let Ok(Some(mut surface)) = self.unbind_surface_from_context(context) { self.destroy_surface(context, &mut surface)?; } + let context: &mut AngleContext = context.try_into()?; + if context.egl_context == egl::NO_CONTEXT { + return Ok(()); + } + EGL_FUNCTIONS.with(|egl| unsafe { egl.MakeCurrent( self.egl_display, @@ -372,6 +377,7 @@ impl Device { /// Returns the descriptor that this context was created with. pub fn context_descriptor(&self, context: &Context) -> ContextDescriptor { + let context = context.angle().expect("Passed incorrect context type"); unsafe { EglContextDescriptor::from_egl_context( &context.gl, @@ -382,31 +388,35 @@ impl Device { } } - /// Makes the context the current OpenGL context for this thread. - /// - /// After calling this function, it is valid to use OpenGL rendering commands. - pub fn make_context_current(&self, context: &Context) -> Result<(), Error> { - unsafe { - let (egl_draw_surface, egl_read_surface) = match context.framebuffer { - Framebuffer::Surface(ref surface) => (surface.egl_surface, surface.egl_surface), - Framebuffer::None => (egl::NO_SURFACE, egl::NO_SURFACE), - Framebuffer::External(ref surfaces) => (surfaces.draw, surfaces.read), - }; + fn make_context_current_inner(&self, context: &AngleContext) -> Result<(), Error> { + let (egl_draw_surface, egl_read_surface) = match context.framebuffer { + Framebuffer::Surface(ref surface) => (surface.egl_surface, surface.egl_surface), + Framebuffer::None => (egl::NO_SURFACE, egl::NO_SURFACE), + Framebuffer::External(ref surfaces) => (surfaces.draw, surfaces.read), + }; - EGL_FUNCTIONS.with(|egl| { - let result = egl.MakeCurrent( + EGL_FUNCTIONS.with(|egl| { + let result = unsafe { + egl.MakeCurrent( self.egl_display, egl_draw_surface, egl_read_surface, context.egl_context, - ); - if result == egl::FALSE { - let err = egl.GetError().to_windowing_api_error(); - return Err(Error::MakeCurrentFailed(err)); - } - Ok(()) - }) - } + ) + }; + if result == egl::FALSE { + let err = unsafe { egl.GetError() }.to_windowing_api_error(); + return Err(Error::MakeCurrentFailed(err)); + } + Ok(()) + }) + } + + /// Makes the context the current OpenGL context for this thread. + /// + /// After calling this function, it is valid to use OpenGL rendering commands. + pub fn make_context_current(&self, context: &Context) -> Result<(), Error> { + self.make_context_current_inner(context.angle()?) } /// Removes the current OpenGL context from this thread. @@ -419,14 +429,14 @@ impl Device { pub(crate) fn temporarily_make_context_current( &self, - context: &Context, + context: &AngleContext, ) -> Result { let guard = CurrentContextGuard::new(); - self.make_context_current(context)?; + self.make_context_current_inner(context)?; Ok(guard) } - pub(crate) fn context_is_current(&self, context: &Context) -> bool { + pub(crate) fn context_is_current(&self, context: &AngleContext) -> bool { EGL_FUNCTIONS.with(|egl| unsafe { egl.GetCurrentContext() == context.egl_context }) } @@ -477,6 +487,10 @@ impl Device { context: &mut Context, surface: Surface, ) -> Result<(), (Error, Surface)> { + let context: &mut AngleContext = match context.try_into() { + Ok(context) => context, + Err(error) => return Err((error, surface)), + }; if context.id != surface.context_id { return Err((Error::IncompatibleSurface, surface)); } @@ -514,7 +528,7 @@ impl Device { if is_current { // We need to make ourselves current again, because the surface changed. - drop(self.make_context_current(context)); + drop(self.make_context_current_inner(context)); } Ok(()) @@ -528,6 +542,7 @@ impl Device { &self, context: &mut Context, ) -> Result, Error> { + let context: &mut AngleContext = context.try_into()?; match context.framebuffer { Framebuffer::None => return Ok(None), Framebuffer::External(_) => return Err(Error::ExternalRenderTarget), @@ -559,6 +574,7 @@ impl Device { /// Widget surfaces are internally double-buffered, so changes to them don't /// show up in their associated widgets until this method is called. pub fn present_bound_surface(&self, context: &mut Context) -> Result<(), Error> { + let context: &mut AngleContext = context.try_into()?; match &context.framebuffer { Framebuffer::Surface(surface) => surface.present(self), _ => Ok(()), @@ -571,6 +587,7 @@ impl Device { context: &mut Context, size: Size2D, ) -> Result<(), Error> { + let context: &mut AngleContext = context.try_into()?; if let Framebuffer::Surface(surface) = &mut context.framebuffer { surface.resize(size); } @@ -583,6 +600,7 @@ impl Device { /// a new one, the new context might have the same ID as the destroyed one. #[inline] pub fn context_id(&self, context: &Context) -> ContextID { + let context = context.angle().expect("Passed incorrect context type"); context.id } @@ -590,6 +608,7 @@ impl Device { /// /// This includes, most notably, the OpenGL framebuffer object needed to render to the surface. pub fn context_surface_info(&self, context: &Context) -> Result, Error> { + let context = context.angle()?; match context.framebuffer { Framebuffer::None => Ok(None), Framebuffer::External(_) => Err(Error::ExternalRenderTarget), @@ -599,6 +618,7 @@ impl Device { /// Given a context, returns its underlying EGL context and attached surfaces. 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::External(ExternalEGLSurfaces { draw, read }) => (draw, read), @@ -643,6 +663,7 @@ impl Device { 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); + let context = context.angle()?; unsafe { let attributes = [ @@ -746,6 +767,7 @@ impl Device { ) -> Result { let context_descriptor = self.context_descriptor(context).egl()?.clone(); let egl_config = self.context_descriptor_to_egl_config(&context_descriptor); + let context = context.angle()?; unsafe { EGL_FUNCTIONS.with(|egl| { @@ -875,6 +897,11 @@ impl Device { local_egl_surface: EGLSurface, local_keyed_mutex: Option>, ) -> Result { + let context = match context.angle() { + Ok(context) => context, + Err(error) => return Err((error, surface)), + }; + EGL_FUNCTIONS.with(|egl| { unsafe { let _guard = self.temporarily_make_context_current(context); @@ -944,6 +971,7 @@ impl Device { context: &mut Context, surface: &mut Surface, ) -> Result<(), Error> { + let context = context.angle()?; if context.id != surface.context_id { return Err(Error::IncompatibleSurface); } @@ -982,6 +1010,11 @@ impl Device { context: &mut Context, mut surface_texture: SurfaceTexture, ) -> Result { + let context = match context.angle() { + Ok(context) => context, + Err(error) => return Err((error, surface_texture)), + }; + unsafe { if let Some(texture) = surface_texture.gl_texture.take() { context.gl.delete_texture(texture); diff --git a/src/cgl/context.rs b/src/cgl/context.rs index 2e043b66..d707881f 100644 --- a/src/cgl/context.rs +++ b/src/cgl/context.rs @@ -13,23 +13,8 @@ use std::ptr; use std::rc::Rc; use std::thread; -/// Represents an OpenGL rendering context. -/// -/// A context allows you to issue rendering commands to a surface. When initially created, a -/// context has no attached surface, so rendering commands will fail or be ignored. Typically, you -/// attach a surface to the context before rendering. -/// -/// Contexts take ownership of the surfaces attached to them. In order to mutate a surface in any -/// way other than rendering to it (e.g. presenting it to a window, which causes a buffer swap), it -/// must first be detached from its context. Each surface is associated with a single context upon -/// creation and may not be rendered to from any other context. However, you can wrap a surface in -/// a surface texture, which allows the surface to be read from another context. -/// -/// OpenGL objects may not be shared across contexts directly, but surface textures effectively -/// allow for sharing of texture data. Contexts are local to a single thread and device. -/// -/// A context must be explicitly destroyed with `destroy_context()`, or a panic will occur. -pub struct Context { +/// An implementation of [`crate::Context`] for CGL. +pub struct CglContext { pub(crate) cgl_context: CGLContextObj, pub(crate) id: ContextID, pub(crate) framebuffer: Framebuffer, @@ -39,7 +24,7 @@ pub struct Context { /// Wraps a native CGL context object. pub struct NativeContext(pub CGLContextObj); -impl Drop for Context { +impl Drop for CglContext { #[inline] fn drop(&mut self) { if !self.cgl_context.is_null() && !thread::panicking() { diff --git a/src/cgl/device.rs b/src/cgl/device.rs index dc78e0fa..950c4eaa 100644 --- a/src/cgl/device.rs +++ b/src/cgl/device.rs @@ -1,7 +1,7 @@ //! A handle to the device. (This is a no-op, because handles are implicit in Apple's Core OpenGL.) use super::connection::Connection; -use super::context::CglContextDescriptor; +use super::context::{CglContext, CglContextDescriptor}; use crate::base::io_surface::device::Device as SystemDevice; use crate::cgl::context::{CurrentContextGuard, NativeContext}; use crate::cgl::error::ToWindowingApiError; @@ -178,6 +178,11 @@ impl Device { descriptor: &ContextDescriptor, share_with: Option<&Context>, ) -> Result { + let share_with = match share_with { + Some(share_with) => Some(share_with.cgl()?), + None => None, + }; + // Take a lock so that we're only creating one context at a time. `CGLChoosePixelFormat` // will fail, returning `kCGLBadConnection`, if multiple threads try to open a display // connection simultaneously. @@ -198,14 +203,14 @@ impl Device { make_cgl_context_current(cgl_context)?; // Wrap and return the context. - let context = Context { + let context = CglContext { cgl_context, id: *next_context_id, framebuffer: Framebuffer::None, gl: Rc::new(Gl::from_loader_function(get_proc_address)), }; next_context_id.0 += 1; - Ok(context) + Ok(context.into()) } } @@ -218,7 +223,7 @@ impl Device { native_context: NativeContext, ) -> Result { let mut next_context_id = CREATE_CONTEXT_MUTEX.lock().unwrap(); - let context = Context { + let context = CglContext { cgl_context: native_context.0, id: *next_context_id, framebuffer: Framebuffer::None, @@ -226,13 +231,14 @@ impl Device { }; next_context_id.0 += 1; mem::forget(native_context); - Ok(context) + Ok(context.into()) } /// Destroys a context. /// /// The context must have been created on this device. pub fn destroy_context(&self, context: &mut Context) -> Result<(), Error> { + let context: &mut CglContext = context.try_into()?; if context.cgl_context.is_null() { return Ok(()); } @@ -240,7 +246,7 @@ impl Device { if let Framebuffer::Surface(mut surface) = mem::replace(&mut context.framebuffer, Framebuffer::None) { - self.destroy_surface(context, &mut surface)?; + self.destroy_surface_inner(context, &mut surface)?; } unsafe { @@ -255,6 +261,7 @@ impl Device { /// Returns the descriptor that this context was created with. #[inline] pub fn context_descriptor(&self, context: &Context) -> ContextDescriptor { + let context = context.cgl().expect("Passed incorrect context type"); unsafe { let mut cgl_pixel_format = CGLGetPixelFormat(context.cgl_context); cgl_pixel_format = CGLRetainPixelFormat(cgl_pixel_format); @@ -266,6 +273,7 @@ impl Device { /// /// After calling this function, it is valid to use OpenGL rendering commands. pub fn make_context_current(&self, context: &Context) -> Result<(), Error> { + let context = context.cgl()?; make_cgl_context_current(context.cgl_context) } @@ -307,6 +315,11 @@ impl Device { context: &mut Context, new_surface: Surface, ) -> Result<(), (Error, Surface)> { + let context: &mut CglContext = match context.try_into() { + Ok(context) => context, + Err(error) => return Err((error, new_surface)), + }; + match context.framebuffer { Framebuffer::External(_) => return Err((Error::ExternalRenderTarget, new_surface)), Framebuffer::Surface(_) => return Err((Error::SurfaceAlreadyBound, new_surface)), @@ -329,12 +342,16 @@ impl Device { &self, context: &mut Context, ) -> Result, Error> { - match context.framebuffer { - Framebuffer::External(_) => return Err(Error::ExternalRenderTarget), - Framebuffer::None | Framebuffer::Surface(_) => {} - } + let framebuffer = { + let context: &mut CglContext = context.try_into()?; + match context.framebuffer { + Framebuffer::External(_) => return Err(Error::ExternalRenderTarget), + Framebuffer::None | Framebuffer::Surface(_) => {} + }; + mem::replace(&mut context.framebuffer, Framebuffer::None) + }; - match mem::replace(&mut context.framebuffer, Framebuffer::None) { + match framebuffer { Framebuffer::External(_) => unreachable!(), Framebuffer::None => Ok(None), Framebuffer::Surface(surface) => { @@ -344,6 +361,7 @@ impl Device { // server. let _guard = self.temporarily_make_context_current(context)?; + let context = context.cgl()?; let gl = &context.gl; unsafe { gl.flush(); @@ -363,6 +381,7 @@ impl Device { /// Widget surfaces are internally double-buffered, so changes to them don't /// show up in their associated widgets until this method is called. pub fn present_bound_surface(&self, context: &mut Context) -> Result<(), Error> { + let context: &mut CglContext = context.try_into()?; if let Framebuffer::Surface(surface) = &mut context.framebuffer { // Presenting the surface is not a GL operation on macOS, it's just // CoreAnimation and IOSurface management. This means that it will @@ -385,6 +404,8 @@ impl Device { let _guard = self.temporarily_make_context_current(context); let context_descriptor = self.context_descriptor(context); let context_attributes = self.context_descriptor_attributes(&context_descriptor); + + let context: &mut CglContext = context.try_into()?; if let Framebuffer::Surface(surface) = &mut context.framebuffer { return self.resize_inner(surface, size, &context.gl, context_attributes); } @@ -458,6 +479,7 @@ impl Device { /// /// This includes, most notably, the OpenGL framebuffer object needed to render to the surface. pub fn context_surface_info(&self, context: &Context) -> Result, Error> { + let context = context.cgl()?; match context.framebuffer { Framebuffer::None => Ok(None), Framebuffer::External(_) => Err(Error::ExternalRenderTarget), @@ -471,6 +493,7 @@ impl Device { /// a new one, the new context might have the same ID as the destroyed one. #[inline] pub fn context_id(&self, context: &Context) -> ContextID { + let context = context.cgl().expect("Passed incorrect context type"); context.id } @@ -480,6 +503,7 @@ impl Device { /// returning. #[inline] pub fn native_context(&self, context: &Context) -> NativeContext { + let context = context.cgl().expect("Passed incorrect context type"); unsafe { NativeContext(CGLRetainContext(context.cgl_context)) } } @@ -497,6 +521,10 @@ impl Device { self.0.set_surface_flipped(&mut system_surface, true); let _guard = self.temporarily_make_context_current(context); + let context_descriptor = self.context_descriptor(context); + let context_attributes = self.context_descriptor_attributes(&context_descriptor); + + let context = context.cgl()?; let gl = &context.gl; unsafe { let texture_object = @@ -514,9 +542,6 @@ impl Device { 0, ); - let context_descriptor = self.context_descriptor(context); - let context_attributes = self.context_descriptor_attributes(&context_descriptor); - let mut renderbuffers = Renderbuffers::new(gl, &system_surface.size, &context_attributes); renderbuffers.bind_to_current_framebuffer(gl); @@ -566,6 +591,11 @@ impl Device { let _guard = self.temporarily_make_context_current(context).unwrap(); + let context = match context.cgl() { + Ok(context) => context, + Err(error) => return Err((error, surface)), + }; + let texture_object = self.bind_to_gl_texture( &context.gl, &surface.system_surface.io_surface, @@ -619,16 +649,9 @@ impl Device { } } - /// 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( + fn destroy_surface_inner( &self, - context: &mut Context, + context: &mut CglContext, surface: &mut Surface, ) -> Result<(), Error> { let gl = &context.gl; @@ -650,6 +673,22 @@ impl Device { self.0.destroy_surface(&mut surface.system_surface) } + /// 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 CglContext = context.try_into()?; + self.destroy_surface_inner(context, surface) + } + /// Destroys a surface texture and returns the underlying surface. /// /// The supplied context must be the same context the surface texture was created with, or an @@ -662,7 +701,11 @@ impl Device { context: &mut Context, mut surface_texture: SurfaceTexture, ) -> Result { - let gl = &context.gl; + let concrete_context = match context.cgl() { + Ok(concrete_context) => concrete_context, + Err(error) => return Err((error, surface_texture)), + }; + let gl = &concrete_context.gl; if let Some(texture) = surface_texture.texture_object.take() { unsafe { gl.delete_texture(texture); @@ -696,6 +739,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: &Context, surface: &mut Surface) -> Result<(), Error> { + let context = context.cgl()?; self.0.present_surface(&mut surface.system_surface)?; surface.bind_to_texture(&context.gl); Ok(()) @@ -708,14 +752,15 @@ impl Device { surface: &mut Surface, size: Size2D, ) -> Result<(), Error> { - if context.id != surface.context_id { + let concrete_context = context.cgl()?; + if concrete_context.id != surface.context_id { return Err(Error::IncompatibleSurface); } let _guard = self.temporarily_make_context_current(context); let context_descriptor = self.context_descriptor(context); let context_attributes = self.context_descriptor_attributes(&context_descriptor); - self.resize_inner(surface, size, &context.gl, context_attributes) + self.resize_inner(surface, size, &concrete_context.gl, context_attributes) } pub(crate) fn resize_inner( diff --git a/src/chains.rs b/src/chains.rs index 42128f5e..00432778 100644 --- a/src/chains.rs +++ b/src/chains.rs @@ -23,7 +23,7 @@ #![allow(missing_docs)] use crate::device::Device as DeviceAPI; -use crate::{ContextID, Error, SurfaceAccess, SurfaceInfo, SurfaceType}; +use crate::{Context, ContextID, Error, SurfaceAccess, SurfaceInfo, SurfaceType}; use euclid::default::Size2D; use fnv::{FnvHashMap, FnvHashSet}; use glow as gl; @@ -68,7 +68,7 @@ impl BackBuffer { fn take_surface( &mut self, device: &Device, - context: &mut Device::Context, + context: &mut Context, ) -> Result { let new_back_buffer = match self { BackBuffer::Attached => BackBuffer::TakenAttached, @@ -85,7 +85,7 @@ impl BackBuffer { fn take_surface_texture( &mut self, device: &Device, - context: &mut Device::Context, + context: &mut Context, ) -> Result { let surface = self.take_surface(device, context)?; device @@ -98,7 +98,7 @@ impl BackBuffer { fn replace_surface( &mut self, device: &Device, - context: &mut Device::Context, + context: &mut Context, surface: Device::Surface, ) -> Result<(), Error> { let new_back_buffer = match self { @@ -119,7 +119,7 @@ impl BackBuffer { fn replace_surface_texture( &mut self, device: &Device, - context: &mut Device::Context, + context: &mut Context, surface_texture: Device::SurfaceTexture, ) -> Result<(), Error> { let surface = device @@ -131,7 +131,7 @@ impl BackBuffer { impl SwapChainData { // Returns `Ok` if `context` is the producer context for this swap chain. - fn validate_context(&self, device: &Device, context: &Device::Context) -> Result<(), Error> { + fn validate_context(&self, device: &Device, context: &Context) -> Result<(), Error> { if self.context_id == device.context_id(context) { Ok(()) } else { @@ -145,7 +145,7 @@ impl SwapChainData { fn swap_buffers( &mut self, device: &Device, - context: &mut Device::Context, + context: &mut Context, preserve_buffer: PreserveBuffer<'_>, ) -> Result<(), Error> { debug!("Swap buffers on context {:?}", self.context_id); @@ -231,7 +231,7 @@ impl SwapChainData { fn take_attachment_from( &mut self, device: &Device, - context: &mut Device::Context, + context: &mut Context, other: &mut SwapChainData, ) -> Result<(), Error> { self.validate_context(device, context)?; @@ -256,7 +256,7 @@ impl SwapChainData { fn resize( &mut self, device: &Device, - context: &mut Device::Context, + context: &mut Context, size: Size2D, ) -> Result<(), Error> { debug!( @@ -292,7 +292,7 @@ impl SwapChainData { fn take_surface_texture( &mut self, device: &Device, - context: &mut Device::Context, + context: &mut Context, ) -> Result { self.validate_context(device, context)?; self.back_buffer.take_surface_texture(device, context) @@ -303,7 +303,7 @@ impl SwapChainData { fn recycle_surface_texture( &mut self, device: &Device, - context: &mut Device::Context, + context: &mut Context, surface_texture: Device::SurfaceTexture, ) -> Result<(), Error> { self.validate_context(device, context)?; @@ -339,7 +339,7 @@ impl SwapChainData { fn clear_surface( &mut self, device: &Device, - context: &mut Device::Context, + context: &mut Context, gl: &Gl, color: [f32; 4], ) -> Result<(), Error> { @@ -452,7 +452,7 @@ impl SwapChainData { // Destroy the swap chain. // Called by the producer. // Returns an error if `context` is not the producer context for this swap chain. - fn destroy(&mut self, device: &Device, context: &mut Device::Context) -> Result<(), Error> { + fn destroy(&mut self, device: &Device, context: &mut Context) -> Result<(), Error> { self.validate_context(device, context)?; let surfaces = self .pending_surface @@ -489,7 +489,7 @@ impl SwapChain { pub fn swap_buffers( &self, device: &Device, - context: &mut Device::Context, + context: &mut Context, preserve_buffer: PreserveBuffer<'_>, ) -> Result<(), Error> { self.lock().swap_buffers(device, context, preserve_buffer) @@ -502,7 +502,7 @@ impl SwapChain { pub fn take_attachment_from( &self, device: &Device, - context: &mut Device::Context, + context: &mut Context, other: &SwapChain, ) -> Result<(), Error> { self.lock() @@ -517,7 +517,7 @@ impl SwapChain { pub fn resize( &self, device: &Device, - context: &mut Device::Context, + context: &mut Context, size: Size2D, ) -> Result<(), Error> { self.lock().resize(device, context, size) @@ -534,7 +534,7 @@ impl SwapChain { pub fn take_surface_texture( &self, device: &Device, - context: &mut Device::Context, + context: &mut Context, ) -> Result { self.lock().take_surface_texture(device, context) } @@ -544,7 +544,7 @@ impl SwapChain { pub fn recycle_surface_texture( &self, device: &Device, - context: &mut Device::Context, + context: &mut Context, surface_texture: Device::SurfaceTexture, ) -> Result<(), Error> { self.lock() @@ -564,7 +564,7 @@ impl SwapChain { pub fn clear_surface( &self, device: &Device, - context: &mut Device::Context, + context: &mut Context, gl: &Gl, color: [f32; 4], ) -> Result<(), Error> { @@ -579,14 +579,14 @@ impl SwapChain { /// Destroy the swap chain. /// Called by the producer. /// Returns an error if `context` is not the producer context for this swap chain. - pub fn destroy(&self, device: &Device, context: &mut Device::Context) -> Result<(), Error> { + pub fn destroy(&self, device: &Device, context: &mut Context) -> Result<(), Error> { self.lock().destroy(device, context) } /// Create a new attached swap chain pub fn create_attached( device: &Device, - context: &mut Device::Context, + context: &mut Context, surface_access: SurfaceAccess, ) -> Result, Error> { let size = device.context_surface_info(context).unwrap().unwrap().size; @@ -603,7 +603,7 @@ impl SwapChain { /// Create a new detached swap chain pub fn create_detached( device: &Device, - context: &mut Device::Context, + context: &mut Context, surface_access: SurfaceAccess, size: Size2D, ) -> Result, Error> { @@ -694,7 +694,7 @@ where &self, id: SwapChainID, device: &Device, - context: &mut Device::Context, + context: &mut Context, surface_access: SurfaceAccess, ) -> Result<(), Error> { match self.table_mut().entry(id.clone()) { @@ -717,7 +717,7 @@ where id: SwapChainID, size: Size2D, device: &Device, - context: &mut Device::Context, + context: &mut Context, surface_access: SurfaceAccess, ) -> Result<(), Error> { match self.table_mut().entry(id.clone()) { @@ -743,7 +743,7 @@ where &self, id: SwapChainID, device: &Device, - context: &mut Device::Context, + context: &mut Context, ) -> Result<(), Error> { if let Some(swap_chain) = self.table_mut().remove(&id) { swap_chain.destroy(device, context)?; @@ -759,7 +759,7 @@ where pub fn iter( &self, device: &Device, - context: &mut Device::Context, + context: &mut Context, ) -> impl Iterator)> { self.ids() .get(&device.context_id(context)) diff --git a/src/context.rs b/src/context.rs index e26b4711..3d802e2f 100644 --- a/src/context.rs +++ b/src/context.rs @@ -4,12 +4,28 @@ use crate::gl; use crate::info::GLVersion; +use crate::macros::enum_conversion; use crate::Gl; use std::ffi::CStr; use std::os::raw::c_char; use std::sync::Mutex; +#[cfg(all(windows_platform, feature = "sm-angle"))] +use crate::angle::context::AngleContext; +#[cfg(macos_platform)] +use crate::cgl::context::CglContext; +#[cfg(any(android_platform, ohos_platform))] +use crate::hardware_buffer::context::HardwareBufferContext; +#[cfg(free_unix)] +use crate::mesa_surfaceless::context::SurfacelessMesaContext; +#[cfg(wayland_platform)] +use crate::wayland::context::WaylandContext; +#[cfg(all(windows_platform, not(feature = "sm-no-wgl")))] +use crate::wgl::context::WglContext; +#[cfg(x11_platform)] +use crate::x11::context::X11Context; + /// A unique ID among all currently-allocated contexts. /// /// If you destroy a context, subsequently-allocated contexts might reuse the same ID. @@ -91,3 +107,76 @@ pub(crate) fn current_context_uses_compatibility_profile(gl: &Gl) -> bool { gl.supported_extensions().contains("GL_ARB_compatibility") } } + +/// Represents an OpenGL rendering context. +/// +/// A context allows you to issue rendering commands to a surface. When initially created, a +/// context has no attached surface, so rendering commands will fail or be ignored. Typically, you +/// attach a surface to the context before rendering. +/// +/// Contexts take ownership of the surfaces attached to them. In order to mutate a surface in any +/// way other than rendering to it (e.g. presenting it to a window, which causes a buffer swap), it +/// must first be detached from its context. Each surface is associated with a single context upon +/// creation and may not be rendered to from any other context. However, you can wrap a surface in +/// a surface texture, which allows the surface to be read from another context. +/// +/// OpenGL objects may not be shared across contexts directly, but surface textures effectively +/// allow for sharing of texture data. Contexts are local to a single thread and device. +/// +/// A context must be explicitly destroyed with `destroy_context()`, or a panic will occur. +pub enum Context { + /// An ANGLE context for Windows systems. + #[cfg(all(windows_platform, feature = "sm-angle"))] + Angle(AngleContext), + /// A context for Apple systems. + #[cfg(macos_platform)] + Cgl(CglContext), + /// A hardware buffer context for OHOS and Android systems. + #[cfg(any(android_platform, ohos_platform))] + HardwareBuffer(HardwareBufferContext), + /// A surfaceless Mesa context for X11 / Wayland systems. + #[cfg(free_unix)] + SurfacelessMesa(SurfacelessMesaContext), + /// A context for Wayland systems. + #[cfg(wayland_platform)] + Wayland(WaylandContext), + /// A WGL context for Windows systems. + #[cfg(all(windows_platform, not(feature = "sm-no-wgl")))] + Wgl(WglContext), + /// A context for X11 systems. + #[cfg(x11_platform)] + X11(X11Context), +} + +#[cfg(all(windows_platform, feature = "sm-angle"))] +enum_conversion!(Context, Angle, AngleContext, angle, IncompatibleContext); +#[cfg(macos_platform)] +enum_conversion!(Context, Cgl, CglContext, cgl, IncompatibleContext); +#[cfg(any(android_platform, ohos_platform))] +enum_conversion!( + Context, + HardwareBuffer, + HardwareBufferContext, + hardware_buffer, + IncompatibleContext +); +#[cfg(free_unix)] +enum_conversion!( + Context, + SurfacelessMesa, + SurfacelessMesaContext, + surfaceless_mesa, + IncompatibleContext +); +#[cfg(wayland_platform)] +enum_conversion!( + Context, + Wayland, + WaylandContext, + wayland, + IncompatibleContext +); +#[cfg(all(windows_platform, not(feature = "sm-no-wgl")))] +enum_conversion!(Context, Wgl, WglContext, wgl, IncompatibleContext); +#[cfg(x11_platform)] +enum_conversion!(Context, X11, X11Context, x11, IncompatibleContext); diff --git a/src/device.rs b/src/device.rs index 6bb1152c..7f663bfa 100644 --- a/src/device.rs +++ b/src/device.rs @@ -2,7 +2,7 @@ use super::connection::Connection as ConnectionInterface; use crate::{ - Adapter, ContextAttributes, ContextDescriptor, ContextID, Error, GLApi, SurfaceAccess, + Adapter, Context, ContextAttributes, ContextDescriptor, ContextID, Error, GLApi, SurfaceAccess, SurfaceInfo, SurfaceType, }; use euclid::default::Size2D; @@ -19,8 +19,6 @@ where { /// The connection type associated with this device. type Connection; - /// The context type associated with this device. - type Context; /// The surface type associated with this device. type Surface; /// The surface texture type associated with this device. @@ -54,21 +52,21 @@ where fn create_context( &self, descriptor: &ContextDescriptor, - share_with: Option<&Self::Context>, - ) -> Result; + share_with: Option<&Context>, + ) -> Result; /// Destroys a context. /// /// The context must have been created on this device. - fn destroy_context(&self, context: &mut Self::Context) -> Result<(), Error>; + fn destroy_context(&self, context: &mut Context) -> Result<(), Error>; /// Returns the descriptor that this context was created with. - fn context_descriptor(&self, context: &Self::Context) -> ContextDescriptor; + fn context_descriptor(&self, context: &Context) -> ContextDescriptor; /// Makes the context the current OpenGL context for this thread. /// /// After calling this function, it is valid to use OpenGL rendering commands. - fn make_context_current(&self, context: &Self::Context) -> Result<(), Error>; + fn make_context_current(&self, context: &Context) -> Result<(), Error>; /// Removes the current OpenGL context from this thread. /// @@ -89,7 +87,7 @@ where /// /// This method is typically used with a function like `gl::load_with()` from the `gl` crate to /// load OpenGL function pointers. - fn get_proc_address(&self, context: &Self::Context, symbol_name: &str) -> *const c_void; + fn get_proc_address(&self, context: &Context, symbol_name: &str) -> *const c_void; /// Attaches a surface to a context for rendering. /// @@ -103,7 +101,7 @@ where /// If an error is returned, the surface is returned alongside it. fn bind_surface_to_context( &self, - context: &mut Self::Context, + context: &mut Context, surface: Self::Surface, ) -> Result<(), (Error, Self::Surface)>; @@ -113,19 +111,19 @@ where /// surface is safe to read from immediately when this function returns. fn unbind_surface_from_context( &self, - context: &mut Self::Context, + context: &mut Context, ) -> Result, Error>; /// Returns a unique ID representing a context. /// /// This ID is unique to all currently-allocated contexts. If you destroy a context and create /// a new one, the new context might have the same ID as the destroyed one. - fn context_id(&self, context: &Self::Context) -> ContextID; + fn context_id(&self, context: &Context) -> ContextID; /// Returns various information about the surface attached to a context. /// /// This includes, most notably, the OpenGL framebuffer object needed to render to the surface. - fn context_surface_info(&self, context: &Self::Context) -> Result, Error>; + fn context_surface_info(&self, context: &Context) -> Result, Error>; // surface.rs @@ -135,7 +133,7 @@ where /// up in a `SurfaceTexture` for reading by other contexts. fn create_surface( &self, - context: &Self::Context, + context: &Context, surface_access: SurfaceAccess, surface_type: SurfaceType<'_>, ) -> Result; @@ -152,7 +150,7 @@ where /// Calling this method on a widget surface returns a `WidgetAttached` error. fn create_surface_texture( &self, - context: &mut Self::Context, + context: &mut Context, surface: Self::Surface, ) -> Result; @@ -165,7 +163,7 @@ where /// the `drop` method. fn destroy_surface( &self, - context: &mut Self::Context, + context: &mut Context, surface: &mut Self::Surface, ) -> Result<(), Error>; @@ -178,7 +176,7 @@ where /// occur. fn destroy_surface_texture( &self, - context: &mut Self::Context, + context: &mut Context, surface_texture: Self::SurfaceTexture, ) -> Result; @@ -192,7 +190,7 @@ where /// /// Widget surfaces are internally double-buffered, so changes to them don't /// show up in their associated widgets until this method is called. - fn present_bound_surface(&self, context: &mut Self::Context) -> Result<(), Error>; + fn present_bound_surface(&self, context: &mut Context) -> Result<(), Error>; /// Displays the contents of a widget surface on screen. /// @@ -201,23 +199,15 @@ where /// /// The supplied context must match the context the surface was created with, or an /// `IncompatibleSurface` error is returned. - fn present_surface( - &self, - context: &Self::Context, - surface: &mut Self::Surface, - ) -> Result<(), Error>; + fn present_surface(&self, context: &Context, surface: &mut Self::Surface) -> Result<(), Error>; /// If the currently bound surface is a widget surface, resize it, - fn resize_bound_surface( - &self, - context: &mut Self::Context, - size: Size2D, - ) -> Result<(), Error>; + fn resize_bound_surface(&self, context: &mut Context, size: Size2D) -> Result<(), Error>; /// Resizes a widget surface. fn resize_surface( &self, - context: &Self::Context, + context: &Context, surface: &mut Self::Surface, size: Size2D, ) -> Result<(), Error>; diff --git a/src/hardware_buffer/context.rs b/src/hardware_buffer/context.rs index 42804959..0fb4bc57 100644 --- a/src/hardware_buffer/context.rs +++ b/src/hardware_buffer/context.rs @@ -11,23 +11,8 @@ use std::thread; pub use crate::base::egl::context::NativeContext; -/// Represents an OpenGL rendering context. -/// -/// A context allows you to issue rendering commands to a surface. When initially created, a -/// context has no attached surface, so rendering commands will fail or be ignored. Typically, you -/// attach a surface to the context before rendering. -/// -/// Contexts take ownership of the surfaces attached to them. In order to mutate a surface in any -/// way other than rendering to it (e.g. presenting it to a window, which causes a buffer swap), it -/// must first be detached from its context. Each surface is associated with a single context upon -/// creation and may not be rendered to from any other context. However, you can wrap a surface in -/// a surface texture, which allows the surface to be read from another context. -/// -/// OpenGL objects may not be shared across contexts directly, but surface textures effectively -/// allow for sharing of texture data. Contexts are local to a single thread and device. -/// -/// A context must be explicitly destroyed with `destroy_context()`, or a panic will occur. -pub struct Context { +/// An implementation of [`crate::Context`] for Android and OHOS platforms. +pub struct HardwareBufferContext { pub(crate) egl_context: EGLContext, pub(crate) id: ContextID, pub(crate) pbuffer: EGLSurface, @@ -36,7 +21,7 @@ pub struct Context { pub(crate) context_is_owned: bool, } -impl Drop for Context { +impl Drop for HardwareBufferContext { #[inline] fn drop(&mut self) { if self.egl_context != egl::NO_CONTEXT && !thread::panicking() { diff --git a/src/hardware_buffer/device.rs b/src/hardware_buffer/device.rs index 0fe0a4bd..ccad454c 100644 --- a/src/hardware_buffer/device.rs +++ b/src/hardware_buffer/device.rs @@ -2,7 +2,7 @@ use super::adapter::HardwareBufferAdapter; use super::connection::Connection; -use super::context::NativeContext; +use super::context::{HardwareBufferContext, NativeContext}; use crate::base::egl::context::{self, CurrentContextGuard}; use crate::base::egl::device::EGL_FUNCTIONS; use crate::base::egl::error::ToWindowingApiError; @@ -132,10 +132,13 @@ impl Device { descriptor: &ContextDescriptor, share_with: Option<&Context>, ) -> Result { - let mut next_context_id = CREATE_CONTEXT_MUTEX.lock().unwrap(); + let share_with = match share_with { + Some(share_with) => Some(share_with.hardware_buffer()?), + None => None, + }; + let mut next_context_id = CREATE_CONTEXT_MUTEX.lock().unwrap(); let egl_display = self.egl_display; - unsafe { // Create the EGL context. let gl_api = self.gl_api(); @@ -158,7 +161,7 @@ impl Device { })?; // Wrap up the EGL context. - let context = Context { + let context = HardwareBufferContext { egl_context, id: *next_context_id, pbuffer, @@ -167,7 +170,7 @@ impl Device { gl: Gl::from_loader_function(context::get_proc_address), }; next_context_id.0 += 1; - Ok(context) + Ok(context.into()) } } @@ -187,7 +190,7 @@ impl Device { context::create_dummy_pbuffer(self.egl_display, native_context.egl_context).unwrap(); // Create the context. - let context = Context { + let context = HardwareBufferContext { egl_context: native_context.egl_context, id: *next_context_id, pbuffer, @@ -200,24 +203,29 @@ impl Device { }; next_context_id.0 += 1; - Ok(context) + Ok(context.into()) } /// Destroys a context. /// /// The context must have been created on this device. pub fn destroy_context(&self, context: &mut Context) -> Result<(), Error> { - if context.egl_context == egl::NO_CONTEXT { - return Ok(()); - } - - unsafe { - if let Framebuffer::Surface(mut target) = + { + 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) - { + }; + + if let Framebuffer::Surface(mut target) = framebuffer { self.destroy_surface(context, &mut target)?; } + }; + unsafe { + let context: &mut HardwareBufferContext = context.try_into()?; EGL_FUNCTIONS.with(|egl| { let result = egl.DestroySurface(self.egl_display, context.pbuffer); assert_ne!(result, egl::FALSE); @@ -244,6 +252,9 @@ impl Device { /// Returns the descriptor that this context was created with. pub fn context_descriptor(&self, context: &Context) -> ContextDescriptor { + let context = context + .hardware_buffer() + .expect("Passed incorrect context type"); unsafe { EglContextDescriptor::from_egl_context( &context.gl, @@ -258,6 +269,7 @@ impl Device { /// /// After calling this function, it is valid to use OpenGL rendering commands. pub fn make_context_current(&self, context: &Context) -> Result<(), Error> { + let context = context.hardware_buffer()?; unsafe { let egl_display = self.egl_display; let egl_context = context.egl_context; @@ -310,6 +322,11 @@ impl Device { context: &mut Context, new_surface: Surface, ) -> Result<(), (Error, Surface)> { + let context: &mut HardwareBufferContext = match context.try_into() { + Ok(context) => context, + Err(error) => return Err((error, new_surface)), + }; + if context.id != new_surface.context_id { return Err((Error::IncompatibleSurface, new_surface)); } @@ -332,7 +349,7 @@ impl Device { &self, context: &mut Context, ) -> Result, Error> { - match context.framebuffer { + match context.hardware_buffer()?.framebuffer { Framebuffer::External { .. } => return Err(Error::ExternalRenderTarget), Framebuffer::None => return Ok(None), Framebuffer::Surface(_) => {} @@ -342,6 +359,7 @@ impl Device { // // FIXME(pcwalton): Is this necessary? let _guard = self.temporarily_make_context_current(context)?; + let context: &mut HardwareBufferContext = context.try_into()?; unsafe { context.gl.flush(); }; @@ -358,6 +376,7 @@ impl Device { /// Widget surfaces are internally double-buffered, so changes to them don't /// show up in their associated widgets until this method is called. pub fn present_bound_surface(&self, context: &mut Context) -> Result<(), Error> { + let context = context.hardware_buffer()?; match &context.framebuffer { Framebuffer::Surface(surface) => self.present_surface_inner(context, surface), _ => Ok(()), @@ -370,6 +389,7 @@ impl Device { context: &mut Context, size: Size2D, ) -> Result<(), Error> { + let context: &mut HardwareBufferContext = context.try_into()?; if let Framebuffer::Surface(surface) = &mut context.framebuffer { surface.resize(size); } @@ -399,7 +419,7 @@ impl Device { context::get_proc_address(symbol_name) } - pub(crate) fn context_to_egl_config(&self, context: &Context) -> EGLConfig { + pub(crate) fn context_to_egl_config(&self, context: &HardwareBufferContext) -> EGLConfig { unsafe { context::egl_config_from_id( self.egl_display, @@ -427,6 +447,9 @@ impl Device { /// a new one, the new context might have the same ID as the destroyed one. #[inline] pub fn context_id(&self, context: &Context) -> ContextID { + let context = context + .hardware_buffer() + .expect("Passed incorrect context type"); context.id } @@ -434,6 +457,7 @@ impl Device { /// /// This includes, most notably, the OpenGL framebuffer object needed to render to the surface. pub fn context_surface_info(&self, context: &Context) -> Result, Error> { + let context = context.hardware_buffer()?; match context.framebuffer { Framebuffer::None => Ok(None), Framebuffer::External { .. } => Err(Error::ExternalRenderTarget), @@ -443,6 +467,9 @@ impl Device { /// Given a context, returns its underlying EGL context and attached surfaces. pub fn native_context(&self, context: &Context) -> NativeContext { + let context = context + .hardware_buffer() + .expect("Passed incorrect context type"); let (egl_draw_surface, egl_read_surface) = match context.framebuffer { Framebuffer::Surface(Surface { objects: SurfaceObjects::Window { egl_surface }, diff --git a/src/hardware_buffer/surface/android_surface.rs b/src/hardware_buffer/surface/android_surface.rs index d29b13c8..2cdf8857 100644 --- a/src/hardware_buffer/surface/android_surface.rs +++ b/src/hardware_buffer/surface/android_surface.rs @@ -11,7 +11,6 @@ use super::super::android_ffi::{ use super::super::android_ffi::{ AHARDWAREBUFFER_USAGE_CPU_WRITE_NEVER, AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER, }; -use super::super::context::Context; use super::super::device::Device; use super::{Surface, SurfaceTexture}; use crate::base::egl::device::EGL_FUNCTIONS; @@ -20,11 +19,12 @@ use crate::base::egl::ffi::EGL_EXTENSION_FUNCTIONS; use crate::base::egl::ffi::EGL_IMAGE_PRESERVED_KHR; use crate::base::egl::ffi::EGL_NATIVE_BUFFER_ANDROID; 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::renderbuffers::Renderbuffers; +use crate::{egl, Context}; use crate::{Error, SurfaceAccess, SurfaceID, SurfaceInfo, SurfaceType, WindowingApiError}; use euclid::default::Size2D; @@ -77,7 +77,8 @@ impl Device { size: &Size2D, ) -> Result { let _guard = self.temporarily_make_context_current(context)?; - let gl = &context.gl; + let concrete_context = context.hardware_buffer()?; + let gl = &concrete_context.gl; unsafe { // Create a native hardware buffer. let hardware_buffer_desc = AHardwareBuffer_Desc { @@ -100,7 +101,7 @@ impl Device { } // Create an EGL image, and bind it to a texture. - let egl_image = self.create_egl_image(context, hardware_buffer); + let egl_image = self.create_egl_image(concrete_context, hardware_buffer); // Initialize and bind the image to the texture. let texture_object = @@ -126,7 +127,7 @@ impl Device { Ok(Surface { size: *size, - context_id: context.id, + context_id: concrete_context.id, objects: SurfaceObjects::HardwareBuffer { hardware_buffer, egl_image, @@ -146,6 +147,7 @@ impl Device { ) -> Result { let width = ANativeWindow_getWidth(native_window); let height = ANativeWindow_getHeight(native_window); + let context = context.hardware_buffer()?; EGL_FUNCTIONS.with(|egl| { let egl_surface = egl.CreateWindowSurface( @@ -190,6 +192,11 @@ impl Device { Ok(guard) => guard, Err(err) => return Err((err, surface)), }; + let context: &mut HardwareBufferContext = match context.try_into() { + Ok(context) => context, + Err(error) => return Err((error, surface)), + }; + let gl = &context.gl; let local_egl_image = self.create_egl_image(context, hardware_buffer); @@ -211,7 +218,7 @@ impl Device { #[allow(non_snake_case)] unsafe fn create_egl_image( &self, - _: &Context, + _: &HardwareBufferContext, hardware_buffer: *mut AHardwareBuffer, ) -> EGLImageKHR { // Get the native client buffer. @@ -254,6 +261,7 @@ impl Device { context: &mut Context, surface: &mut Surface, ) -> Result<(), Error> { + let context: &mut HardwareBufferContext = context.try_into()?; if context.id != surface.context_id { return Err(Error::IncompatibleSurface); } @@ -313,6 +321,10 @@ impl Device { mut 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 gl = &context.gl; unsafe { if let Some(texture) = surface_texture.texture_object.take() { diff --git a/src/hardware_buffer/surface/mod.rs b/src/hardware_buffer/surface/mod.rs index 2c153725..b383f96e 100644 --- a/src/hardware_buffer/surface/mod.rs +++ b/src/hardware_buffer/surface/mod.rs @@ -2,6 +2,7 @@ use crate::base::egl::ffi::EGLImageKHR; use crate::context::ContextID; +use crate::hardware_buffer::context::HardwareBufferContext; use crate::{Context, Device, Error}; use crate::base::egl::device::EGL_FUNCTIONS; @@ -99,12 +100,12 @@ 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, surface) + self.present_surface_inner(context.hardware_buffer()?, surface) } pub(crate) fn present_surface_inner( &self, - context: &Context, + context: &HardwareBufferContext, surface: &Surface, ) -> Result<(), Error> { if context.id != surface.context_id { diff --git a/src/hardware_buffer/surface/ohos_surface.rs b/src/hardware_buffer/surface/ohos_surface.rs index 707f3842..4a020cd6 100644 --- a/src/hardware_buffer/surface/ohos_surface.rs +++ b/src/hardware_buffer/surface/ohos_surface.rs @@ -17,10 +17,11 @@ use crate::egl; use crate::egl::types::{EGLSurface, EGLint}; use crate::gl; use crate::gl_utils; +use crate::hardware_buffer::context::HardwareBufferContext; use crate::renderbuffers::Renderbuffers; +use crate::Context; use crate::{Error, SurfaceAccess, SurfaceID, SurfaceInfo, SurfaceType}; -use super::super::context::Context; use super::super::device::Device; use super::super::ohos_ffi::{ eglGetNativeClientBufferANDROID, NativeWindowOperation, OHNativeWindow, OH_NativeBuffer, @@ -86,13 +87,14 @@ impl Device { stride: 10, // used same magic number as android. I have no idea }; - let gl = &context.gl; + let concrete_context = context.hardware_buffer()?; + let gl = &concrete_context.gl; unsafe { let hardware_buffer = OH_NativeBuffer_Alloc(&config as *const _); assert!(!hardware_buffer.is_null(), "Failed to create native buffer"); // Create an EGL image, and bind it to a texture. - let egl_image = self.create_egl_image(context, hardware_buffer); + let egl_image = self.create_egl_image(concrete_context, hardware_buffer); // Initialize and bind the image to the texture. let texture_object = @@ -118,7 +120,7 @@ impl Device { Ok(Surface { size: *size, - context_id: context.id, + context_id: concrete_context.id, objects: SurfaceObjects::HardwareBuffer { hardware_buffer, egl_image, @@ -148,6 +150,8 @@ impl Device { &mut width as *mut i32, ) }; + + let context = context.hardware_buffer()?; assert_eq!(result, 0, "Failed to determine size of native window"); EGL_FUNCTIONS.with(|egl| { let egl_surface = egl.CreateWindowSurface( @@ -192,6 +196,11 @@ impl Device { Ok(guard) => guard, Err(err) => return Err((err, surface)), }; + + let context: &mut HardwareBufferContext = match context.try_into() { + Ok(context) => context, + Err(error) => return Err((error, surface)), + }; let gl = &context.gl; let local_egl_image = self.create_egl_image(context, hardware_buffer); @@ -213,7 +222,7 @@ impl Device { #[allow(non_snake_case)] unsafe fn create_egl_image( &self, - _: &Context, + _: &HardwareBufferContext, hardware_buffer: *mut OH_NativeBuffer, ) -> EGLImageKHR { let client_buffer = eglGetNativeClientBufferANDROID(hardware_buffer as *const _); @@ -250,6 +259,7 @@ impl Device { context: &mut Context, surface: &mut Surface, ) -> Result<(), Error> { + let context: &mut HardwareBufferContext = context.try_into()?; if context.id != surface.context_id { return Err(Error::IncompatibleSurface); } @@ -310,6 +320,11 @@ impl Device { mut 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 gl = &context.gl; unsafe { if let Some(texture) = surface_texture.texture_object.take() { diff --git a/src/lib.rs b/src/lib.rs index b8ece8de..dfa8b0f7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -60,14 +60,13 @@ pub use wayland as default; #[cfg(all(windows_platform, not(angle_default)))] pub use wgl as default; -pub use crate::context::{ContextAttributeFlags, ContextAttributes, ContextID}; +pub use crate::context::{Context, ContextAttributeFlags, ContextAttributes, ContextID}; 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 adapter::{Adapter, AdapterPreferences, PowerPreference, RenderingPreference}; pub use default::connection::Connection; -pub use default::context::Context; pub use default::device::Device; pub use default::surface::{Surface, SurfaceTexture}; pub(crate) use glow::{self as gl, Context as Gl}; diff --git a/src/macros.rs b/src/macros.rs index 946b526d..c23cf7f7 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -35,7 +35,6 @@ macro_rules! implement_interfaces { () => { mod implementation { use super::connection::Connection; - use super::context::Context; use super::device::Device; use super::surface::{Surface, SurfaceTexture}; use euclid::default::Size2D; @@ -47,8 +46,8 @@ macro_rules! implement_interfaces { use $crate::info::GLApi; use $crate::Error; use $crate::{ - ContextAttributes, ContextDescriptor, ContextID, SurfaceAccess, SurfaceInfo, - SurfaceType, + Context, ContextAttributes, ContextDescriptor, ContextID, SurfaceAccess, + SurfaceInfo, SurfaceType, }; impl ConnectionInterface for Connection { @@ -87,7 +86,6 @@ macro_rules! implement_interfaces { impl DeviceInterface for Device { type Connection = Connection; - type Context = Context; type Surface = Surface; type SurfaceTexture = SurfaceTexture; @@ -122,23 +120,23 @@ macro_rules! implement_interfaces { fn create_context( &self, descriptor: &ContextDescriptor, - share_with: Option<&Self::Context>, - ) -> Result { + share_with: Option<&Context>, + ) -> Result { Device::create_context(self, descriptor, share_with) } #[inline] - fn destroy_context(&self, context: &mut Self::Context) -> Result<(), Error> { + fn destroy_context(&self, context: &mut Context) -> Result<(), Error> { Device::destroy_context(self, context) } #[inline] - fn context_descriptor(&self, context: &Self::Context) -> ContextDescriptor { + fn context_descriptor(&self, context: &Context) -> ContextDescriptor { Device::context_descriptor(self, context) } #[inline] - fn make_context_current(&self, context: &Self::Context) -> Result<(), Error> { + fn make_context_current(&self, context: &Context) -> Result<(), Error> { Device::make_context_current(self, context) } @@ -156,18 +154,14 @@ macro_rules! implement_interfaces { } #[inline] - fn get_proc_address( - &self, - context: &Self::Context, - symbol_name: &str, - ) -> *const c_void { + fn get_proc_address(&self, context: &Context, symbol_name: &str) -> *const c_void { Device::get_proc_address(self, context, symbol_name) } #[inline] fn bind_surface_to_context( &self, - context: &mut Self::Context, + context: &mut Context, surface: Self::Surface, ) -> Result<(), (Error, Self::Surface)> { Device::bind_surface_to_context(self, context, surface) @@ -176,20 +170,20 @@ macro_rules! implement_interfaces { #[inline] fn unbind_surface_from_context( &self, - context: &mut Self::Context, + context: &mut Context, ) -> Result, Error> { Device::unbind_surface_from_context(self, context) } #[inline] - fn context_id(&self, context: &Self::Context) -> ContextID { + fn context_id(&self, context: &Context) -> ContextID { Device::context_id(self, context) } #[inline] fn context_surface_info( &self, - context: &Self::Context, + context: &Context, ) -> Result, Error> { Device::context_surface_info(self, context) } @@ -199,7 +193,7 @@ macro_rules! implement_interfaces { #[inline] fn create_surface( &self, - context: &Self::Context, + context: &Context, surface_access: SurfaceAccess, surface_type: SurfaceType<'_>, ) -> Result { @@ -209,7 +203,7 @@ macro_rules! implement_interfaces { #[inline] fn create_surface_texture( &self, - context: &mut Self::Context, + context: &mut Context, surface: Self::Surface, ) -> Result { Device::create_surface_texture(self, context, surface) @@ -218,7 +212,7 @@ macro_rules! implement_interfaces { #[inline] fn destroy_surface( &self, - context: &mut Self::Context, + context: &mut Context, surface: &mut Self::Surface, ) -> Result<(), Error> { Device::destroy_surface(self, context, surface) @@ -227,7 +221,7 @@ macro_rules! implement_interfaces { #[inline] fn destroy_surface_texture( &self, - context: &mut Self::Context, + context: &mut Context, surface_texture: Self::SurfaceTexture, ) -> Result { Device::destroy_surface_texture(self, context, surface_texture) @@ -239,14 +233,14 @@ macro_rules! implement_interfaces { } #[inline] - fn present_bound_surface(&self, context: &mut Self::Context) -> Result<(), Error> { + fn present_bound_surface(&self, context: &mut Context) -> Result<(), Error> { Device::present_bound_surface(self, context) } #[inline] fn present_surface( &self, - context: &Self::Context, + context: &Context, surface: &mut Self::Surface, ) -> Result<(), Error> { Device::present_surface(self, context, surface) @@ -291,13 +285,24 @@ macro_rules! implement_interfaces { /// A macro that takes care of producing the boilerplate for conversion to and /// from an inner type within an enum. macro_rules! enum_conversion { - ($enum:ty, $variant:ident, $type:ty, $name:ident, $error:ident) => { + ($enum:ident, $variant:ident, $type:ty, $name:ident, $error:ident) => { impl From<$type> for $enum { fn from(connection: $type) -> Self { Self::$variant(connection) } } + impl<'a> TryFrom<&'a mut $enum> for &'a mut $type { + type Error = crate::Error; + fn try_from(value: &'a mut $enum) -> Result<&'a mut $type, Self::Error> { + #[allow(unreachable_patterns)] + match value { + $enum::$variant(inner) => Ok(inner), + _ => Err(crate::Error::$error), + } + } + } + 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/context.rs b/src/mesa_surfaceless/context.rs index 486b1b7a..94b67212 100644 --- a/src/mesa_surfaceless/context.rs +++ b/src/mesa_surfaceless/context.rs @@ -4,20 +4,5 @@ use crate::base::egl::context::EGLBackedContext; pub use crate::base::egl::context::NativeContext; use crate::Gl; -/// Represents an OpenGL rendering context. -/// -/// A context allows you to issue rendering commands to a surface. When initially created, a -/// context has no attached surface, so rendering commands will fail or be ignored. Typically, you -/// attach a surface to the context before rendering. -/// -/// Contexts take ownership of the surfaces attached to them. In order to mutate a surface in any -/// way other than rendering to it (e.g. presenting it to a window, which causes a buffer swap), it -/// must first be detached from its context. Each surface is associated with a single context upon -/// creation and may not be rendered to from any other context. However, you can wrap a surface in -/// a surface texture, which allows the surface to be read from another context. -/// -/// OpenGL objects may not be shared across contexts directly, but surface textures effectively -/// allow for sharing of texture data. Contexts are local to a single thread and device. -/// -/// A context must be explicitly destroyed with `destroy_context()`, or a panic will occur. -pub struct Context(pub(crate) EGLBackedContext, pub(crate) Gl); +/// An implementation of [`crate::Context`] for Surfaceless Mesa. +pub struct SurfacelessMesaContext(pub(crate) EGLBackedContext, pub(crate) Gl); diff --git a/src/mesa_surfaceless/device.rs b/src/mesa_surfaceless/device.rs index 10589d55..6e1831c8 100644 --- a/src/mesa_surfaceless/device.rs +++ b/src/mesa_surfaceless/device.rs @@ -7,9 +7,9 @@ use crate::base::egl::surface::EGLBackedSurface; use crate::context::ContextID; use crate::egl::types::EGLint; use crate::free_unix::adapter::FreeUnixAdapter; -use crate::mesa_surfaceless::context::{Context, NativeContext}; +use crate::mesa_surfaceless::context::{NativeContext, SurfacelessMesaContext}; use crate::mesa_surfaceless::surface::{Surface, SurfaceTexture}; -use crate::{egl, Adapter, EglContextDescriptor}; +use crate::{egl, Adapter, Context, EglContextDescriptor}; use crate::{gl, ContextDescriptor}; use crate::{ContextAttributes, Gl, SurfaceInfo}; use crate::{Error, GLApi, SurfaceAccess, SurfaceType}; @@ -116,6 +116,11 @@ impl Device { descriptor: &ContextDescriptor, share_with: Option<&Context>, ) -> Result { + let share_with = match share_with { + Some(share_with) => Some(share_with.surfaceless_mesa()?), + None => None, + }; + unsafe { let context = EGLBackedContext::new( self.native_connection.egl_display, @@ -124,10 +129,13 @@ impl Device { self.gl_api(), )?; context.make_current(self.native_connection.egl_display)?; - Ok(Context( - context, - Gl::from_loader_function(context::get_proc_address), - )) + Ok( + SurfacelessMesaContext( + context, + Gl::from_loader_function(context::get_proc_address), + ) + .into(), + ) } } @@ -141,10 +149,11 @@ impl Device { &self, native_context: NativeContext, ) -> Result { - Ok(Context( + Ok(SurfacelessMesaContext( EGLBackedContext::from_native_context(native_context), Gl::from_loader_function(context::get_proc_address), - )) + ) + .into()) } /// Destroys a context. @@ -155,6 +164,7 @@ impl Device { self.destroy_surface(context, &mut surface)?; } + let context: &mut SurfacelessMesaContext = context.try_into()?; unsafe { context.0.destroy(self.native_connection.egl_display); Ok(()) @@ -164,12 +174,18 @@ impl Device { /// Given a context, returns its underlying EGL context and attached surfaces. #[inline] pub fn native_context(&self, context: &Context) -> NativeContext { + let context = context + .surfaceless_mesa() + .expect("Passed incorrect context type"); context.0.native_context() } /// Returns the descriptor that this context was created with. #[inline] pub fn context_descriptor(&self, context: &Context) -> ContextDescriptor { + let context = context + .surfaceless_mesa() + .expect("Passed incorrect context type"); unsafe { EglContextDescriptor::from_egl_context( &context.1, @@ -185,6 +201,7 @@ impl Device { /// After calling this function, it is valid to use OpenGL rendering commands. #[inline] pub fn make_context_current(&self, context: &Context) -> Result<(), Error> { + let context = context.surfaceless_mesa()?; unsafe { context.0.make_current(self.native_connection.egl_display) } } @@ -247,6 +264,10 @@ impl Device { context: &mut Context, surface: Surface, ) -> Result<(), (Error, Surface)> { + let context: &mut SurfacelessMesaContext = match context.try_into() { + Ok(context) => context, + Err(error) => return Err((error, surface)), + }; unsafe { context .0 @@ -263,6 +284,7 @@ impl Device { &self, context: &mut Context, ) -> Result, Error> { + let context: &mut SurfacelessMesaContext = context.try_into()?; unsafe { context .0 @@ -277,6 +299,7 @@ impl Device { /// Widget surfaces are internally double-buffered, so changes to them don't /// show up in their associated widgets until this method is called. pub fn present_bound_surface(&self, context: &mut Context) -> Result<(), Error> { + let context: &mut SurfacelessMesaContext = context.try_into()?; context .0 .present_bound_surface(self.native_connection.egl_display) @@ -288,6 +311,7 @@ impl Device { context: &mut Context, size: Size2D, ) -> Result<(), Error> { + let context: &mut SurfacelessMesaContext = context.try_into()?; context.0.resize_bound_surface(size) } @@ -297,6 +321,9 @@ impl Device { /// a new one, the new context might have the same ID as the destroyed one. #[inline] pub fn context_id(&self, context: &Context) -> ContextID { + let context = context + .surfaceless_mesa() + .expect("Passed incorrect context type"); context.0.id } @@ -305,6 +332,7 @@ impl Device { /// This includes, most notably, the OpenGL framebuffer object needed to render to the surface. #[inline] pub fn context_surface_info(&self, context: &Context) -> Result, Error> { + let context = context.surfaceless_mesa()?; context.0.surface_info() } @@ -332,6 +360,7 @@ impl Device { let _guard = self.temporarily_make_context_current(context)?; let context_descriptor = self.context_descriptor(context); let context_attributes = self.context_descriptor_attributes(&context_descriptor); + let context = context.surfaceless_mesa()?; Ok(Surface(EGLBackedSurface::new_generic( &context.1, @@ -363,6 +392,11 @@ impl Device { Err(err) => return Err((err, surface)), }; + let context = match context.surfaceless_mesa() { + Ok(context) => context, + Err(error) => return Err((error, surface)), + }; + match surface.0.to_surface_texture(&context.1) { Ok(surface_texture) => Ok(SurfaceTexture(surface_texture)), Err((err, surface)) => Err((err, Surface(surface))), @@ -382,6 +416,7 @@ impl Device { surface: &mut Surface, ) -> Result<(), Error> { let egl_display = self.native_connection.egl_display; + let context: &mut SurfacelessMesaContext = context.try_into()?; let window = surface.0.destroy(&context.1, egl_display, context.0.id)?; debug_assert!(window.is_none()); Ok(()) @@ -399,8 +434,12 @@ impl Device { context: &mut Context, surface_texture: SurfaceTexture, ) -> Result { + let concrete_context = match context.surfaceless_mesa() { + Ok(concrete_context) => concrete_context, + Err(error) => return Err((error, surface_texture)), + }; match self.temporarily_make_context_current(context) { - Ok(_guard) => Ok(Surface(surface_texture.0.destroy(&context.1))), + Ok(_guard) => Ok(Surface(surface_texture.0.destroy(&concrete_context.1))), Err(err) => Err((err, surface_texture)), } } @@ -413,6 +452,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: &Context, surface: &mut Surface) -> Result<(), Error> { + let context = context.surfaceless_mesa()?; surface .0 .present(self.native_connection.egl_display, context.0.egl_context) diff --git a/src/multi/context.rs b/src/multi/context.rs index 27caf376..898ecc84 100644 --- a/src/multi/context.rs +++ b/src/multi/context.rs @@ -5,37 +5,10 @@ use euclid::default::Size2D; use super::device::Device; use super::surface::Surface; use crate::device::Device as DeviceInterface; -use crate::{ContextAttributes, ContextDescriptor, ContextID, Error, SurfaceInfo}; +use crate::{Context, ContextAttributes, ContextDescriptor, ContextID, Error, SurfaceInfo}; use std::os::raw::c_void; -/// Represents an OpenGL rendering context. -/// -/// A context allows you to issue rendering commands to a surface. When initially created, a -/// context has no attached surface, so rendering commands will fail or be ignored. Typically, you -/// attach a surface to the context before rendering. -/// -/// Contexts take ownership of the surfaces attached to them. In order to mutate a surface in any -/// way other than rendering to it (e.g. presenting it to a window, which causes a buffer swap), it -/// must first be detached from its context. Each surface is associated with a single context upon -/// creation and may not be rendered to from any other context. However, you can wrap a surface in -/// a surface texture, which allows the surface to be read from another context. -/// -/// OpenGL objects may not be shared across contexts directly, but surface textures effectively -/// allow for sharing of texture data. Contexts are local to a single thread and device. -/// -/// A context must be explicitly destroyed with `destroy_context()`, or a panic will occur. -pub enum Context -where - Def: DeviceInterface, - Alt: DeviceInterface, -{ - /// The default rendering context type. - Default(Def::Context), - /// The alternate rendering context type. - Alternate(Alt::Context), -} - impl Device where Def: DeviceInterface, @@ -61,76 +34,39 @@ where pub fn create_context( &self, descriptor: &ContextDescriptor, - share_with: Option<&Context>, - ) -> Result, Error> { + share_with: Option<&Context>, + ) -> Result { match self { - Device::Default(device) => { - let shared = match share_with { - Some(Context::Default(other)) => Some(other), - Some(_) => { - return Err(Error::IncompatibleSharedContext); - } - None => None, - }; - device - .create_context(descriptor, shared) - .map(Context::Default) - } - Device::Alternate(device) => { - let shared = match share_with { - Some(Context::Alternate(other)) => Some(other), - Some(_) => { - return Err(Error::IncompatibleSharedContext); - } - None => None, - }; - device - .create_context(descriptor, shared) - .map(Context::Alternate) - } + Device::Default(device) => device.create_context(descriptor, share_with), + Device::Alternate(device) => device.create_context(descriptor, share_with), } } /// Destroys a context. /// /// The context must have been created on this device. - pub fn destroy_context(&self, context: &mut Context) -> Result<(), Error> { - match (self, &mut *context) { - (Device::Default(device), &mut Context::Default(ref mut context)) => { - device.destroy_context(context) - } - (Device::Alternate(device), &mut Context::Alternate(ref mut context)) => { - device.destroy_context(context) - } - _ => Err(Error::IncompatibleContext), + pub fn destroy_context(&self, context: &mut Context) -> Result<(), Error> { + match self { + Device::Default(device) => device.destroy_context(context), + Device::Alternate(device) => device.destroy_context(context), } } /// Returns the descriptor that this context was created with. - pub fn context_descriptor(&self, context: &Context) -> ContextDescriptor { - match (self, context) { - (Device::Default(device), Context::Default(context)) => { - device.context_descriptor(context) - } - (Device::Alternate(device), Context::Alternate(context)) => { - device.context_descriptor(context) - } - _ => panic!("Incompatible context!"), + pub fn context_descriptor(&self, context: &Context) -> ContextDescriptor { + match self { + Device::Default(device) => device.context_descriptor(context), + Device::Alternate(device) => device.context_descriptor(context), } } /// Makes the context the current OpenGL context for this thread. /// /// After calling this function, it is valid to use OpenGL rendering commands. - pub fn make_context_current(&self, context: &Context) -> Result<(), Error> { - match (self, context) { - (Device::Default(device), Context::Default(context)) => { - device.make_context_current(context) - } - (Device::Alternate(device), Context::Alternate(context)) => { - device.make_context_current(context) - } - _ => Err(Error::IncompatibleContext), + pub fn make_context_current(&self, context: &Context) -> Result<(), Error> { + match self { + Device::Default(device) => device.make_context_current(context), + Device::Alternate(device) => device.make_context_current(context), } } @@ -157,25 +93,22 @@ where /// If an error is returned, the surface is returned alongside it. pub fn bind_surface_to_context( &self, - context: &mut Context, + context: &mut Context, surface: Surface, ) -> Result<(), (Error, Surface)> { - match (self, &mut *context) { - (Device::Default(device), &mut Context::Default(ref mut context)) => match 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), &mut Context::Alternate(ref mut context)) => { - match surface { - Surface::Alternate(surface) => device - .bind_surface_to_context(context, surface) - .map_err(|(err, surface)| (err, Surface::Alternate(surface))), - _ => Err((Error::IncompatibleSurface, surface)), - } - } - _ => Err((Error::IncompatibleContext, 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)), + }, } } @@ -185,16 +118,15 @@ where /// surface is safe to read from immediately when this function returns. pub fn unbind_surface_from_context( &self, - context: &mut Context, + context: &mut Context, ) -> Result>, Error> { - match (self, &mut *context) { - (Device::Default(device), &mut Context::Default(ref mut context)) => device + match self { + Device::Default(device) => device .unbind_surface_from_context(context) .map(|surface| surface.map(Surface::Default)), - (Device::Alternate(device), &mut Context::Alternate(ref mut context)) => device + Device::Alternate(device) => device .unbind_surface_from_context(context) .map(|surface| surface.map(Surface::Alternate)), - _ => Err(Error::IncompatibleContext), } } @@ -203,32 +135,22 @@ where /// /// Widget surfaces are internally double-buffered, so changes to them don't /// show up in their associated widgets until this method is called. - pub fn present_bound_surface(&self, context: &mut Context) -> Result<(), Error> { - match (self, context) { - (Device::Default(device), Context::Default(context)) => { - device.present_bound_surface(context) - } - (Device::Alternate(device), Context::Alternate(context)) => { - device.present_bound_surface(context) - } - _ => Err(Error::IncompatibleContext), + pub fn present_bound_surface(&self, context: &mut Context) -> Result<(), Error> { + match self { + Device::Default(device) => device.present_bound_surface(context), + Device::Alternate(device) => device.present_bound_surface(context), } } /// Resizes the currently bound surface. pub fn resize_bound_surface( &self, - context: &mut Context, + context: &mut Context, size: Size2D, ) -> Result<(), Error> { - match (self, context) { - (Device::Default(device), Context::Default(context)) => { - device.resize_bound_surface(context, size) - } - (Device::Alternate(device), Context::Alternate(context)) => { - device.resize_bound_surface(context, size) - } - _ => Err(Error::IncompatibleContext), + match self { + Device::Default(device) => device.resize_bound_surface(context, size), + Device::Alternate(device) => device.resize_bound_surface(context, size), } } @@ -250,19 +172,10 @@ where /// /// This method is typically used with a function like `gl::load_with()` from the `gl` crate to /// load OpenGL function pointers. - pub fn get_proc_address( - &self, - context: &Context, - symbol_name: &str, - ) -> *const c_void { - match (self, context) { - (Device::Default(device), Context::Default(context)) => { - device.get_proc_address(context, symbol_name) - } - (Device::Alternate(device), Context::Alternate(context)) => { - device.get_proc_address(context, symbol_name) - } - _ => panic!("Incompatible context!"), + pub fn get_proc_address(&self, context: &Context, symbol_name: &str) -> *const c_void { + match self { + Device::Default(device) => device.get_proc_address(context, symbol_name), + Device::Alternate(device) => device.get_proc_address(context, symbol_name), } } @@ -270,29 +183,20 @@ where /// /// This ID is unique to all currently-allocated contexts. If you destroy a context and create /// a new one, the new context might have the same ID as the destroyed one. - pub fn context_id(&self, context: &Context) -> ContextID { - match (self, context) { - (Device::Default(device), Context::Default(context)) => device.context_id(context), - (Device::Alternate(device), Context::Alternate(context)) => device.context_id(context), - _ => panic!("Incompatible context!"), + pub fn context_id(&self, context: &Context) -> ContextID { + match self { + Device::Default(device) => device.context_id(context), + Device::Alternate(device) => device.context_id(context), } } /// Returns various information about the surface attached to a context. /// /// This includes, most notably, the OpenGL framebuffer object needed to render to the surface. - pub fn context_surface_info( - &self, - context: &Context, - ) -> Result, Error> { - match (self, context) { - (Device::Default(device), Context::Default(context)) => { - device.context_surface_info(context) - } - (Device::Alternate(device), Context::Alternate(context)) => { - device.context_surface_info(context) - } - _ => Err(Error::IncompatibleContext), + pub fn context_surface_info(&self, context: &Context) -> Result, Error> { + match self { + Device::Default(device) => device.context_surface_info(context), + Device::Alternate(device) => device.context_surface_info(context), } } } diff --git a/src/multi/device.rs b/src/multi/device.rs index bae4438a..88899dbc 100644 --- a/src/multi/device.rs +++ b/src/multi/device.rs @@ -1,13 +1,13 @@ //! A device abstraction that allows the choice of backends dynamically. use super::connection::Connection; -use super::context::Context; use super::surface::{Surface, SurfaceTexture}; use crate::connection::Connection as ConnectionInterface; use crate::context::ContextAttributes; use crate::device::Device as DeviceInterface; use crate::{ - Adapter, ContextDescriptor, ContextID, Error, GLApi, SurfaceAccess, SurfaceInfo, SurfaceType, + Adapter, Context, ContextDescriptor, ContextID, Error, GLApi, SurfaceAccess, SurfaceInfo, + SurfaceType, }; use euclid::default::Size2D; use glow::Texture; @@ -68,7 +68,6 @@ where Alt::Connection: ConnectionInterface, { type Connection = Connection; - type Context = Context; type Surface = Surface; type SurfaceTexture = SurfaceTexture; @@ -103,23 +102,23 @@ where fn create_context( &self, descriptor: &ContextDescriptor, - share_with: Option<&Context>, - ) -> Result, Error> { + share_with: Option<&Context>, + ) -> Result { Device::create_context(self, descriptor, share_with) } #[inline] - fn destroy_context(&self, context: &mut Context) -> Result<(), Error> { + fn destroy_context(&self, context: &mut Context) -> Result<(), Error> { Device::destroy_context(self, context) } #[inline] - fn context_descriptor(&self, context: &Context) -> ContextDescriptor { + fn context_descriptor(&self, context: &Context) -> ContextDescriptor { Device::context_descriptor(self, context) } #[inline] - fn make_context_current(&self, context: &Context) -> Result<(), Error> { + fn make_context_current(&self, context: &Context) -> Result<(), Error> { Device::make_context_current(self, context) } @@ -137,14 +136,14 @@ where } #[inline] - fn get_proc_address(&self, context: &Context, symbol_name: &str) -> *const c_void { + fn get_proc_address(&self, context: &Context, symbol_name: &str) -> *const c_void { Device::get_proc_address(self, context, symbol_name) } #[inline] fn bind_surface_to_context( &self, - context: &mut Context, + context: &mut Context, surface: Surface, ) -> Result<(), (Error, Surface)> { Device::bind_surface_to_context(self, context, surface) @@ -153,21 +152,18 @@ where #[inline] fn unbind_surface_from_context( &self, - context: &mut Context, + context: &mut Context, ) -> Result>, Error> { Device::unbind_surface_from_context(self, context) } #[inline] - fn context_id(&self, context: &Context) -> ContextID { + fn context_id(&self, context: &Context) -> ContextID { Device::context_id(self, context) } #[inline] - fn context_surface_info( - &self, - context: &Context, - ) -> Result, Error> { + fn context_surface_info(&self, context: &Context) -> Result, Error> { Device::context_surface_info(self, context) } @@ -176,7 +172,7 @@ where #[inline] fn create_surface( &self, - context: &Context, + context: &Context, surface_access: SurfaceAccess, surface_type: SurfaceType<'_>, ) -> Result, Error> { @@ -186,7 +182,7 @@ where #[inline] fn create_surface_texture( &self, - context: &mut Context, + context: &mut Context, surface: Surface, ) -> Result, (Error, Surface)> { Device::create_surface_texture(self, context, surface) @@ -195,7 +191,7 @@ where #[inline] fn destroy_surface( &self, - context: &mut Context, + context: &mut Context, surface: &mut Surface, ) -> Result<(), Error> { Device::destroy_surface(self, context, surface) @@ -204,7 +200,7 @@ where #[inline] fn destroy_surface_texture( &self, - context: &mut Context, + context: &mut Context, surface_texture: SurfaceTexture, ) -> Result, (Error, SurfaceTexture)> { Device::destroy_surface_texture(self, context, surface_texture) @@ -216,23 +212,19 @@ where } #[inline] - fn present_bound_surface(&self, context: &mut Context) -> Result<(), Error> { + fn present_bound_surface(&self, context: &mut Context) -> Result<(), Error> { Device::present_bound_surface(self, context) } #[inline] - fn resize_bound_surface( - &self, - context: &mut Self::Context, - size: Size2D, - ) -> Result<(), Error> { + fn resize_bound_surface(&self, context: &mut Context, size: Size2D) -> Result<(), Error> { Device::resize_bound_surface(self, context, size) } #[inline] fn present_surface( &self, - context: &Context, + context: &Context, surface: &mut Surface, ) -> Result<(), Error> { Device::present_surface(self, context, surface) @@ -241,7 +233,7 @@ where #[inline] fn resize_surface( &self, - context: &Context, + context: &Context, surface: &mut Surface, size: Size2D, ) -> Result<(), Error> { diff --git a/src/multi/surface.rs b/src/multi/surface.rs index 17962a80..fa20f0c0 100644 --- a/src/multi/surface.rs +++ b/src/multi/surface.rs @@ -1,9 +1,8 @@ //! A surface abstraction that allows the choice of backends dynamically. -use super::context::Context; use super::device::Device; use crate::device::Device as DeviceInterface; -use crate::{Error, SurfaceAccess, SurfaceInfo, SurfaceType}; +use crate::{Context, Error, SurfaceAccess, SurfaceInfo, SurfaceType}; use euclid::default::Size2D; use glow::Texture; @@ -87,18 +86,17 @@ where /// up in a `SurfaceTexture` for reading by other contexts. pub fn create_surface( &self, - context: &Context, + context: &Context, surface_access: SurfaceAccess, surface_type: SurfaceType<'_>, ) -> Result, Error> { - match (self, context) { - (Device::Default(device), Context::Default(context)) => device + match self { + Device::Default(device) => device .create_surface(context, surface_access, surface_type) .map(Surface::Default), - (Device::Alternate(device), Context::Alternate(context)) => device + Device::Alternate(device) => device .create_surface(context, surface_access, surface_type) .map(Surface::Alternate), - _ => Err(Error::IncompatibleContext), } } @@ -114,11 +112,11 @@ where /// Calling this method on a widget surface returns a `WidgetAttached` error. pub fn create_surface_texture( &self, - context: &mut Context, + context: &mut Context, surface: Surface, ) -> Result, (Error, Surface)> { - match (self, &mut *context) { - (Device::Default(device), &mut Context::Default(ref mut context)) => match surface { + 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)), @@ -127,18 +125,15 @@ where } _ => Err((Error::IncompatibleSurface, surface)), }, - (Device::Alternate(device), &mut Context::Alternate(ref mut context)) => { - 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))), - } + 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)), } - } - _ => Err((Error::IncompatibleContext, surface)), + _ => Err((Error::IncompatibleSurface, surface)), + }, } } @@ -151,20 +146,18 @@ where /// the `drop` method. pub fn destroy_surface( &self, - context: &mut Context, + context: &mut Context, surface: &mut Surface, ) -> Result<(), Error> { - match (self, &mut *context) { - (Device::Default(device), &mut Context::Default(ref mut context)) => match *surface { + match self { + Device::Default(device) => match *surface { Surface::Default(ref mut surface) => device.destroy_surface(context, surface), _ => Err(Error::IncompatibleSurface), }, - (Device::Alternate(device), &mut Context::Alternate(ref mut context)) => match *surface - { + Device::Alternate(device) => match *surface { Surface::Alternate(ref mut surface) => device.destroy_surface(context, surface), _ => Err(Error::IncompatibleSurface), }, - _ => Err(Error::IncompatibleContext), } } @@ -177,37 +170,32 @@ where /// occur. pub fn destroy_surface_texture( &self, - context: &mut Context, + context: &mut Context, surface_texture: SurfaceTexture, ) -> Result, (Error, SurfaceTexture)> { - match (self, &mut *context) { - (Device::Default(device), &mut Context::Default(ref mut context)) => { - 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))) - } + 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), &mut Context::Alternate(ref mut context)) => { - 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::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)), } - } - _ => Err((Error::IncompatibleContext, surface_texture)), + _ => Err((Error::IncompatibleSurfaceTexture, surface_texture)), + }, } } @@ -220,41 +208,39 @@ where /// `IncompatibleSurface` error is returned. pub fn present_surface( &self, - context: &Context, + context: &Context, surface: &mut Surface, ) -> Result<(), Error> { - match (self, context) { - (Device::Default(device), Context::Default(context)) => match *surface { + match self { + Device::Default(device) => match *surface { Surface::Default(ref mut surface) => device.present_surface(context, surface), _ => Err(Error::IncompatibleSurface), }, - (Device::Alternate(device), Context::Alternate(context)) => match *surface { + Device::Alternate(device) => match *surface { Surface::Alternate(ref mut surface) => device.present_surface(context, surface), _ => Err(Error::IncompatibleSurface), }, - _ => Err(Error::IncompatibleContext), } } /// Resizes a widget surface. pub fn resize_surface( &self, - context: &Context, + context: &Context, surface: &mut Surface, size: Size2D, ) -> Result<(), Error> { - match (self, context) { - (Device::Default(device), Context::Default(context)) => match *surface { + match self { + Device::Default(device) => match *surface { Surface::Default(ref mut surface) => device.resize_surface(context, surface, size), _ => Err(Error::IncompatibleSurface), }, - (Device::Alternate(device), Context::Alternate(context)) => match *surface { + Device::Alternate(device) => match *surface { Surface::Alternate(ref mut surface) => { device.resize_surface(context, surface, size) } _ => Err(Error::IncompatibleSurface), }, - _ => Err(Error::IncompatibleContext), } } diff --git a/src/tests.rs b/src/tests.rs index d3cab55e..da7e41c4 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -7,13 +7,13 @@ #![allow(missing_docs)] use super::connection::Connection; -use super::context::{Context, NativeContext}; +use super::context::NativeContext; use super::device::Device; use super::surface::Surface; use crate::{ - gl, Adapter, AdapterPreferences, ContextAttributeFlags, ContextAttributes, ContextDescriptor, - Error, GLApi, GLVersion, Gl, PowerPreference, RenderingPreference, SurfaceAccess, SurfaceType, - WindowingApiError, + gl, Adapter, AdapterPreferences, Context, ContextAttributeFlags, ContextAttributes, + ContextDescriptor, Error, GLApi, GLVersion, Gl, PowerPreference, RenderingPreference, + SurfaceAccess, SurfaceType, WindowingApiError, }; use euclid::default::Size2D; diff --git a/src/unix.rs b/src/unix.rs index 10b79015..c4dfc53d 100644 --- a/src/unix.rs +++ b/src/unix.rs @@ -15,35 +15,6 @@ pub mod connection { pub type Connection = MultiConnection; } -/// OpenGL rendering contexts. -pub mod context { - use crate::mesa_surfaceless::device::Device as SWDevice; - use crate::multi::context::Context as MultiContext; - use crate::multi::device::Device as MultiDevice; - use crate::wayland::device::Device as WaylandDevice; - use crate::x11::device::Device as X11Device; - type HWDevice = MultiDevice; - - /// Represents an OpenGL rendering context. - /// - /// A context allows you to issue rendering commands to a surface. When initially created, a - /// context has no attached surface, so rendering commands will fail or be ignored. Typically, - /// you attach a surface to the context before rendering. - /// - /// Contexts take ownership of the surfaces attached to them. In order to mutate a surface in - /// any way other than rendering to it (e.g. presenting it to a window, which causes a buffer - /// swap), it must first be detached from its context. Each surface is associated with a single - /// context upon creation and may not be rendered to from any other context. However, you can - /// wrap a surface in a surface texture, which allows the surface to be read from another - /// context. - /// - /// OpenGL objects may not be shared across contexts directly, but surface textures effectively - /// allow for sharing of texture data. Contexts are local to a single thread and device. - /// - /// A context must be explicitly destroyed with `destroy_context()`, or a panic will occur. - pub type Context = MultiContext; -} - /// Thread-local handles to devices. pub mod device { use crate::mesa_surfaceless::device::Device as SWDevice; diff --git a/src/wayland/context.rs b/src/wayland/context.rs index 05fa92e8..a6ada9a4 100644 --- a/src/wayland/context.rs +++ b/src/wayland/context.rs @@ -4,20 +4,5 @@ use crate::base::egl::context::EGLBackedContext; pub use crate::base::egl::context::NativeContext; use crate::Gl; -/// Represents an OpenGL rendering context. -/// -/// A context allows you to issue rendering commands to a surface. When initially created, a -/// context has no attached surface, so rendering commands will fail or be ignored. Typically, you -/// attach a surface to the context before rendering. -/// -/// Contexts take ownership of the surfaces attached to them. In order to mutate a surface in any -/// way other than rendering to it (e.g. presenting it to a window, which causes a buffer swap), it -/// must first be detached from its context. Each surface is associated with a single context upon -/// creation and may not be rendered to from any other context. However, you can wrap a surface in -/// a surface texture, which allows the surface to be read from another context. -/// -/// OpenGL objects may not be shared across contexts directly, but surface textures effectively -/// allow for sharing of texture data. Contexts are local to a single thread and device. -/// -/// A context must be explicitly destroyed with `destroy_context()`, or a panic will occur. -pub struct Context(pub(crate) EGLBackedContext, pub(crate) Gl); +/// An implementation of [`crate::Context`] for Wayland. +pub struct WaylandContext(pub(crate) EGLBackedContext, pub(crate) Gl); diff --git a/src/wayland/device.rs b/src/wayland/device.rs index 4f6c7b03..cfab60b9 100644 --- a/src/wayland/device.rs +++ b/src/wayland/device.rs @@ -1,7 +1,7 @@ //! A wrapper around Wayland `EGLDisplay`s. use super::connection::{Connection, NativeConnectionWrapper}; -use super::context::{Context, NativeContext}; +use super::context::{NativeContext, WaylandContext}; use super::surface::{Surface, SurfaceDataGuard, SurfaceTexture}; use crate::base::egl::context::{self, CurrentContextGuard, EGLBackedContext}; use crate::base::egl::surface::EGLBackedSurface; @@ -10,8 +10,8 @@ use crate::egl::types::EGLint; use crate::free_unix::adapter::FreeUnixAdapter; use crate::surface::Framebuffer; use crate::{ - egl, gl, Adapter, ContextAttributes, ContextDescriptor, EglContextDescriptor, Error, GLApi, Gl, - SurfaceAccess, SurfaceInfo, SurfaceType, + egl, gl, Adapter, Context, ContextAttributes, ContextDescriptor, EglContextDescriptor, Error, + GLApi, Gl, SurfaceAccess, SurfaceInfo, SurfaceType, }; use euclid::default::Size2D; use glow::Texture; @@ -121,6 +121,11 @@ impl Device { descriptor: &ContextDescriptor, share_with: Option<&Context>, ) -> Result { + let share_with = match share_with { + Some(share_with) => Some(share_with.wayland()?), + None => None, + }; + unsafe { let context = EGLBackedContext::new( self.native_connection.egl_display, @@ -129,10 +134,7 @@ impl Device { self.gl_api(), )?; context.make_current(self.native_connection.egl_display)?; - Ok(Context( - context, - Gl::from_loader_function(context::get_proc_address), - )) + Ok(WaylandContext(context, Gl::from_loader_function(context::get_proc_address)).into()) } } @@ -146,10 +148,11 @@ impl Device { &self, native_context: NativeContext, ) -> Result { - Ok(Context( + Ok(WaylandContext( EGLBackedContext::from_native_context(native_context), Gl::from_loader_function(context::get_proc_address), - )) + ) + .into()) } /// Destroys a context. @@ -160,6 +163,7 @@ impl Device { self.destroy_surface(context, &mut surface)?; } + let context: &mut WaylandContext = context.try_into()?; unsafe { context.0.destroy(self.native_connection.egl_display); Ok(()) @@ -169,12 +173,14 @@ impl Device { /// Given a context, returns its underlying EGL context and attached surfaces. #[inline] pub fn native_context(&self, context: &Context) -> NativeContext { + let context = context.wayland().expect("Passed incorrect context type"); context.0.native_context() } /// Returns the descriptor that this context was created with. #[inline] pub fn context_descriptor(&self, context: &Context) -> ContextDescriptor { + let context = context.wayland().expect("Passed incorrect context type"); unsafe { EglContextDescriptor::from_egl_context( &context.1, @@ -190,6 +196,7 @@ impl Device { /// After calling this function, it is valid to use OpenGL rendering commands. #[inline] pub fn make_context_current(&self, context: &Context) -> Result<(), Error> { + let context = context.wayland()?; unsafe { context.0.make_current(self.native_connection.egl_display) } } @@ -252,6 +259,11 @@ impl Device { context: &mut Context, surface: Surface, ) -> Result<(), (Error, Surface)> { + let context: &mut WaylandContext = match context.try_into() { + Ok(context) => context, + Err(error) => return Err((error, surface)), + }; + unsafe { context .0 @@ -268,6 +280,7 @@ impl Device { &self, context: &mut Context, ) -> Result, Error> { + let context: &mut WaylandContext = context.try_into()?; unsafe { context .0 @@ -282,6 +295,7 @@ impl Device { /// Widget surfaces are internally double-buffered, so changes to them don't /// show up in their associated widgets until this method is called. pub fn present_bound_surface(&self, context: &mut Context) -> Result<(), Error> { + let context: &mut WaylandContext = context.try_into()?; context .0 .present_bound_surface(self.native_connection.egl_display) @@ -293,6 +307,7 @@ impl Device { context: &mut Context, size: Size2D, ) -> Result<(), Error> { + let context: &mut WaylandContext = context.try_into()?; match &mut context.0.framebuffer { Framebuffer::Surface(surface) => surface.resize_for_wayland(size), _ => Ok(()), @@ -305,6 +320,7 @@ impl Device { /// a new one, the new context might have the same ID as the destroyed one. #[inline] pub fn context_id(&self, context: &Context) -> ContextID { + let context = context.wayland().expect("Passed incorrect context type"); context.0.id } @@ -313,6 +329,7 @@ impl Device { /// This includes, most notably, the OpenGL framebuffer object needed to render to the surface. #[inline] pub fn context_surface_info(&self, context: &Context) -> Result, Error> { + let context = context.wayland()?; context.0.surface_info() } @@ -354,6 +371,7 @@ impl Device { let _guard = self.temporarily_make_context_current(context)?; let context_descriptor = self.context_descriptor(context); let context_attributes = self.context_descriptor_attributes(&context_descriptor); + let context = context.wayland()?; Ok(Surface(EGLBackedSurface::new_generic( &context.1, @@ -376,13 +394,12 @@ impl Device { assert!(!egl_window.is_null()); let context_descriptor = self.context_descriptor(context); - let context_descriptor = context_descriptor - .egl() - .expect("Passed incompatible context descriptor"); + let context_descriptor = context_descriptor.egl()?; let egl_config = context::egl_config_from_id( self.native_connection.egl_display, context_descriptor.egl_config_id, ); + let context = context.wayland()?; Ok(Surface(EGLBackedSurface::new_window( self.native_connection.egl_display, @@ -413,6 +430,11 @@ impl Device { Err(err) => return Err((err, surface)), }; + let context = match context.wayland() { + Ok(context) => context, + Err(error) => return Err((error, surface)), + }; + match surface.0.to_surface_texture(&context.1) { Ok(surface_texture) => Ok(SurfaceTexture(surface_texture)), Err((err, surface)) => Err((err, Surface(surface))), @@ -432,6 +454,7 @@ impl Device { surface: &mut Surface, ) -> Result<(), Error> { let egl_display = self.native_connection.egl_display; + let context = context.wayland()?; if let Some(wayland_egl_window) = surface.0.destroy(&context.1, egl_display, context.0.id)? { @@ -455,8 +478,13 @@ impl Device { context: &mut Context, surface_texture: SurfaceTexture, ) -> Result { + let concrete_context = match context.wayland() { + Ok(concrete_context) => concrete_context, + Err(error) => return Err((error, surface_texture)), + }; + match self.temporarily_make_context_current(context) { - Ok(_guard) => Ok(Surface(surface_texture.0.destroy(&context.1))), + Ok(_guard) => Ok(Surface(surface_texture.0.destroy(&concrete_context.1))), Err(err) => Err((err, surface_texture)), } } @@ -469,6 +497,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: &Context, surface: &mut Surface) -> Result<(), Error> { + let context = context.wayland()?; surface .0 .present(self.native_connection.egl_display, context.0.egl_context) diff --git a/src/wgl/context.rs b/src/wgl/context.rs index e85bf85f..aa121277 100644 --- a/src/wgl/context.rs +++ b/src/wgl/context.rs @@ -93,23 +93,8 @@ pub struct WglContextDescriptor { pub(crate) compatibility_profile: bool, } -/// Represents an OpenGL rendering context. -/// -/// A context allows you to issue rendering commands to a surface. When initially created, a -/// context has no attached surface, so rendering commands will fail or be ignored. Typically, you -/// attach a surface to the context before rendering. -/// -/// Contexts take ownership of the surfaces attached to them. In order to mutate a surface in any -/// way other than rendering to it (e.g. presenting it to a window, which causes a buffer swap), it -/// must first be detached from its context. Each surface is associated with a single context upon -/// creation and may not be rendered to from any other context. However, you can wrap a surface in -/// a surface texture, which allows the surface to be read from another context. -/// -/// OpenGL objects may not be shared across contexts directly, but surface textures effectively -/// allow for sharing of texture data. Contexts are local to a single thread and device. -/// -/// A context must be explicitly destroyed with `destroy_context()`, or a panic will occur. -pub struct Context { +/// An implementation of [`crate::Context`] for WGL. +pub struct WglContext { pub(crate) glrc: HGLRC, pub(crate) id: ContextID, pub(crate) gl: Gl, @@ -325,7 +310,7 @@ extern "system" fn extension_loader_window_proc( #[must_use] pub(crate) struct FramebufferGuard<'a> { - context: &'a Context, + context: &'a WglContext, old_read_framebuffer: Option, old_draw_framebuffer: Option, } @@ -345,7 +330,7 @@ impl<'a> Drop for FramebufferGuard<'a> { } impl<'a> FramebufferGuard<'a> { - pub(crate) fn new(context: &'a Context) -> FramebufferGuard<'a> { + pub(crate) fn new(context: &'a WglContext) -> FramebufferGuard<'a> { unsafe { let current_draw_framebuffer = context .gl diff --git a/src/wgl/device.rs b/src/wgl/device.rs index b8d327b2..4f8e6169 100644 --- a/src/wgl/device.rs +++ b/src/wgl/device.rs @@ -9,12 +9,12 @@ use crate::surface::Framebuffer; use crate::wgl::adapter::WglAdapter; use crate::wgl::connection::Connection; use crate::wgl::context::{ - Context, ContextStatus, CurrentContextGuard, FramebufferGuard, NativeContext, + ContextStatus, CurrentContextGuard, FramebufferGuard, NativeContext, WglContext, WglContextDescriptor, OPENGL_LIBRARY, WGL_EXTENSION_FUNCTIONS, }; use crate::wgl::surface::{Surface, SurfaceDataGuard, SurfaceTexture, Win32Objects}; use crate::{ - gl, gl_utils, Adapter, AdapterPreferences, ContextAttributeFlags, ContextAttributes, + gl, gl_utils, Adapter, AdapterPreferences, Context, ContextAttributeFlags, ContextAttributes, ContextDescriptor, Error, GLApi, GLVersion, Gl, PowerPreference, SurfaceAccess, SurfaceInfo, SurfaceType, }; @@ -329,6 +329,11 @@ impl Device { descriptor: &ContextDescriptor, share_with: Option<&Context>, ) -> Result { + let share_with = match share_with { + Some(share_with) => Some(share_with.wgl()?), + None => None, + }; + let wglCreateContextAttribsARB = match WGL_EXTENSION_FUNCTIONS.CreateContextAttribsARB { None => return Err(Error::RequiredExtensionUnavailable), Some(wglCreateContextAttribsARB) => wglCreateContextAttribsARB, @@ -382,7 +387,7 @@ impl Device { } // Create the initial context. - let context = Context { + let context = WglContext { glrc, id: *next_context_id, gl, @@ -391,7 +396,7 @@ impl Device { status: ContextStatus::Owned, }; next_context_id.0 += 1; - Ok(context) + Ok(context.into()) } } @@ -417,7 +422,7 @@ impl Device { Gl::from_loader_function(get_proc_address) }; - let context = Context { + let context = WglContext { glrc: native_context.0, id: *next_context_id, gl, @@ -426,21 +431,22 @@ impl Device { status: ContextStatus::Referenced, }; next_context_id.0 += 1; - Ok(context) + Ok(context.into()) } /// Destroys a context. /// /// The context must have been created on this device. pub fn destroy_context(&self, context: &mut Context) -> Result<(), Error> { - if context.status == ContextStatus::Destroyed { - return Ok(()); - } - if let Ok(Some(mut surface)) = self.unbind_surface_from_context(context) { self.destroy_surface(context, &mut surface)?; } + let context: &mut WglContext = context.try_into()?; + if context.status == ContextStatus::Destroyed { + return Ok(()); + } + unsafe { if wglGetCurrentContext() == context.glrc { wglMakeCurrent(ptr::null_mut(), ptr::null_mut()); @@ -458,14 +464,15 @@ impl Device { /// Returns the descriptor that this context was created with. pub fn context_descriptor(&self, context: &Context) -> ContextDescriptor { + let concrete_context = context.wgl().expect("Passed incorrect context type"); unsafe { - let dc_guard = self.get_context_dc(context); + let dc_guard = self.get_context_dc(concrete_context); let pixel_format = wingdi::GetPixelFormat(dc_guard.dc); let _guard = self.temporarily_make_context_current(context); - - let gl_version = GLVersion::current(&context.gl); - let compatibility_profile = current_context_uses_compatibility_profile(&context.gl); + let gl_version = GLVersion::current(&concrete_context.gl); + let compatibility_profile = + current_context_uses_compatibility_profile(&concrete_context.gl); WglContextDescriptor { pixel_format, @@ -538,7 +545,7 @@ impl Device { pub(crate) fn temporarily_bind_framebuffer<'a>( &self, - context: &'a Context, + context: &'a WglContext, framebuffer: Option, ) -> FramebufferGuard<'a> { unsafe { @@ -557,10 +564,7 @@ impl Device { Ok(guard) } - /// Makes the context the current OpenGL context for this thread. - /// - /// After calling this function, it is valid to use OpenGL rendering commands. - pub fn make_context_current(&self, context: &Context) -> Result<(), Error> { + fn make_context_current_inner(&self, context: &WglContext) -> Result<(), Error> { unsafe { let dc_guard = self.get_context_dc(context); let ok = wglMakeCurrent(dc_guard.dc, context.glrc); @@ -572,6 +576,14 @@ impl Device { } } + /// Makes the context the current OpenGL context for this thread. + /// + /// After calling this function, it is valid to use OpenGL rendering commands. + pub fn make_context_current(&self, context: &Context) -> Result<(), Error> { + let context = context.wgl()?; + self.make_context_current_inner(context) + } + /// Removes the current OpenGL context from this thread. /// /// After calling this function, OpenGL rendering commands will fail until a new context is @@ -601,7 +613,7 @@ impl Device { } #[inline] - fn context_is_current(&self, context: &Context) -> bool { + fn context_is_current(&self, context: &WglContext) -> bool { unsafe { wglGetCurrentContext() == context.glrc } } @@ -620,6 +632,11 @@ impl Device { context: &mut Context, surface: Surface, ) -> Result<(), (Error, Surface)> { + let context: &mut WglContext = match context.try_into() { + Ok(context) => context, + Err(error) => return Err((error, surface)), + }; + if context.id != surface.context_id { return Err((Error::IncompatibleSurface, surface)); } @@ -637,7 +654,7 @@ impl Device { if is_current { // We need to make ourselves current again, because the surface changed. - drop(self.make_context_current(context)); + drop(self.make_context_current_inner(context)); } Ok(()) @@ -651,6 +668,7 @@ impl Device { &self, context: &mut Context, ) -> Result, Error> { + let context: &mut WglContext = context.try_into()?; match mem::replace(&mut context.framebuffer, Framebuffer::None) { Framebuffer::Surface(surface) => { self.unlock_surface(&surface); @@ -667,6 +685,7 @@ impl Device { /// Widget surfaces are internally double-buffered, so changes to them don't /// show up in their associated widgets until this method is called. pub fn present_bound_surface(&self, context: &mut Context) -> Result<(), Error> { + let context: &mut WglContext = context.try_into()?; match &context.framebuffer { Framebuffer::Surface(surface) => surface.present(), _ => Ok(()), @@ -679,13 +698,14 @@ impl Device { context: &mut Context, size: Size2D, ) -> Result<(), Error> { + let context: &mut WglContext = context.try_into()?; if let Framebuffer::Surface(surface) = &mut context.framebuffer { surface.resize(size); } Ok(()) } - pub(crate) fn get_context_dc<'a>(&self, context: &'a Context) -> DCGuard<'a> { + pub(crate) fn get_context_dc<'a>(&self, context: &'a WglContext) -> DCGuard<'a> { unsafe { match context.framebuffer { Framebuffer::Surface(Surface { @@ -708,6 +728,7 @@ impl Device { /// a new one, the new context might have the same ID as the destroyed one. #[inline] pub fn context_id(&self, context: &Context) -> ContextID { + let context = context.wgl().expect("Passed incorrect context type"); context.id } @@ -715,6 +736,7 @@ impl Device { /// /// This includes, most notably, the OpenGL framebuffer object needed to render to the surface. pub fn context_surface_info(&self, context: &Context) -> Result, Error> { + let context = context.wgl()?; match context.framebuffer { Framebuffer::None => Ok(None), Framebuffer::External(()) => Err(Error::ExternalRenderTarget), @@ -725,6 +747,7 @@ impl Device { /// Given a context, returns its underlying `HGLRC`. #[inline] pub fn native_context(&self, context: &Context) -> NativeContext { + let context = context.wgl().expect("Passed incorrect context type"); NativeContext(context.glrc) } @@ -815,7 +838,8 @@ impl Device { assert_ne!(ok, FALSE); // Make our texture object on the GL side. - let gl_texture = context.gl.create_texture().unwrap(); + let concrete_context = context.wgl()?; + let gl_texture = concrete_context.gl.create_texture().unwrap(); // Bind the GL texture to the D3D11 texture. let gl_dx_interop_object = (dx_interop_functions.DXRegisterObjectNV)( @@ -836,11 +860,11 @@ impl Device { } // Build our FBO. - let gl_framebuffer = context.gl.create_framebuffer().unwrap(); - let _guard = self.temporarily_bind_framebuffer(context, Some(gl_framebuffer)); + let gl_framebuffer = concrete_context.gl.create_framebuffer().unwrap(); + let _guard = self.temporarily_bind_framebuffer(concrete_context, Some(gl_framebuffer)); // Attach the reflected D3D11 texture to that FBO. - context.gl.framebuffer_texture_2d( + concrete_context.gl.framebuffer_texture_2d( gl::FRAMEBUFFER, gl::COLOR_ATTACHMENT0, SURFACE_GL_TEXTURE_TARGET, @@ -851,15 +875,15 @@ impl Device { // Create renderbuffers as appropriate, and attach them. let context_descriptor = self.context_descriptor(context); let context_attributes = self.context_descriptor_attributes(&context_descriptor); - let renderbuffers = Renderbuffers::new(&context.gl, size, &context_attributes); - renderbuffers.bind_to_current_framebuffer(&context.gl); + let renderbuffers = Renderbuffers::new(&concrete_context.gl, size, &context_attributes); + renderbuffers.bind_to_current_framebuffer(&concrete_context.gl); // FIXME(pcwalton): Do we need to acquire the keyed mutex, or does the GL driver do // that? Ok(Surface { size: *size, - context_id: context.id, + context_id: concrete_context.id, win32_objects: Win32Objects::Texture { d3d11_texture, dxgi_share_handle, @@ -878,6 +902,7 @@ impl Device { context: &Context, window_handle: HWND, ) -> Result { + let context = context.wgl()?; unsafe { // Get the bounds of the native HWND. let mut widget_rect = mem::zeroed(); @@ -923,11 +948,12 @@ impl Device { .as_ref() .expect("How did you make a surface without DX interop?"); - if context.id != surface.context_id { + if context.wgl()?.id != surface.context_id { return Err(Error::IncompatibleSurface); } let _guard = self.temporarily_make_context_current(context)?; + let context = context.wgl()?; unsafe { match surface.win32_objects { @@ -996,6 +1022,10 @@ impl Device { Ok(guard) => guard, Err(err) => return Err((err, surface)), }; + let context = match context.wgl() { + Ok(context) => context, + Err(error) => return Err((error, surface)), + }; unsafe { // Create a new texture wrapping the shared handle. @@ -1096,6 +1126,10 @@ impl Device { Ok(guard) => guard, Err(err) => return Err((err, surface_texture)), }; + let context = match context.wgl() { + Ok(context) => context, + Err(error) => return Err((error, surface_texture)), + }; unsafe { // Unlock the texture. diff --git a/src/x11/context.rs b/src/x11/context.rs index e4d540aa..c0cd81ce 100644 --- a/src/x11/context.rs +++ b/src/x11/context.rs @@ -4,20 +4,5 @@ use crate::base::egl::context::EGLBackedContext; pub use crate::base::egl::context::NativeContext; use crate::Gl; -/// Represents an OpenGL rendering context. -/// -/// A context allows you to issue rendering commands to a surface. When initially created, a -/// context has no attached surface, so rendering commands will fail or be ignored. Typically, you -/// attach a surface to the context before rendering. -/// -/// Contexts take ownership of the surfaces attached to them. In order to mutate a surface in any -/// way other than rendering to it (e.g. presenting it to a window, which causes a buffer swap), it -/// must first be detached from its context. Each surface is associated with a single context upon -/// creation and may not be rendered to from any other context. However, you can wrap a surface in -/// a surface texture, which allows the surface to be read from another context. -/// -/// OpenGL objects may not be shared across contexts directly, but surface textures effectively -/// allow for sharing of texture data. Contexts are local to a single thread and device. -/// -/// A context must be explicitly destroyed with `destroy_context()`, or a panic will occur. -pub struct Context(pub(crate) EGLBackedContext, pub(crate) Gl); +/// An implementation of [`crate::Context`] for X11. +pub struct X11Context(pub(crate) EGLBackedContext, pub(crate) Gl); diff --git a/src/x11/device.rs b/src/x11/device.rs index 98296350..5d7bddfa 100644 --- a/src/x11/device.rs +++ b/src/x11/device.rs @@ -1,7 +1,7 @@ //! A wrapper around X11 `EGLDisplay`s. use super::connection::{Connection, NativeConnectionWrapper}; -use super::context::{Context, NativeContext}; +use super::context::NativeContext; use super::surface::Surface; use crate::base::egl::{ context::{self, CurrentContextGuard, EGLBackedContext}, @@ -10,10 +10,11 @@ use crate::base::egl::{ 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::{ - egl, gl, Adapter, ContextAttributes, ContextDescriptor, EglContextDescriptor, Error, GLApi, Gl, - SurfaceAccess, SurfaceInfo, SurfaceType, + egl, gl, Adapter, Context, ContextAttributes, ContextDescriptor, EglContextDescriptor, Error, + GLApi, Gl, SurfaceAccess, SurfaceInfo, SurfaceType, }; use euclid::default::Size2D; use glow::Texture; @@ -118,6 +119,10 @@ impl Device { descriptor: &ContextDescriptor, share_with: Option<&Context>, ) -> Result { + let share_with = match share_with { + Some(share_with) => Some(share_with.x11()?), + None => None, + }; unsafe { let context = EGLBackedContext::new( self.native_connection.egl_display, @@ -126,10 +131,7 @@ impl Device { self.gl_api(), )?; context.make_current(self.native_connection.egl_display)?; - Ok(Context( - context, - Gl::from_loader_function(context::get_proc_address), - )) + Ok(X11Context(context, Gl::from_loader_function(context::get_proc_address)).into()) } } @@ -143,10 +145,11 @@ impl Device { &self, native_context: NativeContext, ) -> Result { - Ok(Context( + Ok(X11Context( EGLBackedContext::from_native_context(native_context), Gl::from_loader_function(context::get_proc_address), - )) + ) + .into()) } /// Destroys a context. @@ -157,6 +160,7 @@ impl Device { self.destroy_surface(context, &mut surface)?; } + let context: &mut X11Context = context.try_into()?; unsafe { context.0.destroy(self.native_connection.egl_display); Ok(()) @@ -166,12 +170,14 @@ impl Device { /// Given a context, returns its underlying EGL context and attached surfaces. #[inline] pub fn native_context(&self, context: &Context) -> NativeContext { + let context = context.x11().expect("Passed incorrect context type"); context.0.native_context() } /// Returns the descriptor that this context was created with. #[inline] pub fn context_descriptor(&self, context: &Context) -> ContextDescriptor { + let context = context.x11().expect("Passed incorrect context type"); unsafe { EglContextDescriptor::from_egl_context( &context.1, @@ -187,6 +193,7 @@ impl Device { /// After calling this function, it is valid to use OpenGL rendering commands. #[inline] pub fn make_context_current(&self, context: &Context) -> Result<(), Error> { + let context = context.x11()?; unsafe { context.0.make_current(self.native_connection.egl_display) } } @@ -249,6 +256,11 @@ impl Device { context: &mut Context, surface: Surface, ) -> Result<(), (Error, Surface)> { + let context: &mut X11Context = match context.try_into() { + Ok(context) => context, + Err(error) => return Err((error, surface)), + }; + unsafe { context .0 @@ -265,6 +277,7 @@ impl Device { &self, context: &mut Context, ) -> Result, Error> { + let context: &mut X11Context = context.try_into()?; unsafe { context .0 @@ -279,6 +292,7 @@ impl Device { /// Widget surfaces are internally double-buffered, so changes to them don't /// show up in their associated widgets until this method is called. pub fn present_bound_surface(&self, context: &mut Context) -> Result<(), Error> { + let context: &mut X11Context = context.try_into()?; context .0 .present_bound_surface(self.native_connection.egl_display) @@ -290,6 +304,7 @@ impl Device { context: &mut Context, size: Size2D, ) -> Result<(), Error> { + let context: &mut X11Context = context.try_into()?; context.0.resize_bound_surface(size) } @@ -299,6 +314,7 @@ impl Device { /// a new one, the new context might have the same ID as the destroyed one. #[inline] pub fn context_id(&self, context: &Context) -> ContextID { + let context = context.x11().expect("Passed incorrect context type"); context.0.id } @@ -307,6 +323,7 @@ impl Device { /// This includes, most notably, the OpenGL framebuffer object needed to render to the surface. #[inline] pub fn context_surface_info(&self, context: &Context) -> Result, Error> { + let context = context.x11()?; context.0.surface_info() } @@ -339,6 +356,7 @@ impl Device { let _guard = self.temporarily_make_context_current(context)?; let context_descriptor = self.context_descriptor(context); let context_attributes = self.context_descriptor_attributes(&context_descriptor); + let context = context.x11()?; Ok(Surface(EGLBackedSurface::new_generic( &context.1, @@ -355,6 +373,7 @@ impl Device { context: &Context, mut x11_window: Window, ) -> Result { + let context = context.x11()?; let egl_config_id = context::get_context_attr( self.native_connection.egl_display, context.0.egl_context, @@ -408,6 +427,11 @@ impl Device { Err(err) => return Err((err, surface)), }; + let context = match context.x11() { + Ok(context) => context, + Err(error) => return Err((error, surface)), + }; + match surface.0.to_surface_texture(&context.1) { Ok(surface_texture) => Ok(SurfaceTexture(surface_texture)), Err((err, surface)) => Err((err, Surface(surface))), @@ -427,6 +451,7 @@ impl Device { surface: &mut Surface, ) -> Result<(), Error> { let egl_display = self.native_connection.egl_display; + let context = context.x11()?; surface.0.destroy(&context.1, egl_display, context.0.id)?; Ok(()) } @@ -443,8 +468,12 @@ impl Device { context: &mut Context, surface_texture: SurfaceTexture, ) -> Result { + let concrete_context = match context.x11() { + Ok(concrete_context) => concrete_context, + Err(error) => return Err((error, surface_texture)), + }; match self.temporarily_make_context_current(context) { - Ok(_guard) => Ok(Surface(surface_texture.0.destroy(&context.1))), + Ok(_guard) => Ok(Surface(surface_texture.0.destroy(&concrete_context.1))), Err(err) => Err((err, surface_texture)), } } @@ -457,6 +486,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: &Context, surface: &mut Surface) -> Result<(), Error> { + let context = context.x11()?; surface .0 .present(self.native_connection.egl_display, context.0.egl_context) From f129f12adf5c0963afb08d4a2186f36f428ad74a Mon Sep 17 00:00:00 2001 From: Martin Robinson Date: Sun, 27 Sep 2026 21:08:12 +0200 Subject: [PATCH 2/2] Respond to review comments Signed-off-by: Martin Robinson --- src/angle/device.rs | 14 +++++--------- src/cgl/device.rs | 5 +---- src/hardware_buffer/device.rs | 6 +----- src/mesa_surfaceless/device.rs | 6 +----- src/wayland/device.rs | 6 +----- src/wgl/device.rs | 6 +----- src/x11/device.rs | 5 +---- 7 files changed, 11 insertions(+), 37 deletions(-) diff --git a/src/angle/device.rs b/src/angle/device.rs index 7ab4bb0e..34c3ddaf 100644 --- a/src/angle/device.rs +++ b/src/angle/device.rs @@ -270,11 +270,7 @@ impl Device { descriptor: &ContextDescriptor, share_with: Option<&Context>, ) -> Result { - let share_with = match share_with { - Some(share_with) => Some(share_with.angle()?), - None => None, - }; - + let share_with = share_with.map(Context::angle).transpose()?; let (egl_context, id) = { let mut next_context_id_lock = CREATE_CONTEXT_MUTEX.lock().unwrap(); let egl_context = unsafe { @@ -347,15 +343,15 @@ impl Device { /// /// The context must have been created on this device. pub fn destroy_context(&self, context: &mut Context) -> Result<(), Error> { + if context.angle()?.egl_context == egl::NO_CONTEXT { + return Ok(()); + } + if let Ok(Some(mut surface)) = self.unbind_surface_from_context(context) { self.destroy_surface(context, &mut surface)?; } let context: &mut AngleContext = context.try_into()?; - if context.egl_context == egl::NO_CONTEXT { - return Ok(()); - } - EGL_FUNCTIONS.with(|egl| unsafe { egl.MakeCurrent( self.egl_display, diff --git a/src/cgl/device.rs b/src/cgl/device.rs index 950c4eaa..b0bcd300 100644 --- a/src/cgl/device.rs +++ b/src/cgl/device.rs @@ -178,10 +178,7 @@ impl Device { descriptor: &ContextDescriptor, share_with: Option<&Context>, ) -> Result { - let share_with = match share_with { - Some(share_with) => Some(share_with.cgl()?), - None => None, - }; + let share_with = share_with.map(Context::cgl).transpose()?; // Take a lock so that we're only creating one context at a time. `CGLChoosePixelFormat` // will fail, returning `kCGLBadConnection`, if multiple threads try to open a display diff --git a/src/hardware_buffer/device.rs b/src/hardware_buffer/device.rs index ccad454c..9449b6a3 100644 --- a/src/hardware_buffer/device.rs +++ b/src/hardware_buffer/device.rs @@ -132,11 +132,7 @@ impl Device { descriptor: &ContextDescriptor, share_with: Option<&Context>, ) -> Result { - let share_with = match share_with { - Some(share_with) => Some(share_with.hardware_buffer()?), - None => None, - }; - + let share_with = share_with.map(Context::hardware_buffer).transpose()?; let mut next_context_id = CREATE_CONTEXT_MUTEX.lock().unwrap(); let egl_display = self.egl_display; unsafe { diff --git a/src/mesa_surfaceless/device.rs b/src/mesa_surfaceless/device.rs index 6e1831c8..c4fbf5a3 100644 --- a/src/mesa_surfaceless/device.rs +++ b/src/mesa_surfaceless/device.rs @@ -116,11 +116,7 @@ impl Device { descriptor: &ContextDescriptor, share_with: Option<&Context>, ) -> Result { - let share_with = match share_with { - Some(share_with) => Some(share_with.surfaceless_mesa()?), - None => None, - }; - + let share_with = share_with.map(Context::surfaceless_mesa).transpose()?; unsafe { let context = EGLBackedContext::new( self.native_connection.egl_display, diff --git a/src/wayland/device.rs b/src/wayland/device.rs index cfab60b9..3bc0d8c1 100644 --- a/src/wayland/device.rs +++ b/src/wayland/device.rs @@ -121,11 +121,7 @@ impl Device { descriptor: &ContextDescriptor, share_with: Option<&Context>, ) -> Result { - let share_with = match share_with { - Some(share_with) => Some(share_with.wayland()?), - None => None, - }; - + let share_with = share_with.map(Context::wayland).transpose()?; unsafe { let context = EGLBackedContext::new( self.native_connection.egl_display, diff --git a/src/wgl/device.rs b/src/wgl/device.rs index 4f8e6169..36c95bdc 100644 --- a/src/wgl/device.rs +++ b/src/wgl/device.rs @@ -329,11 +329,7 @@ impl Device { descriptor: &ContextDescriptor, share_with: Option<&Context>, ) -> Result { - let share_with = match share_with { - Some(share_with) => Some(share_with.wgl()?), - None => None, - }; - + let share_with = share_with.map(Context::wgl).transpose()?; let wglCreateContextAttribsARB = match WGL_EXTENSION_FUNCTIONS.CreateContextAttribsARB { None => return Err(Error::RequiredExtensionUnavailable), Some(wglCreateContextAttribsARB) => wglCreateContextAttribsARB, diff --git a/src/x11/device.rs b/src/x11/device.rs index 5d7bddfa..27537fa6 100644 --- a/src/x11/device.rs +++ b/src/x11/device.rs @@ -119,10 +119,7 @@ impl Device { descriptor: &ContextDescriptor, share_with: Option<&Context>, ) -> Result { - let share_with = match share_with { - Some(share_with) => Some(share_with.x11()?), - None => None, - }; + let share_with = share_with.map(Context::x11).transpose()?; unsafe { let context = EGLBackedContext::new( self.native_connection.egl_display,