Skip to main content

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: &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        Ok(())
225    }
226
227    // Swap the attached swap chain.
228    // Called by the producer.
229    // Returns an error if `context` is not the producer context for both swap chains.
230    // Returns an error if this swap chain is attached, or the other swap chain is detached.
231    fn take_attachment_from(
232        &mut self,
233        device: &Device,
234        context: &mut Device::Context,
235        other: &mut SwapChainData<Device>,
236    ) -> Result<(), Error> {
237        self.validate_context(device, context)?;
238        other.validate_context(device, context)?;
239        let our_surface = self.back_buffer.take_surface(device, context)?;
240        let their_surface = other.back_buffer.take_surface(device, context)?;
241        mem::swap(&mut self.back_buffer, &mut other.back_buffer);
242        self.back_buffer
243            .replace_surface(device, context, our_surface)?;
244        other
245            .back_buffer
246            .replace_surface(device, context, their_surface)?;
247        Ok(())
248    }
249
250    // Resize the swap chain.
251    // This creates a new back buffer of the appropriate size,
252    // and destroys the old one.
253    // Called by the producer.
254    // Returns an error if `context` is not the producer context for this swap chain.
255    // Returns an error if `size` is smaller than (1, 1).
256    fn resize(
257        &mut self,
258        device: &Device,
259        context: &mut Device::Context,
260        size: Size2D<i32>,
261    ) -> Result<(), Error> {
262        debug!(
263            "Resizing context {:?} to {:?}",
264            device.context_id(context),
265            size
266        );
267        self.validate_context(device, context)?;
268        if (size.width < 1) || (size.height < 1) {
269            return Err(Error::Failed);
270        }
271        let surface_type = SurfaceType::Generic { size };
272        let new_back_buffer = device.create_surface(context, self.surface_access, surface_type)?;
273        let mut old_back_buffer = self.back_buffer.take_surface(device, context)?;
274        self.back_buffer
275            .replace_surface(device, context, new_back_buffer)?;
276        device.destroy_surface(context, &mut old_back_buffer)?;
277        for mut surface in self.recycled_surfaces.drain(..) {
278            device.destroy_surface(context, &mut surface)?;
279        }
280        self.size = size;
281        Ok(())
282    }
283
284    // Get the current size.
285    // Called by a consumer.
286    fn size(&self) -> Size2D<i32> {
287        self.size
288    }
289
290    // Take the current back buffer.
291    // Called by a producer.
292    fn take_surface_texture(
293        &mut self,
294        device: &Device,
295        context: &mut Device::Context,
296    ) -> Result<Device::SurfaceTexture, Error> {
297        self.validate_context(device, context)?;
298        self.back_buffer.take_surface_texture(device, context)
299    }
300
301    // Recycle the current back buffer.
302    // Called by a producer.
303    fn recycle_surface_texture(
304        &mut self,
305        device: &Device,
306        context: &mut Device::Context,
307        surface_texture: Device::SurfaceTexture,
308    ) -> Result<(), Error> {
309        self.validate_context(device, context)?;
310        self.back_buffer
311            .replace_surface_texture(device, context, surface_texture)
312    }
313
314    // Take the current front buffer.
315    // Returns the most recent recycled surface if there is no current front buffer.
316    // Called by a consumer.
317    fn take_surface(&mut self) -> Option<Device::Surface> {
318        self.pending_surface
319            .take()
320            .or_else(|| self.recycled_surfaces.pop())
321    }
322
323    // Take the current front buffer.
324    // Returns `None` if there is no current front buffer.
325    // Called by a consumer.
326    fn take_pending_surface(&mut self) -> Option<Device::Surface> {
327        self.pending_surface.take()
328    }
329
330    // Recycle the current front buffer.
331    // Called by a consumer.
332    fn recycle_surface(&mut self, surface: Device::Surface) {
333        self.recycled_surfaces.push(surface)
334    }
335
336    // Clear the current back buffer.
337    // Called by the producer.
338    // Returns an error if `context` is not the producer context for this swap chain.
339    fn clear_surface(
340        &mut self,
341        device: &Device,
342        context: &mut Device::Context,
343        gl: &Gl,
344        color: [f32; 4],
345    ) -> Result<(), Error> {
346        self.validate_context(device, context)?;
347
348        // Save the current GL state
349        let draw_fbo;
350        let read_fbo;
351        let mut clear_color = [0., 0., 0., 0.];
352        let mut clear_depth = [0.];
353        let mut clear_stencil = [0];
354        let color_mask;
355        let depth_mask;
356        let mut stencil_mask = [0];
357        let scissor_enabled = unsafe { gl.is_enabled(gl::SCISSOR_TEST) };
358        let rasterizer_enabled = unsafe { gl.is_enabled(gl::RASTERIZER_DISCARD) };
359        unsafe {
360            draw_fbo = gl.get_parameter_framebuffer(gl::DRAW_FRAMEBUFFER_BINDING);
361            read_fbo = gl.get_parameter_framebuffer(gl::READ_FRAMEBUFFER_BINDING);
362            gl.get_parameter_f32_slice(gl::COLOR_CLEAR_VALUE, &mut clear_color[..]);
363            gl.get_parameter_f32_slice(gl::DEPTH_CLEAR_VALUE, &mut clear_depth[..]);
364            gl.get_parameter_i32_slice(gl::STENCIL_CLEAR_VALUE, &mut clear_stencil[..]);
365            depth_mask = gl.get_parameter_bool(gl::DEPTH_WRITEMASK);
366            gl.get_parameter_i32_slice(gl::STENCIL_WRITEMASK, &mut stencil_mask[..]);
367            color_mask = gl.get_parameter_bool_array::<4>(gl::COLOR_WRITEMASK);
368        }
369
370        // Make the back buffer the current surface
371        let reattach = if self.is_attached() {
372            None
373        } else {
374            let surface = self.back_buffer.take_surface(device, context)?;
375            let mut reattach = device.unbind_surface_from_context(context)?;
376            if let Err((err, mut surface)) = device.bind_surface_to_context(context, surface) {
377                debug!("Oh no, destroying surfaces");
378                let _ = device.destroy_surface(context, &mut surface);
379                if let Some(ref mut reattach) = reattach {
380                    let _ = device.destroy_surface(context, reattach);
381                }
382                return Err(err);
383            }
384            reattach
385        };
386
387        // Clear it
388        let fbo = device
389            .context_surface_info(context)
390            .unwrap()
391            .unwrap()
392            .framebuffer_object;
393        unsafe {
394            gl.bind_framebuffer(gl::FRAMEBUFFER, fbo);
395            gl.clear_color(color[0], color[1], color[2], color[3]);
396            gl.clear_depth(1.);
397            gl.clear_stencil(0);
398            gl.disable(gl::SCISSOR_TEST);
399            gl.disable(gl::RASTERIZER_DISCARD);
400            gl.depth_mask(true);
401            gl.stencil_mask(0xFFFFFFFF);
402            gl.color_mask(true, true, true, true);
403            gl.clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT | gl::STENCIL_BUFFER_BIT);
404        }
405        // Reattach the old surface
406        if let Some(surface) = reattach {
407            let mut old_surface = device.unbind_surface_from_context(context)?.unwrap();
408            if let Err((err, mut surface)) = device.bind_surface_to_context(context, surface) {
409                debug!("Oh no, destroying surface");
410                let _ = device.destroy_surface(context, &mut surface);
411                let _ = device.destroy_surface(context, &mut old_surface);
412                return Err(err);
413            }
414            self.back_buffer
415                .replace_surface(device, context, old_surface)?;
416        }
417
418        // Restore the GL state
419        unsafe {
420            gl.bind_framebuffer(gl::DRAW_FRAMEBUFFER, draw_fbo);
421            gl.bind_framebuffer(gl::READ_FRAMEBUFFER, read_fbo);
422            gl.clear_color(
423                clear_color[0],
424                clear_color[1],
425                clear_color[2],
426                clear_color[3],
427            );
428            gl.color_mask(color_mask[0], color_mask[1], color_mask[2], color_mask[3]);
429            gl.clear_depth(clear_depth[0] as f64);
430            gl.clear_stencil(clear_stencil[0]);
431            gl.depth_mask(depth_mask);
432            gl.stencil_mask(stencil_mask[0] as _);
433            if scissor_enabled {
434                gl.enable(gl::SCISSOR_TEST);
435            }
436            if rasterizer_enabled {
437                gl.enable(gl::RASTERIZER_DISCARD);
438            }
439        }
440
441        Ok(())
442    }
443
444    /// Is this the attached swap chain?
445    fn is_attached(&self) -> bool {
446        match self.back_buffer {
447            BackBuffer::Attached | BackBuffer::TakenAttached => true,
448            BackBuffer::Detached(_) | BackBuffer::TakenDetached => false,
449        }
450    }
451
452    // Destroy the swap chain.
453    // Called by the producer.
454    // Returns an error if `context` is not the producer context for this swap chain.
455    fn destroy(&mut self, device: &Device, context: &mut Device::Context) -> Result<(), Error> {
456        self.validate_context(device, context)?;
457        let surfaces = self
458            .pending_surface
459            .take()
460            .into_iter()
461            .chain(self.back_buffer.take_surface(device, context).into_iter())
462            .chain(self.recycled_surfaces.drain(..));
463        for mut surface in surfaces {
464            device.destroy_surface(context, &mut surface)?;
465        }
466        Ok(())
467    }
468}
469
470/// A thread-safe swap chain.
471pub struct SwapChain<Device: DeviceAPI>(Arc<Mutex<SwapChainData<Device>>>);
472
473// We can't derive Clone unfortunately
474impl<Device: DeviceAPI> Clone for SwapChain<Device> {
475    fn clone(&self) -> Self {
476        SwapChain(self.0.clone())
477    }
478}
479
480impl<Device: DeviceAPI> SwapChain<Device> {
481    // Guarantee unique access to the swap chain data
482    fn lock(&self) -> MutexGuard<SwapChainData<Device>> {
483        self.0.lock().unwrap_or_else(|err| err.into_inner())
484    }
485
486    /// Swap the back and front buffers.
487    /// Called by the producer.
488    /// Returns an error if `context` is not the producer context for this swap chain.
489    pub fn swap_buffers(
490        &self,
491        device: &Device,
492        context: &mut Device::Context,
493        preserve_buffer: PreserveBuffer<'_>,
494    ) -> Result<(), Error> {
495        self.lock().swap_buffers(device, context, preserve_buffer)
496    }
497
498    /// Swap the attached swap chain.
499    /// Called by the producer.
500    /// Returns an error if `context` is not the producer context for both swap chains.
501    /// Returns an error if this swap chain is attached, or the other swap chain is detached.
502    pub fn take_attachment_from(
503        &self,
504        device: &Device,
505        context: &mut Device::Context,
506        other: &SwapChain<Device>,
507    ) -> Result<(), Error> {
508        self.lock()
509            .take_attachment_from(device, context, &mut *other.lock())
510    }
511
512    /// Resize the swap chain.
513    /// This creates a new back buffer of the appropriate size,
514    /// and destroys the old one.
515    /// Called by the producer.
516    /// Returns an error if `context` is not the producer context for this swap chain.
517    pub fn resize(
518        &self,
519        device: &Device,
520        context: &mut Device::Context,
521        size: Size2D<i32>,
522    ) -> Result<(), Error> {
523        self.lock().resize(device, context, size)
524    }
525
526    /// Get the current size.
527    /// Called by a consumer.
528    pub fn size(&self) -> Size2D<i32> {
529        self.lock().size()
530    }
531
532    /// Take the current back buffer.
533    /// Called by a producer.
534    pub fn take_surface_texture(
535        &self,
536        device: &Device,
537        context: &mut Device::Context,
538    ) -> Result<Device::SurfaceTexture, Error> {
539        self.lock().take_surface_texture(device, context)
540    }
541
542    /// Recycle the current back buffer.
543    /// Called by a producer.
544    pub fn recycle_surface_texture(
545        &self,
546        device: &Device,
547        context: &mut Device::Context,
548        surface_texture: Device::SurfaceTexture,
549    ) -> Result<(), Error> {
550        self.lock()
551            .recycle_surface_texture(device, context, surface_texture)
552    }
553
554    /// Take the current front buffer.
555    /// Returns `None` if there is no current front buffer.
556    /// Called by a consumer.
557    pub fn take_pending_surface(&self) -> Option<Device::Surface> {
558        self.lock().take_pending_surface()
559    }
560
561    /// Clear the current back buffer.
562    /// Called by the producer.
563    /// Returns an error if `context` is not the producer context for this swap chain.
564    pub fn clear_surface(
565        &self,
566        device: &Device,
567        context: &mut Device::Context,
568        gl: &Gl,
569        color: [f32; 4],
570    ) -> Result<(), Error> {
571        self.lock().clear_surface(device, context, gl, color)
572    }
573
574    /// Is this the attached swap chain?
575    pub fn is_attached(&self) -> bool {
576        self.lock().is_attached()
577    }
578
579    /// Destroy the swap chain.
580    /// Called by the producer.
581    /// Returns an error if `context` is not the producer context for this swap chain.
582    pub fn destroy(&self, device: &Device, context: &mut Device::Context) -> Result<(), Error> {
583        self.lock().destroy(device, context)
584    }
585
586    /// Create a new attached swap chain
587    pub fn create_attached(
588        device: &Device,
589        context: &mut Device::Context,
590        surface_access: SurfaceAccess,
591    ) -> Result<SwapChain<Device>, Error> {
592        let size = device.context_surface_info(context).unwrap().unwrap().size;
593        Ok(SwapChain(Arc::new(Mutex::new(SwapChainData {
594            size,
595            context_id: device.context_id(context),
596            surface_access,
597            back_buffer: BackBuffer::Attached,
598            pending_surface: None,
599            recycled_surfaces: Vec::new(),
600        }))))
601    }
602
603    /// Create a new detached swap chain
604    pub fn create_detached(
605        device: &Device,
606        context: &mut Device::Context,
607        surface_access: SurfaceAccess,
608        size: Size2D<i32>,
609    ) -> Result<SwapChain<Device>, Error> {
610        let surface_type = SurfaceType::Generic { size };
611        let surface = device.create_surface(context, surface_access, surface_type)?;
612        Ok(SwapChain(Arc::new(Mutex::new(SwapChainData {
613            size,
614            context_id: device.context_id(context),
615            surface_access,
616            back_buffer: BackBuffer::Detached(surface),
617            pending_surface: None,
618            recycled_surfaces: Vec::new(),
619        }))))
620    }
621}
622
623impl<Device> SwapChainAPI for SwapChain<Device>
624where
625    Device: 'static + DeviceAPI,
626    Device::Surface: Send,
627{
628    type Surface = Device::Surface;
629
630    /// Take the current front buffer.
631    /// Returns the most recent recycled surface if there is no current front buffer.
632    /// Called by a consumer.
633    fn take_surface(&self) -> Option<Device::Surface> {
634        self.lock().take_surface()
635    }
636
637    /// Recycle the current front buffer.
638    /// Called by a consumer.
639    fn recycle_surface(&self, surface: Device::Surface) {
640        self.lock().recycle_surface(surface)
641    }
642}
643
644/// A thread-safe collection of swap chains.
645#[derive(Default)]
646pub struct SwapChains<SwapChainID: Eq + Hash, Device: DeviceAPI> {
647    // The swap chain ids, indexed by context id
648    ids: Arc<Mutex<FnvHashMap<ContextID, FnvHashSet<SwapChainID>>>>,
649    // The swap chains, indexed by swap chain id
650    table: Arc<RwLock<FnvHashMap<SwapChainID, SwapChain<Device>>>>,
651}
652
653// We can't derive Clone unfortunately
654impl<SwapChainID: Eq + Hash, Device: DeviceAPI> Clone for SwapChains<SwapChainID, Device> {
655    fn clone(&self) -> Self {
656        SwapChains {
657            ids: self.ids.clone(),
658            table: self.table.clone(),
659        }
660    }
661}
662
663impl<SwapChainID, Device> SwapChains<SwapChainID, Device>
664where
665    SwapChainID: Clone + Eq + Hash + Debug,
666    Device: DeviceAPI,
667{
668    /// Create a new collection.
669    pub fn new() -> SwapChains<SwapChainID, Device> {
670        SwapChains {
671            ids: Arc::new(Mutex::new(FnvHashMap::default())),
672            table: Arc::new(RwLock::new(FnvHashMap::default())),
673        }
674    }
675
676    // Lock the ids
677    fn ids(&self) -> MutexGuard<FnvHashMap<ContextID, FnvHashSet<SwapChainID>>> {
678        self.ids.lock().unwrap_or_else(|err| err.into_inner())
679    }
680
681    // Lock the lookup table
682    fn table(&self) -> RwLockReadGuard<FnvHashMap<SwapChainID, SwapChain<Device>>> {
683        self.table.read().unwrap_or_else(|err| err.into_inner())
684    }
685
686    // Lock the lookup table for writing
687    fn table_mut(&self) -> RwLockWriteGuard<FnvHashMap<SwapChainID, SwapChain<Device>>> {
688        self.table.write().unwrap_or_else(|err| err.into_inner())
689    }
690
691    /// Create a new attached swap chain and insert it in the table.
692    /// Returns an error if the `id` is already in the table.
693    pub fn create_attached_swap_chain(
694        &self,
695        id: SwapChainID,
696        device: &Device,
697        context: &mut Device::Context,
698        surface_access: SurfaceAccess,
699    ) -> Result<(), Error> {
700        match self.table_mut().entry(id.clone()) {
701            Entry::Occupied(_) => Err(Error::Failed)?,
702            Entry::Vacant(entry) => {
703                entry.insert(SwapChain::create_attached(device, context, surface_access)?)
704            }
705        };
706        self.ids()
707            .entry(device.context_id(context))
708            .or_insert_with(Default::default)
709            .insert(id);
710        Ok(())
711    }
712
713    /// Create a new dettached swap chain and insert it in the table.
714    /// Returns an error if the `id` is already in the table.
715    pub fn create_detached_swap_chain(
716        &self,
717        id: SwapChainID,
718        size: Size2D<i32>,
719        device: &Device,
720        context: &mut Device::Context,
721        surface_access: SurfaceAccess,
722    ) -> Result<(), Error> {
723        match self.table_mut().entry(id.clone()) {
724            Entry::Occupied(_) => Err(Error::Failed)?,
725            Entry::Vacant(entry) => entry.insert(SwapChain::create_detached(
726                device,
727                context,
728                surface_access,
729                size,
730            )?),
731        };
732        self.ids()
733            .entry(device.context_id(context))
734            .or_insert_with(Default::default)
735            .insert(id);
736        Ok(())
737    }
738
739    /// Destroy a swap chain.
740    /// Called by the producer.
741    /// Returns an error if `context` is not the producer context for the swap chain.
742    pub fn destroy(
743        &self,
744        id: SwapChainID,
745        device: &Device,
746        context: &mut Device::Context,
747    ) -> Result<(), Error> {
748        if let Some(swap_chain) = self.table_mut().remove(&id) {
749            swap_chain.destroy(device, context)?;
750        }
751        if let Some(ids) = self.ids().get_mut(&device.context_id(context)) {
752            ids.remove(&id);
753        }
754        Ok(())
755    }
756
757    /// Iterate over all the swap chains for a particular producer context.
758    /// Called by the producer.
759    pub fn iter(
760        &self,
761        device: &Device,
762        context: &mut Device::Context,
763    ) -> impl Iterator<Item = (SwapChainID, SwapChain<Device>)> {
764        self.ids()
765            .get(&device.context_id(context))
766            .iter()
767            .flat_map(|ids| ids.iter())
768            .filter_map(|id| Some((id.clone(), self.table().get(id)?.clone())))
769            .collect::<Vec<_>>()
770            .into_iter()
771    }
772}
773
774impl<SwapChainID, Device> SwapChainsAPI<SwapChainID> for SwapChains<SwapChainID, Device>
775where
776    SwapChainID: 'static + Clone + Eq + Hash + Debug + Sync + Send,
777    Device: 'static + DeviceAPI,
778    Device::Surface: Send,
779{
780    type Surface = Device::Surface;
781    type SwapChain = SwapChain<Device>;
782
783    /// Get a swap chain
784    fn get(&self, id: SwapChainID) -> Option<SwapChain<Device>> {
785        debug!("Getting swap chain {:?}", id);
786        self.table().get(&id).cloned()
787    }
788}
789
790/// The consumer's view of a swap chain
791pub trait SwapChainAPI: 'static + Clone + Send {
792    type Surface;
793
794    /// Take the current front buffer.
795    fn take_surface(&self) -> Option<Self::Surface>;
796
797    /// Recycle the current front buffer.
798    fn recycle_surface(&self, surface: Self::Surface);
799}
800
801/// The consumer's view of a collection of swap chains
802pub trait SwapChainsAPI<SwapChainID>: 'static + Clone + Send {
803    type Surface;
804    type SwapChain: SwapChainAPI<Surface = Self::Surface>;
805
806    /// Get a swap chain
807    fn get(&self, id: SwapChainID) -> Option<Self::SwapChain>;
808}