Skip to main content

surfman/platform/unix/wayland/
context.rs

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