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}