surfman/platform/unix/wayland/
context.rs

1// surfman/surfman/src/platform/unix/wayland/context.rs
2//
3//! OpenGL rendering contexts on Wayland.
4
5use super::device::Device;
6use super::surface::Surface;
7use crate::context::ContextID;
8use crate::egl;
9use crate::egl::types::EGLint;
10use crate::platform::generic::egl::context::{self, CurrentContextGuard, EGLBackedContext};
11use crate::{ContextAttributes, Error, Gl, SurfaceInfo};
12
13use std::os::raw::c_void;
14
15pub use crate::platform::generic::egl::context::{ContextDescriptor, NativeContext};
16
17/// Represents an OpenGL rendering context.
18///
19/// A context allows you to issue rendering commands to a surface. When initially created, a
20/// context has no attached surface, so rendering commands will fail or be ignored. Typically, you
21/// attach a surface to the context before rendering.
22///
23/// Contexts take ownership of the surfaces attached to them. In order to mutate a surface in any
24/// way other than rendering to it (e.g. presenting it to a window, which causes a buffer swap), it
25/// must first be detached from its context. Each surface is associated with a single context upon
26/// creation and may not be rendered to from any other context. However, you can wrap a surface in
27/// a surface texture, which allows the surface to be read from another context.
28///
29/// OpenGL objects may not be shared across contexts directly, but surface textures effectively
30/// allow for sharing of texture data. Contexts are local to a single thread and device.
31///
32/// A context must be explicitly destroyed with `destroy_context()`, or a panic will occur.
33pub struct Context(pub(crate) EGLBackedContext, pub(crate) Gl);
34
35impl Device {
36    /// Creates a context descriptor with the given attributes.
37    ///
38    /// Context descriptors are local to this device.
39    #[inline]
40    pub fn create_context_descriptor(
41        &self,
42        attributes: &ContextAttributes,
43    ) -> Result<ContextDescriptor, Error> {
44        // Set environment variables as appropriate.
45        self.adapter.set_environment_variables();
46
47        unsafe {
48            ContextDescriptor::new(
49                self.native_connection.egl_display,
50                attributes,
51                &[
52                    egl::SURFACE_TYPE as EGLint,
53                    egl::WINDOW_BIT as EGLint,
54                    egl::RENDERABLE_TYPE as EGLint,
55                    egl::OPENGL_BIT as EGLint,
56                ],
57            )
58        }
59    }
60
61    /// Creates a new OpenGL context.
62    ///
63    /// The context initially has no surface attached. Until a surface is bound to it, rendering
64    /// commands will fail or have no effect.
65    #[inline]
66    pub fn create_context(
67        &mut self,
68        descriptor: &ContextDescriptor,
69        share_with: Option<&Context>,
70    ) -> Result<Context, Error> {
71        unsafe {
72            let context = EGLBackedContext::new(
73                self.native_connection.egl_display,
74                descriptor,
75                share_with.map(|ctx| &ctx.0),
76                self.gl_api(),
77            )?;
78            context.make_current(self.native_connection.egl_display)?;
79            Ok(Context(
80                context,
81                Gl::from_loader_function(context::get_proc_address),
82            ))
83        }
84    }
85
86    /// Wraps an `EGLContext` in a native context and returns it.
87    ///
88    /// The context is not retained, as there is no way to do this in the EGL API. Therefore,
89    /// it is the caller's responsibility to ensure that the returned `Context` object remains
90    /// alive as long as the `EGLContext` is.
91    #[inline]
92    pub unsafe fn create_context_from_native_context(
93        &self,
94        native_context: NativeContext,
95    ) -> Result<Context, Error> {
96        Ok(Context(
97            EGLBackedContext::from_native_context(native_context),
98            Gl::from_loader_function(context::get_proc_address),
99        ))
100    }
101
102    /// Destroys a context.
103    ///
104    /// The context must have been created on this device.
105    pub fn destroy_context(&self, context: &mut Context) -> Result<(), Error> {
106        if let Ok(Some(mut surface)) = self.unbind_surface_from_context(context) {
107            self.destroy_surface(context, &mut surface)?;
108        }
109
110        unsafe {
111            context.0.destroy(self.native_connection.egl_display);
112            Ok(())
113        }
114    }
115
116    /// Given a context, returns its underlying EGL context and attached surfaces.
117    #[inline]
118    pub fn native_context(&self, context: &Context) -> NativeContext {
119        context.0.native_context()
120    }
121
122    /// Returns the descriptor that this context was created with.
123    #[inline]
124    pub fn context_descriptor(&self, context: &Context) -> ContextDescriptor {
125        unsafe {
126            ContextDescriptor::from_egl_context(
127                &context.1,
128                self.native_connection.egl_display,
129                context.0.egl_context,
130            )
131        }
132    }
133
134    /// Makes the context the current OpenGL context for this thread.
135    ///
136    /// After calling this function, it is valid to use OpenGL rendering commands.
137    #[inline]
138    pub fn make_context_current(&self, context: &Context) -> Result<(), Error> {
139        unsafe { context.0.make_current(self.native_connection.egl_display) }
140    }
141
142    /// Removes the current OpenGL context from this thread.
143    ///
144    /// After calling this function, OpenGL rendering commands will fail until a new context is
145    /// made current.
146    #[inline]
147    pub fn make_no_context_current(&self) -> Result<(), Error> {
148        unsafe { context::make_no_context_current(self.native_connection.egl_display) }
149    }
150
151    #[inline]
152    pub(crate) fn temporarily_make_context_current(
153        &self,
154        context: &Context,
155    ) -> Result<CurrentContextGuard, Error> {
156        let guard = CurrentContextGuard::new();
157        self.make_context_current(context)?;
158        Ok(guard)
159    }
160
161    /// Returns the attributes that the context descriptor was created with.
162    #[inline]
163    pub fn context_descriptor_attributes(
164        &self,
165        context_descriptor: &ContextDescriptor,
166    ) -> ContextAttributes {
167        unsafe { context_descriptor.attributes(self.native_connection.egl_display) }
168    }
169
170    /// Fetches the address of an OpenGL function associated with this context.
171    ///
172    /// OpenGL functions are local to a context. You should not use OpenGL functions on one context
173    /// with any other context.
174    ///
175    /// This method is typically used with a function like `gl::load_with()` from the `gl` crate to
176    /// load OpenGL function pointers.
177    #[inline]
178    pub fn get_proc_address(&self, _: &Context, symbol_name: &str) -> *const c_void {
179        context::get_proc_address(symbol_name)
180    }
181
182    /// Attaches a surface to a context for rendering.
183    ///
184    /// This function takes ownership of the surface. The surface must have been created with this
185    /// context, or an `IncompatibleSurface` error is returned.
186    ///
187    /// If this function is called with a surface already bound, a `SurfaceAlreadyBound` error is
188    /// returned. To avoid this error, first unbind the existing surface with
189    /// `unbind_surface_from_context`.
190    ///
191    /// If an error is returned, the surface is returned alongside it.
192    #[inline]
193    pub fn bind_surface_to_context(
194        &self,
195        context: &mut Context,
196        surface: Surface,
197    ) -> Result<(), (Error, Surface)> {
198        unsafe {
199            context
200                .0
201                .bind_surface(self.native_connection.egl_display, surface.0)
202                .map_err(|(err, surface)| (err, Surface(surface)))
203        }
204    }
205
206    /// Removes and returns any attached surface from this context.
207    ///
208    /// Any pending OpenGL commands targeting this surface will be automatically flushed, so the
209    /// surface is safe to read from immediately when this function returns.
210    pub fn unbind_surface_from_context(
211        &self,
212        context: &mut Context,
213    ) -> Result<Option<Surface>, Error> {
214        unsafe {
215            context
216                .0
217                .unbind_surface(&context.1, self.native_connection.egl_display)
218                .map(|maybe_surface| maybe_surface.map(Surface))
219        }
220    }
221
222    /// Returns a unique ID representing a context.
223    ///
224    /// This ID is unique to all currently-allocated contexts. If you destroy a context and create
225    /// a new one, the new context might have the same ID as the destroyed one.
226    #[inline]
227    pub fn context_id(&self, context: &Context) -> ContextID {
228        context.0.id
229    }
230
231    /// Returns various information about the surface attached to a context.
232    ///
233    /// This includes, most notably, the OpenGL framebuffer object needed to render to the surface.
234    #[inline]
235    pub fn context_surface_info(&self, context: &Context) -> Result<Option<SurfaceInfo>, Error> {
236        context.0.surface_info()
237    }
238}