Skip to main content

surfman/multi/
context.rs

1//! A context abstraction that allows the choice of backends dynamically.
2
3use euclid::default::Size2D;
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
54impl<Def, Alt> Device<Def, Alt>
55where
56    Def: DeviceInterface,
57    Alt: DeviceInterface,
58{
59    /// Creates a context descriptor with the given attributes.
60    ///
61    /// Context descriptors are local to this device.
62    pub fn create_context_descriptor(
63        &self,
64        attributes: &ContextAttributes,
65    ) -> Result<ContextDescriptor<Def, Alt>, Error> {
66        match *self {
67            Device::Default(ref device) => device
68                .create_context_descriptor(attributes)
69                .map(ContextDescriptor::Default),
70            Device::Alternate(ref device) => device
71                .create_context_descriptor(attributes)
72                .map(ContextDescriptor::Alternate),
73        }
74    }
75
76    /// Creates a new OpenGL context.
77    ///
78    /// The context initially has no surface attached. Until a surface is bound to it, rendering
79    /// commands will fail or have no effect.
80    pub fn create_context(
81        &self,
82        descriptor: &ContextDescriptor<Def, Alt>,
83        share_with: Option<&Context<Def, Alt>>,
84    ) -> Result<Context<Def, Alt>, Error> {
85        match (self, descriptor) {
86            (Device::Default(device), ContextDescriptor::Default(descriptor)) => {
87                let shared = match share_with {
88                    Some(Context::Default(other)) => Some(other),
89                    Some(_) => {
90                        return Err(Error::IncompatibleSharedContext);
91                    }
92                    None => None,
93                };
94                device
95                    .create_context(descriptor, shared)
96                    .map(Context::Default)
97            }
98            (Device::Alternate(device), ContextDescriptor::Alternate(descriptor)) => {
99                let shared = match share_with {
100                    Some(Context::Alternate(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::Alternate)
109            }
110            _ => Err(Error::IncompatibleContextDescriptor),
111        }
112    }
113
114    /// Destroys a context.
115    ///
116    /// The context must have been created on this device.
117    pub fn destroy_context(&self, context: &mut Context<Def, Alt>) -> Result<(), Error> {
118        match (self, &mut *context) {
119            (Device::Default(device), &mut Context::Default(ref mut context)) => {
120                device.destroy_context(context)
121            }
122            (Device::Alternate(device), &mut Context::Alternate(ref mut context)) => {
123                device.destroy_context(context)
124            }
125            _ => Err(Error::IncompatibleContext),
126        }
127    }
128
129    /// Returns the descriptor that this context was created with.
130    pub fn context_descriptor(&self, context: &Context<Def, Alt>) -> ContextDescriptor<Def, Alt> {
131        match (self, context) {
132            (Device::Default(device), Context::Default(context)) => {
133                ContextDescriptor::Default(device.context_descriptor(context))
134            }
135            (Device::Alternate(device), Context::Alternate(context)) => {
136                ContextDescriptor::Alternate(device.context_descriptor(context))
137            }
138            _ => panic!("Incompatible context!"),
139        }
140    }
141
142    /// Makes the context the current OpenGL context for this thread.
143    ///
144    /// After calling this function, it is valid to use OpenGL rendering commands.
145    pub fn make_context_current(&self, context: &Context<Def, Alt>) -> Result<(), Error> {
146        match (self, context) {
147            (Device::Default(device), Context::Default(context)) => {
148                device.make_context_current(context)
149            }
150            (Device::Alternate(device), Context::Alternate(context)) => {
151                device.make_context_current(context)
152            }
153            _ => Err(Error::IncompatibleContext),
154        }
155    }
156
157    /// Removes the current OpenGL context from this thread.
158    ///
159    /// After calling this function, OpenGL rendering commands will fail until a new context is
160    /// made current.
161    pub fn make_no_context_current(&self) -> Result<(), Error> {
162        match self {
163            Device::Default(device) => device.make_no_context_current(),
164            Device::Alternate(device) => device.make_no_context_current(),
165        }
166    }
167
168    /// Attaches a surface to a context for rendering.
169    ///
170    /// This function takes ownership of the surface. The surface must have been created with this
171    /// context, or an `IncompatibleSurface` error is returned.
172    ///
173    /// If this function is called with a surface already bound, a `SurfaceAlreadyBound` error is
174    /// returned. To avoid this error, first unbind the existing surface with
175    /// `unbind_surface_from_context`.
176    ///
177    /// If an error is returned, the surface is returned alongside it.
178    pub fn bind_surface_to_context(
179        &self,
180        context: &mut Context<Def, Alt>,
181        surface: Surface<Def, Alt>,
182    ) -> Result<(), (Error, Surface<Def, Alt>)> {
183        match (self, &mut *context) {
184            (Device::Default(device), &mut Context::Default(ref mut context)) => match surface {
185                Surface::Default(surface) => device
186                    .bind_surface_to_context(context, surface)
187                    .map_err(|(err, surface)| (err, Surface::Default(surface))),
188                _ => Err((Error::IncompatibleSurface, surface)),
189            },
190            (Device::Alternate(device), &mut Context::Alternate(ref mut context)) => {
191                match surface {
192                    Surface::Alternate(surface) => device
193                        .bind_surface_to_context(context, surface)
194                        .map_err(|(err, surface)| (err, Surface::Alternate(surface))),
195                    _ => Err((Error::IncompatibleSurface, surface)),
196                }
197            }
198            _ => Err((Error::IncompatibleContext, surface)),
199        }
200    }
201
202    /// Removes and returns any attached surface from this context.
203    ///
204    /// Any pending OpenGL commands targeting this surface will be automatically flushed, so the
205    /// surface is safe to read from immediately when this function returns.
206    pub fn unbind_surface_from_context(
207        &self,
208        context: &mut Context<Def, Alt>,
209    ) -> Result<Option<Surface<Def, Alt>>, Error> {
210        match (self, &mut *context) {
211            (Device::Default(device), &mut Context::Default(ref mut context)) => device
212                .unbind_surface_from_context(context)
213                .map(|surface| surface.map(Surface::Default)),
214            (Device::Alternate(device), &mut Context::Alternate(ref mut context)) => device
215                .unbind_surface_from_context(context)
216                .map(|surface| surface.map(Surface::Alternate)),
217            _ => Err(Error::IncompatibleContext),
218        }
219    }
220
221    /// Displays the contents of the currently bound surface to the screen, if
222    /// it is a widget surface.
223    ///
224    /// Widget surfaces are internally double-buffered, so changes to them don't
225    /// show up in their associated widgets until this method is called.
226    pub fn present_bound_surface(&self, context: &mut Context<Def, Alt>) -> Result<(), Error> {
227        match (self, context) {
228            (Device::Default(device), Context::Default(context)) => {
229                device.present_bound_surface(context)
230            }
231            (Device::Alternate(device), Context::Alternate(context)) => {
232                device.present_bound_surface(context)
233            }
234            _ => Err(Error::IncompatibleContext),
235        }
236    }
237
238    /// Resizes the currently bound surface.
239    pub fn resize_bound_surface(
240        &self,
241        context: &mut Context<Def, Alt>,
242        size: Size2D<i32>,
243    ) -> Result<(), Error> {
244        match (self, context) {
245            (Device::Default(device), Context::Default(context)) => {
246                device.resize_bound_surface(context, size)
247            }
248            (Device::Alternate(device), Context::Alternate(context)) => {
249                device.resize_bound_surface(context, size)
250            }
251            _ => Err(Error::IncompatibleContext),
252        }
253    }
254
255    /// Returns the attributes that the context descriptor was created with.
256    pub fn context_descriptor_attributes(
257        &self,
258        context_descriptor: &ContextDescriptor<Def, Alt>,
259    ) -> ContextAttributes {
260        match (self, context_descriptor) {
261            (Device::Default(device), ContextDescriptor::Default(context_descriptor)) => {
262                device.context_descriptor_attributes(context_descriptor)
263            }
264            (Device::Alternate(device), ContextDescriptor::Alternate(context_descriptor)) => {
265                device.context_descriptor_attributes(context_descriptor)
266            }
267            _ => panic!("Incompatible context!"),
268        }
269    }
270
271    /// Fetches the address of an OpenGL function associated with this context.
272    ///
273    /// OpenGL functions are local to a context. You should not use OpenGL functions on one context
274    /// with any other context.
275    ///
276    /// This method is typically used with a function like `gl::load_with()` from the `gl` crate to
277    /// load OpenGL function pointers.
278    pub fn get_proc_address(
279        &self,
280        context: &Context<Def, Alt>,
281        symbol_name: &str,
282    ) -> *const c_void {
283        match (self, context) {
284            (Device::Default(device), Context::Default(context)) => {
285                device.get_proc_address(context, symbol_name)
286            }
287            (Device::Alternate(device), Context::Alternate(context)) => {
288                device.get_proc_address(context, symbol_name)
289            }
290            _ => panic!("Incompatible context!"),
291        }
292    }
293
294    /// Returns a unique ID representing a context.
295    ///
296    /// This ID is unique to all currently-allocated contexts. If you destroy a context and create
297    /// a new one, the new context might have the same ID as the destroyed one.
298    pub fn context_id(&self, context: &Context<Def, Alt>) -> ContextID {
299        match (self, context) {
300            (Device::Default(device), Context::Default(context)) => device.context_id(context),
301            (Device::Alternate(device), Context::Alternate(context)) => device.context_id(context),
302            _ => panic!("Incompatible context!"),
303        }
304    }
305
306    /// Returns various information about the surface attached to a context.
307    ///
308    /// This includes, most notably, the OpenGL framebuffer object needed to render to the surface.
309    pub fn context_surface_info(
310        &self,
311        context: &Context<Def, Alt>,
312    ) -> Result<Option<SurfaceInfo>, Error> {
313        match (self, context) {
314            (Device::Default(device), Context::Default(context)) => {
315                device.context_surface_info(context)
316            }
317            (Device::Alternate(device), Context::Alternate(context)) => {
318                device.context_surface_info(context)
319            }
320            _ => Err(Error::IncompatibleContext),
321        }
322    }
323}