#[cfg(feature = "gpu")]
pub mod applied_filter;
#[cfg(all(target_os = "android", feature = "gpu"))]
pub mod capture_composite;
#[cfg(feature = "gpu")]
pub mod capture_format;
#[cfg(feature = "gpu")]
pub mod gpu_runtime;
#[cfg(feature = "gpu")]
pub mod gpu_surface;
#[cfg(feature = "gpu")]
pub mod gpu_surface_input;
#[cfg(all(target_os = "android", feature = "gpu"))]
pub mod hardware_buffer;
pub mod picture;
#[cfg(feature = "gpu")]
pub mod view_effect;
pub mod view_renderer;
#[cfg(all(feature = "gpu", not(any(target_os = "macos", target_os = "ios"))))]
fn acquire_surface_texture(
surface: &wgpu::Surface<'_>,
gpu: &waterui_graphics::shared_context::SharedGpuContext,
config: &wgpu::SurfaceConfiguration,
context: &'static str,
) -> Option<wgpu::SurfaceTexture> {
let device = &gpu.device;
match checked_surface_acquire(surface, device) {
Ok(
wgpu::CurrentSurfaceTexture::Success(output)
| wgpu::CurrentSurfaceTexture::Suboptimal(output),
) => Some(output),
Ok(wgpu::CurrentSurfaceTexture::Occluded) => {
tracing::debug!(context, "surface is occluded; skipping frame");
None
}
Ok(wgpu::CurrentSurfaceTexture::Timeout) => panic!("{context}: surface timeout"),
Ok(
status @ (wgpu::CurrentSurfaceTexture::Lost
| wgpu::CurrentSurfaceTexture::Outdated
| wgpu::CurrentSurfaceTexture::Validation),
) => retry_surface_acquire(surface, gpu, config, context, format_args!("{status:?}")),
Err(error) => retry_surface_acquire(surface, gpu, config, context, format_args!("{error}")),
}
}
#[cfg(all(feature = "gpu", not(any(target_os = "macos", target_os = "ios"))))]
fn retry_surface_acquire(
surface: &wgpu::Surface<'_>,
gpu: &waterui_graphics::shared_context::SharedGpuContext,
config: &wgpu::SurfaceConfiguration,
context: &'static str,
first_failure: std::fmt::Arguments<'_>,
) -> Option<wgpu::SurfaceTexture> {
if let Some(reason) = gpu.device_lost_reason() {
tracing::debug!(context, reason, "GPU device lost; skipping the frame");
return None;
}
let device = &gpu.device;
tracing::debug!(
context,
"surface acquire failed ({first_failure}); reconfiguring and retrying"
);
checked_surface_configure(surface, device, config, context);
match checked_surface_acquire(surface, device) {
Ok(
wgpu::CurrentSurfaceTexture::Success(output)
| wgpu::CurrentSurfaceTexture::Suboptimal(output),
) => Some(output),
Ok(wgpu::CurrentSurfaceTexture::Occluded) => {
tracing::debug!(context, "surface is occluded; skipping frame");
None
}
Ok(status) => {
panic!("{context}: acquire after reconfigure failed: {status:?} with {config:?}")
}
Err(error) => {
panic!("{context}: acquire after reconfigure failed: {error} with {config:?}")
}
}
}
#[cfg(all(feature = "gpu", not(any(target_os = "macos", target_os = "ios"))))]
pub fn run_gpu_frame<T>(
gpu: &waterui_graphics::shared_context::SharedGpuContext,
scope: &'static str,
frame: impl FnOnce() -> T,
) -> Option<T> {
match std::panic::catch_unwind(std::panic::AssertUnwindSafe(frame)) {
Ok(done) => Some(done),
Err(payload) => {
if gpu.device_lost_reason().is_some() {
tracing::warn!(
scope,
"GPU device was lost mid-frame; dropping it and rebuilding on the next render"
);
None
} else {
std::panic::resume_unwind(payload)
}
}
}
}
#[cfg(all(feature = "gpu", not(any(target_os = "macos", target_os = "ios"))))]
fn checked_surface_acquire(
surface: &wgpu::Surface<'_>,
device: &wgpu::Device,
) -> Result<wgpu::CurrentSurfaceTexture, wgpu::Error> {
let scope = device.push_error_scope(wgpu::ErrorFilter::Validation);
let status = surface.get_current_texture();
pollster::block_on(scope.pop()).map_or_else(|| Ok(status), Err)
}
#[cfg(all(feature = "gpu", not(any(target_os = "macos", target_os = "ios"))))]
pub fn checked_surface_configure(
surface: &wgpu::Surface<'_>,
device: &wgpu::Device,
config: &wgpu::SurfaceConfiguration,
context: &'static str,
) {
let scope = device.push_error_scope(wgpu::ErrorFilter::Validation);
surface.configure(device, config);
if let Some(error) = pollster::block_on(scope.pop()) {
panic!("{context}: surface configure failed: {error} with {config:?}");
}
}