use core::ffi::c_void;
use std::cell::{Cell, RefCell};
use std::rc::Rc;
use std::sync::Arc;
use std::time::{Duration, Instant};
use alloc::boxed::Box;
use alloc::vec;
use executor_core::spawn_local;
#[cfg(any(target_os = "macos", target_os = "ios"))]
use {
objc2::{rc::Retained, runtime::ProtocolObject},
objc2_metal::{MTLPixelFormat, MTLTexture, MTLTextureType},
wgpu_hal::{Api, api::Metal as MetalApi},
};
use waterui_graphics::gpu_surface::{
GestureState, GpuContext, GpuFrame, GpuSurface, PointerState, RedrawHandle,
};
use waterui_graphics::shared_context::{GpuRuntime, GpuSubmissionCompletionDriver};
use crate::components::layouting::layout::{WuiProposalSize, WuiViewDimensions};
use crate::{IntoFFI, IntoRust};
#[repr(C)]
#[derive(Debug)]
pub struct WuiGpuSurface {
pub surface: *mut c_void,
pub has_picture_in_picture_host_id: bool,
pub picture_in_picture_host_id: u64,
}
impl IntoFFI for GpuSurface {
type FFI = WuiGpuSurface;
fn into_ffi(self) -> Self::FFI {
let boxed = Box::new(self);
let picture_in_picture_host_id = boxed.picture_in_picture_host();
let ptr = Box::into_raw(boxed).cast::<c_void>();
WuiGpuSurface {
surface: ptr,
has_picture_in_picture_host_id: picture_in_picture_host_id.is_some(),
picture_in_picture_host_id: picture_in_picture_host_id.unwrap_or(0),
}
}
}
ffi_view!(GpuSurface, WuiGpuSurface, gpu_surface);
pub struct WuiGpuSurfaceState {
runtime: GpuRuntime,
wgpu_surface: Option<wgpu::Surface<'static>>,
config: Option<wgpu::SurfaceConfiguration>,
renderer_format: Cell<Option<wgpu::TextureFormat>>,
setup_ready: Rc<Cell<bool>>,
msaa_max_samples: core::num::NonZeroU32,
semantic: Rc<RefCell<Option<GpuSurfaceSemantic>>>,
priority: i32,
current_width: u32,
current_height: u32,
pointer_state: PointerState,
gesture_state: GestureState,
start_time: Instant,
last_frame_time: Instant,
redraw_handle: RedrawHandle,
}
impl core::fmt::Debug for WuiGpuSurfaceState {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("WuiGpuSurfaceState")
.field("current_width", &self.current_width)
.field("current_height", &self.current_height)
.finish_non_exhaustive()
}
}
struct GpuSurfaceSemantic {
gpu_surface: GpuSurface,
env: waterui::Environment,
}
#[derive(Debug)]
pub struct WuiGpuCaptureFence {
completion_driver: GpuSubmissionCompletionDriver,
submission: wgpu::SubmissionIndex,
}
pub type WuiGpuCaptureCompletionCallback = unsafe extern "C" fn(context: *mut c_void);
pub type WuiGpuCaptureCompletionDrop = unsafe extern "C" fn(context: *mut c_void);
struct ForeignGpuCaptureCompletion {
context: usize,
callback: WuiGpuCaptureCompletionCallback,
drop: WuiGpuCaptureCompletionDrop,
}
impl ForeignGpuCaptureCompletion {
fn complete(self) {
unsafe { (self.callback)(self.context as *mut c_void) };
}
}
impl Drop for ForeignGpuCaptureCompletion {
fn drop(&mut self) {
unsafe { (self.drop)(self.context as *mut c_void) };
}
}
fn advance_frame_timing(state: &mut WuiGpuSurfaceState) -> (Duration, Duration) {
let now = Instant::now();
let elapsed = now.duration_since(state.start_time);
let delta = now
.duration_since(state.last_frame_time)
.min(Duration::from_millis(100));
state.last_frame_time = now;
(elapsed, delta)
}
pub type WuiGpuSurfaceRedrawCallback = unsafe extern "C" fn(context: *mut c_void);
struct ForeignRedrawTarget {
context: usize,
wake: WuiGpuSurfaceRedrawCallback,
drop: WuiGpuSurfaceRedrawCallback,
}
unsafe impl Send for ForeignRedrawTarget {}
unsafe impl Sync for ForeignRedrawTarget {}
impl ForeignRedrawTarget {
fn wake(&self) {
unsafe { (self.wake)(self.context as *mut c_void) };
}
}
impl Drop for ForeignRedrawTarget {
fn drop(&mut self) {
unsafe { (self.drop)(self.context as *mut c_void) };
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct WuiGpuSurfaceHdrPreference {
pub has_preference: bool,
pub prefers_hdr: bool,
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn waterui_gpu_surface_hdr_preference(
surface: *const WuiGpuSurface,
) -> WuiGpuSurfaceHdrPreference {
let wui_surface = unsafe { &*surface };
let gpu_surface = unsafe { &*(wui_surface.surface as *const GpuSurface) };
let explicit = gpu_surface.resolved_hdr_preference();
WuiGpuSurfaceHdrPreference {
has_preference: explicit.is_some(),
prefers_hdr: explicit.unwrap_or(false),
}
}
fn attached_surface_format(
capabilities: &wgpu::SurfaceCapabilities,
renderer_format: Option<wgpu::TextureFormat>,
prefer_hdr: bool,
) -> wgpu::TextureFormat {
renderer_format.map_or_else(
|| {
waterui_graphics::gpu_surface::preferred_surface_format_with_preference(
capabilities,
prefer_hdr,
)
},
|format| {
assert!(
capabilities.formats.contains(&format),
"waterui_gpu_surface_attach: replacement surface does not support the renderer's established format {format:?}"
);
format
},
)
}
fn create_attached_surface(
runtime: &GpuRuntime,
layer: *mut c_void,
width: u32,
height: u32,
renderer_format: Option<wgpu::TextureFormat>,
prefer_hdr: bool,
) -> (wgpu::Surface<'static>, wgpu::SurfaceConfiguration) {
assert!(
width > 0 && height > 0,
"waterui_gpu_surface_attach: native surface dimensions must be non-zero, got {width}x{height}"
);
let gpu = runtime.context();
let wgpu_surface = create_surface_from_layer(&gpu.instance, layer);
let surface_caps = wgpu_surface.get_capabilities(&gpu.adapter);
let format = attached_surface_format(&surface_caps, renderer_format, prefer_hdr);
assert!(
surface_caps
.present_modes
.contains(&wgpu::PresentMode::Fifo),
"waterui_gpu_surface_attach: surface does not support FIFO presentation"
);
let alpha_mode = [
wgpu::CompositeAlphaMode::PreMultiplied,
wgpu::CompositeAlphaMode::PostMultiplied,
wgpu::CompositeAlphaMode::Inherit,
wgpu::CompositeAlphaMode::Opaque,
]
.into_iter()
.find(|mode| surface_caps.alpha_modes.contains(mode))
.unwrap_or_else(|| {
panic!("waterui_gpu_surface_attach: surface reports no supported composite alpha mode")
});
let config = wgpu::SurfaceConfiguration {
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
format,
width,
height,
present_mode: wgpu::PresentMode::Fifo,
alpha_mode,
view_formats: vec![],
desired_maximum_frame_latency: 2,
};
super::checked_surface_configure(
&wgpu_surface,
&gpu.device,
&config,
"waterui_gpu_surface_attach",
);
(wgpu_surface, config)
}
fn start_renderer_setup(state: &WuiGpuSurfaceState, format: wgpu::TextureFormat) {
if let Some(existing) = state.renderer_format.get() {
assert_eq!(
existing, format,
"GpuSurface target format changed after renderer setup started"
);
return;
}
state.renderer_format.set(Some(format));
let mut semantic = state
.semantic
.borrow_mut()
.take()
.expect("GpuSurface semantic renderer is unavailable before setup starts");
let semantic_slot = Rc::clone(&state.semantic);
let setup_ready = Rc::clone(&state.setup_ready);
let runtime = state.runtime.clone();
let redraw_handle = state.redraw_handle.clone();
let msaa_max_samples = state.msaa_max_samples;
spawn_local(async move {
let gpu = runtime.context();
let ctx = GpuContext::new(
&gpu.adapter,
&gpu.device,
&gpu.queue,
format,
gpu.shader_cache.as_ref(),
gpu.scene_renderer(),
msaa_max_samples,
redraw_handle.clone(),
);
let GpuSurfaceSemantic { gpu_surface, env } = &mut semantic;
gpu_surface.setup(&ctx, env).await;
semantic_slot.replace(Some(semantic));
setup_ready.set(true);
redraw_handle.request_redraw();
})
.detach();
}
fn with_semantic_mut<T>(
state: &WuiGpuSurfaceState,
use_semantic: impl FnOnce(&mut GpuSurfaceSemantic) -> T,
) -> T {
assert!(
state.setup_ready.get(),
"GpuSurface renderer used before asynchronous setup completed"
);
let mut semantic = state.semantic.borrow_mut();
use_semantic(
semantic
.as_mut()
.expect("GpuSurface ready state is missing its semantic renderer"),
)
}
fn attached_surface<'a>(
state: &'a WuiGpuSurfaceState,
scope: &'static str,
) -> &'a wgpu::Surface<'static> {
state
.wgpu_surface
.as_ref()
.unwrap_or_else(|| panic!("{scope}: native surface is detached"))
}
fn attached_config<'a>(
state: &'a WuiGpuSurfaceState,
scope: &'static str,
) -> &'a wgpu::SurfaceConfiguration {
state
.config
.as_ref()
.unwrap_or_else(|| panic!("{scope}: native surface is detached"))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn waterui_gpu_surface_create(
surface: *mut WuiGpuSurface,
env: *const crate::WuiEnv,
) -> *mut WuiGpuSurfaceState {
let wui_surface = unsafe { &mut *surface };
assert!(
!wui_surface.surface.is_null(),
"waterui_gpu_surface_create: descriptor was already consumed"
);
let gpu_surface: GpuSurface =
unsafe { *Box::from_raw(wui_surface.surface.cast::<GpuSurface>()) };
wui_surface.surface = core::ptr::null_mut();
let msaa_max_samples = gpu_surface.msaa_sample_limit();
let priority = gpu_surface.priority();
let env = unsafe { &*env }.0.clone();
let runtime = super::gpu_runtime::gpu_runtime(&env);
let now = Instant::now();
Box::into_raw(Box::new(WuiGpuSurfaceState {
runtime,
wgpu_surface: None,
renderer_format: Cell::new(None),
setup_ready: Rc::new(Cell::new(false)),
msaa_max_samples,
config: None,
semantic: Rc::new(RefCell::new(Some(GpuSurfaceSemantic { gpu_surface, env }))),
priority,
current_width: 0,
current_height: 0,
pointer_state: PointerState::default(),
gesture_state: GestureState::default(),
start_time: now,
last_frame_time: now
.checked_sub(Duration::from_secs_f32(1.0 / 60.0))
.unwrap(),
redraw_handle: RedrawHandle::new(),
}))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn waterui_gpu_surface_measure(
state: *const WuiGpuSurfaceState,
proposal: WuiProposalSize,
) -> WuiViewDimensions {
let state = unsafe { crate::borrow_ffi(state) };
measure_state(state, unsafe { proposal.into_rust() }).into_ffi()
}
pub(crate) fn measure_state(
state: &WuiGpuSurfaceState,
proposal: waterui_core::layout::ProposalSize,
) -> waterui_core::layout::ViewDimensions {
let semantic = state.semantic.borrow();
semantic.as_ref().map_or_else(
|| {
waterui_core::layout::ViewDimensions::new(waterui_core::layout::Size::new(
proposal.width.unwrap_or(0.0),
proposal.height.unwrap_or(0.0),
))
},
|semantic| semantic.gpu_surface.measure(proposal),
)
}
#[unsafe(no_mangle)]
pub const unsafe extern "C" fn waterui_gpu_surface_priority(
state: *const WuiGpuSurfaceState,
) -> i32 {
let state = unsafe { crate::borrow_ffi(state) };
priority_state(state)
}
pub(crate) const fn priority_state(state: &WuiGpuSurfaceState) -> i32 {
state.priority
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn waterui_gpu_surface_attach(
state: *mut WuiGpuSurfaceState,
layer: *mut c_void,
width: u32,
height: u32,
prefers_hdr: bool,
) {
let state = unsafe { crate::borrow_ffi_mut(state) };
assert!(
state.wgpu_surface.is_none(),
"waterui_gpu_surface_attach: native surface is already attached"
);
let (surface, config) = create_attached_surface(
&state.runtime,
layer,
width,
height,
state.renderer_format.get(),
prefers_hdr,
);
state.current_width = width;
state.current_height = height;
let format = config.format;
state.config = Some(config);
state.wgpu_surface = Some(surface);
start_renderer_setup(state, format);
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn waterui_gpu_surface_detach(state: *mut WuiGpuSurfaceState) {
let state = unsafe { crate::borrow_ffi_mut(state) };
let surface = state
.wgpu_surface
.take()
.expect("waterui_gpu_surface_detach: native surface is already detached");
drop(surface);
state.config = None;
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn waterui_gpu_surface_set_redraw_callback(
state: *mut WuiGpuSurfaceState,
context: *mut c_void,
wake: WuiGpuSurfaceRedrawCallback,
drop_callback: WuiGpuSurfaceRedrawCallback,
) {
let state = unsafe { crate::borrow_ffi_mut(state) };
let target = ForeignRedrawTarget {
context: context as usize,
wake,
drop: drop_callback,
};
state
.redraw_handle
.set_waker(Some(Arc::new(move || target.wake())));
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn waterui_gpu_surface_is_ready(state: *const WuiGpuSurfaceState) -> bool {
let state = unsafe { crate::borrow_ffi(state) };
state.setup_ready.get()
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn waterui_gpu_surface_render(
state: *mut WuiGpuSurfaceState,
width: u32,
height: u32,
scale: f64,
) -> bool {
let state = unsafe { crate::borrow_ffi_mut(state) };
assert!(
width > 0 && height > 0,
"waterui_gpu_surface_render: dimensions must be non-zero"
);
assert!(
scale.is_finite() && scale > 0.0,
"waterui_gpu_surface_render: scale must be a positive, finite device-pixel ratio, got {scale}"
);
if width != state.current_width || height != state.current_height {
{
let config = state
.config
.as_mut()
.expect("waterui_gpu_surface_render: native surface is detached");
config.width = width;
config.height = height;
}
super::checked_surface_configure(
attached_surface(state, "waterui_gpu_surface_render"),
&state.runtime.context().device,
attached_config(state, "waterui_gpu_surface_render"),
"waterui_gpu_surface_render",
);
state.current_width = width;
state.current_height = height;
}
let format = attached_config(state, "waterui_gpu_surface_render").format;
assert!(
state.setup_ready.get(),
"waterui_gpu_surface_render called before asynchronous setup completed"
);
let Some(output) = super::acquire_surface_texture(
attached_surface(state, "waterui_gpu_surface_render"),
&state.runtime.context().device,
attached_config(state, "waterui_gpu_surface_render"),
"waterui_gpu_surface_render",
) else {
return false;
};
let view = output.texture.create_view(&wgpu::TextureViewDescriptor {
label: Some("GpuSurface Frame View"),
format: Some(format),
..Default::default()
});
let (elapsed, delta) = advance_frame_timing(state);
let mut frame = GpuFrame::new(
&state.runtime.context().device,
&state.runtime.context().queue,
&output.texture,
view,
format,
width,
height,
scale,
state.pointer_state,
state.gesture_state,
elapsed,
delta,
);
let _ = state.redraw_handle.take_dirty();
with_semantic_mut(state, |semantic| {
semantic.gpu_surface.render(&mut frame);
});
let needs_redraw = frame.was_redraw_requested() || state.redraw_handle.take_dirty();
output.present();
needs_redraw
}
#[cfg(any(target_os = "macos", target_os = "ios"))]
fn metal_texture_format(texture: &ProtocolObject<dyn MTLTexture>) -> wgpu::TextureFormat {
match texture.pixelFormat() {
MTLPixelFormat::BGRA8Unorm => wgpu::TextureFormat::Bgra8Unorm,
MTLPixelFormat::BGRA8Unorm_sRGB => wgpu::TextureFormat::Bgra8UnormSrgb,
MTLPixelFormat::RGBA16Float => wgpu::TextureFormat::Rgba16Float,
other => panic!("GpuSurface external Metal texture has unsupported format {other:?}"),
}
}
#[cfg(any(target_os = "macos", target_os = "ios"))]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn waterui_gpu_surface_prepare_metal_texture(
state: *mut WuiGpuSurfaceState,
texture: *mut c_void,
) {
let state = unsafe { crate::borrow_ffi_mut(state) };
let texture = unsafe { &*texture.cast::<ProtocolObject<dyn MTLTexture>>() };
start_renderer_setup(state, metal_texture_format(texture));
}
#[cfg(any(target_os = "macos", target_os = "ios"))]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn waterui_gpu_surface_render_to_metal_texture(
state: *mut WuiGpuSurfaceState,
texture: *mut core::ffi::c_void,
width: u32,
height: u32,
scale: f64,
) -> *mut WuiGpuCaptureFence {
assert!(
scale.is_finite() && scale > 0.0,
"waterui_gpu_surface_render_to_metal_texture: scale must be a positive, finite device-pixel ratio, got {scale}"
);
let state = unsafe { &mut *state };
let metal_texture = unsafe {
Retained::<ProtocolObject<dyn MTLTexture>>::retain(texture.cast())
.expect("waterui_gpu_surface_render_to_metal_texture received a null texture")
};
let target_format = metal_texture_format(&metal_texture);
assert_eq!(
state.renderer_format.get(),
Some(target_format),
"waterui_gpu_surface_render_to_metal_texture called before preparing this target format"
);
assert!(
state.setup_ready.get(),
"waterui_gpu_surface_render_to_metal_texture called before asynchronous setup completed"
);
let hal_texture = unsafe {
<MetalApi as Api>::Device::texture_from_raw(
metal_texture,
target_format,
MTLTextureType::Type2D,
1,
1,
wgpu_hal::CopyExtent {
width,
height,
depth: 1,
},
)
};
let texture_desc = wgpu::TextureDescriptor {
label: Some("GpuSurface Imported Metal Texture"),
size: wgpu::Extent3d {
width,
height,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: target_format,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
view_formats: &[],
};
let wgpu_texture = unsafe {
state
.runtime
.context()
.device
.create_texture_from_hal::<MetalApi>(hal_texture, &texture_desc)
};
let view = wgpu_texture.create_view(&wgpu::TextureViewDescriptor {
label: Some("GpuSurface Metal Frame View"),
format: Some(target_format),
..Default::default()
});
let (elapsed, delta) = advance_frame_timing(state);
let mut frame = GpuFrame::new(
&state.runtime.context().device,
&state.runtime.context().queue,
&wgpu_texture,
view,
target_format,
width,
height,
scale,
state.pointer_state,
state.gesture_state,
elapsed,
delta,
);
let _ = state.redraw_handle.take_dirty();
with_semantic_mut(state, |semantic| {
semantic.gpu_surface.render(&mut frame);
});
if frame.was_redraw_requested() || state.redraw_handle.take_dirty() {
state.redraw_handle.request_redraw();
}
let submission = state.runtime.context().queue.submit([]);
Box::into_raw(Box::new(WuiGpuCaptureFence {
completion_driver: state.runtime.context().submission_completion_driver(),
submission,
}))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn waterui_gpu_capture_fence_on_complete(
fence: *mut WuiGpuCaptureFence,
context: *mut c_void,
callback: WuiGpuCaptureCompletionCallback,
drop: WuiGpuCaptureCompletionDrop,
) {
let fence = unsafe { Box::from_raw(fence) };
let completion = ForeignGpuCaptureCompletion {
context: context as usize,
callback,
drop,
};
fence
.completion_driver
.on_complete(fence.submission, move || completion.complete());
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn waterui_gpu_surface_drop(state: *mut WuiGpuSurfaceState) {
unsafe {
let state = Box::from_raw(state);
state.redraw_handle.set_waker(None);
}
}
#[repr(C)]
#[derive(Debug)]
pub struct WuiPointerState {
pub has_position: bool,
pub x: f32,
pub y: f32,
pub has_hit: bool,
pub hit_x: f32,
pub hit_y: f32,
}
#[inline]
const fn pointer_state_from_ffi(pointer: &WuiPointerState) -> PointerState {
PointerState {
position: if pointer.has_position {
Some(waterui_core::layout::Point::new(pointer.x, pointer.y))
} else {
None
},
hit: if pointer.has_hit {
Some(waterui_core::layout::Point::new(
pointer.hit_x,
pointer.hit_y,
))
} else {
None
},
}
}
#[repr(C)]
#[derive(Debug)]
pub struct WuiGestureState {
pub active: bool,
pub pinch_scale: f32,
pub has_pinch_center: bool,
pub pinch_center_x: f32,
pub pinch_center_y: f32,
pub pan_offset_x: f32,
pub pan_offset_y: f32,
pub double_tap: bool,
}
#[inline]
const fn gesture_state_from_ffi(gesture: &WuiGestureState) -> GestureState {
GestureState {
pinch_scale: gesture.pinch_scale,
pinch_center: if gesture.has_pinch_center {
Some(waterui_core::layout::Point::new(
gesture.pinch_center_x,
gesture.pinch_center_y,
))
} else {
None
},
pan_offset: waterui_core::layout::Point::new(gesture.pan_offset_x, gesture.pan_offset_y),
double_tap: gesture.double_tap,
active: gesture.active,
}
}
#[repr(C)]
#[derive(Debug)]
pub struct WuiGpuSurfaceInput {
pub pointer: WuiPointerState,
pub gesture: WuiGestureState,
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn waterui_gpu_surface_set_input(
state: *mut WuiGpuSurfaceState,
input: WuiGpuSurfaceInput,
) {
let state = unsafe { crate::borrow_ffi_mut(state) };
state.pointer_state = pointer_state_from_ffi(&input.pointer);
state.gesture_state = gesture_state_from_ffi(&input.gesture);
}
#[cfg(any(target_os = "macos", target_os = "ios"))]
pub(crate) fn create_surface_from_layer(
instance: &wgpu::Instance,
layer: *mut c_void,
) -> wgpu::Surface<'static> {
unsafe {
instance
.create_surface_unsafe(wgpu::SurfaceTargetUnsafe::CoreAnimationLayer(layer))
.expect("failed to create wgpu surface from CAMetalLayer")
}
}
#[cfg(target_os = "android")]
pub(crate) fn create_surface_from_layer(
instance: &wgpu::Instance,
layer: *mut c_void,
) -> wgpu::Surface<'static> {
use raw_window_handle::{AndroidNdkWindowHandle, RawWindowHandle};
use std::ptr::NonNull;
let window_ptr = NonNull::new(layer).expect("ANativeWindow pointer must be non-null");
let handle = AndroidNdkWindowHandle::new(window_ptr);
unsafe {
instance
.create_surface_unsafe(wgpu::SurfaceTargetUnsafe::RawHandle {
raw_display_handle: Some(raw_window_handle::RawDisplayHandle::Android(
raw_window_handle::AndroidDisplayHandle::new(),
)),
raw_window_handle: RawWindowHandle::AndroidNdk(handle),
})
.expect("failed to create wgpu surface from ANativeWindow")
}
}
#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "android")))]
pub(crate) fn create_surface_from_layer(
_instance: &wgpu::Instance,
_layer: *mut c_void,
) -> wgpu::Surface<'static> {
panic!("native GpuSurface presentation is unsupported on this platform")
}
#[cfg(test)]
mod tests {
use super::*;
fn surface_capabilities() -> wgpu::SurfaceCapabilities {
wgpu::SurfaceCapabilities {
formats: vec![
wgpu::TextureFormat::Bgra8UnormSrgb,
wgpu::TextureFormat::Rgba16Float,
],
..wgpu::SurfaceCapabilities::default()
}
}
#[test]
fn first_surface_uses_dynamic_range_preference() {
let capabilities = surface_capabilities();
assert_eq!(
attached_surface_format(&capabilities, None, false),
wgpu::TextureFormat::Bgra8UnormSrgb
);
assert_eq!(
attached_surface_format(&capabilities, None, true),
wgpu::TextureFormat::Rgba16Float
);
}
#[test]
fn replacement_surface_reuses_established_renderer_format() {
let capabilities = surface_capabilities();
assert_eq!(
attached_surface_format(&capabilities, Some(wgpu::TextureFormat::Rgba16Float), false,),
wgpu::TextureFormat::Rgba16Float
);
}
#[test]
#[should_panic(
expected = "replacement surface does not support the renderer's established format"
)]
fn replacement_surface_rejects_unsupported_renderer_format() {
let capabilities = wgpu::SurfaceCapabilities {
formats: vec![wgpu::TextureFormat::Bgra8UnormSrgb],
..wgpu::SurfaceCapabilities::default()
};
let _ =
attached_surface_format(&capabilities, Some(wgpu::TextureFormat::Rgba16Float), true);
}
}