use crate::{
conv,
hub::{GfxBackend, Global, Token},
resource,
DeviceId,
Extent3d,
Features,
Input,
LifeGuard,
Stored,
SwapChainId,
TextureViewId,
};
#[cfg(feature = "local")]
use crate::{gfx_select, hub::GLOBAL};
use hal::{self, device::Device as _, queue::CommandQueue as _, window::PresentationSurface as _};
use smallvec::SmallVec;
#[cfg(feature = "local")]
use std::marker::PhantomData;
const FRAME_TIMEOUT_MS: u64 = 1000;
pub const DESIRED_NUM_FRAMES: u32 = 3;
#[derive(Debug)]
pub struct SwapChain<B: hal::Backend> {
pub(crate) life_guard: LifeGuard,
pub(crate) device_id: Stored<DeviceId>,
pub(crate) desc: SwapChainDescriptor,
pub(crate) num_frames: hal::window::SwapImageIndex,
pub(crate) semaphore: B::Semaphore,
pub(crate) acquired_view_id: Option<Stored<TextureViewId>>,
}
#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub enum PresentMode {
NoVsync = 0,
Vsync = 1,
}
#[repr(C)]
#[derive(Clone, Debug)]
pub struct SwapChainDescriptor {
pub usage: resource::TextureUsage,
pub format: resource::TextureFormat,
pub width: u32,
pub height: u32,
pub present_mode: PresentMode,
}
impl SwapChainDescriptor {
pub(crate) fn to_hal(
&self,
num_frames: u32,
features: &Features,
) -> hal::window::SwapchainConfig {
let mut config = hal::window::SwapchainConfig::new(
self.width,
self.height,
conv::map_texture_format(self.format, *features),
num_frames,
);
config.image_usage = conv::map_texture_usage(self.usage, hal::format::Aspects::COLOR);
config.composite_alpha_mode = hal::window::CompositeAlphaMode::OPAQUE;
config.present_mode = match self.present_mode {
PresentMode::NoVsync => hal::window::PresentMode::IMMEDIATE,
PresentMode::Vsync => hal::window::PresentMode::FIFO,
};
config
}
pub fn to_texture_desc(&self) -> resource::TextureDescriptor {
resource::TextureDescriptor {
size: Extent3d {
width: self.width,
height: self.height,
depth: 1,
},
mip_level_count: 1,
array_layer_count: 1,
sample_count: 1,
dimension: resource::TextureDimension::D2,
format: self.format,
usage: self.usage,
}
}
}
#[repr(C)]
#[derive(Debug)]
pub struct SwapChainOutput {
pub view_id: TextureViewId,
}
#[derive(Debug)]
pub enum SwapChainGetNextTextureError {
GpuProcessingTimeout,
}
pub fn swap_chain_get_next_texture<B: GfxBackend>(
global: &Global,
swap_chain_id: SwapChainId,
view_id_in: Input<TextureViewId>,
) -> Result<SwapChainOutput, SwapChainGetNextTextureError> {
let hub = B::hub(global);
let mut token = Token::root();
let (mut surface_guard, mut token) = global.surfaces.write(&mut token);
let surface = &mut surface_guard[swap_chain_id.to_surface_id()];
let (device_guard, mut token) = hub.devices.read(&mut token);
let (mut swap_chain_guard, mut token) = hub.swap_chains.write(&mut token);
let sc = &mut swap_chain_guard[swap_chain_id];
let device = &device_guard[sc.device_id.value];
let (image, _) = {
let suf = B::get_surface_mut(surface);
match unsafe { suf.acquire_image(FRAME_TIMEOUT_MS * 1_000_000) } {
Ok(surface_image) => surface_image,
Err(hal::window::AcquireError::Timeout) => {
return Err(SwapChainGetNextTextureError::GpuProcessingTimeout);
}
Err(e) => {
log::warn!("acquire_image() failed ({:?}), reconfiguring swapchain", e);
let desc = sc.desc.to_hal(sc.num_frames, &device.features);
unsafe {
suf.configure_swapchain(&device.raw, desc).unwrap();
suf.acquire_image(FRAME_TIMEOUT_MS * 1_000_000).unwrap()
}
}
}
};
let view = resource::TextureView {
inner: resource::TextureViewInner::SwapChain {
image,
source_id: Stored {
value: swap_chain_id,
ref_count: sc.life_guard.ref_count.clone(),
},
framebuffers: SmallVec::new(),
},
format: sc.desc.format,
extent: hal::image::Extent {
width: sc.desc.width,
height: sc.desc.height,
depth: 1,
},
samples: 1,
range: hal::image::SubresourceRange {
aspects: hal::format::Aspects::COLOR,
layers: 0 .. 1,
levels: 0 .. 1,
},
life_guard: LifeGuard::new(),
};
let ref_count = view.life_guard.ref_count.clone();
let (view_id, _) = hub.texture_views.new_identity(view_id_in);
hub.texture_views.register(view_id, view, &mut token);
assert!(
sc.acquired_view_id.is_none(),
"Swap chain image is already acquired"
);
sc.acquired_view_id = Some(Stored {
value: view_id,
ref_count,
});
Ok(SwapChainOutput { view_id })
}
#[cfg(feature = "local")]
#[no_mangle]
pub extern "C" fn wgpu_swap_chain_get_next_texture(swap_chain_id: SwapChainId) -> SwapChainOutput {
gfx_select!(swap_chain_id => swap_chain_get_next_texture(&*GLOBAL, swap_chain_id, PhantomData)).unwrap_or(SwapChainOutput {
view_id: TextureViewId::ERROR,
})
}
pub fn swap_chain_present<B: GfxBackend>(global: &Global, swap_chain_id: SwapChainId) {
let hub = B::hub(global);
let mut token = Token::root();
let (mut surface_guard, mut token) = global.surfaces.write(&mut token);
let surface = &mut surface_guard[swap_chain_id.to_surface_id()];
let (mut device_guard, mut token) = hub.devices.write(&mut token);
let (mut swap_chain_guard, mut token) = hub.swap_chains.write(&mut token);
let sc = &mut swap_chain_guard[swap_chain_id];
let device = &mut device_guard[sc.device_id.value];
let view_id = sc
.acquired_view_id
.take()
.expect("Swap chain image is not acquired");
let (view, _) = hub.texture_views.unregister(view_id.value, &mut token);
let (image, framebuffers) = match view.inner {
resource::TextureViewInner::Native { .. } => unreachable!(),
resource::TextureViewInner::SwapChain {
image, framebuffers, ..
} => (image, framebuffers),
};
let err = unsafe {
let queue = &mut device.queue_group.queues[0];
queue.present_surface(B::get_surface_mut(surface), image, Some(&sc.semaphore))
};
if let Err(e) = err {
log::warn!("present failed: {:?}", e);
}
for fbo in framebuffers {
unsafe {
device.raw.destroy_framebuffer(fbo);
}
}
}
#[cfg(feature = "local")]
#[no_mangle]
pub extern "C" fn wgpu_swap_chain_present(swap_chain_id: SwapChainId) {
gfx_select!(swap_chain_id => swap_chain_present(&*GLOBAL, swap_chain_id))
}