surfman/
chains.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5//! An implementation of thread-safe swap chains for the `surfman` surface manager.
6//!
7//! The role of a swap chain is to allow surfaces to be communicated between contexts,
8//! often in different threads. Each swap chain has a *producer* context,
9//! responsible for creating and destroying surfaces, and a number of *consumer* contexts,
10//! (usually just one) which take surfaces from the swap chain, and return them for recycling.
11//!
12//! Each swap chain has a *back buffer*, that is the current surface that the producer context may draw to.
13//! Each swap chain has a *front buffer*, that is the most recent surface the producer context finished drawing to.
14//!
15//! The producer may *swap* these buffers when it has finished drawing and has a surface ready to display.
16//!
17//! The consumer may *take* the front buffer, display it, then *recycle* it.
18//!
19//! Each producer context has one *attached* swap chain, whose back buffer is the current surface of the context.
20//! The producer may change the attached swap chain, attaching a currently unattached swap chain,
21//! and detaching the currently attached one.
22
23#![allow(missing_docs)]
24
25use crate::device::Device as DeviceAPI;
26use crate::{ContextID, Error, SurfaceAccess, SurfaceInfo, SurfaceType};
27use euclid::default::Size2D;
28use fnv::{FnvHashMap, FnvHashSet};
29use glow as gl;
30use glow::Context as Gl;
31use glow::HasContext;
32use log::debug;
33use std::collections::hash_map::Entry;
34use std::fmt::Debug;
35use std::hash::Hash;
36use std::mem;
37use std::sync::{Arc, Mutex, MutexGuard, RwLock, RwLockReadGuard, RwLockWriteGuard};
38
39// The data stored for each swap chain.
40struct SwapChainData<Device: DeviceAPI> {
41    // The size of the back buffer
42    size: Size2D<i32>,
43    // The id of the producer context
44    context_id: ContextID,
45    // The surface access mode for the context.
46    surface_access: SurfaceAccess,
47    // The back buffer of the swap chain.
48    back_buffer: BackBuffer<Device>,
49    // Some if the producing context has finished drawing a new front buffer, ready to be displayed.
50    pending_surface: Option<Device::Surface>,
51    // All of the surfaces that have already been displayed, ready to be recycled.
52    recycled_surfaces: Vec<Device::Surface>,
53}
54
55pub enum PreserveBuffer<'a> {
56    Yes(&'a Gl),
57    No,
58}
59
60enum BackBuffer<Device: DeviceAPI> {
61    Attached,
62    Detached(Device::Surface),
63    TakenAttached,
64    TakenDetached,
65}
66
67impl<Device: DeviceAPI> BackBuffer<Device> {
68    fn take_surface(
69        &mut self,
70        device: &Device,
71        context: &mut Device::Context,
72    ) -> Result<Device::Surface, Error> {
73        let new_back_buffer = match self {
74            BackBuffer::Attached => BackBuffer::TakenAttached,
75            BackBuffer::Detached(_) => BackBuffer::TakenDetached,
76            _ => return Err(Error::Failed),
77        };
78        let surface = match mem::replace(self, new_back_buffer) {
79            BackBuffer::Attached => device.unbind_surface_from_context(context)?.unwrap(),
80            BackBuffer::Detached(surface) => surface,
81            _ => unreachable!(),
82        };
83        Ok(surface)
84    }
85    fn take_surface_texture(
86        &mut self,
87        device: &Device,
88        context: &mut Device::Context,
89    ) -> Result<Device::SurfaceTexture, Error> {
90        let surface = self.take_surface(device, context)?;
91        device
92            .create_surface_texture(context, surface)
93            .map_err(|(err, surface)| {
94                let _ = self.replace_surface(device, context, surface);
95                err
96            })
97    }
98    fn replace_surface(
99        &mut self,
100        device: &Device,
101        context: &mut Device::Context,
102        surface: Device::Surface,
103    ) -> Result<(), Error> {
104        let new_back_buffer = match self {
105            BackBuffer::TakenAttached => {
106                if let Err((err, mut surface)) = device.bind_surface_to_context(context, surface) {
107                    debug!("Oh no, destroying surface");
108                    let _ = device.destroy_surface(context, &mut surface);
109                    return Err(err);
110                }
111                BackBuffer::Attached
112            }
113            BackBuffer::TakenDetached => BackBuffer::Detached(surface),
114            _ => return Err(Error::Failed),
115        };
116        *self = new_back_buffer;
117        Ok(())
118    }
119    fn replace_surface_texture(
120        &mut self,
121        device: &Device,
122        context: &mut Device::Context,
123        surface_texture: Device::SurfaceTexture,
124    ) -> Result<(), Error> {
125        let surface = device
126            .destroy_surface_texture(context, surface_texture)
127            .map_err(|(err, _)| err)?;
128        self.replace_surface(device, context, surface)
129    }
130}
131
132impl<Device: DeviceAPI> SwapChainData<Device> {
133    // Returns `Ok` if `context` is the producer context for this swap chain.
134    fn validate_context(&self, device: &Device, context: &Device::Context) -> Result<(), Error> {
135        if self.context_id == device.context_id(context) {
136            Ok(())
137        } else {
138            Err(Error::IncompatibleContext)
139        }
140    }
141
142    // Swap the back and front buffers.
143    // Called by the producer.
144    // Returns an error if `context` is not the producer context for this swap chain.
145    fn swap_buffers(
146        &mut self,
147        device: &mut Device,
148        context: &mut Device::Context,
149        preserve_buffer: PreserveBuffer<'_>,
150    ) -> Result<(), Error> {
151        debug!("Swap buffers on context {:?}", self.context_id);
152        self.validate_context(device, context)?;
153
154        // Recycle the old front buffer
155        if let Some(old_front_buffer) = self.pending_surface.take() {
156            let SurfaceInfo { id, size, .. } = device.surface_info(&old_front_buffer);
157            debug!(
158                "Recycling surface {:?} ({:?}) for context {:?}",
159                id, size, self.context_id
160            );
161            self.recycle_surface(old_front_buffer);
162        }
163
164        // Fetch a new back buffer, recycling presented buffers if possible.
165        let new_back_buffer = self
166            .recycled_surfaces
167            .iter()
168            .position(|surface| device.surface_info(surface).size == self.size)
169            .map(|index| {
170                debug!("Recycling surface for context {:?}", self.context_id);
171                Ok(self.recycled_surfaces.swap_remove(index))
172            })
173            .unwrap_or_else(|| {
174                debug!(
175                    "Creating a new surface ({:?}) for context {:?}",
176                    self.size, self.context_id
177                );
178                let surface_type = SurfaceType::Generic { size: self.size };
179                device.create_surface(context, self.surface_access, surface_type)
180            })?;
181
182        let back_info = device.surface_info(&new_back_buffer);
183
184        // Swap the buffers
185        debug!(
186            "Surface {:?} is the new back buffer for context {:?}",
187            device.surface_info(&new_back_buffer).id,
188            self.context_id
189        );
190        let new_front_buffer = self.back_buffer.take_surface(device, context)?;
191        self.back_buffer
192            .replace_surface(device, context, new_back_buffer)?;
193
194        if let PreserveBuffer::Yes(gl) = preserve_buffer {
195            let front_info = device.surface_info(&new_front_buffer);
196            unsafe {
197                gl.bind_framebuffer(gl::READ_FRAMEBUFFER, front_info.framebuffer_object);
198                debug_assert_eq!(gl.get_error(), gl::NO_ERROR);
199                gl.bind_framebuffer(gl::DRAW_FRAMEBUFFER, back_info.framebuffer_object);
200                debug_assert_eq!(gl.get_error(), gl::NO_ERROR);
201                gl.blit_framebuffer(
202                    0,
203                    0,
204                    front_info.size.width,
205                    front_info.size.height,
206                    0,
207                    0,
208                    back_info.size.width,
209                    back_info.size.height,
210                    gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT | gl::STENCIL_BUFFER_BIT,
211                    gl::NEAREST,
212                );
213                debug_assert_eq!(gl.get_error(), gl::NO_ERROR);
214            }
215        }
216
217        // Update the state
218        debug!(
219            "Surface {:?} is the new front buffer for context {:?}",
220            device.surface_info(&new_front_buffer).id,
221            self.context_id
222        );
223        self.pending_surface = Some(new_front_buffer);
224        for mut surface in self.recycled_surfaces.drain(..) {
225            debug!("Destroying a surface for context {:?}", self.context_id);
226            device.destroy_surface(context, &mut surface)?;
227        }
228
229        Ok(())
230    }
231
232    // Swap the attached swap chain.
233    // Called by the producer.
234    // Returns an error if `context` is not the producer context for both swap chains.
235    // Returns an error if this swap chain is attached, or the other swap chain is detached.
236    fn take_attachment_from(
237        &mut self,
238        device: &mut Device,
239        context: &mut Device::Context,
240        other: &mut SwapChainData<Device>,
241    ) -> Result<(), Error> {
242        self.validate_context(device, context)?;
243        other.validate_context(device, context)?;
244        let our_surface = self.back_buffer.take_surface(device, context)?;
245        let their_surface = other.back_buffer.take_surface(device, context)?;
246        mem::swap(&mut self.back_buffer, &mut other.back_buffer);
247        self.back_buffer
248            .replace_surface(device, context, our_surface)?;
249        other
250            .back_buffer
251            .replace_surface(device, context, their_surface)?;
252        Ok(())
253    }
254
255    // Resize the swap chain.
256    // This creates a new back buffer of the appropriate size,
257    // and destroys the old one.
258    // Called by the producer.
259    // Returns an error if `context` is not the producer context for this swap chain.
260    // Returns an error if `size` is smaller than (1, 1).
261    fn resize(
262        &mut self,
263        device: &mut Device,
264        context: &mut Device::Context,
265        size: Size2D<i32>,
266    ) -> Result<(), Error> {
267        debug!(
268            "Resizing context {:?} to {:?}",
269            device.context_id(context),
270            size
271        );
272        self.validate_context(device, context)?;
273        if (size.width < 1) || (size.height < 1) {
274            return Err(Error::Failed);
275        }
276        let surface_type = SurfaceType::Generic { size };
277        let new_back_buffer = device.create_surface(context, self.surface_access, surface_type)?;
278        let mut old_back_buffer = self.back_buffer.take_surface(device, context)?;
279        self.back_buffer
280            .replace_surface(device, context, new_back_buffer)?;
281        device.destroy_surface(context, &mut old_back_buffer)?;
282        self.size = size;
283        Ok(())
284    }
285
286    // Get the current size.
287    // Called by a consumer.
288    fn size(&self) -> Size2D<i32> {
289        self.size
290    }
291
292    // Take the current back buffer.
293    // Called by a producer.
294    fn take_surface_texture(
295        &mut self,
296        device: &Device,
297        context: &mut Device::Context,
298    ) -> Result<Device::SurfaceTexture, Error> {
299        self.validate_context(device, context)?;
300        self.back_buffer.take_surface_texture(device, context)
301    }
302
303    // Recycle the current back buffer.
304    // Called by a producer.
305    fn recycle_surface_texture(
306        &mut self,
307        device: &Device,
308        context: &mut Device::Context,
309        surface_texture: Device::SurfaceTexture,
310    ) -> Result<(), Error> {
311        self.validate_context(device, context)?;
312        self.back_buffer
313            .replace_surface_texture(device, context, surface_texture)
314    }
315
316    // Take the current front buffer.
317    // Returns the most recent recycled surface if there is no current front buffer.
318    // Called by a consumer.
319    fn take_surface(&mut self) -> Option<Device::Surface> {
320        self.pending_surface
321            .take()
322            .or_else(|| self.recycled_surfaces.pop())
323    }
324
325    // Take the current front buffer.
326    // Returns `None` if there is no current front buffer.
327    // Called by a consumer.
328    fn take_pending_surface(&mut self) -> Option<Device::Surface> {
329        self.pending_surface.take()
330    }
331
332    // Recycle the current front buffer.
333    // Called by a consumer.
334    fn recycle_surface(&mut self, surface: Device::Surface) {
335        self.recycled_surfaces.push(surface)
336    }
337
338    // Clear the current back buffer.
339    // Called by the producer.
340    // Returns an error if `context` is not the producer context for this swap chain.
341    fn clear_surface(
342        &mut self,
343        device: &mut Device,
344        context: &mut Device::Context,
345        gl: &Gl,
346        color: [f32; 4],
347    ) -> Result<(), Error> {
348        self.validate_context(device, context)?;
349
350        // Save the current GL state
351        let draw_fbo;
352        let read_fbo;
353        let mut clear_color = [0., 0., 0., 0.];
354        let mut clear_depth = [0.];
355        let mut clear_stencil = [0];
356        let color_mask;
357        let depth_mask;
358        let mut stencil_mask = [0];
359        let scissor_enabled = unsafe { gl.is_enabled(gl::SCISSOR_TEST) };
360        let rasterizer_enabled = unsafe { gl.is_enabled(gl::RASTERIZER_DISCARD) };
361        unsafe {
362            draw_fbo = gl.get_parameter_framebuffer(gl::DRAW_FRAMEBUFFER_BINDING);
363            read_fbo = gl.get_parameter_framebuffer(gl::READ_FRAMEBUFFER_BINDING);
364            gl.get_parameter_f32_slice(gl::COLOR_CLEAR_VALUE, &mut clear_color[..]);
365            gl.get_parameter_f32_slice(gl::DEPTH_CLEAR_VALUE, &mut clear_depth[..]);
366            gl.get_parameter_i32_slice(gl::STENCIL_CLEAR_VALUE, &mut clear_stencil[..]);
367            depth_mask = gl.get_parameter_bool(gl::DEPTH_WRITEMASK);
368            gl.get_parameter_i32_slice(gl::STENCIL_WRITEMASK, &mut stencil_mask[..]);
369            color_mask = gl.get_parameter_bool_array::<4>(gl::COLOR_WRITEMASK);
370        }
371
372        // Make the back buffer the current surface
373        let reattach = if self.is_attached() {
374            None
375        } else {
376            let surface = self.back_buffer.take_surface(device, context)?;
377            let mut reattach = device.unbind_surface_from_context(context)?;
378            if let Err((err, mut surface)) = device.bind_surface_to_context(context, surface) {
379                debug!("Oh no, destroying surfaces");
380                let _ = device.destroy_surface(context, &mut surface);
381                if let Some(ref mut reattach) = reattach {
382                    let _ = device.destroy_surface(context, reattach);
383                }
384                return Err(err);
385            }
386            reattach
387        };
388
389        // Clear it
390        let fbo = device
391            .context_surface_info(context)
392            .unwrap()
393            .unwrap()
394            .framebuffer_object;
395        unsafe {
396            gl.bind_framebuffer(gl::FRAMEBUFFER, fbo);
397            gl.clear_color(color[0], color[1], color[2], color[3]);
398            gl.clear_depth(1.);
399            gl.clear_stencil(0);
400            gl.disable(gl::SCISSOR_TEST);
401            gl.disable(gl::RASTERIZER_DISCARD);
402            gl.depth_mask(true);
403            gl.stencil_mask(0xFFFFFFFF);
404            gl.color_mask(true, true, true, true);
405            gl.clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT | gl::STENCIL_BUFFER_BIT);
406        }
407        // Reattach the old surface
408        if let Some(surface) = reattach {
409            let mut old_surface = device.unbind_surface_from_context(context)?.unwrap();
410            if let Err((err, mut surface)) = device.bind_surface_to_context(context, surface) {
411                debug!("Oh no, destroying surface");
412                let _ = device.destroy_surface(context, &mut surface);
413                let _ = device.destroy_surface(context, &mut old_surface);
414                return Err(err);
415            }
416            self.back_buffer
417                .replace_surface(device, context, old_surface)?;
418        }
419
420        // Restore the GL state
421        unsafe {
422            gl.bind_framebuffer(gl::DRAW_FRAMEBUFFER, draw_fbo);
423            gl.bind_framebuffer(gl::READ_FRAMEBUFFER, read_fbo);
424            gl.clear_color(
425                clear_color[0],
426                clear_color[1],
427                clear_color[2],
428                clear_color[3],
429            );
430            gl.color_mask(color_mask[0], color_mask[1], color_mask[2], color_mask[3]);
431            gl.clear_depth(clear_depth[0] as f64);
432            gl.clear_stencil(clear_stencil[0]);
433            gl.depth_mask(depth_mask);
434            gl.stencil_mask(stencil_mask[0] as _);
435            if scissor_enabled {
436                gl.enable(gl::SCISSOR_TEST);
437            }
438            if rasterizer_enabled {
439                gl.enable(gl::RASTERIZER_DISCARD);
440            }
441        }
442
443        Ok(())
444    }
445
446    /// Is this the attached swap chain?
447    fn is_attached(&self) -> bool {
448        match self.back_buffer {
449            BackBuffer::Attached | BackBuffer::TakenAttached => true,
450            BackBuffer::Detached(_) | BackBuffer::TakenDetached => false,
451        }
452    }
453
454    // Destroy the swap chain.
455    // Called by the producer.
456    // Returns an error if `context` is not the producer context for this swap chain.
457    fn destroy(&mut self, device: &mut Device, context: &mut Device::Context) -> Result<(), Error> {
458        self.validate_context(device, context)?;
459        let surfaces = self
460            .pending_surface
461            .take()
462            .into_iter()
463            .chain(self.back_buffer.take_surface(device, context).into_iter())
464            .chain(self.recycled_surfaces.drain(..));
465        for mut surface in surfaces {
466            device.destroy_surface(context, &mut surface)?;
467        }
468        Ok(())
469    }
470}
471
472/// A thread-safe swap chain.
473pub struct SwapChain<Device: DeviceAPI>(Arc<Mutex<SwapChainData<Device>>>);
474
475// We can't derive Clone unfortunately
476impl<Device: DeviceAPI> Clone for SwapChain<Device> {
477    fn clone(&self) -> Self {
478        SwapChain(self.0.clone())
479    }
480}
481
482impl<Device: DeviceAPI> SwapChain<Device> {
483    // Guarantee unique access to the swap chain data
484    fn lock(&self) -> MutexGuard<SwapChainData<Device>> {
485        self.0.lock().unwrap_or_else(|err| err.into_inner())
486    }
487
488    /// Swap the back and front buffers.
489    /// Called by the producer.
490    /// Returns an error if `context` is not the producer context for this swap chain.
491    pub fn swap_buffers(
492        &self,
493        device: &mut Device,
494        context: &mut Device::Context,
495        preserve_buffer: PreserveBuffer<'_>,
496    ) -> Result<(), Error> {
497        self.lock().swap_buffers(device, context, preserve_buffer)
498    }
499
500    /// Swap the attached swap chain.
501    /// Called by the producer.
502    /// Returns an error if `context` is not the producer context for both swap chains.
503    /// Returns an error if this swap chain is attached, or the other swap chain is detached.
504    pub fn take_attachment_from(
505        &self,
506        device: &mut Device,
507        context: &mut Device::Context,
508        other: &SwapChain<Device>,
509    ) -> Result<(), Error> {
510        self.lock()
511            .take_attachment_from(device, context, &mut *other.lock())
512    }
513
514    /// Resize the swap chain.
515    /// This creates a new back buffer of the appropriate size,
516    /// and destroys the old one.
517    /// Called by the producer.
518    /// Returns an error if `context` is not the producer context for this swap chain.
519    pub fn resize(
520        &self,
521        device: &mut Device,
522        context: &mut Device::Context,
523        size: Size2D<i32>,
524    ) -> Result<(), Error> {
525        self.lock().resize(device, context, size)
526    }
527
528    /// Get the current size.
529    /// Called by a consumer.
530    pub fn size(&self) -> Size2D<i32> {
531        self.lock().size()
532    }
533
534    /// Take the current back buffer.
535    /// Called by a producer.
536    pub fn take_surface_texture(
537        &self,
538        device: &Device,
539        context: &mut Device::Context,
540    ) -> Result<Device::SurfaceTexture, Error> {
541        self.lock().take_surface_texture(device, context)
542    }
543
544    /// Recycle the current back buffer.
545    /// Called by a producer.
546    pub fn recycle_surface_texture(
547        &self,
548        device: &Device,
549        context: &mut Device::Context,
550        surface_texture: Device::SurfaceTexture,
551    ) -> Result<(), Error> {
552        self.lock()
553            .recycle_surface_texture(device, context, surface_texture)
554    }
555
556    /// Take the current front buffer.
557    /// Returns `None` if there is no current front buffer.
558    /// Called by a consumer.
559    pub fn take_pending_surface(&self) -> Option<Device::Surface> {
560        self.lock().take_pending_surface()
561    }
562
563    /// Clear the current back buffer.
564    /// Called by the producer.
565    /// Returns an error if `context` is not the producer context for this swap chain.
566    pub fn clear_surface(
567        &self,
568        device: &mut Device,
569        context: &mut Device::Context,
570        gl: &Gl,
571        color: [f32; 4],
572    ) -> Result<(), Error> {
573        self.lock().clear_surface(device, context, gl, color)
574    }
575
576    /// Is this the attached swap chain?
577    pub fn is_attached(&self) -> bool {
578        self.lock().is_attached()
579    }
580
581    /// Destroy the swap chain.
582    /// Called by the producer.
583    /// Returns an error if `context` is not the producer context for this swap chain.
584    pub fn destroy(&self, device: &mut Device, context: &mut Device::Context) -> Result<(), Error> {
585        self.lock().destroy(device, context)
586    }
587
588    /// Create a new attached swap chain
589    pub fn create_attached(
590        device: &mut Device,
591        context: &mut Device::Context,
592        surface_access: SurfaceAccess,
593    ) -> Result<SwapChain<Device>, Error> {
594        let size = device.context_surface_info(context).unwrap().unwrap().size;
595        Ok(SwapChain(Arc::new(Mutex::new(SwapChainData {
596            size,
597            context_id: device.context_id(context),
598            surface_access,
599            back_buffer: BackBuffer::Attached,
600            pending_surface: None,
601            recycled_surfaces: Vec::new(),
602        }))))
603    }
604
605    /// Create a new detached swap chain
606    pub fn create_detached(
607        device: &mut Device,
608        context: &mut Device::Context,
609        surface_access: SurfaceAccess,
610        size: Size2D<i32>,
611    ) -> Result<SwapChain<Device>, Error> {
612        let surface_type = SurfaceType::Generic { size };
613        let surface = device.create_surface(context, surface_access, surface_type)?;
614        Ok(SwapChain(Arc::new(Mutex::new(SwapChainData {
615            size,
616            context_id: device.context_id(context),
617            surface_access,
618            back_buffer: BackBuffer::Detached(surface),
619            pending_surface: None,
620            recycled_surfaces: Vec::new(),
621        }))))
622    }
623}
624
625impl<Device> SwapChainAPI for SwapChain<Device>
626where
627    Device: 'static + DeviceAPI,
628    Device::Surface: Send,
629{
630    type Surface = Device::Surface;
631
632    /// Take the current front buffer.
633    /// Returns the most recent recycled surface if there is no current front buffer.
634    /// Called by a consumer.
635    fn take_surface(&self) -> Option<Device::Surface> {
636        self.lock().take_surface()
637    }
638
639    /// Recycle the current front buffer.
640    /// Called by a consumer.
641    fn recycle_surface(&self, surface: Device::Surface) {
642        self.lock().recycle_surface(surface)
643    }
644}
645
646/// A thread-safe collection of swap chains.
647#[derive(Default)]
648pub struct SwapChains<SwapChainID: Eq + Hash, Device: DeviceAPI> {
649    // The swap chain ids, indexed by context id
650    ids: Arc<Mutex<FnvHashMap<ContextID, FnvHashSet<SwapChainID>>>>,
651    // The swap chains, indexed by swap chain id
652    table: Arc<RwLock<FnvHashMap<SwapChainID, SwapChain<Device>>>>,
653}
654
655// We can't derive Clone unfortunately
656impl<SwapChainID: Eq + Hash, Device: DeviceAPI> Clone for SwapChains<SwapChainID, Device> {
657    fn clone(&self) -> Self {
658        SwapChains {
659            ids: self.ids.clone(),
660            table: self.table.clone(),
661        }
662    }
663}
664
665impl<SwapChainID, Device> SwapChains<SwapChainID, Device>
666where
667    SwapChainID: Clone + Eq + Hash + Debug,
668    Device: DeviceAPI,
669{
670    /// Create a new collection.
671    pub fn new() -> SwapChains<SwapChainID, Device> {
672        SwapChains {
673            ids: Arc::new(Mutex::new(FnvHashMap::default())),
674            table: Arc::new(RwLock::new(FnvHashMap::default())),
675        }
676    }
677
678    // Lock the ids
679    fn ids(&self) -> MutexGuard<FnvHashMap<ContextID, FnvHashSet<SwapChainID>>> {
680        self.ids.lock().unwrap_or_else(|err| err.into_inner())
681    }
682
683    // Lock the lookup table
684    fn table(&self) -> RwLockReadGuard<FnvHashMap<SwapChainID, SwapChain<Device>>> {
685        self.table.read().unwrap_or_else(|err| err.into_inner())
686    }
687
688    // Lock the lookup table for writing
689    fn table_mut(&self) -> RwLockWriteGuard<FnvHashMap<SwapChainID, SwapChain<Device>>> {
690        self.table.write().unwrap_or_else(|err| err.into_inner())
691    }
692
693    /// Create a new attached swap chain and insert it in the table.
694    /// Returns an error if the `id` is already in the table.
695    pub fn create_attached_swap_chain(
696        &self,
697        id: SwapChainID,
698        device: &mut Device,
699        context: &mut Device::Context,
700        surface_access: SurfaceAccess,
701    ) -> Result<(), Error> {
702        match self.table_mut().entry(id.clone()) {
703            Entry::Occupied(_) => Err(Error::Failed)?,
704            Entry::Vacant(entry) => {
705                entry.insert(SwapChain::create_attached(device, context, surface_access)?)
706            }
707        };
708        self.ids()
709            .entry(device.context_id(context))
710            .or_insert_with(Default::default)
711            .insert(id);
712        Ok(())
713    }
714
715    /// Create a new dettached swap chain and insert it in the table.
716    /// Returns an error if the `id` is already in the table.
717    pub fn create_detached_swap_chain(
718        &self,
719        id: SwapChainID,
720        size: Size2D<i32>,
721        device: &mut Device,
722        context: &mut Device::Context,
723        surface_access: SurfaceAccess,
724    ) -> Result<(), Error> {
725        match self.table_mut().entry(id.clone()) {
726            Entry::Occupied(_) => Err(Error::Failed)?,
727            Entry::Vacant(entry) => entry.insert(SwapChain::create_detached(
728                device,
729                context,
730                surface_access,
731                size,
732            )?),
733        };
734        self.ids()
735            .entry(device.context_id(context))
736            .or_insert_with(Default::default)
737            .insert(id);
738        Ok(())
739    }
740
741    /// Destroy a swap chain.
742    /// Called by the producer.
743    /// Returns an error if `context` is not the producer context for the swap chain.
744    pub fn destroy(
745        &self,
746        id: SwapChainID,
747        device: &mut Device,
748        context: &mut Device::Context,
749    ) -> Result<(), Error> {
750        if let Some(swap_chain) = self.table_mut().remove(&id) {
751            swap_chain.destroy(device, context)?;
752        }
753        if let Some(ids) = self.ids().get_mut(&device.context_id(context)) {
754            ids.remove(&id);
755        }
756        Ok(())
757    }
758
759    /// Destroy all the swap chains for a particular producer context.
760    /// Called by the producer.
761    pub fn destroy_all(
762        &self,
763        device: &mut Device,
764        context: &mut Device::Context,
765    ) -> Result<(), Error> {
766        if let Some(mut ids) = self.ids().remove(&device.context_id(context)) {
767            for id in ids.drain() {
768                if let Some(swap_chain) = self.table_mut().remove(&id) {
769                    swap_chain.destroy(device, context)?;
770                }
771            }
772        }
773        Ok(())
774    }
775
776    /// Iterate over all the swap chains for a particular producer context.
777    /// Called by the producer.
778    pub fn iter(
779        &self,
780        device: &mut Device,
781        context: &mut Device::Context,
782    ) -> impl Iterator<Item = (SwapChainID, SwapChain<Device>)> {
783        self.ids()
784            .get(&device.context_id(context))
785            .iter()
786            .flat_map(|ids| ids.iter())
787            .filter_map(|id| Some((id.clone(), self.table().get(id)?.clone())))
788            .collect::<Vec<_>>()
789            .into_iter()
790    }
791}
792
793impl<SwapChainID, Device> SwapChainsAPI<SwapChainID> for SwapChains<SwapChainID, Device>
794where
795    SwapChainID: 'static + Clone + Eq + Hash + Debug + Sync + Send,
796    Device: 'static + DeviceAPI,
797    Device::Surface: Send,
798{
799    type Surface = Device::Surface;
800    type SwapChain = SwapChain<Device>;
801
802    /// Get a swap chain
803    fn get(&self, id: SwapChainID) -> Option<SwapChain<Device>> {
804        debug!("Getting swap chain {:?}", id);
805        self.table().get(&id).cloned()
806    }
807}
808
809/// The consumer's view of a swap chain
810pub trait SwapChainAPI: 'static + Clone + Send {
811    type Surface;
812
813    /// Take the current front buffer.
814    fn take_surface(&self) -> Option<Self::Surface>;
815
816    /// Recycle the current front buffer.
817    fn recycle_surface(&self, surface: Self::Surface);
818}
819
820/// The consumer's view of a collection of swap chains
821pub trait SwapChainsAPI<SwapChainID>: 'static + Clone + Send {
822    type Surface;
823    type SwapChain: SwapChainAPI<Surface = Self::Surface>;
824
825    /// Get a swap chain
826    fn get(&self, id: SwapChainID) -> Option<Self::SwapChain>;
827}