surfman/platform/generic/multi/
context.rs

1// surfman/surfman/src/platform/generic/multi/context.rs
2//
3//! A context abstraction that allows the choice of backends dynamically.
4
5use super::device::Device;
6use super::surface::Surface;
7use crate::device::Device as DeviceInterface;
8use crate::{ContextAttributes, ContextID, Error, SurfaceInfo};
9
10use std::os::raw::c_void;
11
12/// Represents an OpenGL rendering context.
13///
14/// A context allows you to issue rendering commands to a surface. When initially created, a
15/// context has no attached surface, so rendering commands will fail or be ignored. Typically, you
16/// attach a surface to the context before rendering.
17///
18/// Contexts take ownership of the surfaces attached to them. In order to mutate a surface in any
19/// way other than rendering to it (e.g. presenting it to a window, which causes a buffer swap), it
20/// must first be detached from its context. Each surface is associated with a single context upon
21/// creation and may not be rendered to from any other context. However, you can wrap a surface in
22/// a surface texture, which allows the surface to be read from another context.
23///
24/// OpenGL objects may not be shared across contexts directly, but surface textures effectively
25/// allow for sharing of texture data. Contexts are local to a single thread and device.
26///
27/// A context must be explicitly destroyed with `destroy_context()`, or a panic will occur.
28pub enum Context<Def, Alt>
29where
30    Def: DeviceInterface,
31    Alt: DeviceInterface,
32{
33    /// The default rendering context type.
34    Default(Def::Context),
35    /// The alternate rendering context type.
36    Alternate(Alt::Context),
37}
38
39/// Information needed to create a context. Some APIs call this a "config" or a "pixel format".
40///
41/// These are local to a device.
42#[derive(Clone)]
43pub enum ContextDescriptor<Def, Alt>
44where
45    Def: DeviceInterface,
46    Alt: DeviceInterface,
47{
48    /// The default context descriptor type.
49    Default(Def::ContextDescriptor),
50    /// The alternate context descriptor type.
51    Alternate(Alt::ContextDescriptor),
52}
53
54/// Wraps a platform-specific native context.
55pub enum NativeContext<Def, Alt>
56where
57    Def: DeviceInterface,
58    Alt: DeviceInterface,
59{
60    /// The default context type.
61    Default(Def::NativeContext),
62    /// The alternate context type.
63    Alternate(Alt::NativeContext),
64}
65
66impl<Def, Alt> Device<Def, Alt>
67where
68    Def: DeviceInterface,
69    Alt: DeviceInterface,
70{
71    /// Creates a context descriptor with the given attributes.
72    ///
73    /// Context descriptors are local to this device.
74    pub fn create_context_descriptor(
75        &self,
76        attributes: &ContextAttributes,
77    ) -> Result<ContextDescriptor<Def, Alt>, Error> {
78        match *self {
79            Device::Default(ref device) => device
80                .create_context_descriptor(attributes)
81                .map(ContextDescriptor::Default),
82            Device::Alternate(ref device) => device
83                .create_context_descriptor(attributes)
84                .map(ContextDescriptor::Alternate),
85        }
86    }
87
88    /// Creates a new OpenGL context.
89    ///
90    /// The context initially has no surface attached. Until a surface is bound to it, rendering
91    /// commands will fail or have no effect.
92    pub fn create_context(
93        &mut self,
94        descriptor: &ContextDescriptor<Def, Alt>,
95        share_with: Option<&Context<Def, Alt>>,
96    ) -> Result<Context<Def, Alt>, Error> {
97        match (&mut *self, descriptor) {
98            (&mut Device::Default(ref mut device), ContextDescriptor::Default(descriptor)) => {
99                let shared = match share_with {
100                    Some(Context::Default(other)) => Some(other),
101                    Some(_) => {
102                        return Err(Error::IncompatibleSharedContext);
103                    }
104                    None => None,
105                };
106                device
107                    .create_context(descriptor, shared)
108                    .map(Context::Default)
109            }
110            (&mut Device::Alternate(ref mut device), ContextDescriptor::Alternate(descriptor)) => {
111                let shared = match share_with {
112                    Some(Context::Alternate(other)) => Some(other),
113                    Some(_) => {
114                        return Err(Error::IncompatibleSharedContext);
115                    }
116                    None => None,
117                };
118                device
119                    .create_context(descriptor, shared)
120                    .map(Context::Alternate)
121            }
122            _ => Err(Error::IncompatibleContextDescriptor),
123        }
124    }
125
126    /// Wraps an existing native context in a `Context` object.
127    pub unsafe fn create_context_from_native_context(
128        &self,
129        native_context: NativeContext<Def, Alt>,
130    ) -> Result<Context<Def, Alt>, Error> {
131        match self {
132            Device::Default(device) => match native_context {
133                NativeContext::Default(native_context) => device
134                    .create_context_from_native_context(native_context)
135                    .map(Context::Default),
136                _ => Err(Error::IncompatibleNativeContext),
137            },
138            Device::Alternate(device) => match native_context {
139                NativeContext::Alternate(native_context) => device
140                    .create_context_from_native_context(native_context)
141                    .map(Context::Alternate),
142                _ => Err(Error::IncompatibleNativeContext),
143            },
144        }
145    }
146
147    /// Destroys a context.
148    ///
149    /// The context must have been created on this device.
150    pub fn destroy_context(&self, context: &mut Context<Def, Alt>) -> Result<(), Error> {
151        match (self, &mut *context) {
152            (Device::Default(device), &mut Context::Default(ref mut context)) => {
153                device.destroy_context(context)
154            }
155            (Device::Alternate(device), &mut Context::Alternate(ref mut context)) => {
156                device.destroy_context(context)
157            }
158            _ => Err(Error::IncompatibleContext),
159        }
160    }
161
162    /// Returns the native context underlying this context.
163    pub fn native_context(&self, context: &Context<Def, Alt>) -> NativeContext<Def, Alt> {
164        match (self, context) {
165            (Device::Default(device), Context::Default(context)) => {
166                NativeContext::Default(device.native_context(context))
167            }
168            (Device::Alternate(device), Context::Alternate(context)) => {
169                NativeContext::Alternate(device.native_context(context))
170            }
171            _ => panic!("Incompatible context!"),
172        }
173    }
174
175    /// Returns the descriptor that this context was created with.
176    pub fn context_descriptor(&self, context: &Context<Def, Alt>) -> ContextDescriptor<Def, Alt> {
177        match (self, context) {
178            (Device::Default(device), Context::Default(context)) => {
179                ContextDescriptor::Default(device.context_descriptor(context))
180            }
181            (Device::Alternate(device), Context::Alternate(context)) => {
182                ContextDescriptor::Alternate(device.context_descriptor(context))
183            }
184            _ => panic!("Incompatible context!"),
185        }
186    }
187
188    /// Makes the context the current OpenGL context for this thread.
189    ///
190    /// After calling this function, it is valid to use OpenGL rendering commands.
191    pub fn make_context_current(&self, context: &Context<Def, Alt>) -> Result<(), Error> {
192        match (self, context) {
193            (Device::Default(device), Context::Default(context)) => {
194                device.make_context_current(context)
195            }
196            (Device::Alternate(device), Context::Alternate(context)) => {
197                device.make_context_current(context)
198            }
199            _ => Err(Error::IncompatibleContext),
200        }
201    }
202
203    /// Removes the current OpenGL context from this thread.
204    ///
205    /// After calling this function, OpenGL rendering commands will fail until a new context is
206    /// made current.
207    pub fn make_no_context_current(&self) -> Result<(), Error> {
208        match self {
209            Device::Default(device) => device.make_no_context_current(),
210            Device::Alternate(device) => device.make_no_context_current(),
211        }
212    }
213
214    /// Attaches a surface to a context for rendering.
215    ///
216    /// This function takes ownership of the surface. The surface must have been created with this
217    /// context, or an `IncompatibleSurface` error is returned.
218    ///
219    /// If this function is called with a surface already bound, a `SurfaceAlreadyBound` error is
220    /// returned. To avoid this error, first unbind the existing surface with
221    /// `unbind_surface_from_context`.
222    ///
223    /// If an error is returned, the surface is returned alongside it.
224    pub fn bind_surface_to_context(
225        &self,
226        context: &mut Context<Def, Alt>,
227        surface: Surface<Def, Alt>,
228    ) -> Result<(), (Error, Surface<Def, Alt>)> {
229        match (self, &mut *context) {
230            (Device::Default(device), &mut Context::Default(ref mut context)) => match surface {
231                Surface::Default(surface) => device
232                    .bind_surface_to_context(context, surface)
233                    .map_err(|(err, surface)| (err, Surface::Default(surface))),
234                _ => Err((Error::IncompatibleSurface, surface)),
235            },
236            (Device::Alternate(device), &mut Context::Alternate(ref mut context)) => {
237                match surface {
238                    Surface::Alternate(surface) => device
239                        .bind_surface_to_context(context, surface)
240                        .map_err(|(err, surface)| (err, Surface::Alternate(surface))),
241                    _ => Err((Error::IncompatibleSurface, surface)),
242                }
243            }
244            _ => Err((Error::IncompatibleContext, surface)),
245        }
246    }
247
248    /// Removes and returns any attached surface from this context.
249    ///
250    /// Any pending OpenGL commands targeting this surface will be automatically flushed, so the
251    /// surface is safe to read from immediately when this function returns.
252    pub fn unbind_surface_from_context(
253        &self,
254        context: &mut Context<Def, Alt>,
255    ) -> Result<Option<Surface<Def, Alt>>, Error> {
256        match (self, &mut *context) {
257            (Device::Default(device), &mut Context::Default(ref mut context)) => device
258                .unbind_surface_from_context(context)
259                .map(|surface| surface.map(Surface::Default)),
260            (Device::Alternate(device), &mut Context::Alternate(ref mut context)) => device
261                .unbind_surface_from_context(context)
262                .map(|surface| surface.map(Surface::Alternate)),
263            _ => Err(Error::IncompatibleContext),
264        }
265    }
266
267    /// Returns the attributes that the context descriptor was created with.
268    pub fn context_descriptor_attributes(
269        &self,
270        context_descriptor: &ContextDescriptor<Def, Alt>,
271    ) -> ContextAttributes {
272        match (self, context_descriptor) {
273            (Device::Default(device), ContextDescriptor::Default(context_descriptor)) => {
274                device.context_descriptor_attributes(context_descriptor)
275            }
276            (Device::Alternate(device), ContextDescriptor::Alternate(context_descriptor)) => {
277                device.context_descriptor_attributes(context_descriptor)
278            }
279            _ => panic!("Incompatible context!"),
280        }
281    }
282
283    /// Fetches the address of an OpenGL function associated with this context.
284    ///
285    /// OpenGL functions are local to a context. You should not use OpenGL functions on one context
286    /// with any other context.
287    ///
288    /// This method is typically used with a function like `gl::load_with()` from the `gl` crate to
289    /// load OpenGL function pointers.
290    pub fn get_proc_address(
291        &self,
292        context: &Context<Def, Alt>,
293        symbol_name: &str,
294    ) -> *const c_void {
295        match (self, context) {
296            (Device::Default(device), Context::Default(context)) => {
297                device.get_proc_address(context, symbol_name)
298            }
299            (Device::Alternate(device), Context::Alternate(context)) => {
300                device.get_proc_address(context, symbol_name)
301            }
302            _ => panic!("Incompatible context!"),
303        }
304    }
305
306    /// Returns a unique ID representing a context.
307    ///
308    /// This ID is unique to all currently-allocated contexts. If you destroy a context and create
309    /// a new one, the new context might have the same ID as the destroyed one.
310    pub fn context_id(&self, context: &Context<Def, Alt>) -> ContextID {
311        match (self, context) {
312            (Device::Default(device), Context::Default(context)) => device.context_id(context),
313            (Device::Alternate(device), Context::Alternate(context)) => device.context_id(context),
314            _ => panic!("Incompatible context!"),
315        }
316    }
317
318    /// Returns various information about the surface attached to a context.
319    ///
320    /// This includes, most notably, the OpenGL framebuffer object needed to render to the surface.
321    pub fn context_surface_info(
322        &self,
323        context: &Context<Def, Alt>,
324    ) -> Result<Option<SurfaceInfo>, Error> {
325        match (self, context) {
326            (Device::Default(device), Context::Default(context)) => {
327                device.context_surface_info(context)
328            }
329            (Device::Alternate(device), Context::Alternate(context)) => {
330                device.context_surface_info(context)
331            }
332            _ => Err(Error::IncompatibleContext),
333        }
334    }
335}