use euclid::default::Size2D;
use fnv::FnvHashMap;
use fnv::FnvHashSet;
use log::debug;
use std::collections::hash_map::Entry;
use std::fmt::Debug;
use std::hash::Hash;
use std::mem;
use std::sync::Arc;
use std::sync::Mutex;
use std::sync::MutexGuard;
use std::sync::RwLock;
use std::sync::RwLockReadGuard;
use std::sync::RwLockWriteGuard;
use sparkle::gl;
use sparkle::gl::GLuint;
use sparkle::gl::Gl;
use surfman::platform::generic::universal::context::Context;
use surfman::platform::generic::universal::device::Device;
use surfman::platform::generic::universal::surface::Surface;
use surfman::ContextID;
use surfman::Error;
use surfman::SurfaceAccess;
use surfman::SurfaceInfo;
use surfman::SurfaceType;
use surfman_chains_api::SwapChainAPI;
use surfman_chains_api::SwapChainsAPI;
pub trait SurfaceProvider {
fn recycle_front_buffer(&mut self, device: &mut Device, context_id: ContextID);
fn recycle_surface(&mut self, surface: Surface);
fn provide_surface(
&mut self,
device: &mut Device,
context: &mut Context,
context_id: ContextID,
size: Size2D<i32>,
) -> Result<Surface, Error>;
fn take_front_buffer(&mut self) -> Option<Surface>;
fn set_front_buffer(
&mut self,
device: &mut Device,
context: &mut Context,
context_id: ContextID,
new_front_buffer: Surface,
) -> Result<(), Error>;
fn create_sized_surface(
&mut self,
device: &mut Device,
context: &mut Context,
size: Size2D<i32>,
) -> Result<Surface, Error>;
fn destroy_all_surfaces(
&mut self,
device: &mut Device,
context: &mut Context,
) -> Result<(), Error>;
}
struct SwapChainData {
surface_provider: Box<dyn SurfaceProvider + Send>,
size: Size2D<i32>,
context_id: ContextID,
unattached_surface: Option<Surface>,
}
impl SwapChainData {
fn validate_context(&self, device: &Device, context: &Context) -> Result<(), Error> {
if self.context_id == device.context_id(context) {
Ok(())
} else {
Err(Error::IncompatibleContext)
}
}
fn swap_buffers(&mut self, device: &mut Device, context: &mut Context) -> Result<(), Error> {
debug!("Swap buffers on context {:?}", self.context_id);
self.validate_context(device, context)?;
self.surface_provider
.recycle_front_buffer(device, self.context_id);
let new_back_buffer =
self.surface_provider
.provide_surface(device, context, self.context_id, self.size)?;
debug!(
"Surface {:?} is the new back buffer for context {:?}",
device.surface_info(&new_back_buffer).id,
self.context_id
);
let new_front_buffer = match self.unattached_surface.as_mut() {
Some(surface) => {
debug!("Replacing unattached surface");
mem::replace(surface, new_back_buffer)
}
None => {
debug!("Replacing attached surface");
let new_front_buffer = device.unbind_surface_from_context(context)?.unwrap();
device.bind_surface_to_context(context, new_back_buffer)?;
new_front_buffer
}
};
debug!(
"Surface {:?} is the new front buffer for context {:?}",
device.surface_info(&new_front_buffer).id,
self.context_id
);
self.surface_provider.set_front_buffer(
device,
context,
self.context_id,
new_front_buffer,
)?;
Ok(())
}
fn take_attachment_from(
&mut self,
device: &mut Device,
context: &mut Context,
other: &mut SwapChainData,
) -> Result<(), Error> {
self.validate_context(device, context)?;
other.validate_context(device, context)?;
if let (Some(surface), true) = (
self.unattached_surface.take(),
other.unattached_surface.is_none(),
) {
debug!("Attaching surface {:?}", device.surface_info(&surface).id);
let old_surface = device.unbind_surface_from_context(context)?.unwrap();
device.bind_surface_to_context(context, surface)?;
debug!(
"Detaching surface {:?}",
device.surface_info(&old_surface).id
);
other.unattached_surface = Some(old_surface);
Ok(())
} else {
Err(Error::Failed)
}
}
fn resize(
&mut self,
device: &mut Device,
context: &mut Context,
size: Size2D<i32>,
) -> Result<(), Error> {
debug!(
"Resizing context {:?} to {:?}",
device.context_id(context),
size
);
self.validate_context(device, context)?;
if (size.width < 1) || (size.height < 1) {
return Err(Error::Failed);
}
let new_back_buffer = self
.surface_provider
.create_sized_surface(device, context, size)?;
debug!(
"Surface {:?} is the new back buffer for context {:?}",
device.surface_info(&new_back_buffer).id,
self.context_id
);
let old_back_buffer = match self.unattached_surface.as_mut() {
Some(surface) => mem::replace(surface, new_back_buffer),
None => {
let old_back_buffer = device.unbind_surface_from_context(context)?.unwrap();
device.bind_surface_to_context(context, new_back_buffer)?;
old_back_buffer
}
};
device.destroy_surface(context, old_back_buffer)?;
self.size = size;
Ok(())
}
fn size(&self) -> Size2D<i32> {
self.size
}
fn take_surface(&mut self) -> Option<Surface> {
self.surface_provider.take_front_buffer()
}
fn clear_surface(
&mut self,
device: &mut Device,
context: &mut Context,
gl: &Gl,
) -> Result<(), Error> {
self.validate_context(device, context)?;
let mut bound_fbos = [0, 0];
let mut clear_color = [0., 0., 0., 0.];
let mut clear_depth = [0.];
let mut clear_stencil = [0];
let mut color_mask = [0, 0, 0, 0];
let mut depth_mask = [0];
let mut stencil_mask = [0];
let scissor_enabled = gl.is_enabled(gl::SCISSOR_TEST);
let rasterizer_enabled = gl.is_enabled(gl::RASTERIZER_DISCARD);
unsafe {
gl.get_integer_v(gl::DRAW_FRAMEBUFFER_BINDING, &mut bound_fbos[0..]);
gl.get_integer_v(gl::READ_FRAMEBUFFER_BINDING, &mut bound_fbos[1..]);
gl.get_float_v(gl::COLOR_CLEAR_VALUE, &mut clear_color[..]);
gl.get_float_v(gl::DEPTH_CLEAR_VALUE, &mut clear_depth[..]);
gl.get_integer_v(gl::STENCIL_CLEAR_VALUE, &mut clear_stencil[..]);
gl.get_boolean_v(gl::DEPTH_WRITEMASK, &mut depth_mask[..]);
gl.get_integer_v(gl::STENCIL_WRITEMASK, &mut stencil_mask[..]);
gl.get_boolean_v(gl::COLOR_WRITEMASK, &mut color_mask[..]);
}
let reattach = match self.unattached_surface.take() {
Some(surface) => {
let reattach = device.unbind_surface_from_context(context)?;
device.bind_surface_to_context(context, surface)?;
reattach
}
None => None,
};
let fbo = device
.context_surface_info(context)
.unwrap()
.unwrap()
.framebuffer_object;
gl.bind_framebuffer(gl::FRAMEBUFFER, fbo);
gl.clear_color(0., 0., 0., 0.);
gl.clear_depth(1.);
gl.clear_stencil(0);
gl.disable(gl::SCISSOR_TEST);
gl.disable(gl::RASTERIZER_DISCARD);
gl.depth_mask(true);
gl.stencil_mask(0xFFFFFFFF);
gl.color_mask(true, true, true, true);
gl.clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT | gl::STENCIL_BUFFER_BIT);
if let Some(surface) = reattach {
let old_surface = device.unbind_surface_from_context(context)?.unwrap();
device.bind_surface_to_context(context, surface)?;
self.unattached_surface = Some(old_surface);
}
gl.bind_framebuffer(gl::DRAW_FRAMEBUFFER, bound_fbos[0] as GLuint);
gl.bind_framebuffer(gl::READ_FRAMEBUFFER, bound_fbos[1] as GLuint);
gl.clear_color(
clear_color[0],
clear_color[1],
clear_color[2],
clear_color[3],
);
gl.color_mask(
color_mask[0] != 0,
color_mask[1] != 0,
color_mask[2] != 0,
color_mask[3] != 0,
);
gl.clear_depth(clear_depth[0] as f64);
gl.clear_stencil(clear_stencil[0]);
gl.depth_mask(depth_mask[0] != 0);
gl.stencil_mask(stencil_mask[0] as gl::GLuint);
if scissor_enabled {
gl.enable(gl::SCISSOR_TEST);
}
if rasterizer_enabled {
gl.enable(gl::RASTERIZER_DISCARD);
}
Ok(())
}
fn destroy(&mut self, device: &mut Device, context: &mut Context) -> Result<(), Error> {
self.validate_context(device, context)?;
for surface in self.unattached_surface.take().into_iter() {
device.destroy_surface(context, surface)?;
}
self.surface_provider
.destroy_all_surfaces(device, context)?;
Ok(())
}
fn recycle_surface(&mut self, surface: Surface) {
self.surface_provider.recycle_surface(surface);
}
}
#[derive(Clone)]
pub struct SwapChain(Arc<Mutex<SwapChainData>>);
impl SwapChain {
fn lock(&self) -> MutexGuard<SwapChainData> {
self.0.lock().unwrap_or_else(|err| err.into_inner())
}
pub fn swap_buffers(&self, device: &mut Device, context: &mut Context) -> Result<(), Error> {
self.lock().swap_buffers(device, context)
}
pub fn take_attachment_from(
&self,
device: &mut Device,
context: &mut Context,
other: &SwapChain,
) -> Result<(), Error> {
self.lock()
.take_attachment_from(device, context, &mut *other.lock())
}
pub fn resize(
&self,
device: &mut Device,
context: &mut Context,
size: Size2D<i32>,
) -> Result<(), Error> {
self.lock().resize(device, context, size)
}
pub fn size(&self) -> Size2D<i32> {
self.lock().size()
}
pub fn clear_surface(
&self,
device: &mut Device,
context: &mut Context,
gl: &Gl,
) -> Result<(), Error> {
self.lock().clear_surface(device, context, gl)
}
pub fn is_attached(&self) -> bool {
self.lock().unattached_surface.is_none()
}
pub fn destroy(&self, device: &mut Device, context: &mut Context) -> Result<(), Error> {
self.lock().destroy(device, context)
}
pub fn create_attached(
device: &mut Device,
context: &mut Context,
surface_provider: Box<dyn SurfaceProvider + Send>,
) -> Result<SwapChain, Error> {
let size = device.context_surface_info(context).unwrap().unwrap().size;
Ok(SwapChain(Arc::new(Mutex::new(SwapChainData {
size,
context_id: device.context_id(context),
unattached_surface: None,
surface_provider,
}))))
}
pub fn create_detached(
device: &mut Device,
context: &mut Context,
mut surface_provider: Box<dyn SurfaceProvider + Send>,
size: Size2D<i32>,
) -> Result<SwapChain, Error> {
let surface =
surface_provider.provide_surface(device, context, device.context_id(context), size)?;
Ok(SwapChain(Arc::new(Mutex::new(SwapChainData {
surface_provider,
size,
context_id: device.context_id(context),
unattached_surface: Some(surface),
}))))
}
}
impl SwapChainAPI for SwapChain {
type Surface = Surface;
fn take_surface(&self) -> Option<Surface> {
self.lock().take_surface()
}
fn recycle_surface(&self, surface: Surface) {
self.lock().recycle_surface(surface)
}
}
pub struct SurfmanProvider {
surface_access: SurfaceAccess,
pending_surface: Option<Surface>,
recycled_surfaces: Vec<Surface>,
}
impl SurfmanProvider {
pub fn new(surface_access: SurfaceAccess) -> Self {
Self {
surface_access,
pending_surface: None,
recycled_surfaces: vec![],
}
}
}
impl SurfaceProvider for SurfmanProvider {
fn recycle_front_buffer(&mut self, device: &mut Device, context_id: ContextID) {
if let Some(old_front_buffer) = self.pending_surface.take() {
let SurfaceInfo { id, size, .. } = device.surface_info(&old_front_buffer);
debug!(
"Recycling surface {:?} ({:?}) for context {:?}",
id, size, context_id
);
self.recycle_surface(old_front_buffer);
}
}
fn recycle_surface(&mut self, surface: Surface) {
self.recycled_surfaces.push(surface)
}
fn provide_surface(
&mut self,
device: &mut Device,
context: &mut Context,
context_id: ContextID,
size: Size2D<i32>,
) -> Result<Surface, Error> {
self.recycled_surfaces
.iter()
.position(|surface| device.surface_info(surface).size == size)
.map(|index| {
debug!("Recyling surface for context {:?}", context_id);
Ok(self.recycled_surfaces.swap_remove(index))
})
.unwrap_or_else(|| {
debug!(
"Creating a new surface ({:?}) for context {:?}",
size, context_id
);
let surface_type = SurfaceType::Generic { size: size };
device.create_surface(context, self.surface_access, &surface_type)
})
}
fn take_front_buffer(&mut self) -> Option<Surface> {
self.pending_surface
.take()
.or_else(|| self.recycled_surfaces.pop())
}
fn set_front_buffer(
&mut self,
device: &mut Device,
context: &mut Context,
context_id: ContextID,
new_front_buffer: Surface,
) -> Result<(), Error> {
self.pending_surface = Some(new_front_buffer);
for surface in self.recycled_surfaces.drain(..) {
debug!("Destroying a surface for context {:?}", context_id);
device.destroy_surface(context, surface)?;
}
Ok(())
}
fn create_sized_surface(
&mut self,
device: &mut Device,
context: &mut Context,
size: Size2D<i32>,
) -> Result<Surface, Error> {
let surface_type = SurfaceType::Generic { size };
let new_back_buffer = device.create_surface(context, self.surface_access, &surface_type)?;
Ok(new_back_buffer)
}
fn destroy_all_surfaces(
&mut self,
device: &mut Device,
context: &mut Context,
) -> Result<(), Error> {
let surfaces = self
.pending_surface
.take()
.into_iter()
.chain(self.recycled_surfaces.drain(..));
for surface in surfaces {
device.destroy_surface(context, surface)?;
}
Ok(())
}
}
#[derive(Clone, Default)]
pub struct SwapChains<SwapChainID: Eq + Hash> {
ids: Arc<Mutex<FnvHashMap<ContextID, FnvHashSet<SwapChainID>>>>,
table: Arc<RwLock<FnvHashMap<SwapChainID, SwapChain>>>,
}
impl<SwapChainID: Clone + Eq + Hash + Debug> SwapChains<SwapChainID> {
pub fn new() -> SwapChains<SwapChainID> {
SwapChains {
ids: Arc::new(Mutex::new(FnvHashMap::default())),
table: Arc::new(RwLock::new(FnvHashMap::default())),
}
}
fn ids(&self) -> MutexGuard<FnvHashMap<ContextID, FnvHashSet<SwapChainID>>> {
self.ids.lock().unwrap_or_else(|err| err.into_inner())
}
fn table(&self) -> RwLockReadGuard<FnvHashMap<SwapChainID, SwapChain>> {
self.table.read().unwrap_or_else(|err| err.into_inner())
}
fn table_mut(&self) -> RwLockWriteGuard<FnvHashMap<SwapChainID, SwapChain>> {
self.table.write().unwrap_or_else(|err| err.into_inner())
}
pub fn create_attached_swap_chain(
&self,
id: SwapChainID,
device: &mut Device,
context: &mut Context,
surface_provider: Box<dyn SurfaceProvider + Send>,
) -> Result<(), Error> {
match self.table_mut().entry(id.clone()) {
Entry::Occupied(_) => Err(Error::Failed)?,
Entry::Vacant(entry) => entry.insert(SwapChain::create_attached(
device,
context,
surface_provider,
)?),
};
self.ids()
.entry(device.context_id(context))
.or_insert_with(Default::default)
.insert(id);
Ok(())
}
pub fn create_detached_swap_chain(
&self,
id: SwapChainID,
size: Size2D<i32>,
device: &mut Device,
context: &mut Context,
surface_provider: Box<dyn SurfaceProvider + Send>,
) -> Result<(), Error> {
match self.table_mut().entry(id.clone()) {
Entry::Occupied(_) => Err(Error::Failed)?,
Entry::Vacant(entry) => entry.insert(SwapChain::create_detached(
device,
context,
surface_provider,
size,
)?),
};
self.ids()
.entry(device.context_id(context))
.or_insert_with(Default::default)
.insert(id);
Ok(())
}
pub fn destroy(
&self,
id: SwapChainID,
device: &mut Device,
context: &mut Context,
) -> Result<(), Error> {
if let Some(swap_chain) = self.table_mut().remove(&id) {
swap_chain.destroy(device, context)?;
}
if let Some(ids) = self.ids().get_mut(&device.context_id(context)) {
ids.remove(&id);
}
Ok(())
}
pub fn destroy_all(&self, device: &mut Device, context: &mut Context) -> Result<(), Error> {
if let Some(mut ids) = self.ids().remove(&device.context_id(context)) {
for id in ids.drain() {
if let Some(swap_chain) = self.table_mut().remove(&id) {
swap_chain.destroy(device, context)?;
}
}
}
Ok(())
}
pub fn iter(
&self,
device: &mut Device,
context: &mut Context,
) -> impl Iterator<Item = (SwapChainID, SwapChain)> {
self.ids()
.get(&device.context_id(context))
.iter()
.flat_map(|ids| ids.iter())
.filter_map(|id| Some((id.clone(), self.table().get(id)?.clone())))
.collect::<Vec<_>>()
.into_iter()
}
}
impl<SwapChainID> SwapChainsAPI<SwapChainID> for SwapChains<SwapChainID>
where
SwapChainID: 'static + Clone + Eq + Hash + Debug + Sync + Send,
{
type Surface = Surface;
type SwapChain = SwapChain;
fn get(&self, id: SwapChainID) -> Option<SwapChain> {
debug!("Getting swap chain {:?}", id);
self.table().get(&id).cloned()
}
}