surfman/platform/unix/generic/
context.rs

1// surfman/surfman/src/platform/unix/generic/context.rs
2//
3//! OpenGL rendering contexts on surfaceless Mesa.
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::PBUFFER_BIT as EGLint,
54                    egl::RENDERABLE_TYPE as EGLint,
55                    egl::OPENGL_BIT as EGLint,
56                    egl::COLOR_BUFFER_TYPE as EGLint,
57                    egl::RGB_BUFFER as EGLint,
58                ],
59            )
60        }
61    }
62
63    /// Creates a new OpenGL context.
64    ///
65    /// The context initially has no surface attached. Until a surface is bound to it, rendering
66    /// commands will fail or have no effect.
67    #[inline]
68    pub fn create_context(
69        &mut self,
70        descriptor: &ContextDescriptor,
71        share_with: Option<&Context>,
72    ) -> Result<Context, Error> {
73        unsafe {
74            let context = EGLBackedContext::new(
75                self.native_connection.egl_display,
76                descriptor,
77                share_with.map(|ctx| &ctx.0),
78                self.gl_api(),
79            )?;
80            context.make_current(self.native_connection.egl_display)?;
81            Ok(Context(
82                context,
83                Gl::from_loader_function(context::get_proc_address),
84            ))
85        }
86    }
87
88    /// Wraps an `EGLContext` in a native context and returns it. The context must be current.
89    ///
90    /// The context is not retained, as there is no way to do this in the EGL API. Therefore,
91    /// it is the caller's responsibility to ensure that the returned `Context` object remains
92    /// alive as long as the `EGLContext` is.
93    #[inline]
94    pub unsafe fn create_context_from_native_context(
95        &self,
96        native_context: NativeContext,
97    ) -> Result<Context, Error> {
98        Ok(Context(
99            EGLBackedContext::from_native_context(native_context),
100            Gl::from_loader_function(context::get_proc_address),
101        ))
102    }
103
104    /// Destroys a context.
105    ///
106    /// The context must have been created on this device.
107    pub fn destroy_context(&self, context: &mut Context) -> Result<(), Error> {
108        if let Ok(Some(mut surface)) = self.unbind_surface_from_context(context) {
109            self.destroy_surface(context, &mut surface)?;
110        }
111
112        unsafe {
113            context.0.destroy(self.native_connection.egl_display);
114            Ok(())
115        }
116    }
117
118    /// Given a context, returns its underlying EGL context and attached surfaces.
119    #[inline]
120    pub fn native_context(&self, context: &Context) -> NativeContext {
121        context.0.native_context()
122    }
123
124    /// Returns the descriptor that this context was created with.
125    #[inline]
126    pub fn context_descriptor(&self, context: &Context) -> ContextDescriptor {
127        unsafe {
128            ContextDescriptor::from_egl_context(
129                &context.1,
130                self.native_connection.egl_display,
131                context.0.egl_context,
132            )
133        }
134    }
135
136    /// Makes the context the current OpenGL context for this thread.
137    ///
138    /// After calling this function, it is valid to use OpenGL rendering commands.
139    #[inline]
140    pub fn make_context_current(&self, context: &Context) -> Result<(), Error> {
141        unsafe { context.0.make_current(self.native_connection.egl_display) }
142    }
143
144    /// Removes the current OpenGL context from this thread.
145    ///
146    /// After calling this function, OpenGL rendering commands will fail until a new context is
147    /// made current.
148    #[inline]
149    pub fn make_no_context_current(&self) -> Result<(), Error> {
150        unsafe { context::make_no_context_current(self.native_connection.egl_display) }
151    }
152
153    #[inline]
154    pub(crate) fn temporarily_make_context_current(
155        &self,
156        context: &Context,
157    ) -> Result<CurrentContextGuard, Error> {
158        let guard = CurrentContextGuard::new();
159        self.make_context_current(context)?;
160        Ok(guard)
161    }
162
163    /// Returns the attributes that the context descriptor was created with.
164    #[inline]
165    pub fn context_descriptor_attributes(
166        &self,
167        context_descriptor: &ContextDescriptor,
168    ) -> ContextAttributes {
169        unsafe { context_descriptor.attributes(self.native_connection.egl_display) }
170    }
171
172    /// Fetches the address of an OpenGL function associated with this context.
173    ///
174    /// OpenGL functions are local to a context. You should not use OpenGL functions on one context
175    /// with any other context.
176    ///
177    /// This method is typically used with a function like `gl::load_with()` from the `gl` crate to
178    /// load OpenGL function pointers.
179    #[inline]
180    pub fn get_proc_address(&self, _: &Context, symbol_name: &str) -> *const c_void {
181        context::get_proc_address(symbol_name)
182    }
183
184    /// Attaches a surface to a context for rendering.
185    ///
186    /// This function takes ownership of the surface. The surface must have been created with this
187    /// context, or an `IncompatibleSurface` error is returned.
188    ///
189    /// If this function is called with a surface already bound, a `SurfaceAlreadyBound` error is
190    /// returned. To avoid this error, first unbind the existing surface with
191    /// `unbind_surface_from_context`.
192    ///
193    /// If an error is returned, the surface is returned alongside it.
194    #[inline]
195    pub fn bind_surface_to_context(
196        &self,
197        context: &mut Context,
198        surface: Surface,
199    ) -> Result<(), (Error, Surface)> {
200        unsafe {
201            context
202                .0
203                .bind_surface(self.native_connection.egl_display, surface.0)
204                .map_err(|(err, surface)| (err, Surface(surface)))
205        }
206    }
207
208    /// Removes and returns any attached surface from this context.
209    ///
210    /// Any pending OpenGL commands targeting this surface will be automatically flushed, so the
211    /// surface is safe to read from immediately when this function returns.
212    pub fn unbind_surface_from_context(
213        &self,
214        context: &mut Context,
215    ) -> Result<Option<Surface>, Error> {
216        unsafe {
217            context
218                .0
219                .unbind_surface(&context.1, self.native_connection.egl_display)
220                .map(|maybe_surface| maybe_surface.map(Surface))
221        }
222    }
223
224    /// Returns a unique ID representing a context.
225    ///
226    /// This ID is unique to all currently-allocated contexts. If you destroy a context and create
227    /// a new one, the new context might have the same ID as the destroyed one.
228    #[inline]
229    pub fn context_id(&self, context: &Context) -> ContextID {
230        context.0.id
231    }
232
233    /// Returns various information about the surface attached to a context.
234    ///
235    /// This includes, most notably, the OpenGL framebuffer object needed to render to the surface.
236    #[inline]
237    pub fn context_surface_info(&self, context: &Context) -> Result<Option<SurfaceInfo>, Error> {
238        context.0.surface_info()
239    }
240}