Revision control

Copy as Markdown

Other Tools

use alloc::borrow::ToOwned as _;
use objc2::{
available,
rc::{autoreleasepool, Retained},
runtime::ProtocolObject,
ClassType, Message,
};
use objc2_core_foundation::CGSize;
use objc2_foundation::NSObjectProtocol;
use objc2_metal::MTLTextureType;
use objc2_quartz_core::{CAMetalDrawable, CAMetalLayer};
use parking_lot::{Mutex, RwLock};
use super::OsFeatures;
impl super::Surface {
pub fn new(layer: Retained<CAMetalLayer>) -> Self {
Self {
render_layer: Mutex::new(layer),
swapchain_format: RwLock::new(None),
extent: RwLock::new(wgt::Extent3d::default()),
}
}
pub fn from_layer(layer: &CAMetalLayer) -> Self {
assert!(layer.isKindOfClass(CAMetalLayer::class()));
Self::new(layer.retain())
}
pub fn render_layer(&self) -> &Mutex<Retained<CAMetalLayer>> {
&self.render_layer
}
/// Gets the current dimensions of the `Surface`.
///
/// This function is safe to call off of the main thread. However, note that
/// `bounds` and `contentsScale` may be modified by the main thread while
/// this function is running, possibly resulting in the two values being out
/// of sync. This is sound, as these properties are accessed atomically.
pub(super) fn dimensions(&self) -> wgt::Extent3d {
let (size, scale) = {
let render_layer = self.render_layer.lock();
let bounds = render_layer.bounds();
let contents_scale = render_layer.contentsScale();
(bounds.size, contents_scale)
};
wgt::Extent3d {
width: (size.width * scale) as u32,
height: (size.height * scale) as u32,
depth_or_array_layers: 1,
}
}
}
impl crate::Surface for super::Surface {
type A = super::Api;
unsafe fn configure(
&self,
device: &super::Device,
config: &crate::SurfaceConfiguration,
) -> Result<(), crate::SurfaceError> {
log::debug!("build swapchain {config:?}");
let caps = &device.shared.private_caps;
*self.swapchain_format.write() = Some(config.format);
*self.extent.write() = config.extent;
let render_layer = self.render_layer.lock();
let framebuffer_only = config.usage == wgt::TextureUses::COLOR_TARGET;
let display_sync = match config.present_mode {
wgt::PresentMode::Fifo => true,
wgt::PresentMode::Immediate => false,
m => unreachable!("Unsupported present mode: {m:?}"),
};
let drawable_size = CGSize::new(config.extent.width as f64, config.extent.height as f64);
match config.composite_alpha_mode {
wgt::CompositeAlphaMode::Opaque => render_layer.setOpaque(true),
wgt::CompositeAlphaMode::PostMultiplied => render_layer.setOpaque(false),
_ => (),
}
let device_raw = &device.shared.device;
render_layer.setDevice(Some(device_raw));
render_layer.setPixelFormat(caps.map_format(config.format));
render_layer.setFramebufferOnly(framebuffer_only);
// opt-in to Metal EDR
// EDR potentially more power used in display and more bandwidth, memory footprint.
let wants_edr = config.format == wgt::TextureFormat::Rgba16Float;
if wants_edr != render_layer.wantsExtendedDynamicRangeContent() {
render_layer.setWantsExtendedDynamicRangeContent(wants_edr);
}
// this gets ignored on iOS for certain OS/device combinations (iphone5s iOS 10.3)
render_layer.setMaximumDrawableCount(config.maximum_frame_latency as usize + 1);
render_layer.setDrawableSize(drawable_size);
if available!(macos = 10.13, ios = 11.0, tvos = 11.0, visionos = 1.0) {
render_layer.setAllowsNextDrawableTimeout(false);
}
if OsFeatures::display_sync() {
render_layer.setDisplaySyncEnabled(display_sync);
}
Ok(())
}
unsafe fn unconfigure(&self, _device: &super::Device) {
*self.swapchain_format.write() = None;
}
unsafe fn acquire_texture(
&self,
_timeout_ms: Option<core::time::Duration>, //TODO
_fence: &super::Fence,
) -> Result<Option<crate::AcquiredSurfaceTexture<super::Api>>, crate::SurfaceError> {
let render_layer = self.render_layer.lock();
let (drawable, texture) = match autoreleasepool(|_| {
render_layer
.nextDrawable()
.map(|drawable| (drawable.to_owned(), drawable.texture().to_owned()))
}) {
Some(pair) => pair,
None => return Ok(None),
};
let swapchain_format = self.swapchain_format.read().unwrap();
let extent = self.extent.read();
let suf_texture = super::SurfaceTexture {
texture: super::Texture {
raw: texture,
format: swapchain_format,
raw_type: MTLTextureType::Type2D,
array_layers: 1,
mip_levels: 1,
copy_size: crate::CopyExtent {
width: extent.width,
height: extent.height,
depth: 1,
},
},
drawable: ProtocolObject::from_retained(drawable),
present_with_transaction: render_layer.presentsWithTransaction(),
};
Ok(Some(crate::AcquiredSurfaceTexture {
texture: suf_texture,
suboptimal: false,
}))
}
unsafe fn discard_texture(&self, _texture: super::SurfaceTexture) {}
}