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