From cbaa7b95fb8d398ee4616568d73fd1f4e8c4db6f Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Tue, 17 Dec 2024 00:23:14 -0800 Subject: [PATCH 1/3] metal: Migrate from metal-rs to objc2 --- blade-graphics/Cargo.toml | 66 ++- blade-graphics/src/lib.rs | 1 - blade-graphics/src/metal/command.rs | 613 +++++++++++++++------------ blade-graphics/src/metal/mod.rs | 385 +++++++++-------- blade-graphics/src/metal/pipeline.rs | 215 +++++----- blade-graphics/src/metal/resource.rs | 330 +++++++------- blade-graphics/src/metal/surface.rs | 165 ++++--- 7 files changed, 975 insertions(+), 800 deletions(-) diff --git a/blade-graphics/Cargo.toml b/blade-graphics/Cargo.toml index 38b3745..3e4de39 100644 --- a/blade-graphics/Cargo.toml +++ b/blade-graphics/Cargo.toml @@ -21,15 +21,67 @@ raw-window-handle = "0.6" [target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies] block = "0.1" -core-graphics-types = "0.1" -#TODO: switch to crates once these are published: -# - https://github.com/gfx-rs/metal-rs/pull/335 -# - https://github.com/gfx-rs/metal-rs/pull/336 -# - https://github.com/gfx-rs/metal-rs/pull/337 -metal = { git = "https://github.com/gfx-rs/metal-rs", rev = "ef768ff9d742ae6a0f4e83ddc8031264e7d460c4" } -objc = "0.2.5" +objc2 = "0.5" +objc2-foundation = { version = "0.2", features = ["NSArray"] } +objc2-metal = { version = "0.2", features = [ + "MTLTypes", + "MTLPixelFormat", + "MTLResource", + "MTLBuffer", + "MTLTexture", + "MTLSampler", + "MTLDrawable", + "MTLAccelerationStructure", + "MTLAccelerationStructureTypes", + "MTLCounters", + "MTLLibrary", + "MTLStageInputOutputDescriptor", + "MTLComputePipeline", + "MTLVertexDescriptor", + "MTLDepthStencil", + "MTLComputePipeline", + "MTLRenderPipeline", + "MTLCommandBuffer", + "MTLCommandEncoder", + "MTLAccelerationStructureCommandEncoder", + "MTLBlitCommandEncoder", + "MTLComputeCommandEncoder", + "MTLRenderCommandEncoder", + "MTLBlitPass", + "MTLComputePass", + "MTLRenderPass", + "MTLCommandQueue", + "MTLDevice", + "MTLCaptureManager", + "MTLCaptureScope", + "block2", +] } +objc2-quartz-core = { version = "0.2", features = [ + "objc2-metal", + "CALayer", + "CAMetalLayer", +] } naga = { workspace = true, features = ["msl-out"] } +[target.'cfg(target_os = "macos")'.dependencies] +objc2-app-kit = { version = "0.2", features = [ + "objc2-quartz-core", + "NSResponder", + "NSView", + "NSWindow", +] } + +[target.'cfg(target_os = "ios")'.dependencies] +objc2-ui-kit = { version = "0.2", features = [ + "objc2-quartz-core", + "UIResponder", + "UIView", + "UIWindow", + "UIScene", + "UIWindowScene", + "UIScreen", +] } + [target.'cfg(any(vulkan, windows, target_os = "linux", target_os = "android", target_os = "freebsd"))'.dependencies] ash = "0.38" ash-window = "0.13" diff --git a/blade-graphics/src/lib.rs b/blade-graphics/src/lib.rs index 8c233ad..ff4c1e1 100644 --- a/blade-graphics/src/lib.rs +++ b/blade-graphics/src/lib.rs @@ -15,7 +15,6 @@ clippy::missing_safety_doc, )] #![warn( - trivial_casts, trivial_numeric_casts, unused_extern_crates, //TODO: re-enable. Currently doesn't like "mem::size_of" on newer Rust diff --git a/blade-graphics/src/metal/command.rs b/blade-graphics/src/metal/command.rs index 42b1296..e612e49 100644 --- a/blade-graphics/src/metal/command.rs +++ b/blade-graphics/src/metal/command.rs @@ -1,18 +1,26 @@ -use std::{marker::PhantomData, mem, ops::Range, time::Duration}; +use objc2_foundation::{NSArray, NSRange, NSString}; +use objc2_metal::{ + self as metal, MTLAccelerationStructureCommandEncoder as _, MTLBlitCommandEncoder, + MTLCommandBuffer as _, MTLCommandEncoder, MTLComputeCommandEncoder as _, + MTLCounterSampleBuffer, MTLRenderCommandEncoder, +}; +use std::{marker::PhantomData, mem, ops::Range, ptr::NonNull, slice, time::Duration}; impl crate::ShaderBindable for T { fn bind_to(&self, ctx: &mut super::PipelineContext, index: u32) { let slot = ctx.targets[index as usize] as _; - let ptr: *const T = self; - let size = mem::size_of::() as u64; - if let Some(encoder) = ctx.vs_encoder { - encoder.set_vertex_bytes(slot, size, ptr as *const _); - } - if let Some(encoder) = ctx.fs_encoder { - encoder.set_fragment_bytes(slot, size, ptr as *const _); - } - if let Some(encoder) = ctx.cs_encoder { - encoder.set_bytes(slot, size, ptr as *const _); + let size = mem::size_of::(); + unsafe { + let ptr = NonNull::new_unchecked(self as *const _ as *mut _); + if let Some(encoder) = ctx.vs_encoder { + encoder.setVertexBytes_length_atIndex(ptr, size, slot); + } + if let Some(encoder) = ctx.fs_encoder { + encoder.setFragmentBytes_length_atIndex(ptr, size, slot); + } + if let Some(encoder) = ctx.cs_encoder { + encoder.setBytes_length_atIndex(ptr, size, slot); + } } } } @@ -20,14 +28,16 @@ impl crate::ShaderBindable for super::TextureView { fn bind_to(&self, ctx: &mut super::PipelineContext, index: u32) { let slot = ctx.targets[index as usize] as _; let value = Some(self.as_ref()); - if let Some(encoder) = ctx.vs_encoder { - encoder.set_vertex_texture(slot, value); - } - if let Some(encoder) = ctx.fs_encoder { - encoder.set_fragment_texture(slot, value); - } - if let Some(encoder) = ctx.cs_encoder { - encoder.set_texture(slot, value); + unsafe { + if let Some(encoder) = ctx.vs_encoder { + encoder.setVertexTexture_atIndex(value, slot); + } + if let Some(encoder) = ctx.fs_encoder { + encoder.setFragmentTexture_atIndex(value, slot); + } + if let Some(encoder) = ctx.cs_encoder { + encoder.setTexture_atIndex(value, slot); + } } } } @@ -38,17 +48,18 @@ impl<'a, const N: crate::ResourceIndex> crate::ShaderBindable for &'a crate::Tex } impl crate::ShaderBindable for super::Sampler { fn bind_to(&self, ctx: &mut super::PipelineContext, index: u32) { - //self.raw.set_sampler_state(index as _, sampler.as_ref()); let slot = ctx.targets[index as usize] as _; let value = Some(self.as_ref()); - if let Some(encoder) = ctx.vs_encoder { - encoder.set_vertex_sampler_state(slot, value); - } - if let Some(encoder) = ctx.fs_encoder { - encoder.set_fragment_sampler_state(slot, value); - } - if let Some(encoder) = ctx.cs_encoder { - encoder.set_sampler_state(slot, value); + unsafe { + if let Some(encoder) = ctx.vs_encoder { + encoder.setVertexSamplerState_atIndex(value, slot); + } + if let Some(encoder) = ctx.fs_encoder { + encoder.setFragmentSamplerState_atIndex(value, slot); + } + if let Some(encoder) = ctx.cs_encoder { + encoder.setSamplerState_atIndex(value, slot); + } } } } @@ -56,14 +67,16 @@ impl crate::ShaderBindable for crate::BufferPiece { fn bind_to(&self, ctx: &mut super::PipelineContext, index: u32) { let slot = ctx.targets[index as usize] as _; let value = Some(self.buffer.as_ref()); - if let Some(encoder) = ctx.vs_encoder { - encoder.set_vertex_buffer(slot, value, self.offset); - } - if let Some(encoder) = ctx.fs_encoder { - encoder.set_fragment_buffer(slot, value, self.offset); - } - if let Some(encoder) = ctx.cs_encoder { - encoder.set_buffer(slot, value, self.offset); + unsafe { + if let Some(encoder) = ctx.vs_encoder { + encoder.setVertexBuffer_offset_atIndex(value, self.offset as usize, slot); + } + if let Some(encoder) = ctx.fs_encoder { + encoder.setFragmentBuffer_offset_atIndex(value, self.offset as usize, slot); + } + if let Some(encoder) = ctx.cs_encoder { + encoder.setBuffer_offset_atIndex(value, self.offset as usize, slot); + } } } } @@ -76,21 +89,23 @@ impl crate::ShaderBindable for crate::AccelerationStructure { fn bind_to(&self, ctx: &mut super::PipelineContext, index: u32) { let slot = ctx.targets[index as usize] as _; let value = Some(self.as_ref()); - if let Some(encoder) = ctx.vs_encoder { - encoder.set_vertex_acceleration_structure(slot, value); - } - if let Some(encoder) = ctx.fs_encoder { - encoder.set_fragment_acceleration_structure(slot, value); - } - if let Some(encoder) = ctx.cs_encoder { - encoder.set_acceleration_structure(slot, value); + unsafe { + if let Some(encoder) = ctx.vs_encoder { + encoder.setVertexAccelerationStructure_atBufferIndex(value, slot); + } + if let Some(encoder) = ctx.fs_encoder { + encoder.setFragmentAccelerationStructure_atBufferIndex(value, slot); + } + if let Some(encoder) = ctx.cs_encoder { + encoder.setAccelerationStructure_atBufferIndex(value, slot); + } } } } impl super::TimingData { - fn add(&mut self, label: &str) -> u64 { - let counter_index = self.pass_names.len() as u64 * 2; + fn add(&mut self, label: &str) -> usize { + let counter_index = self.pass_names.len() * 2; self.pass_names.push(label.to_string()); counter_index } @@ -101,40 +116,42 @@ impl super::CommandEncoder { if self.enable_debug_groups { //HACK: close the previous group if self.has_open_debug_group { - self.raw.as_mut().unwrap().pop_debug_group(); + self.raw.as_mut().unwrap().popDebugGroup(); } else { self.has_open_debug_group = true; } - self.raw.as_mut().unwrap().push_debug_group(label); + let string = NSString::from_str(label); + self.raw.as_mut().unwrap().pushDebugGroup(&string); } } - pub(super) fn finish(&mut self) -> metal::CommandBuffer { + pub(super) fn finish(&mut self) -> super::RawCommandBuffer { if self.has_open_debug_group { - self.raw.as_mut().unwrap().pop_debug_group(); + self.raw.as_mut().unwrap().popDebugGroup(); } self.raw.take().unwrap() } pub fn transfer(&mut self, label: &str) -> super::TransferCommandEncoder { self.begin_pass(label); - let raw = objc::rc::autoreleasepool(|| { - let descriptor = metal::BlitPassDescriptor::new(); - + let raw = objc2::rc::autoreleasepool(|_| unsafe { + let descriptor = metal::MTLBlitPassDescriptor::new(); if let Some(ref mut td_array) = self.timing_datas { let td = td_array.first_mut().unwrap(); let counter_index = td.add(label); - let sba = descriptor.sample_buffer_attachments().object_at(0).unwrap(); - sba.set_sample_buffer(&td.sample_buffer); - sba.set_start_of_encoder_sample_index(counter_index); - sba.set_end_of_encoder_sample_index(counter_index + 1); + let sba = descriptor + .sampleBufferAttachments() + .objectAtIndexedSubscript(0); + sba.setSampleBuffer(Some(&td.sample_buffer)); + sba.setStartOfEncoderSampleIndex(counter_index); + sba.setEndOfEncoderSampleIndex(counter_index + 1); } self.raw .as_mut() .unwrap() - .blit_command_encoder_with_descriptor(&descriptor) - .to_owned() + .blitCommandEncoderWithDescriptor(&descriptor) + .unwrap() }); super::TransferCommandEncoder { raw, @@ -146,23 +163,24 @@ impl super::CommandEncoder { &mut self, label: &str, ) -> super::AccelerationStructureCommandEncoder { - let raw = objc::rc::autoreleasepool(|| { - let descriptor = metal::AccelerationStructurePassDescriptor::new(); + let raw = objc2::rc::autoreleasepool(|_| unsafe { + let descriptor = metal::MTLAccelerationStructurePassDescriptor::new(); if let Some(ref mut td_array) = self.timing_datas { let td = td_array.first_mut().unwrap(); let counter_index = td.add(label); - let sba = descriptor.sample_buffer_attachments().object_at(0).unwrap(); - sba.set_sample_buffer(&td.sample_buffer); - sba.set_start_of_encoder_sample_index(counter_index); - sba.set_end_of_encoder_sample_index(counter_index + 1); + let sba = descriptor + .sampleBufferAttachments() + .objectAtIndexedSubscript(0); + sba.setSampleBuffer(Some(&td.sample_buffer)); + sba.setStartOfEncoderSampleIndex(counter_index); + sba.setEndOfEncoderSampleIndex(counter_index + 1); } self.raw .as_mut() .unwrap() - .new_acceleration_structure_command_encoder() - .to_owned() + .accelerationStructureCommandEncoderWithDescriptor(&descriptor) }); super::AccelerationStructureCommandEncoder { raw, @@ -171,26 +189,28 @@ impl super::CommandEncoder { } pub fn compute(&mut self, label: &str) -> super::ComputeCommandEncoder { - let raw = objc::rc::autoreleasepool(|| { - let descriptor = metal::ComputePassDescriptor::new(); + let raw = objc2::rc::autoreleasepool(|_| unsafe { + let descriptor = metal::MTLComputePassDescriptor::new(); if self.enable_dispatch_type { - descriptor.set_dispatch_type(metal::MTLDispatchType::Concurrent); + descriptor.setDispatchType(metal::MTLDispatchType::Concurrent); } if let Some(ref mut td_array) = self.timing_datas { let td = td_array.first_mut().unwrap(); let counter_index = td.add(label); - let sba = descriptor.sample_buffer_attachments().object_at(0).unwrap(); - sba.set_sample_buffer(&td.sample_buffer); - sba.set_start_of_encoder_sample_index(counter_index); - sba.set_end_of_encoder_sample_index(counter_index + 1); + let sba = descriptor + .sampleBufferAttachments() + .objectAtIndexedSubscript(0); + sba.setSampleBuffer(Some(&td.sample_buffer)); + sba.setStartOfEncoderSampleIndex(counter_index); + sba.setEndOfEncoderSampleIndex(counter_index + 1); } self.raw .as_mut() .unwrap() - .compute_command_encoder_with_descriptor(&descriptor) - .to_owned() + .computeCommandEncoderWithDescriptor(&descriptor) + .unwrap() }); super::ComputeCommandEncoder { raw, @@ -203,23 +223,24 @@ impl super::CommandEncoder { label: &str, targets: crate::RenderTargetSet, ) -> super::RenderCommandEncoder { - let raw = objc::rc::autoreleasepool(|| { - let descriptor = metal::RenderPassDescriptor::new(); + let raw = objc2::rc::autoreleasepool(|_| { + let descriptor = unsafe { metal::MTLRenderPassDescriptor::new() }; for (i, rt) in targets.colors.iter().enumerate() { - let at_descriptor = descriptor.color_attachments().object_at(i as u64).unwrap(); - at_descriptor.set_texture(Some(rt.view.as_ref())); + let at_descriptor = + unsafe { descriptor.colorAttachments().objectAtIndexedSubscript(i) }; + at_descriptor.setTexture(Some(rt.view.as_ref())); let load_action = match rt.init_op { crate::InitOp::Load => metal::MTLLoadAction::Load, crate::InitOp::Clear(color) => { let clear_color = map_clear_color(color); - at_descriptor.set_clear_color(clear_color); + at_descriptor.setClearColor(clear_color); metal::MTLLoadAction::Clear } crate::InitOp::DontCare => metal::MTLLoadAction::DontCare, }; - at_descriptor.set_load_action(load_action); + at_descriptor.setLoadAction(load_action); let store_action = match rt.finish_op { crate::FinishOp::Store | crate::FinishOp::Ignore => { @@ -227,16 +248,16 @@ impl super::CommandEncoder { } crate::FinishOp::Discard => metal::MTLStoreAction::DontCare, crate::FinishOp::ResolveTo(ref view) => { - at_descriptor.set_resolve_texture(Some(view.as_ref())); + at_descriptor.setResolveTexture(Some(view.as_ref())); metal::MTLStoreAction::MultisampleResolve } }; - at_descriptor.set_store_action(store_action); + at_descriptor.setStoreAction(store_action); } if let Some(ref rt) = targets.depth_stencil { - let at_descriptor = descriptor.depth_attachment().unwrap(); - at_descriptor.set_texture(Some(rt.view.as_ref())); + let at_descriptor = descriptor.depthAttachment(); + at_descriptor.setTexture(Some(rt.view.as_ref())); let load_action = match rt.init_op { crate::InitOp::Load => metal::MTLLoadAction::Load, crate::InitOp::Clear(color) => { @@ -245,7 +266,7 @@ impl super::CommandEncoder { | crate::TextureColor::OpaqueBlack => 0.0, crate::TextureColor::White => 1.0, }; - at_descriptor.set_clear_depth(clear_depth); + at_descriptor.setClearDepth(clear_depth); metal::MTLLoadAction::Clear } crate::InitOp::DontCare => metal::MTLLoadAction::DontCare, @@ -257,24 +278,28 @@ impl super::CommandEncoder { crate::FinishOp::Discard => metal::MTLStoreAction::DontCare, crate::FinishOp::ResolveTo(_) => panic!("Can't resolve depth texture"), }; - at_descriptor.set_load_action(load_action); - at_descriptor.set_store_action(store_action); + at_descriptor.setLoadAction(load_action); + at_descriptor.setStoreAction(store_action); } if let Some(ref mut td_array) = self.timing_datas { let td = td_array.first_mut().unwrap(); let counter_index = td.add(label); - let sba = descriptor.sample_buffer_attachments().object_at(0).unwrap(); - sba.set_sample_buffer(&td.sample_buffer); - sba.set_start_of_vertex_sample_index(counter_index); - sba.set_end_of_fragment_sample_index(counter_index + 1); + unsafe { + let sba = descriptor + .sampleBufferAttachments() + .objectAtIndexedSubscript(0); + sba.setSampleBuffer(Some(&td.sample_buffer)); + sba.setStartOfVertexSampleIndex(counter_index); + sba.setEndOfFragmentSampleIndex(counter_index + 1); + } } self.raw .as_mut() .unwrap() - .new_render_command_encoder(descriptor) - .to_owned() + .renderCommandEncoderWithDescriptor(&descriptor) + .unwrap() }); super::RenderCommandEncoder { @@ -295,9 +320,17 @@ impl crate::traits::CommandEncoder for super::CommandEncoder { td_array.rotate_left(1); let td = td_array.first_mut().unwrap(); if !td.pass_names.is_empty() { - let counters = td - .sample_buffer - .resolve_counter_range(metal::NSRange::new(0, td.pass_names.len() as u64 * 2)); + let ns_data = unsafe { + td.sample_buffer + .resolveCounterRange(NSRange::new(0, td.pass_names.len() * 2)) + .unwrap() + }; + let counters = unsafe { + slice::from_raw_parts( + ns_data.bytes().as_ptr() as *const u64, + ns_data.len() / mem::size_of::(), + ) + }; for (name, chunk) in td.pass_names.drain(..).zip(counters.chunks(2)) { let duration = Duration::from_nanos(chunk[1] - chunk[0]); *self.timings.entry(name).or_default() += duration; @@ -306,12 +339,13 @@ impl crate::traits::CommandEncoder for super::CommandEncoder { } let queue = self.queue.lock().unwrap(); - self.raw = Some(objc::rc::autoreleasepool(|| { - let cmd_buf = queue.new_command_buffer_with_unretained_references(); + self.raw = Some(objc2::rc::autoreleasepool(|_| unsafe { + use metal::MTLCommandQueue as _; + let cmd_buf = queue.commandBufferWithUnretainedReferences().unwrap(); if !self.name.is_empty() { - cmd_buf.set_label(&self.name); + cmd_buf.setLabel(Some(&NSString::from_str(&self.name))); } - cmd_buf.to_owned() + cmd_buf })); self.has_open_debug_group = false; } @@ -319,7 +353,7 @@ impl crate::traits::CommandEncoder for super::CommandEncoder { fn init_texture(&mut self, _texture: super::Texture) {} fn present(&mut self, frame: super::Frame) { - self.raw.as_mut().unwrap().present_drawable(&frame.drawable); + self.raw.as_mut().unwrap().presentDrawable(&frame.drawable); } fn timings(&self) -> &crate::Timings { @@ -333,11 +367,12 @@ impl crate::traits::TransferEncoder for super::TransferCommandEncoder<'_> { type TexturePiece = crate::TexturePiece; fn fill_buffer(&mut self, dst: crate::BufferPiece, size: u64, value: u8) { - let range = metal::NSRange { - location: dst.offset, - length: size, + let range = NSRange { + location: dst.offset as usize, + length: size as usize, }; - self.raw.fill_buffer(dst.buffer.as_ref(), range, value); + self.raw + .fillBuffer_range_value(dst.buffer.as_ref(), range, value); } fn copy_buffer_to_buffer( @@ -346,13 +381,16 @@ impl crate::traits::TransferEncoder for super::TransferCommandEncoder<'_> { dst: crate::BufferPiece, size: u64, ) { - self.raw.copy_from_buffer( - src.buffer.as_ref(), - src.offset, - dst.buffer.as_ref(), - dst.offset, - size, - ); + unsafe { + self.raw + .copyFromBuffer_sourceOffset_toBuffer_destinationOffset_size( + src.buffer.as_ref(), + src.offset as usize, + dst.buffer.as_ref(), + dst.offset as usize, + size as usize, + ) + }; } fn copy_texture_to_texture( &mut self, @@ -360,17 +398,19 @@ impl crate::traits::TransferEncoder for super::TransferCommandEncoder<'_> { dst: crate::TexturePiece, size: crate::Extent, ) { - self.raw.copy_from_texture( - src.texture.as_ref(), - src.array_layer as u64, - src.mip_level as u64, - map_origin(&src.origin), - map_extent(&size), - dst.texture.as_ref(), - dst.array_layer as u64, - dst.mip_level as u64, - map_origin(&dst.origin), - ); + unsafe { + self.raw.copyFromTexture_sourceSlice_sourceLevel_sourceOrigin_sourceSize_toTexture_destinationSlice_destinationLevel_destinationOrigin( + src.texture.as_ref(), + src.array_layer as usize, + src.mip_level as usize, + map_origin(&src.origin), + map_extent(&size), + dst.texture.as_ref(), + dst.array_layer as usize, + dst.mip_level as usize, + map_origin(&dst.origin), + ) + }; } fn copy_buffer_to_texture( @@ -380,18 +420,20 @@ impl crate::traits::TransferEncoder for super::TransferCommandEncoder<'_> { dst: crate::TexturePiece, size: crate::Extent, ) { - self.raw.copy_from_buffer_to_texture( - src.buffer.as_ref(), - src.offset, - bytes_per_row as u64, - 0, - map_extent(&size), - dst.texture.as_ref(), - dst.array_layer as u64, - dst.mip_level as u64, - map_origin(&dst.origin), - metal::MTLBlitOption::empty(), - ); + unsafe { + self.raw.copyFromBuffer_sourceOffset_sourceBytesPerRow_sourceBytesPerImage_sourceSize_toTexture_destinationSlice_destinationLevel_destinationOrigin_options( + src.buffer.as_ref(), + src.offset as usize, + bytes_per_row as usize, + 0, + map_extent(&size), + dst.texture.as_ref(), + dst.array_layer as usize, + dst.mip_level as usize, + map_origin(&dst.origin), + metal::MTLBlitOption::empty(), + ) + }; } fn copy_texture_to_buffer( @@ -401,24 +443,26 @@ impl crate::traits::TransferEncoder for super::TransferCommandEncoder<'_> { bytes_per_row: u32, size: crate::Extent, ) { - self.raw.copy_from_texture_to_buffer( - src.texture.as_ref(), - src.array_layer as u64, - src.mip_level as u64, - map_origin(&src.origin), - map_extent(&size), - dst.buffer.as_ref(), - dst.offset, - bytes_per_row as u64, - 0, - metal::MTLBlitOption::empty(), - ); + unsafe { + self.raw.copyFromTexture_sourceSlice_sourceLevel_sourceOrigin_sourceSize_toBuffer_destinationOffset_destinationBytesPerRow_destinationBytesPerImage_options( + src.texture.as_ref(), + src.array_layer as usize, + src.mip_level as usize, + map_origin(&src.origin), + map_extent(&size), + dst.buffer.as_ref(), + dst.offset as usize, + bytes_per_row as usize, + 0, + metal::MTLBlitOption::empty(), + ) + }; } } impl Drop for super::TransferCommandEncoder<'_> { fn drop(&mut self) { - self.raw.end_encoding(); + self.raw.endEncoding(); } } @@ -437,12 +481,13 @@ impl crate::traits::AccelerationStructureEncoder scratch_data: crate::BufferPiece, ) { let descriptor = super::make_bottom_level_acceleration_structure_desc(meshes); - self.raw.build_acceleration_structure( - acceleration_structure.as_ref(), - &descriptor, - scratch_data.buffer.as_ref(), - scratch_data.offset, - ); + self.raw + .buildAccelerationStructure_descriptor_scratchBuffer_scratchBufferOffset( + acceleration_structure.as_ref(), + &descriptor, + scratch_data.buffer.as_ref(), + scratch_data.offset as usize, + ); } fn build_top_level( @@ -455,31 +500,34 @@ impl crate::traits::AccelerationStructureEncoder ) { let mut primitive_acceleration_structures = Vec::with_capacity(bottom_level.len()); for blas in bottom_level { - primitive_acceleration_structures.push(blas.as_ref()); + primitive_acceleration_structures.push(blas.as_retained()); } - let descriptor = metal::InstanceAccelerationStructureDescriptor::descriptor(); - descriptor.set_instanced_acceleration_structures(&metal::Array::from_slice( - &primitive_acceleration_structures, - )); - descriptor.set_instance_count(instance_count as _); - descriptor.set_instance_descriptor_type( - metal::MTLAccelerationStructureInstanceDescriptorType::UserID, - ); - descriptor.set_instance_descriptor_buffer(instance_data.buffer.as_ref()); - descriptor.set_instance_descriptor_buffer_offset(instance_data.offset); - - self.raw.build_acceleration_structure( - acceleration_structure.as_ref(), - &descriptor, - scratch_data.buffer.as_ref(), - scratch_data.offset, - ); + let descriptor = metal::MTLInstanceAccelerationStructureDescriptor::descriptor(); + descriptor.setInstancedAccelerationStructures(Some(&NSArray::from_vec( + primitive_acceleration_structures, + ))); + descriptor.setInstanceCount(instance_count as usize); + unsafe { + descriptor.setInstanceDescriptorType( + metal::MTLAccelerationStructureInstanceDescriptorType::UserID, + ); + descriptor.setInstanceDescriptorBuffer(Some(instance_data.buffer.as_ref())); + descriptor.setInstanceDescriptorBufferOffset(instance_data.offset as usize); + } + + self.raw + .buildAccelerationStructure_descriptor_scratchBuffer_scratchBufferOffset( + acceleration_structure.as_ref(), + &descriptor, + scratch_data.buffer.as_ref(), + scratch_data.offset as usize, + ); } } impl Drop for super::AccelerationStructureCommandEncoder<'_> { fn drop(&mut self) { - self.raw.end_encoding(); + self.raw.endEncoding(); } } @@ -488,24 +536,28 @@ impl super::ComputeCommandEncoder<'_> { &'p mut self, pipeline: &'p super::ComputePipeline, ) -> super::ComputePipelineContext<'p> { - self.raw.push_debug_group(&pipeline.name); - self.raw.set_compute_pipeline_state(&pipeline.raw); + self.raw.pushDebugGroup(&NSString::from_str(&pipeline.name)); + self.raw.setComputePipelineState(&pipeline.raw); if let Some(index) = pipeline.layout.sizes_buffer_slot { //TODO: get real sizes? shouldn't matter without bounds checks let runtime_sizes = [0u8; 8]; - self.raw.set_bytes( - index as _, - runtime_sizes.len() as _, - runtime_sizes.as_ptr() as *const _, - ); + unsafe { + self.raw.setBytes_length_atIndex( + NonNull::new(runtime_sizes.as_ptr() as *const _ as *mut _).unwrap(), + runtime_sizes.len(), + index as _, + ); + } } for (index, &size) in pipeline.wg_memory_sizes.iter().enumerate() { - self.raw - .set_threadgroup_memory_length(index as _, size as _); + unsafe { + self.raw + .setThreadgroupMemoryLength_atIndex(size as _, index); + } } super::ComputePipelineContext { - encoder: &mut self.raw, + encoder: self.raw.as_ref(), wg_size: pipeline.wg_size, group_mappings: &pipeline.layout.group_mappings, } @@ -514,7 +566,7 @@ impl super::ComputeCommandEncoder<'_> { impl Drop for super::ComputeCommandEncoder<'_> { fn drop(&mut self) { - self.raw.end_encoding(); + self.raw.endEncoding(); } } @@ -526,7 +578,7 @@ impl super::RenderCommandEncoder<'_> { width: rect.w as _, height: rect.h as _, }; - self.raw.set_scissor_rect(scissor); + self.raw.setScissorRect(scissor); } pub fn set_viewport(&mut self, viewport: &crate::Viewport, depth_range: Range) { @@ -538,42 +590,47 @@ impl super::RenderCommandEncoder<'_> { znear: depth_range.start as _, zfar: depth_range.end as _, // TODO: aparently broken on some Intel GPU:s? see wgpu-hal }; - self.raw.set_viewport(viewport); + self.raw.setViewport(viewport); } pub fn with<'p>( &'p mut self, pipeline: &'p super::RenderPipeline, ) -> super::RenderPipelineContext<'p> { - self.raw.push_debug_group(&pipeline.name); - self.raw.set_render_pipeline_state(&pipeline.raw); + self.raw.pushDebugGroup(&NSString::from_str(&pipeline.name)); + self.raw.setRenderPipelineState(&pipeline.raw); if let Some(index) = pipeline.layout.sizes_buffer_slot { //TODO: get real sizes let runtime_sizes = [0u8; 8]; - self.raw.set_vertex_bytes( - index as _, - runtime_sizes.len() as _, - runtime_sizes.as_ptr() as *const _, - ); - self.raw.set_fragment_bytes( - index as _, - runtime_sizes.len() as _, - runtime_sizes.as_ptr() as *const _, - ); + unsafe { + self.raw.setVertexBytes_length_atIndex( + NonNull::new(runtime_sizes.as_ptr() as *const _ as *mut _).unwrap(), + runtime_sizes.len(), + index as _, + ); + self.raw.setFragmentBytes_length_atIndex( + NonNull::new(runtime_sizes.as_ptr() as *const _ as *mut _).unwrap(), + runtime_sizes.len(), + index as _, + ); + } } - self.raw.set_front_facing_winding(pipeline.front_winding); - self.raw.set_cull_mode(pipeline.cull_mode); - self.raw.set_triangle_fill_mode(pipeline.triangle_fill_mode); - self.raw.set_depth_clip_mode(pipeline.depth_clip_mode); + self.raw.setFrontFacingWinding(pipeline.front_winding); + self.raw.setCullMode(pipeline.cull_mode); + self.raw.setTriangleFillMode(pipeline.triangle_fill_mode); + self.raw.setDepthClipMode(pipeline.depth_clip_mode); if let Some((ref state, bias)) = pipeline.depth_stencil { - self.raw.set_depth_stencil_state(state); - self.raw - .set_depth_bias(bias.constant as f32, bias.slope_scale, bias.clamp); + self.raw.setDepthStencilState(Some(state)); + self.raw.setDepthBias_slopeScale_clamp( + bias.constant as f32, + bias.slope_scale, + bias.clamp, + ); } super::RenderPipelineContext { - encoder: &mut self.raw, + encoder: self.raw.as_ref(), primitive_type: pipeline.primitive_type, group_mappings: &pipeline.layout.group_mappings, } @@ -582,7 +639,7 @@ impl super::RenderCommandEncoder<'_> { impl Drop for super::RenderCommandEncoder<'_> { fn drop(&mut self) { - self.raw.end_encoding(); + self.raw.endEncoding(); } } @@ -608,17 +665,18 @@ impl crate::traits::PipelineEncoder for super::ComputePipelineContext<'_> { impl crate::traits::ComputePipelineEncoder for super::ComputePipelineContext<'_> { fn dispatch(&mut self, groups: [u32; 3]) { let raw_count = metal::MTLSize { - width: groups[0] as u64, - height: groups[1] as u64, - depth: groups[2] as u64, + width: groups[0] as usize, + height: groups[1] as usize, + depth: groups[2] as usize, }; - self.encoder.dispatch_thread_groups(raw_count, self.wg_size); + self.encoder + .dispatchThreadgroups_threadsPerThreadgroup(raw_count, self.wg_size); } } impl Drop for super::ComputePipelineContext<'_> { fn drop(&mut self) { - self.encoder.pop_debug_group(); + self.encoder.popDebugGroup(); } } @@ -655,7 +713,7 @@ impl crate::traits::RenderPipelineEncoder for super::RenderPipelineContext<'_> { width: rect.w as _, height: rect.h as _, }; - self.encoder.set_scissor_rect(scissor); + self.encoder.setScissorRect(scissor); } fn set_viewport(&mut self, viewport: &crate::Viewport, depth_range: Range) { @@ -667,15 +725,17 @@ impl crate::traits::RenderPipelineEncoder for super::RenderPipelineContext<'_> { znear: depth_range.start as _, zfar: depth_range.end as _, // TODO: aparently broken on some Intel GPU:s? see wgpu-hal }; - self.encoder.set_viewport(viewport); + self.encoder.setViewport(viewport); } fn bind_vertex(&mut self, index: u32, vertex_buf: crate::BufferPiece) { - self.encoder.set_vertex_buffer( - index as u64, - Some(vertex_buf.buffer.as_ref()), - vertex_buf.offset, - ); + unsafe { + self.encoder.setVertexBuffer_offset_atIndex( + Some(vertex_buf.buffer.as_ref()), + vertex_buf.offset as usize, + index as usize, + ); + } } fn draw( @@ -685,24 +745,31 @@ impl crate::traits::RenderPipelineEncoder for super::RenderPipelineContext<'_> { first_instance: u32, instance_count: u32, ) { - if first_instance != 0 { - self.encoder.draw_primitives_instanced_base_instance( - self.primitive_type, - first_vertex as _, - vertex_count as _, - instance_count as _, - first_instance as _, - ); - } else if instance_count != 1 { - self.encoder.draw_primitives_instanced( - self.primitive_type, - first_vertex as _, - vertex_count as _, - instance_count as _, - ); - } else { - self.encoder - .draw_primitives(self.primitive_type, first_vertex as _, vertex_count as _); + unsafe { + if first_instance != 0 { + self.encoder + .drawPrimitives_vertexStart_vertexCount_instanceCount_baseInstance( + self.primitive_type, + first_vertex as _, + vertex_count as _, + instance_count as _, + first_instance as _, + ); + } else if instance_count != 1 { + self.encoder + .drawPrimitives_vertexStart_vertexCount_instanceCount( + self.primitive_type, + first_vertex as _, + vertex_count as _, + instance_count as _, + ); + } else { + self.encoder.drawPrimitives_vertexStart_vertexCount( + self.primitive_type, + first_vertex as _, + vertex_count as _, + ); + } } } @@ -716,44 +783,50 @@ impl crate::traits::RenderPipelineEncoder for super::RenderPipelineContext<'_> { instance_count: u32, ) { let raw_index_type = super::map_index_type(index_type); - if base_vertex != 0 || start_instance != 0 { - self.encoder - .draw_indexed_primitives_instanced_base_instance( + unsafe { + if base_vertex != 0 || start_instance != 0 { + self.encoder + .drawIndexedPrimitives_indexCount_indexType_indexBuffer_indexBufferOffset_instanceCount_baseVertex_baseInstance( self.primitive_type, index_count as _, raw_index_type, index_buf.buffer.as_ref(), - index_buf.offset, + index_buf.offset as usize, instance_count as _, base_vertex as _, start_instance as _, ); - } else if instance_count != 1 { - self.encoder.draw_indexed_primitives_instanced( + } else if instance_count != 1 { + self.encoder.drawIndexedPrimitives_indexCount_indexType_indexBuffer_indexBufferOffset_instanceCount( self.primitive_type, index_count as _, raw_index_type, index_buf.buffer.as_ref(), - index_buf.offset, + index_buf.offset as usize, instance_count as _, ); - } else { - self.encoder.draw_indexed_primitives( - self.primitive_type, - index_count as _, - raw_index_type, - index_buf.buffer.as_ref(), - index_buf.offset, - ); + } else { + self.encoder + .drawIndexedPrimitives_indexCount_indexType_indexBuffer_indexBufferOffset( + self.primitive_type, + index_count as _, + raw_index_type, + index_buf.buffer.as_ref(), + index_buf.offset as usize, + ); + } } } fn draw_indirect(&mut self, indirect_buf: crate::BufferPiece) { - self.encoder.draw_primitives_indirect( - self.primitive_type, - indirect_buf.buffer.as_ref(), - indirect_buf.offset, - ); + unsafe { + self.encoder + .drawPrimitives_indirectBuffer_indirectBufferOffset( + self.primitive_type, + indirect_buf.buffer.as_ref(), + indirect_buf.offset as usize, + ); + } } fn draw_indexed_indirect( @@ -763,36 +836,38 @@ impl crate::traits::RenderPipelineEncoder for super::RenderPipelineContext<'_> { indirect_buf: crate::BufferPiece, ) { let raw_index_type = super::map_index_type(index_type); - self.encoder.draw_indexed_primitives_indirect( + unsafe { + self.encoder.drawIndexedPrimitives_indexType_indexBuffer_indexBufferOffset_indirectBuffer_indirectBufferOffset( self.primitive_type, raw_index_type, index_buf.buffer.as_ref(), - index_buf.offset, + index_buf.offset as usize, indirect_buf.buffer.as_ref(), - indirect_buf.offset, + indirect_buf.offset as usize, ); + } } } impl Drop for super::RenderPipelineContext<'_> { fn drop(&mut self) { - self.encoder.pop_debug_group(); + self.encoder.popDebugGroup(); } } fn map_origin(origin: &[u32; 3]) -> metal::MTLOrigin { metal::MTLOrigin { - x: origin[0] as u64, - y: origin[1] as u64, - z: origin[2] as u64, + x: origin[0] as usize, + y: origin[1] as usize, + z: origin[2] as usize, } } fn map_extent(extent: &crate::Extent) -> metal::MTLSize { metal::MTLSize { - width: extent.width as u64, - height: extent.height as u64, - depth: extent.depth as u64, + width: extent.width as usize, + height: extent.height as usize, + depth: extent.depth as usize, } } diff --git a/blade-graphics/src/metal/mod.rs b/blade-graphics/src/metal/mod.rs index b570ef5..f72f5df 100644 --- a/blade-graphics/src/metal/mod.rs +++ b/blade-graphics/src/metal/mod.rs @@ -1,3 +1,5 @@ +use objc2::{rc::Retained, runtime::ProtocolObject}; +use objc2_metal::{self as metal, MTLDevice}; use std::{ marker::PhantomData, ptr, @@ -5,42 +7,39 @@ use std::{ thread, time, }; -use metal::foreign_types::{ForeignType as _, ForeignTypeRef as _}; - mod command; mod pipeline; mod resource; mod surface; -const MAX_TIMESTAMPS: u64 = crate::limits::PASS_COUNT as u64 * 2; +const MAX_TIMESTAMPS: usize = crate::limits::PASS_COUNT * 2; pub type PlatformError = (); pub struct Surface { - view: *mut objc::runtime::Object, - render_layer: metal::MetalLayer, + view: Option>, + render_layer: Retained, info: crate::SurfaceInfo, } -unsafe impl Send for Surface {} -unsafe impl Sync for Surface {} - #[derive(Debug)] pub struct Frame { - drawable: metal::MetalDrawable, - texture: metal::Texture, + drawable: Retained>, + texture: Retained>, } +unsafe impl Send for Frame {} +unsafe impl Sync for Frame {} impl Frame { pub fn texture(&self) -> Texture { Texture { - raw: self.texture.as_ptr(), + raw: Retained::as_ptr(&self.texture) as *mut _, } } pub fn texture_view(&self) -> TextureView { TextureView { - raw: self.texture.as_ptr(), + raw: Retained::as_ptr(&self.texture) as *mut _, } } } @@ -50,20 +49,24 @@ struct PrivateInfo { language_version: metal::MTLLanguageVersion, enable_debug_groups: bool, enable_dispatch_type: bool, - timestamp_counter_set: Option, } pub struct Context { - device: Mutex, - queue: Arc>, - capture: Option, + device: Mutex>>, + queue: Arc>>>, + capture: Option>, + timestamp_counter_set: Option>>, info: PrivateInfo, device_information: crate::DeviceInformation, } +// needed for `capture` and `timestamp_counter_set` +unsafe impl Send for Context {} +unsafe impl Sync for Context {} + #[derive(Clone, Copy, Debug, Hash, PartialEq)] pub struct Buffer { - raw: *mut metal::MTLBuffer, + raw: *mut ProtocolObject, } unsafe impl Send for Buffer {} @@ -78,18 +81,19 @@ impl Default for Buffer { } impl Buffer { - fn as_ref(&self) -> &metal::BufferRef { - unsafe { metal::BufferRef::from_ptr(self.raw) } + fn as_ref(&self) -> &ProtocolObject { + unsafe { &*self.raw } } pub fn data(&self) -> *mut u8 { - self.as_ref().contents() as *mut u8 + use metal::MTLBuffer as _; + self.as_ref().contents().as_ptr() as *mut u8 } } #[derive(Clone, Copy, Debug, Hash, PartialEq)] pub struct Texture { - raw: *mut metal::MTLTexture, + raw: *mut ProtocolObject, } unsafe impl Send for Texture {} @@ -104,14 +108,14 @@ impl Default for Texture { } impl Texture { - fn as_ref(&self) -> &metal::TextureRef { - unsafe { metal::TextureRef::from_ptr(self.raw) } + fn as_ref(&self) -> &ProtocolObject { + unsafe { &*self.raw } } } #[derive(Clone, Copy, Debug, Hash, PartialEq)] pub struct TextureView { - raw: *mut metal::MTLTexture, + raw: *mut ProtocolObject, } unsafe impl Send for TextureView {} @@ -126,20 +130,22 @@ impl Default for TextureView { } impl TextureView { - fn as_ref(&self) -> &metal::TextureRef { - unsafe { metal::TextureRef::from_ptr(self.raw) } + fn as_ref(&self) -> &ProtocolObject { + unsafe { &*self.raw } } /// Create a TextureView from a raw Metal Texture. /// Does not keep a reference, need not being destoryed. - pub fn from_metal_texture(raw: &metal::TextureRef) -> Self { - Self { raw: raw.as_ptr() } + pub fn from_metal_texture(raw: &Retained>) -> Self { + Self { + raw: Retained::into_raw(raw.clone()), + } } } #[derive(Clone, Copy, Debug, Hash, PartialEq)] pub struct Sampler { - raw: *mut metal::MTLSamplerState, + raw: *mut ProtocolObject, } unsafe impl Send for Sampler {} @@ -154,14 +160,14 @@ impl Default for Sampler { } impl Sampler { - fn as_ref(&self) -> &metal::SamplerStateRef { - unsafe { metal::SamplerStateRef::from_ptr(self.raw) } + fn as_ref(&self) -> &ProtocolObject { + unsafe { &*self.raw } } } #[derive(Clone, Copy, Debug, Hash, PartialEq)] pub struct AccelerationStructure { - raw: *mut metal::MTLAccelerationStructure, + raw: *mut ProtocolObject, } unsafe impl Send for AccelerationStructure {} @@ -176,28 +182,30 @@ impl Default for AccelerationStructure { } impl AccelerationStructure { - fn as_ref(&self) -> &metal::AccelerationStructureRef { - unsafe { metal::AccelerationStructureRef::from_ptr(self.raw) } + fn as_ref(&self) -> &ProtocolObject { + unsafe { &*self.raw } + } + fn as_retained(&self) -> Retained> { + unsafe { Retained::retain(self.raw).unwrap() } } } //TODO: make this copyable? #[derive(Clone, Debug)] pub struct SyncPoint { - cmd_buf: metal::CommandBuffer, + cmd_buf: Retained>, } -#[derive(Debug)] struct TimingData { pass_names: Vec, - sample_buffer: metal::CounterSampleBuffer, + sample_buffer: Retained>, } -#[derive(Debug)] +type RawCommandBuffer = Retained>; pub struct CommandEncoder { - raw: Option, + raw: Option, name: String, - queue: Arc>, + queue: Arc>>>, enable_debug_groups: bool, enable_dispatch_type: bool, has_open_debug_group: bool, @@ -218,17 +226,18 @@ struct PipelineLayout { sizes_buffer_slot: Option, } -#[derive(Debug)] pub struct ComputePipeline { - raw: metal::ComputePipelineState, + raw: Retained>, name: String, #[allow(dead_code)] - lib: metal::Library, + lib: Retained>, layout: PipelineLayout, wg_size: metal::MTLSize, wg_memory_sizes: Box<[u32]>, } +unsafe impl Send for ComputePipeline {} +unsafe impl Sync for ComputePipeline {} impl ComputePipeline { pub fn get_workgroup_size(&self) -> [u32; 3] { [ @@ -239,123 +248,122 @@ impl ComputePipeline { } } -#[derive(Debug)] pub struct RenderPipeline { - raw: metal::RenderPipelineState, + raw: Retained>, name: String, #[allow(dead_code)] - vs_lib: metal::Library, + vs_lib: Retained>, #[allow(dead_code)] - fs_lib: Option, + fs_lib: Option>>, layout: PipelineLayout, primitive_type: metal::MTLPrimitiveType, triangle_fill_mode: metal::MTLTriangleFillMode, front_winding: metal::MTLWinding, cull_mode: metal::MTLCullMode, depth_clip_mode: metal::MTLDepthClipMode, - depth_stencil: Option<(metal::DepthStencilState, super::DepthBiasState)>, + depth_stencil: Option<( + Retained>, + super::DepthBiasState, + )>, } -#[derive(Debug)] +unsafe impl Send for RenderPipeline {} +unsafe impl Sync for RenderPipeline {} + pub struct TransferCommandEncoder<'a> { - raw: metal::BlitCommandEncoder, + raw: Retained>, phantom: PhantomData<&'a CommandEncoder>, } -#[derive(Debug)] pub struct AccelerationStructureCommandEncoder<'a> { - raw: metal::AccelerationStructureCommandEncoder, + raw: Retained>, phantom: PhantomData<&'a CommandEncoder>, } -#[derive(Debug)] pub struct ComputeCommandEncoder<'a> { - raw: metal::ComputeCommandEncoder, + raw: Retained>, phantom: PhantomData<&'a CommandEncoder>, } -#[derive(Debug)] pub struct RenderCommandEncoder<'a> { - raw: metal::RenderCommandEncoder, + raw: Retained>, phantom: PhantomData<&'a CommandEncoder>, } pub struct PipelineContext<'a> { //raw: metal::ArgumentEncoderRef, - cs_encoder: Option<&'a metal::ComputeCommandEncoderRef>, - vs_encoder: Option<&'a metal::RenderCommandEncoderRef>, - fs_encoder: Option<&'a metal::RenderCommandEncoderRef>, + cs_encoder: Option<&'a ProtocolObject>, + vs_encoder: Option<&'a ProtocolObject>, + fs_encoder: Option<&'a ProtocolObject>, targets: &'a [u32], } -#[derive(Debug)] pub struct ComputePipelineContext<'a> { - encoder: &'a mut metal::ComputeCommandEncoder, + encoder: &'a ProtocolObject, wg_size: metal::MTLSize, group_mappings: &'a [ShaderDataMapping], } -#[derive(Debug)] pub struct RenderPipelineContext<'a> { - encoder: &'a mut metal::RenderCommandEncoder, + encoder: &'a ProtocolObject, primitive_type: metal::MTLPrimitiveType, group_mappings: &'a [ShaderDataMapping], } fn map_texture_format(format: crate::TextureFormat) -> metal::MTLPixelFormat { use crate::TextureFormat as Tf; - use metal::MTLPixelFormat::*; + use metal::MTLPixelFormat as Mpf; match format { - Tf::R8Unorm => R8Unorm, - Tf::Rg8Unorm => RG8Unorm, - Tf::Rg8Snorm => RG8Snorm, - Tf::Rgba8Unorm => RGBA8Unorm, - Tf::Rgba8UnormSrgb => RGBA8Unorm_sRGB, - Tf::Bgra8Unorm => BGRA8Unorm, - Tf::Bgra8UnormSrgb => BGRA8Unorm_sRGB, - Tf::Rgba8Snorm => RGBA8Snorm, - Tf::R16Float => R16Float, - Tf::Rg16Float => RG16Float, - Tf::Rgba16Float => RGBA16Float, - Tf::R32Float => R32Float, - Tf::Rg32Float => RG32Float, - Tf::Rgba32Float => RGBA32Float, - Tf::R32Uint => R32Uint, - Tf::Rg32Uint => RG32Uint, - Tf::Rgba32Uint => RGBA32Uint, - Tf::Depth32Float => Depth32Float, - Tf::Bc1Unorm => BC1_RGBA, - Tf::Bc1UnormSrgb => BC1_RGBA_sRGB, - Tf::Bc2Unorm => BC2_RGBA, - Tf::Bc2UnormSrgb => BC2_RGBA_sRGB, - Tf::Bc3Unorm => BC3_RGBA, - Tf::Bc3UnormSrgb => BC3_RGBA_sRGB, - Tf::Bc4Unorm => BC4_RUnorm, - Tf::Bc4Snorm => BC4_RSnorm, - Tf::Bc5Unorm => BC5_RGUnorm, - Tf::Bc5Snorm => BC5_RGSnorm, - Tf::Bc6hUfloat => BC6H_RGBUfloat, - Tf::Bc6hFloat => BC6H_RGBFloat, - Tf::Bc7Unorm => BC7_RGBAUnorm, - Tf::Bc7UnormSrgb => BC7_RGBAUnorm_sRGB, - Tf::Rgb10a2Unorm => RGB10A2Unorm, - Tf::Rg11b10Ufloat => RG11B10Float, - Tf::Rgb9e5Ufloat => RGB9E5Float, + Tf::R8Unorm => Mpf::R8Unorm, + Tf::Rg8Unorm => Mpf::RG8Unorm, + Tf::Rg8Snorm => Mpf::RG8Snorm, + Tf::Rgba8Unorm => Mpf::RGBA8Unorm, + Tf::Rgba8UnormSrgb => Mpf::RGBA8Unorm_sRGB, + Tf::Bgra8Unorm => Mpf::BGRA8Unorm, + Tf::Bgra8UnormSrgb => Mpf::BGRA8Unorm_sRGB, + Tf::Rgba8Snorm => Mpf::RGBA8Snorm, + Tf::R16Float => Mpf::R16Float, + Tf::Rg16Float => Mpf::RG16Float, + Tf::Rgba16Float => Mpf::RGBA16Float, + Tf::R32Float => Mpf::R32Float, + Tf::Rg32Float => Mpf::RG32Float, + Tf::Rgba32Float => Mpf::RGBA32Float, + Tf::R32Uint => Mpf::R32Uint, + Tf::Rg32Uint => Mpf::RG32Uint, + Tf::Rgba32Uint => Mpf::RGBA32Uint, + Tf::Depth32Float => Mpf::Depth32Float, + Tf::Bc1Unorm => Mpf::BC1_RGBA, + Tf::Bc1UnormSrgb => Mpf::BC1_RGBA_sRGB, + Tf::Bc2Unorm => Mpf::BC2_RGBA, + Tf::Bc2UnormSrgb => Mpf::BC2_RGBA_sRGB, + Tf::Bc3Unorm => Mpf::BC3_RGBA, + Tf::Bc3UnormSrgb => Mpf::BC3_RGBA_sRGB, + Tf::Bc4Unorm => Mpf::BC4_RUnorm, + Tf::Bc4Snorm => Mpf::BC4_RSnorm, + Tf::Bc5Unorm => Mpf::BC5_RGUnorm, + Tf::Bc5Snorm => Mpf::BC5_RGSnorm, + Tf::Bc6hUfloat => Mpf::BC6H_RGBUfloat, + Tf::Bc6hFloat => Mpf::BC6H_RGBFloat, + Tf::Bc7Unorm => Mpf::BC7_RGBAUnorm, + Tf::Bc7UnormSrgb => Mpf::BC7_RGBAUnorm_sRGB, + Tf::Rgb10a2Unorm => Mpf::RGB10A2Unorm, + Tf::Rg11b10Ufloat => Mpf::RG11B10Float, + Tf::Rgb9e5Ufloat => Mpf::RGB9E5Float, } } fn map_compare_function(fun: crate::CompareFunction) -> metal::MTLCompareFunction { use crate::CompareFunction as Cf; - use metal::MTLCompareFunction::*; + use metal::MTLCompareFunction as Mcf; match fun { - Cf::Never => Never, - Cf::Less => Less, - Cf::LessEqual => LessEqual, - Cf::Equal => Equal, - Cf::GreaterEqual => GreaterEqual, - Cf::Greater => Greater, - Cf::NotEqual => NotEqual, - Cf::Always => Always, + Cf::Never => Mcf::Never, + Cf::Less => Mcf::Less, + Cf::LessEqual => Mcf::LessEqual, + Cf::Equal => Mcf::Equal, + Cf::GreaterEqual => Mcf::GreaterEqual, + Cf::Greater => Mcf::Greater, + Cf::NotEqual => Mcf::NotEqual, + Cf::Always => Mcf::Always, } } @@ -430,18 +438,23 @@ impl Context { log::warn!("Unable to filter devices by ID"); } - let device = metal::Device::system_default() + let device = Retained::from_raw(metal::MTLCreateSystemDefaultDevice()) .ok_or(super::NotSupportedError::NoSupportedDeviceFound)?; - let queue = device.new_command_queue(); + let queue = device.newCommandQueue().unwrap(); let auto_capture_everything = false; let capture = if desc.capture && auto_capture_everything { - objc::rc::autoreleasepool(|| { - let capture_manager = metal::CaptureManager::shared(); - let default_capture_scope = capture_manager.new_capture_scope_with_device(&device); - capture_manager.set_default_capture_scope(&default_capture_scope); - capture_manager.start_capture_with_scope(&default_capture_scope); - default_capture_scope.begin_scope(); + use metal::MTLCaptureScope as _; + objc2::rc::autoreleasepool(|_| { + let capture_manager = metal::MTLCaptureManager::sharedCaptureManager(); + let default_capture_scope = capture_manager.newCaptureScopeWithDevice(&device); + capture_manager.setDefaultCaptureScope(Some(&default_capture_scope)); + let capture_desc = metal::MTLCaptureDescriptor::new(); + capture_desc.set_capture_scope(&default_capture_scope); + capture_manager + .startCaptureWithDescriptor_error(&capture_desc) + .unwrap(); + default_capture_scope.beginScope(); Some(capture_manager.to_owned()) }) } else { @@ -456,15 +469,18 @@ impl Context { let mut timestamp_counter_set = None; if desc.timing { - for counter_set in device.counter_sets() { - if counter_set.name() == "timestamp" { - timestamp_counter_set = Some(counter_set); + use metal::MTLCounterSet as _; + if let Some(counter_sets) = device.counterSets() { + for counter_set in counter_sets { + if counter_set.name().as_ref() == objc2_foundation::ns_string!("timestamp") { + timestamp_counter_set = Some(counter_set); + } } } if timestamp_counter_set.is_none() { log::warn!("Timing counters are not supported by the device"); } else if !device - .supports_counter_sampling(metal::MTLCounterSamplingPoint::AtStageBoundary) + .supportsCounterSampling(metal::MTLCounterSamplingPoint::AtStageBoundary) { log::warn!("Timing counters do not support stage boundary"); timestamp_counter_set = None; @@ -475,24 +491,25 @@ impl Context { device: Mutex::new(device), queue: Arc::new(Mutex::new(queue)), capture, + timestamp_counter_set, info: PrivateInfo { //TODO: determine based on OS version - language_version: metal::MTLLanguageVersion::V2_4, + language_version: metal::MTLLanguageVersion::MTLLanguageVersion2_4, enable_debug_groups: desc.capture, enable_dispatch_type: true, - timestamp_counter_set, }, device_information, }) } pub fn capabilities(&self) -> crate::Capabilities { + use metal::MTLDevice as _; let device = self.device.lock().unwrap(); crate::Capabilities { - ray_query: if device.supports_family(metal::MTLGPUFamily::Apple6) { + ray_query: if device.supportsFamily(metal::MTLGPUFamily::Apple6) { crate::ShaderVisibility::all() - } else if device.supports_family(metal::MTLGPUFamily::Mac2) - || device.supports_family(metal::MTLGPUFamily::Metal3) + } else if device.supportsFamily(metal::MTLGPUFamily::Mac2) + || device.supportsFamily(metal::MTLGPUFamily::Metal3) { crate::ShaderVisibility::COMPUTE } else { @@ -507,7 +524,7 @@ impl Context { /// Get an MTLDevice of this context. /// This is platform specific API. - pub fn metal_device(&self) -> metal::Device { + pub fn metal_device(&self) -> Retained> { self.device.lock().unwrap().clone() } } @@ -518,20 +535,27 @@ impl crate::traits::CommandDevice for Context { type SyncPoint = SyncPoint; fn create_command_encoder(&self, desc: super::CommandEncoderDesc) -> CommandEncoder { - let timing_datas = if let Some(ref counter_set) = self.info.timestamp_counter_set { + use metal::MTLDevice as _; + + let timing_datas = if let Some(ref counter_set) = self.timestamp_counter_set { let mut array = Vec::with_capacity(desc.buffer_count as usize); - let csb_desc = metal::CounterSampleBufferDescriptor::new(); - csb_desc.set_counter_set(counter_set); - csb_desc.set_storage_mode(metal::MTLStorageMode::Shared); - csb_desc.set_sample_count(MAX_TIMESTAMPS); + let csb_desc = unsafe { + let desc = metal::MTLCounterSampleBufferDescriptor::new(); + desc.setCounterSet(Some(counter_set)); + desc.setStorageMode(metal::MTLStorageMode::Shared); + desc.setSampleCount(MAX_TIMESTAMPS); + desc + }; for i in 0..desc.buffer_count { - csb_desc.set_label(&format!("{}/counter{}", desc.name, i)); - let sample_buffer = self - .device - .lock() - .unwrap() - .new_counter_sample_buffer_with_descriptor(&csb_desc) - .unwrap(); + let label = format!("{}/counter{}", desc.name, i); + let sample_buffer = unsafe { + csb_desc.setLabel(&objc2_foundation::NSString::from_str(&label)); + self.device + .lock() + .unwrap() + .newCounterSampleBufferWithDescriptor_error(&csb_desc) + .unwrap() + }; array.push(TimingData { sample_buffer, pass_names: Vec::new(), @@ -541,6 +565,7 @@ impl crate::traits::CommandDevice for Context { } else { None }; + CommandEncoder { raw: None, name: desc.name.to_string(), @@ -556,12 +581,14 @@ impl crate::traits::CommandDevice for Context { fn destroy_command_encoder(&self, _command_encoder: &mut CommandEncoder) {} fn submit(&self, encoder: &mut CommandEncoder) -> SyncPoint { + use metal::MTLCommandBuffer as _; let cmd_buf = encoder.finish(); cmd_buf.commit(); SyncPoint { cmd_buf } } fn wait_for(&self, sp: &SyncPoint, timeout_ms: u32) -> bool { + use metal::MTLCommandBuffer as _; let start = time::Instant::now(); loop { if let metal::MTLCommandBufferStatus::Completed = sp.cmd_buf.status() { @@ -577,68 +604,50 @@ impl crate::traits::CommandDevice for Context { impl Drop for Context { fn drop(&mut self) { + use metal::MTLCaptureScope as _; if let Some(capture_manager) = self.capture.take() { - if let Some(scope) = capture_manager.default_capture_scope() { - scope.end_scope(); + if let Some(scope) = capture_manager.defaultCaptureScope() { + scope.endScope(); } - capture_manager.stop_capture(); + capture_manager.stopCapture(); } } } fn make_bottom_level_acceleration_structure_desc( meshes: &[crate::AccelerationStructureMesh], -) -> metal::PrimitiveAccelerationStructureDescriptor { +) -> Retained { let mut geometry_descriptors = Vec::with_capacity(meshes.len()); for mesh in meshes { - let descriptor = metal::AccelerationStructureTriangleGeometryDescriptor::descriptor(); - descriptor.set_opaque(mesh.is_opaque); - descriptor.set_vertex_buffer(Some(mesh.vertex_data.buffer.as_ref())); - descriptor.set_vertex_buffer_offset(mesh.vertex_data.offset); - descriptor.set_vertex_stride(mesh.vertex_stride as _); - descriptor.set_triangle_count(mesh.triangle_count as _); - if let Some(index_type) = mesh.index_type { - descriptor.set_index_buffer(Some(mesh.index_data.buffer.as_ref())); - descriptor.set_index_buffer_offset(mesh.index_data.offset); - descriptor.set_index_type(map_index_type(index_type)); - } - //TODO: requires macOS-13 ? - if false { - let (_, attribute_format) = map_vertex_format(mesh.vertex_format); - descriptor.set_vertex_format(attribute_format); - if !mesh.transform_data.buffer.raw.is_null() { - descriptor - .set_transformation_matrix_buffer(Some(mesh.transform_data.buffer.as_ref())); - descriptor.set_transformation_matrix_buffer_offset(mesh.transform_data.offset); + geometry_descriptors.push(unsafe { + let descriptor = metal::MTLAccelerationStructureTriangleGeometryDescriptor::new(); + descriptor.setOpaque(mesh.is_opaque); + descriptor.setVertexBuffer(Some(mesh.vertex_data.buffer.as_ref())); + descriptor.setVertexBufferOffset(mesh.vertex_data.offset as usize); + descriptor.setVertexStride(mesh.vertex_stride as _); + descriptor.setTriangleCount(mesh.triangle_count as _); + if let Some(index_type) = mesh.index_type { + descriptor.setIndexBuffer(Some(mesh.index_data.buffer.as_ref())); + descriptor.setIndexBufferOffset(mesh.index_data.offset as usize); + descriptor.setIndexType(map_index_type(index_type)); } - } - geometry_descriptors.push(metal::AccelerationStructureGeometryDescriptor::from( - descriptor, - )); + //TODO: requires macOS-13 ? + if false { + let (_, attribute_format) = map_vertex_format(mesh.vertex_format); + descriptor.setVertexFormat(attribute_format); + if !mesh.transform_data.buffer.raw.is_null() { + descriptor + .setTransformationMatrixBuffer(Some(mesh.transform_data.buffer.as_ref())); + descriptor + .setTransformationMatrixBufferOffset(mesh.transform_data.offset as usize); + } + } + Retained::cast(descriptor) + }); } - let geometry_descriptor_array = metal::Array::from_owned_slice(&geometry_descriptors); - let accel_descriptor = metal::PrimitiveAccelerationStructureDescriptor::descriptor(); - accel_descriptor.set_geometry_descriptors(geometry_descriptor_array); + let geometry_descriptor_array = objc2_foundation::NSArray::from_vec(geometry_descriptors); + let accel_descriptor = metal::MTLPrimitiveAccelerationStructureDescriptor::descriptor(); + accel_descriptor.setGeometryDescriptors(Some(&geometry_descriptor_array)); accel_descriptor } - -fn _print_class_methods(class: &objc::runtime::Class) { - let mut count = 0; - let methods = unsafe { objc::runtime::class_copyMethodList(class, &mut count) }; - println!("Class {} methods:", class.name()); - for i in 0..count { - let method = unsafe { &**methods.add(i as usize) }; - println!("\t{}", method.name().name()); - } -} - -fn _print_class_methods_by_name(class_name: &str) { - let class = objc::runtime::Class::get(class_name).unwrap(); - _print_class_methods(class); -} - -fn _print_class_methods_by_object(foreign_object: &impl metal::foreign_types::ForeignType) { - let object = foreign_object.as_ptr() as *mut objc::runtime::Object; - _print_class_methods(unsafe { &*object }.class()); -} diff --git a/blade-graphics/src/metal/pipeline.rs b/blade-graphics/src/metal/pipeline.rs index 2be2efe..e232bd8 100644 --- a/blade-graphics/src/metal/pipeline.rs +++ b/blade-graphics/src/metal/pipeline.rs @@ -1,36 +1,39 @@ use naga::back::msl; +use objc2::{rc::Retained, runtime::ProtocolObject}; +use objc2_foundation::NSString; +use objc2_metal::{self as metal, MTLDevice, MTLLibrary}; fn map_blend_factor(factor: crate::BlendFactor) -> metal::MTLBlendFactor { use crate::BlendFactor as Bf; - use metal::MTLBlendFactor::*; + use metal::MTLBlendFactor as Mbf; match factor { - Bf::Zero => Zero, - Bf::One => One, - Bf::Src => SourceColor, - Bf::OneMinusSrc => OneMinusSourceColor, - Bf::Dst => DestinationColor, - Bf::OneMinusDst => OneMinusDestinationColor, - Bf::SrcAlpha => SourceAlpha, - Bf::OneMinusSrcAlpha => OneMinusSourceAlpha, - Bf::DstAlpha => DestinationAlpha, - Bf::OneMinusDstAlpha => OneMinusDestinationAlpha, - Bf::Constant => BlendColor, - Bf::OneMinusConstant => OneMinusBlendColor, - Bf::SrcAlphaSaturated => SourceAlphaSaturated, + Bf::Zero => Mbf::Zero, + Bf::One => Mbf::One, + Bf::Src => Mbf::SourceColor, + Bf::OneMinusSrc => Mbf::OneMinusSourceColor, + Bf::Dst => Mbf::DestinationColor, + Bf::OneMinusDst => Mbf::OneMinusDestinationColor, + Bf::SrcAlpha => Mbf::SourceAlpha, + Bf::OneMinusSrcAlpha => Mbf::OneMinusSourceAlpha, + Bf::DstAlpha => Mbf::DestinationAlpha, + Bf::OneMinusDstAlpha => Mbf::OneMinusDestinationAlpha, + Bf::Constant => Mbf::BlendColor, + Bf::OneMinusConstant => Mbf::OneMinusBlendColor, + Bf::SrcAlphaSaturated => Mbf::SourceAlphaSaturated, } } fn map_blend_op(operation: crate::BlendOperation) -> metal::MTLBlendOperation { use crate::BlendOperation as Bo; - use metal::MTLBlendOperation::*; + use metal::MTLBlendOperation as Mbo; match operation { - Bo::Add => Add, - Bo::Subtract => Subtract, - Bo::ReverseSubtract => ReverseSubtract, - Bo::Min => Min, - Bo::Max => Max, + Bo::Add => Mbo::Add, + Bo::Subtract => Mbo::Subtract, + Bo::ReverseSubtract => Mbo::ReverseSubtract, + Bo::Min => Mbo::Min, + Bo::Max => Mbo::Max, } } @@ -50,17 +53,17 @@ fn map_blend_component( fn map_stencil_op(op: crate::StencilOperation) -> metal::MTLStencilOperation { use crate::StencilOperation as So; - use metal::MTLStencilOperation::*; + use metal::MTLStencilOperation as Mso; match op { - So::Keep => Keep, - So::Zero => Zero, - So::Replace => Replace, - So::IncrementClamp => IncrementClamp, - So::IncrementWrap => IncrementWrap, - So::DecrementClamp => DecrementClamp, - So::DecrementWrap => DecrementWrap, - So::Invert => Invert, + So::Keep => Mso::Keep, + So::Zero => Mso::Zero, + So::Replace => Mso::Replace, + So::IncrementClamp => Mso::IncrementClamp, + So::IncrementWrap => Mso::IncrementWrap, + So::DecrementClamp => Mso::DecrementClamp, + So::DecrementWrap => Mso::DecrementWrap, + So::Invert => Mso::Invert, } } @@ -68,38 +71,40 @@ fn create_stencil_desc( face: &crate::StencilFaceState, read_mask: u32, write_mask: u32, -) -> metal::StencilDescriptor { - let desc = metal::StencilDescriptor::new(); - desc.set_stencil_compare_function(super::map_compare_function(face.compare)); - desc.set_read_mask(read_mask); - desc.set_write_mask(write_mask); - desc.set_stencil_failure_operation(map_stencil_op(face.fail_op)); - desc.set_depth_failure_operation(map_stencil_op(face.depth_fail_op)); - desc.set_depth_stencil_pass_operation(map_stencil_op(face.pass_op)); +) -> Retained { + let desc = unsafe { metal::MTLStencilDescriptor::new() }; + desc.setStencilCompareFunction(super::map_compare_function(face.compare)); + desc.setReadMask(read_mask); + desc.setWriteMask(write_mask); + desc.setStencilFailureOperation(map_stencil_op(face.fail_op)); + desc.setDepthFailureOperation(map_stencil_op(face.depth_fail_op)); + desc.setDepthStencilPassOperation(map_stencil_op(face.pass_op)); desc } -fn create_depth_stencil_desc(state: &crate::DepthStencilState) -> metal::DepthStencilDescriptor { - let desc = metal::DepthStencilDescriptor::new(); - desc.set_depth_compare_function(super::map_compare_function(state.depth_compare)); - desc.set_depth_write_enabled(state.depth_write_enabled); +fn create_depth_stencil_desc( + state: &crate::DepthStencilState, +) -> Retained { + let desc = unsafe { metal::MTLDepthStencilDescriptor::new() }; + desc.setDepthCompareFunction(super::map_compare_function(state.depth_compare)); + desc.setDepthWriteEnabled(state.depth_write_enabled); let s = &state.stencil; if s.front != crate::StencilFaceState::IGNORE { let face_desc = create_stencil_desc(&s.front, s.read_mask, s.write_mask); - desc.set_front_face_stencil(Some(&face_desc)); + desc.setFrontFaceStencil(Some(&face_desc)); } if s.back != crate::StencilFaceState::IGNORE { let face_desc = create_stencil_desc(&s.back, s.read_mask, s.write_mask); - desc.set_back_face_stencil(Some(&face_desc)); + desc.setBackFaceStencil(Some(&face_desc)); } desc } struct CompiledShader { - library: metal::Library, - function: metal::Function, + library: Retained>, + function: Retained>, attribute_mappings: Vec, wg_size: metal::MTLSize, wg_memory_sizes: Vec, @@ -267,8 +272,8 @@ impl super::Context { let naga_options = msl::Options { lang_version: ( - (self.info.language_version as u32 >> 16) as u8, - self.info.language_version as u8, + (self.info.language_version.0 as u32 >> 16) as u8, + self.info.language_version.0 as u8, ), inline_samplers: Default::default(), spirv_cross_compatibility: false, @@ -295,18 +300,18 @@ impl super::Context { &source ); - let options = metal::CompileOptions::new(); - options.set_language_version(self.info.language_version); - options.set_preserve_invariance(true); + let source_string = NSString::from_str(&source); + let options = metal::MTLCompileOptions::new(); + options.setLanguageVersion(self.info.language_version); + options.setPreserveInvariance(true); let library = self .device .lock() .unwrap() - .new_library_with_source(source.as_ref(), &options) + .newLibraryWithSource_options_error(&source_string, Some(&options)) .unwrap_or_else(|err| { - let string = err.replace("\\n", "\n"); - panic!("MSL compilation error:\n{}", string); + panic!("MSL compilation error:\n{}", err.localizedDescription()); }); let ep = &module.entry_points[ep_index]; @@ -317,7 +322,8 @@ impl super::Context { depth: ep.workgroup_size[2] as _, }; - let function = library.get_function(name, None).unwrap(); + let name_string = NSString::from_str(name); + let function = library.newFunctionWithName(&name_string).unwrap(); CompiledShader { library, @@ -335,11 +341,10 @@ impl crate::traits::ShaderDevice for super::Context { type RenderPipeline = super::RenderPipeline; fn create_compute_pipeline(&self, desc: crate::ComputePipelineDesc) -> super::ComputePipeline { + use metal::MTLDevice as _; let mut layout = make_pipeline_layout(desc.data_layouts, 0); - objc::rc::autoreleasepool(|| { - let descriptor = metal::ComputePipelineDescriptor::new(); - + objc2::rc::autoreleasepool(|_| { let cs = self.load_shader( desc.compute, desc.data_layouts, @@ -347,17 +352,14 @@ impl crate::traits::ShaderDevice for super::Context { &mut layout, ShaderFlags::empty(), ); - descriptor.set_compute_function(Some(&cs.function)); - - if !desc.name.is_empty() { - descriptor.set_label(desc.name); - } + //TODO: use `newComputePipelineStateWithDescriptor_options_reflection` + // https://github.com/madsmtm/objc2/issues/683 let raw = self .device .lock() .unwrap() - .new_compute_pipeline_state(&descriptor) + .newComputePipelineStateWithFunction_error(&cs.function) .unwrap(); super::ComputePipeline { @@ -406,8 +408,8 @@ impl crate::traits::ShaderDevice for super::Context { ), }; - objc::rc::autoreleasepool(|| { - let descriptor = metal::RenderPipelineDescriptor::new(); + objc2::rc::autoreleasepool(|_| { + let descriptor = metal::MTLRenderPipelineDescriptor::new(); let vs = self.load_shader( desc.vertex, @@ -419,46 +421,54 @@ impl crate::traits::ShaderDevice for super::Context { _ => ShaderFlags::empty(), }, ); - descriptor.set_vertex_function(Some(&vs.function)); - descriptor.set_raster_sample_count(desc.multisample_state.sample_count as _); - descriptor.set_alpha_to_coverage_enabled(desc.multisample_state.alpha_to_coverage); + descriptor.setVertexFunction(Some(&vs.function)); + descriptor.setRasterSampleCount(desc.multisample_state.sample_count as _); + descriptor.setAlphaToCoverageEnabled(desc.multisample_state.alpha_to_coverage); // Fragment shader - let fs = desc.fragment.map(|desc_fragment| { - self.load_shader( - desc_fragment, + let fs_lib = if let Some(desc_fs) = desc.fragment { + let fs = self.load_shader( + desc_fs, desc.data_layouts, &[], &mut layout, ShaderFlags::empty(), - ) - }); - descriptor.set_fragment_function(fs.as_ref().map(|fs| fs.function.as_ref())); + ); + descriptor.setFragmentFunction(Some(&fs.function)); + Some(fs.library) + } else { + None + }; - let vertex_descriptor = metal::VertexDescriptor::new(); + let vertex_descriptor = unsafe { metal::MTLVertexDescriptor::new() }; for (i, vf) in desc.vertex_fetches.iter().enumerate() { - let buffer_desc = vertex_descriptor.layouts().object_at(i as u64).unwrap(); - buffer_desc.set_stride(vf.layout.stride as u64); - buffer_desc.set_step_function(if vf.instanced { - metal::MTLVertexStepFunction::PerInstance - } else { - metal::MTLVertexStepFunction::PerVertex - }); + unsafe { + let buffer_desc = vertex_descriptor.layouts().objectAtIndexedSubscript(i); + buffer_desc.setStride(vf.layout.stride as usize); + buffer_desc.setStepFunction(if vf.instanced { + metal::MTLVertexStepFunction::PerInstance + } else { + metal::MTLVertexStepFunction::PerVertex + }) + }; } for (i, mapping) in vs.attribute_mappings.into_iter().enumerate() { - let attribute_desc = vertex_descriptor.attributes().object_at(i as u64).unwrap(); let vf = &desc.vertex_fetches[mapping.buffer_index]; let (_, attrib) = vf.layout.attributes[mapping.attribute_index]; let (vertex_format, _) = super::map_vertex_format(attrib.format); - attribute_desc.set_format(vertex_format); - attribute_desc.set_buffer_index(mapping.buffer_index as u64); - attribute_desc.set_offset(attrib.offset as u64); + unsafe { + let attribute_desc = vertex_descriptor.attributes().objectAtIndexedSubscript(i); + attribute_desc.setFormat(vertex_format); + attribute_desc.setBufferIndex(mapping.buffer_index); + attribute_desc.setOffset(attrib.offset as usize); + } } - descriptor.set_vertex_descriptor(Some(vertex_descriptor)); + descriptor.setVertexDescriptor(Some(&vertex_descriptor)); for (i, ct) in desc.color_targets.iter().enumerate() { - let at_descriptor = descriptor.color_attachments().object_at(i as u64).unwrap(); - at_descriptor.set_pixel_format(super::map_texture_format(ct.format)); + let at_descriptor = + unsafe { descriptor.colorAttachments().objectAtIndexedSubscript(i) }; + at_descriptor.setPixelFormat(super::map_texture_format(ct.format)); let mut write_mask = metal::MTLColorWriteMask::empty(); if ct.write_mask.contains(crate::ColorWrites::RED) { @@ -473,27 +483,27 @@ impl crate::traits::ShaderDevice for super::Context { if ct.write_mask.contains(crate::ColorWrites::ALPHA) { write_mask |= metal::MTLColorWriteMask::Alpha; } - at_descriptor.set_write_mask(write_mask); + at_descriptor.setWriteMask(write_mask); if let Some(ref blend) = ct.blend { - at_descriptor.set_blending_enabled(true); + at_descriptor.setBlendingEnabled(true); let (color_op, color_src, color_dst) = map_blend_component(&blend.color); let (alpha_op, alpha_src, alpha_dst) = map_blend_component(&blend.alpha); - at_descriptor.set_rgb_blend_operation(color_op); - at_descriptor.set_source_rgb_blend_factor(color_src); - at_descriptor.set_destination_rgb_blend_factor(color_dst); + at_descriptor.setRgbBlendOperation(color_op); + at_descriptor.setSourceRGBBlendFactor(color_src); + at_descriptor.setDestinationRGBBlendFactor(color_dst); - at_descriptor.set_alpha_blend_operation(alpha_op); - at_descriptor.set_source_alpha_blend_factor(alpha_src); - at_descriptor.set_destination_alpha_blend_factor(alpha_dst); + at_descriptor.setAlphaBlendOperation(alpha_op); + at_descriptor.setSourceAlphaBlendFactor(alpha_src); + at_descriptor.setDestinationAlphaBlendFactor(alpha_dst); } } let depth_stencil = match desc.depth_stencil { Some(ref ds) => { let raw_format = super::map_texture_format(ds.format); - descriptor.set_depth_attachment_pixel_format(raw_format); + descriptor.setDepthAttachmentPixelFormat(raw_format); //TODO: descriptor.set_stencil_attachment_pixel_format(raw_format); let ds_descriptor = create_depth_stencil_desc(ds); @@ -501,28 +511,29 @@ impl crate::traits::ShaderDevice for super::Context { .device .lock() .unwrap() - .new_depth_stencil_state(&ds_descriptor); + .newDepthStencilStateWithDescriptor(&ds_descriptor) + .unwrap(); Some((raw, ds.bias)) } None => None, }; if !desc.name.is_empty() { - descriptor.set_label(desc.name); + descriptor.setLabel(Some(&NSString::from_str(desc.name))); } let raw = self .device .lock() .unwrap() - .new_render_pipeline_state(&descriptor) + .newRenderPipelineStateWithDescriptor_error(&descriptor) .unwrap(); super::RenderPipeline { raw, name: desc.name.to_string(), vs_lib: vs.library, - fs_lib: fs.map(|fs| fs.library), + fs_lib, layout, primitive_type, triangle_fill_mode, diff --git a/blade-graphics/src/metal/resource.rs b/blade-graphics/src/metal/resource.rs index 84e2a84..0e56a1d 100644 --- a/blade-graphics/src/metal/resource.rs +++ b/blade-graphics/src/metal/resource.rs @@ -1,5 +1,8 @@ -use objc::{msg_send, sel, sel_impl}; -use std::mem; +use metal::{MTLDevice as _, MTLResource as _}; +use objc2::rc::Retained; +use objc2_foundation::{NSRange, NSString}; +use objc2_metal::{self as metal, MTLTexture}; +use std::{mem, ptr}; fn map_texture_usage(usage: crate::TextureUsage) -> metal::MTLTextureUsage { use crate::TextureUsage as Tu; @@ -22,58 +25,61 @@ fn map_texture_usage(usage: crate::TextureUsage) -> metal::MTLTextureUsage { mtl_usage } -fn map_view_dimension(dimension: crate::ViewDimension, sample_count: u64) -> metal::MTLTextureType { +fn map_view_dimension( + dimension: crate::ViewDimension, + sample_count: usize, +) -> metal::MTLTextureType { use crate::ViewDimension as Vd; - use metal::MTLTextureType::*; + use metal::MTLTextureType as Mtt; match dimension { - Vd::D1 => D1, - Vd::D1Array => D1Array, + Vd::D1 => Mtt::MTLTextureType1D, + Vd::D1Array => Mtt::MTLTextureType1DArray, Vd::D2 => { if sample_count <= 1 { - D2 + Mtt::MTLTextureType2D } else { - D2Multisample + Mtt::MTLTextureType2DMultisample } } Vd::D2Array => { if sample_count <= 1 { - D2Array + Mtt::MTLTextureType2DArray } else { - D2MultisampleArray + Mtt::MTLTextureType2DMultisampleArray } } - Vd::D3 => D3, - Vd::Cube => Cube, - Vd::CubeArray => CubeArray, + Vd::D3 => Mtt::MTLTextureType3D, + Vd::Cube => Mtt::Cube, + Vd::CubeArray => Mtt::CubeArray, } } fn map_filter_mode(filter: crate::FilterMode) -> metal::MTLSamplerMinMagFilter { - use metal::MTLSamplerMinMagFilter::*; + use metal::MTLSamplerMinMagFilter as Msf; match filter { - crate::FilterMode::Nearest => Nearest, - crate::FilterMode::Linear => Linear, + crate::FilterMode::Nearest => Msf::Nearest, + crate::FilterMode::Linear => Msf::Linear, } } fn map_address_mode(address: crate::AddressMode) -> metal::MTLSamplerAddressMode { use crate::AddressMode as Am; - use metal::MTLSamplerAddressMode::*; + use metal::MTLSamplerAddressMode as Msam; match address { - Am::Repeat => Repeat, - Am::MirrorRepeat => MirrorRepeat, - Am::ClampToEdge => ClampToEdge, - Am::ClampToBorder => ClampToBorderColor, + Am::Repeat => Msam::Repeat, + Am::MirrorRepeat => Msam::MirrorRepeat, + Am::ClampToEdge => Msam::ClampToEdge, + Am::ClampToBorder => Msam::ClampToBorderColor, } } fn map_border_color(color: crate::TextureColor) -> metal::MTLSamplerBorderColor { use crate::TextureColor as Tc; - use metal::MTLSamplerBorderColor::*; + use metal::MTLSamplerBorderColor as Msbc; match color { - Tc::TransparentBlack => TransparentBlack, - Tc::OpaqueBlack => OpaqueBlack, - Tc::White => OpaqueWhite, + Tc::TransparentBlack => Msbc::TransparentBlack, + Tc::OpaqueBlack => Msbc::OpaqueBlack, + Tc::White => Msbc::OpaqueWhite, } } @@ -87,11 +93,11 @@ impl super::Context { .device .lock() .unwrap() - .acceleration_structure_sizes_with_descriptor(&descriptor); + .accelerationStructureSizesWithDescriptor(&descriptor); crate::AccelerationStructureSizes { - data: accel_sizes.acceleration_structure_size, - scratch: accel_sizes.build_scratch_buffer_size, + data: accel_sizes.accelerationStructureSize as u64, + scratch: accel_sizes.buildScratchBufferSize as u64, } } @@ -99,18 +105,18 @@ impl super::Context { &self, instance_count: u32, ) -> crate::AccelerationStructureSizes { - let descriptor = metal::InstanceAccelerationStructureDescriptor::descriptor(); - descriptor.set_instance_count(instance_count as _); + let descriptor = metal::MTLInstanceAccelerationStructureDescriptor::descriptor(); + descriptor.setInstanceCount(instance_count as _); let accel_sizes = self .device .lock() .unwrap() - .acceleration_structure_sizes_with_descriptor(&descriptor); + .accelerationStructureSizesWithDescriptor(&descriptor); crate::AccelerationStructureSizes { - data: accel_sizes.acceleration_structure_size, - scratch: accel_sizes.build_scratch_buffer_size, + data: accel_sizes.accelerationStructureSize as u64, + scratch: accel_sizes.buildScratchBufferSize as u64, } } @@ -119,26 +125,46 @@ impl super::Context { instances: &[crate::AccelerationStructureInstance], _bottom_level: &[super::AccelerationStructure], ) -> super::Buffer { + fn packed_vec(v: mint::Vector3) -> metal::MTLPackedFloat3 { + metal::MTLPackedFloat3 { + x: v.x, + y: v.y, + z: v.z, + } + } let mut instance_descriptors = Vec::with_capacity(instances.len()); for instance in instances { let transposed = mint::ColumnMatrix3x4::from(instance.transform); instance_descriptors.push(metal::MTLAccelerationStructureUserIDInstanceDescriptor { - acceleration_structure_index: instance.acceleration_structure_index, + transformationMatrix: metal::MTLPackedFloat4x3 { + columns: [ + packed_vec(transposed.x), + packed_vec(transposed.y), + packed_vec(transposed.z), + packed_vec(transposed.w), + ], + }, + options: metal::MTLAccelerationStructureInstanceOptions::MTLAccelerationStructureInstanceOptionNone, mask: instance.mask, - transformation_matrix: transposed.into(), - options: metal::MTLAccelerationStructureInstanceOptions::None, - intersection_function_table_offset: 0, - user_id: instance.custom_index, + intersectionFunctionTableOffset: 0, + accelerationStructureIndex: instance.acceleration_structure_index, + userID: instance.custom_index, }); } - let buffer = self.device.lock().unwrap().new_buffer_with_data( - instance_descriptors.as_ptr() as *const _, - (mem::size_of::() - * instances.len()) as _, - metal::MTLResourceOptions::StorageModeShared, - ); + let object = objc2::rc::autoreleasepool(|_| unsafe { + self.device + .lock() + .unwrap() + .newBufferWithBytes_length_options( + ptr::NonNull::new(instance_descriptors.as_ptr() as *mut _).unwrap(), + mem::size_of::() + * instances.len(), + metal::MTLResourceOptions::MTLResourceStorageModeShared, + ) + .unwrap() + }); super::Buffer { - raw: unsafe { msg_send![buffer.as_ref(), retain] }, + raw: Retained::into_raw(object), } } } @@ -153,29 +179,32 @@ impl crate::traits::ResourceDevice for super::Context { fn create_buffer(&self, desc: crate::BufferDesc) -> super::Buffer { let options = match desc.memory { - crate::Memory::Device => metal::MTLResourceOptions::StorageModePrivate, - crate::Memory::Shared => metal::MTLResourceOptions::StorageModeShared, + crate::Memory::Device => metal::MTLResourceOptions::MTLResourceStorageModePrivate, + crate::Memory::Shared => metal::MTLResourceOptions::MTLResourceStorageModeShared, crate::Memory::Upload => { - metal::MTLResourceOptions::StorageModeShared - | metal::MTLResourceOptions::CPUCacheModeWriteCombined + metal::MTLResourceOptions::MTLResourceStorageModeShared + | metal::MTLResourceOptions::MTLResourceCPUCacheModeWriteCombined } }; - let raw = objc::rc::autoreleasepool(|| { - let raw = self.device.lock().unwrap().new_buffer(desc.size, options); - if !desc.name.is_empty() { - raw.set_label(&desc.name); - } - unsafe { msg_send![raw.as_ref(), retain] } + let object = objc2::rc::autoreleasepool(|_| { + self.device + .lock() + .unwrap() + .newBufferWithLength_options(desc.size as usize, options) + .unwrap() }); - super::Buffer { raw } + if !desc.name.is_empty() { + object.setLabel(Some(&NSString::from_str(desc.name))); + } + super::Buffer { + raw: Retained::into_raw(object), + } } fn sync_buffer(&self, _buffer: super::Buffer) {} fn destroy_buffer(&self, buffer: super::Buffer) { - unsafe { - let () = msg_send![buffer.raw, release]; - } + let _ = unsafe { Retained::from_raw(buffer.raw) }; } fn create_texture(&self, desc: crate::TextureDesc) -> super::Texture { @@ -184,59 +213,59 @@ impl crate::traits::ResourceDevice for super::Context { let mtl_type = match desc.dimension { crate::TextureDimension::D1 => { if desc.array_layer_count > 1 { - metal::MTLTextureType::D1Array + metal::MTLTextureType::MTLTextureType1DArray } else { - metal::MTLTextureType::D1 + metal::MTLTextureType::MTLTextureType1D } } crate::TextureDimension::D2 => { if desc.array_layer_count > 1 { if desc.sample_count <= 1 { - metal::MTLTextureType::D2Array + metal::MTLTextureType::MTLTextureType2DArray } else { - metal::MTLTextureType::D2MultisampleArray + metal::MTLTextureType::MTLTextureType2DMultisampleArray } } else { if desc.sample_count <= 1 { - metal::MTLTextureType::D2 + metal::MTLTextureType::MTLTextureType2D } else { - metal::MTLTextureType::D2Multisample + metal::MTLTextureType::MTLTextureType2DMultisample } } } - crate::TextureDimension::D3 => metal::MTLTextureType::D3, + crate::TextureDimension::D3 => metal::MTLTextureType::MTLTextureType3D, }; let mtl_usage = map_texture_usage(desc.usage); - let raw = objc::rc::autoreleasepool(|| { - let descriptor = metal::TextureDescriptor::new(); - - descriptor.set_texture_type(mtl_type); - descriptor.set_width(desc.size.width as u64); - descriptor.set_height(desc.size.height as u64); - descriptor.set_depth(desc.size.depth as u64); - descriptor.set_array_length(desc.array_layer_count as u64); - descriptor.set_mipmap_level_count(desc.mip_level_count as u64); - descriptor.set_pixel_format(mtl_format); - descriptor.set_sample_count(desc.sample_count as _); - descriptor.set_usage(mtl_usage); - descriptor.set_storage_mode(metal::MTLStorageMode::Private); - - let raw = self.device.lock().unwrap().new_texture(&descriptor); - if !desc.name.is_empty() { - raw.set_label(desc.name); - } - - unsafe { msg_send![raw.as_ref(), retain] } + let object = objc2::rc::autoreleasepool(|_| unsafe { + let descriptor = metal::MTLTextureDescriptor::new(); + descriptor.setTextureType(mtl_type); + descriptor.setWidth(desc.size.width as usize); + descriptor.setHeight(desc.size.height as usize); + descriptor.setDepth(desc.size.depth as usize); + descriptor.setArrayLength(desc.array_layer_count as usize); + descriptor.setMipmapLevelCount(desc.mip_level_count as usize); + descriptor.setPixelFormat(mtl_format); + descriptor.setSampleCount(desc.sample_count as _); + descriptor.setUsage(mtl_usage); + descriptor.setStorageMode(metal::MTLStorageMode::Private); + + self.device + .lock() + .unwrap() + .newTextureWithDescriptor(&descriptor) + .unwrap() }); - - super::Texture { raw } + if !desc.name.is_empty() { + object.setLabel(Some(&NSString::from_str(desc.name))); + } + super::Texture { + raw: Retained::into_raw(object), + } } fn destroy_texture(&self, texture: super::Texture) { - unsafe { - let () = msg_send![texture.raw, release]; - } + let _ = unsafe { Retained::from_raw(texture.raw) }; } fn create_texture_view( @@ -246,115 +275,120 @@ impl crate::traits::ResourceDevice for super::Context { ) -> super::TextureView { let texture = texture.as_ref(); let mtl_format = super::map_texture_format(desc.format); - let mtl_type = map_view_dimension(desc.dimension, texture.sample_count()); + let mtl_type = map_view_dimension(desc.dimension, texture.sampleCount()); let mip_level_count = match desc.subresources.mip_level_count { - Some(count) => count.get() as u64, - None => texture.mipmap_level_count() - desc.subresources.base_mip_level as u64, + Some(count) => count.get() as usize, + None => texture.mipmapLevelCount() - desc.subresources.base_mip_level as usize, }; let array_layer_count = match desc.subresources.array_layer_count { - Some(count) => count.get() as u64, - None => texture.array_length() - desc.subresources.base_array_layer as u64, + Some(count) => count.get() as usize, + None => texture.arrayLength() - desc.subresources.base_array_layer as usize, }; - let raw = objc::rc::autoreleasepool(|| { - let raw = texture.new_texture_view_from_slice( - mtl_format, - mtl_type, - metal::NSRange { - location: desc.subresources.base_mip_level as _, - length: mip_level_count, - }, - metal::NSRange { - location: desc.subresources.base_array_layer as _, - length: array_layer_count, - }, - ); - if !desc.name.is_empty() { - raw.set_label(desc.name); - } - unsafe { msg_send![raw.as_ref(), retain] } + let object = objc2::rc::autoreleasepool(|_| unsafe { + texture + .newTextureViewWithPixelFormat_textureType_levels_slices( + mtl_format, + mtl_type, + NSRange { + location: desc.subresources.base_mip_level as _, + length: mip_level_count, + }, + NSRange { + location: desc.subresources.base_array_layer as _, + length: array_layer_count, + }, + ) + .unwrap() }); - super::TextureView { raw } + if !desc.name.is_empty() { + object.setLabel(Some(&NSString::from_str(desc.name))); + } + super::TextureView { + raw: Retained::into_raw(object), + } } fn destroy_texture_view(&self, view: super::TextureView) { - unsafe { - let () = msg_send![view.raw, release]; - } + let _ = unsafe { Retained::from_raw(view.raw) }; } fn create_sampler(&self, desc: crate::SamplerDesc) -> super::Sampler { - let raw = objc::rc::autoreleasepool(|| { - let descriptor = metal::SamplerDescriptor::new(); + let object = objc2::rc::autoreleasepool(|_| { + let descriptor = metal::MTLSamplerDescriptor::new(); - descriptor.set_min_filter(map_filter_mode(desc.min_filter)); - descriptor.set_mag_filter(map_filter_mode(desc.mag_filter)); - descriptor.set_mip_filter(match desc.mipmap_filter { + descriptor.setMinFilter(map_filter_mode(desc.min_filter)); + descriptor.setMagFilter(map_filter_mode(desc.mag_filter)); + descriptor.setMipFilter(match desc.mipmap_filter { crate::FilterMode::Nearest => metal::MTLSamplerMipFilter::Nearest, crate::FilterMode::Linear => metal::MTLSamplerMipFilter::Linear, }); - descriptor.set_address_mode_s(map_address_mode(desc.address_modes[0])); - descriptor.set_address_mode_t(map_address_mode(desc.address_modes[1])); - descriptor.set_address_mode_r(map_address_mode(desc.address_modes[2])); + descriptor.setSAddressMode(map_address_mode(desc.address_modes[0])); + descriptor.setTAddressMode(map_address_mode(desc.address_modes[1])); + descriptor.setRAddressMode(map_address_mode(desc.address_modes[2])); if desc.anisotropy_clamp > 1 { - descriptor.set_max_anisotropy(desc.anisotropy_clamp as u64); + descriptor.setMaxAnisotropy(desc.anisotropy_clamp as usize); } - descriptor.set_lod_min_clamp(desc.lod_min_clamp); + descriptor.setLodMinClamp(desc.lod_min_clamp); if let Some(lod) = desc.lod_max_clamp { - descriptor.set_lod_max_clamp(lod); + descriptor.setLodMaxClamp(lod); } // optimization - descriptor.set_lod_average(true); + descriptor.setLodAverage(true); if let Some(fun) = desc.compare { - descriptor.set_compare_function(super::map_compare_function(fun)); + descriptor.setCompareFunction(super::map_compare_function(fun)); } if let Some(border_color) = desc.border_color { - descriptor.set_border_color(map_border_color(border_color)); + descriptor.setBorderColor(map_border_color(border_color)); } if !desc.name.is_empty() { - descriptor.set_label(desc.name); + descriptor.setLabel(Some(&NSString::from_str(desc.name))); } - let raw = self.device.lock().unwrap().new_sampler(&descriptor); - unsafe { msg_send![raw.as_ref(), retain] } + self.device + .lock() + .unwrap() + .newSamplerStateWithDescriptor(&descriptor) + .unwrap() }); - super::Sampler { raw } + super::Sampler { + raw: Retained::into_raw(object), + } } fn destroy_sampler(&self, sampler: super::Sampler) { - unsafe { - let () = msg_send![sampler.raw, release]; - } + let _ = unsafe { Retained::from_raw(sampler.raw) }; } fn create_acceleration_structure( &self, desc: crate::AccelerationStructureDesc, ) -> super::AccelerationStructure { - let raw = self - .device - .lock() - .unwrap() - .new_acceleration_structure_with_size(desc.size); + let object = objc2::rc::autoreleasepool(|_| { + //TODO: use `newAccelerationStructureWithDescriptor` + self.device + .lock() + .unwrap() + .newAccelerationStructureWithSize(desc.size as usize) + .unwrap() + }); if !desc.name.is_empty() { - raw.set_label(desc.name); + object.setLabel(Some(&NSString::from_str(desc.name))); } super::AccelerationStructure { - raw: unsafe { msg_send![raw.as_ref(), retain] }, + raw: Retained::into_raw(object), } } fn destroy_acceleration_structure(&self, acceleration_structure: super::AccelerationStructure) { - unsafe { - let () = msg_send![acceleration_structure.raw, release]; - } + let _ = unsafe { Retained::from_raw(acceleration_structure.raw) }; } } diff --git a/blade-graphics/src/metal/surface.rs b/blade-graphics/src/metal/surface.rs index 90e75e3..30e8759 100644 --- a/blade-graphics/src/metal/surface.rs +++ b/blade-graphics/src/metal/surface.rs @@ -1,70 +1,16 @@ -use core_graphics_types::{ - base::CGFloat, - geometry::{CGRect, CGSize}, -}; -use objc::{ - class, msg_send, - runtime::{Object, BOOL, YES}, - sel, sel_impl, -}; - -use std::{mem, ptr}; +use objc2::rc::Retained; +use objc2_foundation::CGSize; +use objc2_quartz_core::CAMetalLayer; -#[cfg(target_os = "macos")] -#[link(name = "QuartzCore", kind = "framework")] -extern "C" { - #[allow(non_upper_case_globals)] - static kCAGravityTopLeft: *mut Object; -} +const SURFACE_INFO: crate::SurfaceInfo = crate::SurfaceInfo { + format: crate::TextureFormat::Rgba8Unorm, + alpha: crate::AlphaMode::Ignored, +}; impl super::Surface { - pub unsafe fn from_view(view: *mut Object) -> Self { - let main_layer: *mut Object = msg_send![view, layer]; - let class = class!(CAMetalLayer); - let is_valid_layer: BOOL = msg_send![main_layer, isKindOfClass: class]; - let raw_layer = if is_valid_layer == YES { - main_layer - } else { - // If the main layer is not a CAMetalLayer, we create a CAMetalLayer and use it. - let new_layer: *mut Object = msg_send![class, new]; - let frame: CGRect = msg_send![main_layer, bounds]; - let () = msg_send![new_layer, setFrame: frame]; - #[cfg(target_os = "ios")] - { - // Unlike NSView, UIView does not allow to replace main layer. - let () = msg_send![main_layer, addSublayer: new_layer]; - let () = msg_send![main_layer, setAutoresizingMask: 0x1Fu64]; - let screen: *mut Object = msg_send![class!(UIScreen), mainScreen]; - let scale_factor: CGFloat = msg_send![screen, nativeScale]; - let () = msg_send![view, setContentScaleFactor: scale_factor]; - }; - #[cfg(target_os = "macos")] - { - let () = msg_send![view, setLayer: new_layer]; - let () = msg_send![view, setWantsLayer: YES]; - let () = msg_send![new_layer, setContentsGravity: kCAGravityTopLeft]; - let window: *mut Object = msg_send![view, window]; - if !window.is_null() { - let scale_factor: CGFloat = msg_send![window, backingScaleFactor]; - let () = msg_send![new_layer, setContentsScale: scale_factor]; - } - } - new_layer - }; - - Self { - view: msg_send![view, retain], - render_layer: mem::transmute::<_, &metal::MetalLayerRef>(raw_layer).to_owned(), - info: crate::SurfaceInfo { - format: crate::TextureFormat::Rgba8Unorm, - alpha: crate::AlphaMode::Ignored, - }, - } - } - /// Get the CALayerMetal for this surface, if any. /// This is platform specific API. - pub fn metal_layer(&self) -> metal::MetalLayer { + pub fn metal_layer(&self) -> Retained { self.render_layer.clone() } @@ -73,9 +19,11 @@ impl super::Surface { } pub fn acquire_frame(&self) -> super::Frame { - let (drawable, texture) = objc::rc::autoreleasepool(|| { - let drawable = self.render_layer.next_drawable().unwrap(); - (drawable.to_owned(), drawable.texture().to_owned()) + use objc2_quartz_core::CAMetalDrawable as _; + let (drawable, texture) = objc2::rc::autoreleasepool(|_| unsafe { + let drawable = self.render_layer.nextDrawable().unwrap(); + let texture = drawable.texture(); + (Retained::cast(drawable), texture) }); super::Frame { drawable, texture } } @@ -86,24 +34,71 @@ impl super::Context { &self, window: &I, ) -> Result { + use objc2_foundation::NSObjectProtocol as _; + Ok(match window.window_handle().unwrap().as_raw() { #[cfg(target_os = "ios")] raw_window_handle::RawWindowHandle::UiKit(handle) => unsafe { - super::Surface::from_view(handle.ui_view.as_ptr() as *mut _) + let view = + Retained::retain(handle.ui_view.as_ptr() as *mut objc2_ui_kit::UIView).unwrap(); + let main_layer = view.layer(); + let render_layer = if main_layer.is_kind_of::() { + Retained::cast(main_layer) + } else { + use objc2_ui_kit::UIViewAutoresizing as Var; + let new_layer = CAMetalLayer::new(); + new_layer.setFrame(main_layer.frame()); + // Unlike NSView, UIView does not allow to replace main layer. + main_layer.addSublayer(&new_layer); + view.setAutoresizingMask( + Var::FlexibleLeftMargin + | Var::FlexibleWidth + | Var::FlexibleRightMargin + | Var::FlexibleTopMargin + | Var::FlexibleHeight + | Var::FlexibleBottomMargin, + ); + if let Some(scene) = view.window().and_then(|w| w.windowScene()) { + new_layer.setContentsScale(scene.screen().nativeScale()); + } + new_layer + }; + super::Surface { + view: Some(Retained::cast(view)), + render_layer, + info: SURFACE_INFO, + } }, #[cfg(target_os = "macos")] raw_window_handle::RawWindowHandle::AppKit(handle) => unsafe { - super::Surface::from_view(handle.ns_view.as_ptr() as *mut _) + let view = Retained::retain(handle.ns_view.as_ptr() as *mut objc2_app_kit::NSView) + .unwrap(); + let main_layer = view.layer().unwrap(); + let render_layer = if main_layer.is_kind_of::() { + Retained::cast(main_layer) + } else { + let new_layer = CAMetalLayer::new(); + new_layer.setFrame(main_layer.frame()); + view.setLayer(Some(&new_layer)); + view.setWantsLayer(true); + new_layer.setContentsGravity(objc2_quartz_core::kCAGravityTopLeft); + if let Some(window) = view.window() { + new_layer.setContentsScale(window.backingScaleFactor()); + } + new_layer + }; + super::Surface { + view: Some(Retained::cast(view)), + render_layer, + info: SURFACE_INFO, + } }, _ => return Err(crate::NotSupportedError::PlatformNotSupported), }) } pub fn destroy_surface(&self, surface: &mut super::Surface) { - unsafe { - let () = msg_send![surface.view, release]; - } - surface.view = ptr::null_mut(); + surface.view = None; } pub fn reconfigure_surface(&self, surface: &mut super::Surface, config: crate::SurfaceConfig) { @@ -126,21 +121,21 @@ impl super::Context { crate::DisplaySync::Recent | crate::DisplaySync::Tear => false, }; - surface.render_layer.set_opaque(!config.transparent); - surface.render_layer.set_device(&*device); - surface - .render_layer - .set_pixel_format(super::map_texture_format(surface.info.format)); - surface - .render_layer - .set_framebuffer_only(config.usage == crate::TextureUsage::TARGET); - surface.render_layer.set_maximum_drawable_count(3); - surface.render_layer.set_drawable_size(CGSize::new( - config.size.width as f64, - config.size.height as f64, - )); unsafe { - let () = msg_send![surface.render_layer, setDisplaySyncEnabled: vsync]; + surface.render_layer.setOpaque(!config.transparent); + surface.render_layer.setDevice(Some(device.as_ref())); + surface + .render_layer + .setPixelFormat(super::map_texture_format(surface.info.format)); + surface + .render_layer + .setFramebufferOnly(config.usage == crate::TextureUsage::TARGET); + surface.render_layer.setMaximumDrawableCount(3); + surface.render_layer.setDrawableSize(CGSize { + width: config.size.width as f64, + height: config.size.height as f64, + }); + surface.render_layer.setDisplaySyncEnabled(vsync); } } } From 961327a3cf9ee8bccfd43c1074f154e320ced2c9 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Sat, 21 Dec 2024 09:24:41 -0800 Subject: [PATCH 2/3] Enable iOS CI --- .github/workflows/check.yaml | 78 +++++++++++++++++++----------------- 1 file changed, 41 insertions(+), 37 deletions(-) diff --git a/.github/workflows/check.yaml b/.github/workflows/check.yaml index ef57775..958bdc5 100644 --- a/.github/workflows/check.yaml +++ b/.github/workflows/check.yaml @@ -14,15 +14,19 @@ jobs: - name: Linux os: ubuntu-latest target: x86_64-unknown-linux-gnu - + - name: Windows os: windows-latest target: x86_64-pc-windows-msvc - - - name: MacOS + + - name: macOS + os: macos-latest + target: aarch64-apple-darwin + + - name: iOS os: macos-latest - target: x86_64-apple-darwin - + target: aarch64-apple-ios + - name: Web os: ubuntu-latest target: wasm32-unknown-unknown @@ -30,44 +34,44 @@ jobs: runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v3 - - name: Setup Rust - uses: actions-rs/toolchain@v1 - with: + - name: Setup Rust + uses: actions-rs/toolchain@v1 + with: toolchain: stable profile: minimal target: ${{ matrix.target }} override: true - - name: Check Basics - uses: actions-rs/cargo@v1 - with: - command: check - args: --target ${{ matrix.target }} --workspace --all --no-default-features - - - name: Test Basics - if: matrix.name != 'Web' - uses: actions-rs/cargo@v1 - with: - command: test - args: -p blade-render --no-default-features - - - name: Test All - if: matrix.name != 'Web' - uses: actions-rs/cargo@v1 - with: - command: test - args: --workspace --all-features - - - name: Test GLES - if: matrix.name == 'Linux' - uses: actions-rs/cargo@v1 - with: - command: build - args: --example bunnymark - env: - RUSTFLAGS: "--cfg gles" + - name: Check Basics + uses: actions-rs/cargo@v1 + with: + command: check + args: --target ${{ matrix.target }} --workspace --all --no-default-features + + - name: Test Basics + if: matrix.name != 'Web' + uses: actions-rs/cargo@v1 + with: + command: test + args: -p blade-render --no-default-features + + - name: Test All + if: matrix.name != 'Web' + uses: actions-rs/cargo@v1 + with: + command: test + args: --workspace --all-features + + - name: Test GLES + if: matrix.name == 'Linux' + uses: actions-rs/cargo@v1 + with: + command: build + args: --example bunnymark + env: + RUSTFLAGS: "--cfg gles" fmt: name: Format From 9f1541a3cfdc5d8e154eb6eab5728fe449322947 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Sat, 21 Dec 2024 09:35:38 -0800 Subject: [PATCH 3/3] Remove block dependency, update naga --- Cargo.toml | 12 +++++++----- blade-graphics/Cargo.toml | 1 - docs/CHANGELOG.md | 1 + 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d1cc0f6..2cad806 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,9 +21,7 @@ glam = { version = "0.28", features = ["mint"] } gltf = { version = "1.1", default-features = false } log = "0.4" mint = "0.5" -naga = { git = "https://github.com/gfx-rs/wgpu", rev = "1a643291c2e8854ba7e4f5445a4388202731bfa1", features = [ - "wgsl-in", -] } +naga = { version = "23.1.0", features = ["wgsl-in"] } profiling = "1" slab = "0.4" strum = { version = "0.25", features = ["derive"] } @@ -90,11 +88,15 @@ del-geo = "=0.1.29" [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] # see https://github.com/emilk/egui/issues/4270 -egui-winit = { version="0.29", default-features=false, features=["links"] } +egui-winit = { version = "0.29", default-features = false, features = [ + "links", +] } [target.'cfg(target_arch = "wasm32")'.dev-dependencies] # see https://github.com/emilk/egui/issues/4270 -egui-winit = { version="0.29", default-features=false, features=["links"] } +egui-winit = { version = "0.29", default-features = false, features = [ + "links", +] } console_error_panic_hook = "0.1.7" console_log = "1" web-sys = { workspace = true, features = ["Window"] } diff --git a/blade-graphics/Cargo.toml b/blade-graphics/Cargo.toml index 3e4de39..bf7e25b 100644 --- a/blade-graphics/Cargo.toml +++ b/blade-graphics/Cargo.toml @@ -20,7 +20,6 @@ naga = { workspace = true } raw-window-handle = "0.6" [target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies] -block = "0.1" objc2 = "0.5" objc2-foundation = { version = "0.2", features = ["NSArray"] } objc2-metal = { version = "0.2", features = [ diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 8204415..69bb05c 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -12,6 +12,7 @@ Changelog for Blade - ability to capture pass GPU timings - ability to force the use of a specific GPU - Metal: + - migrate to "objc2" - support for workgroup memory - concurrent compute dispatches - Egl: