surfman/platform/unix/x11/context.rs
1// surfman/surfman/src/platform/unix/x11/context.rs
2//
3//! OpenGL rendering contexts on X11 via EGL.
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::WINDOW_BIT as EGLint,
56 egl::RENDERABLE_TYPE as EGLint,
57 egl::OPENGL_BIT 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 &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.
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 /// Displays the contents of the currently bound surface to the screen, if
225 /// it is a widget surface.
226 ///
227 /// Widget surfaces are internally double-buffered, so changes to them don't
228 /// show up in their associated widgets until this method is called.
229 pub fn present_bound_surface(&self, context: &mut Context) -> Result<(), Error> {
230 context
231 .0
232 .present_bound_surface(self.native_connection.egl_display)
233 }
234
235 /// If the currently bound surface is a widget surface, resize it,
236 pub fn resize_bound_surface(
237 &self,
238 context: &mut Context,
239 size: Size2D<i32>,
240 ) -> Result<(), Error> {
241 context.0.resize_bound_surface(size)
242 }
243
244 /// Returns a unique ID representing a context.
245 ///
246 /// This ID is unique to all currently-allocated contexts. If you destroy a context and create
247 /// a new one, the new context might have the same ID as the destroyed one.
248 #[inline]
249 pub fn context_id(&self, context: &Context) -> ContextID {
250 context.0.id
251 }
252
253 /// Returns various information about the surface attached to a context.
254 ///
255 /// This includes, most notably, the OpenGL framebuffer object needed to render to the surface.
256 #[inline]
257 pub fn context_surface_info(&self, context: &Context) -> Result<Option<SurfaceInfo>, Error> {
258 context.0.surface_info()
259 }
260}