use super::extra_ffi;
use crate::backends::metal::RafxTextureMetal;
use crate::metal::{RafxDeviceContextMetal, RafxFenceMetal, RafxRawImageMetal, RafxSemaphoreMetal};
use crate::{
RafxExtents3D, RafxFormat, RafxResourceType, RafxResult, RafxSampleCount,
RafxSwapchainColorSpace, RafxSwapchainDef, RafxSwapchainImage, RafxTexture, RafxTextureDef,
RafxTextureDimensions,
};
use core_graphics_types::geometry::CGSize;
use rafx_base::trust_cell::TrustCell;
use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle};
const SWAPCHAIN_IMAGE_COUNT: u32 = 3;
#[derive(Clone)]
pub struct RafxSwapchainMetalEdrInfo {
pub max_edr_color_component_value: f32,
pub max_potential_edr_color_component_value: f32,
pub max_reference_edr_color_component_value: f32,
}
impl RafxSwapchainMetalEdrInfo {
fn new(window: &extra_ffi::NSWindowWrapper) -> Self {
RafxSwapchainMetalEdrInfo {
max_edr_color_component_value: window.max_edr_color_component_value(),
max_potential_edr_color_component_value: window
.max_potential_edr_color_component_value(),
max_reference_edr_color_component_value: window
.max_reference_edr_color_component_value(),
}
}
}
pub struct RafxSwapchainMetal {
device_context: RafxDeviceContextMetal,
window: extra_ffi::NSWindowWrapper,
layer: metal_rs::MetalLayer,
drawable: TrustCell<Option<metal_rs::MetalDrawable>>,
swapchain_def: RafxSwapchainDef,
format: RafxFormat,
color_space: RafxSwapchainColorSpace,
next_swapchain_image_index: u32,
edr_info: RafxSwapchainMetalEdrInfo,
}
unsafe impl Send for RafxSwapchainMetal {}
unsafe impl Sync for RafxSwapchainMetal {}
impl RafxSwapchainMetal {
pub fn swapchain_def(&self) -> &RafxSwapchainDef {
&self.swapchain_def
}
pub fn image_count(&self) -> usize {
SWAPCHAIN_IMAGE_COUNT as usize
}
pub fn format(&self) -> RafxFormat {
self.format
}
pub fn color_space(&self) -> RafxSwapchainColorSpace {
self.color_space
}
pub fn metal_layer(&self) -> &metal_rs::MetalLayerRef {
self.layer.as_ref()
}
pub(crate) fn take_drawable(&self) -> Option<metal_rs::MetalDrawable> {
self.drawable.borrow_mut().take()
}
pub fn edr_info(&self) -> &RafxSwapchainMetalEdrInfo {
&self.edr_info
}
pub fn new(
device_context: &RafxDeviceContextMetal,
_raw_display_handle: &dyn HasRawDisplayHandle,
raw_window_handle: &dyn HasRawWindowHandle,
swapchain_def: &RafxSwapchainDef,
) -> RafxResult<RafxSwapchainMetal> {
let (window, _view, layer) = match raw_window_handle.raw_window_handle() {
#[cfg(target_os = "macos")]
raw_window_handle::RawWindowHandle::AppKit(handle) => unsafe {
(
handle.ns_window,
handle.ns_view,
raw_window_metal::appkit::metal_layer_from_handle(handle),
)
},
#[cfg(target_os = "ios")]
raw_window_handle::RawWindowHandle::IOS(handle) => unsafe {
(
handle.ns_window,
handle.ns_view,
raw_window_metal::ios::metal_layer_from_handle(handle),
)
},
_ => return Err("Cannot create RafxSurfaceMetal on this operating system".into()),
};
let layer = match layer {
raw_window_metal::Layer::Allocated(x) => Some(x),
raw_window_metal::Layer::Existing(x) => Some(x),
raw_window_metal::Layer::None => None,
}
.unwrap();
let layer = unsafe { std::mem::transmute::<_, &metal_rs::MetalLayerRef>(layer).to_owned() };
let window = extra_ffi::NSWindowWrapper::new(window as *mut objc::runtime::Object);
let preferred_color_space = swapchain_def.color_space_priority[0];
let (pixel_format, swapchain_format, is_extended) = match preferred_color_space {
RafxSwapchainColorSpace::Srgb => (
metal_rs::MTLPixelFormat::BGRA8Unorm_sRGB,
RafxFormat::B8G8R8A8_SRGB,
false,
),
RafxSwapchainColorSpace::SrgbExtended | RafxSwapchainColorSpace::DisplayP3Extended => (
metal_rs::MTLPixelFormat::RGBA16Float,
RafxFormat::R16G16B16A16_SFLOAT,
true,
),
};
let preferred_color_space = swapchain_def.color_space_priority[0];
let color_space = core_graphics::color_space::CGColorSpace::create_with_name(
preferred_color_space.into(),
)
.unwrap();
layer.set_device(device_context.device());
layer.set_pixel_format(pixel_format);
layer.set_presents_with_transaction(false);
layer.set_display_sync_enabled(swapchain_def.enable_vsync);
layer.set_wants_extended_dynamic_range_content(is_extended);
extra_ffi::set_colorspace(&layer, &color_space);
layer.set_drawable_size(CGSize::new(
swapchain_def.width as f64,
swapchain_def.height as f64,
));
let swapchain_def = swapchain_def.clone();
let edr_info = RafxSwapchainMetalEdrInfo::new(&window);
Ok(RafxSwapchainMetal {
device_context: device_context.clone(),
window,
layer,
drawable: Default::default(),
swapchain_def,
next_swapchain_image_index: 0,
format: swapchain_format,
color_space: preferred_color_space,
edr_info,
})
}
pub fn rebuild(
&mut self,
swapchain_def: &RafxSwapchainDef,
) -> RafxResult<()> {
self.layer.set_drawable_size(CGSize::new(
swapchain_def.width as f64,
swapchain_def.height as f64,
));
self.swapchain_def = swapchain_def.clone();
Ok(())
}
pub fn acquire_next_image_fence(
&mut self,
_fence: &RafxFenceMetal,
) -> RafxResult<RafxSwapchainImage> {
self.acquire_next_image()
}
pub fn acquire_next_image_semaphore(
&mut self,
_semaphore: &RafxSemaphoreMetal,
) -> RafxResult<RafxSwapchainImage> {
self.acquire_next_image()
}
pub fn acquire_next_image(&mut self) -> RafxResult<RafxSwapchainImage> {
objc::rc::autoreleasepool(|| {
let drawable = self
.layer
.next_drawable()
.ok_or("Timed out while trying to acquire drawable".to_string())?;
let mut old_drawable = self.drawable.borrow_mut();
assert!(old_drawable.is_none());
*old_drawable = Some(drawable.to_owned());
let raw_image = RafxRawImageMetal::Ref(drawable.texture().to_owned());
let resource_type = RafxResourceType::TEXTURE | RafxResourceType::RENDER_TARGET_COLOR;
let image = RafxTextureMetal::from_existing(
&self.device_context,
Some(raw_image),
&RafxTextureDef {
extents: RafxExtents3D {
width: self.swapchain_def.width,
height: self.swapchain_def.height,
depth: 1,
},
array_length: 1,
mip_count: 1,
format: self.format,
resource_type,
sample_count: RafxSampleCount::SampleCount1,
dimensions: RafxTextureDimensions::Dim2D,
},
)?;
let swapchain_image_index = self.next_swapchain_image_index;
self.next_swapchain_image_index += 1;
if self.next_swapchain_image_index >= SWAPCHAIN_IMAGE_COUNT {
self.next_swapchain_image_index = 0;
}
self.edr_info = RafxSwapchainMetalEdrInfo::new(&self.window);
Ok(RafxSwapchainImage {
texture: RafxTexture::Metal(image),
swapchain_image_index,
})
})
}
}