Skip to main content

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