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