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}