#[cfg(feature = "trace")]
use crate::device::trace::Action;
use crate::{
conv,
hub::{GfxBackend, Global, GlobalIdentityHandlerFactory, Input, Token},
id::{DeviceId, SwapChainId, TextureViewId},
resource, LifeGuard, PrivateFeatures, Stored, SubmissionIndex,
};
use hal::{self, device::Device as _, queue::CommandQueue as _, window::PresentationSurface as _};
use wgt::{SwapChainDescriptor, SwapChainStatus};
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>>,
pub(crate) acquired_framebuffers: Vec<B::Framebuffer>,
pub(crate) active_submission_index: SubmissionIndex,
}
pub(crate) fn swap_chain_descriptor_to_hal(
desc: &SwapChainDescriptor,
num_frames: u32,
private_features: PrivateFeatures,
) -> hal::window::SwapchainConfig {
let mut config = hal::window::SwapchainConfig::new(
desc.width,
desc.height,
conv::map_texture_format(desc.format, private_features),
num_frames,
);
config.image_usage = conv::map_texture_usage(desc.usage, hal::format::Aspects::COLOR);
config.composite_alpha_mode = hal::window::CompositeAlphaMode::OPAQUE;
config.present_mode = match desc.present_mode {
wgt::PresentMode::Immediate => hal::window::PresentMode::IMMEDIATE,
wgt::PresentMode::Mailbox => hal::window::PresentMode::MAILBOX,
wgt::PresentMode::Fifo => hal::window::PresentMode::FIFO,
};
config
}
#[repr(C)]
#[derive(Debug)]
pub struct SwapChainOutput {
pub status: SwapChainStatus,
pub view_id: Option<TextureViewId>,
}
impl<G: GlobalIdentityHandlerFactory> Global<G> {
pub fn swap_chain_get_next_texture<B: GfxBackend>(
&self,
swap_chain_id: SwapChainId,
view_id_in: Input<G, TextureViewId>,
) -> SwapChainOutput {
let hub = B::hub(self);
let mut token = Token::root();
let (mut surface_guard, mut token) = self.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];
#[cfg_attr(not(feature = "trace"), allow(unused_variables))]
let device = &device_guard[sc.device_id.value];
let suf = B::get_surface_mut(surface);
let (image, status) = match unsafe { suf.acquire_image(FRAME_TIMEOUT_MS * 1_000_000) } {
Ok((surface_image, None)) => (Some(surface_image), SwapChainStatus::Good),
Ok((surface_image, Some(_))) => (Some(surface_image), SwapChainStatus::Suboptimal),
Err(err) => (
None,
match err {
hal::window::AcquireError::OutOfMemory(_) => SwapChainStatus::OutOfMemory,
hal::window::AcquireError::NotReady => unreachable!(),
hal::window::AcquireError::Timeout => SwapChainStatus::Timeout,
hal::window::AcquireError::OutOfDate => SwapChainStatus::Outdated,
hal::window::AcquireError::SurfaceLost(_) => SwapChainStatus::Lost,
hal::window::AcquireError::DeviceLost(_) => SwapChainStatus::Lost,
},
),
};
let view_id = image.map(|image| {
let view = resource::TextureView {
inner: resource::TextureViewInner::SwapChain {
image,
source_id: Stored {
value: swap_chain_id,
ref_count: sc.life_guard.add_ref(),
},
},
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.add_ref();
let id = hub
.texture_views
.register_identity(view_id_in, view, &mut token);
assert!(
sc.acquired_view_id.is_none(),
"Swap chain image is already acquired"
);
sc.acquired_view_id = Some(Stored {
value: id,
ref_count,
});
id
});
#[cfg(feature = "trace")]
match device.trace {
Some(ref trace) => trace.lock().add(Action::GetSwapChainTexture {
id: view_id,
parent_id: swap_chain_id,
}),
None => (),
};
SwapChainOutput { status, view_id }
}
pub fn swap_chain_present<B: GfxBackend>(&self, swap_chain_id: SwapChainId) {
let hub = B::hub(self);
let mut token = Token::root();
let (mut surface_guard, mut token) = self.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];
#[cfg(feature = "trace")]
match device.trace {
Some(ref trace) => trace.lock().add(Action::PresentSwapChain(swap_chain_id)),
None => (),
};
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 = match view.inner {
resource::TextureViewInner::Native { .. } => unreachable!(),
resource::TextureViewInner::SwapChain { image, .. } => image,
};
let err = {
let sem = if sc.active_submission_index > device.last_completed_submission_index() {
Some(&sc.semaphore)
} else {
None
};
let queue = &mut device.queue_group.queues[0];
unsafe { queue.present_surface(B::get_surface_mut(surface), image, sem) }
};
if let Err(e) = err {
log::warn!("present failed: {:?}", e);
}
for fbo in sc.acquired_framebuffers.drain(..) {
unsafe {
device.raw.destroy_framebuffer(fbo);
}
}
}
}