diff --git a/examples/mesh-shader/main.rs b/examples/mesh-shader/main.rs new file mode 100644 index 0000000..8edb30c --- /dev/null +++ b/examples/mesh-shader/main.rs @@ -0,0 +1,118 @@ +extern crate objc; + +use cocoa::{appkit::NSView, base::id as cocoa_id}; +use core_graphics_types::geometry::CGSize; + +use metal::*; +use objc::{rc::autoreleasepool, runtime::YES}; +use std::mem; +use winit::platform::macos::WindowExtMacOS; + +use winit::{ + event::{Event, WindowEvent}, + event_loop::ControlFlow, +}; + +fn prepare_render_pass_descriptor(descriptor: &RenderPassDescriptorRef, texture: &TextureRef) { + let color_attachment = descriptor.color_attachments().object_at(0).unwrap(); + + color_attachment.set_texture(Some(texture)); + color_attachment.set_load_action(MTLLoadAction::Clear); + color_attachment.set_clear_color(MTLClearColor::new(0.2, 0.2, 0.25, 1.0)); + color_attachment.set_store_action(MTLStoreAction::Store); +} + +fn main() { + let events_loop = winit::event_loop::EventLoop::new(); + let size = winit::dpi::LogicalSize::new(800, 600); + + let window = winit::window::WindowBuilder::new() + .with_inner_size(size) + .with_title("Metal Mesh Shader Example".to_string()) + .build(&events_loop) + .unwrap(); + + let device = Device::system_default().expect("no device found"); + + let layer = MetalLayer::new(); + layer.set_device(&device); + layer.set_pixel_format(MTLPixelFormat::BGRA8Unorm); + layer.set_presents_with_transaction(false); + + unsafe { + let view = window.ns_view() as cocoa_id; + view.setWantsLayer(YES); + view.setLayer(mem::transmute(layer.as_ref())); + } + + let draw_size = window.inner_size(); + layer.set_drawable_size(CGSize::new(draw_size.width as f64, draw_size.height as f64)); + + let library_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("examples/mesh-shader/shaders.metallib"); + let library = device.new_library_with_file(library_path).unwrap(); + + let mesh = library.get_function("mesh_function", None).unwrap(); + let frag = library.get_function("fragment_function", None).unwrap(); + + let pipeline_state_desc = MeshRenderPipelineDescriptor::new(); + pipeline_state_desc + .color_attachments() + .object_at(0) + .unwrap() + .set_pixel_format(MTLPixelFormat::BGRA8Unorm); + pipeline_state_desc.set_mesh_function(Some(&mesh)); + pipeline_state_desc.set_fragment_function(Some(&frag)); + + let pipeline_state = device + .new_mesh_render_pipeline_state(&pipeline_state_desc) + .unwrap(); + + let command_queue = device.new_command_queue(); + + events_loop.run(move |event, _, control_flow| { + autoreleasepool(|| { + *control_flow = ControlFlow::Poll; + + match event { + Event::WindowEvent { event, .. } => match event { + WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, + WindowEvent::Resized(size) => { + layer.set_drawable_size(CGSize::new(size.width as f64, size.height as f64)); + } + _ => (), + }, + Event::MainEventsCleared => { + window.request_redraw(); + } + Event::RedrawRequested(_) => { + let drawable = match layer.next_drawable() { + Some(drawable) => drawable, + None => return, + }; + + let render_pass_descriptor = RenderPassDescriptor::new(); + + prepare_render_pass_descriptor(&render_pass_descriptor, drawable.texture()); + + let command_buffer = command_queue.new_command_buffer(); + let encoder = + command_buffer.new_render_command_encoder(&render_pass_descriptor); + + encoder.set_render_pipeline_state(&pipeline_state); + encoder.draw_mesh_threads( + MTLSize::new(1, 1, 1), + MTLSize::new(1, 1, 1), + MTLSize::new(1, 1, 1), + ); + + encoder.end_encoding(); + + command_buffer.present_drawable(&drawable); + command_buffer.commit(); + } + _ => {} + } + }); + }); +} diff --git a/examples/mesh-shader/shaders.metal b/examples/mesh-shader/shaders.metal new file mode 100644 index 0000000..1a82530 --- /dev/null +++ b/examples/mesh-shader/shaders.metal @@ -0,0 +1,30 @@ +#include + +using namespace metal; + +struct VertexOut { + float4 position [[position]]; +}; + +using mesh_t = mesh; + +[[mesh]] void mesh_function(mesh_t m) { + VertexOut v; + v.position = float4(-1.0, -1.0, 0.0, 1.0); + + m.set_primitive_count(1); + + m.set_vertex(0, v); + v.position = float4(0.0, 1.0, 0.0, 1.0); + m.set_vertex(1, v); + v.position = float4(1.0, -1.0, 0.0, 1.0); + m.set_vertex(2, v); + + m.set_index(0, 0); + m.set_index(1, 1); + m.set_index(2, 2); +} + +fragment half4 fragment_function() { + return half4(0.1, 1.0, 0.1, 1.0); +} \ No newline at end of file diff --git a/examples/mesh-shader/shaders.metallib b/examples/mesh-shader/shaders.metallib new file mode 100644 index 0000000..4af8d60 Binary files /dev/null and b/examples/mesh-shader/shaders.metallib differ diff --git a/src/device.rs b/src/device.rs index 1b4a729..96f1e61 100644 --- a/src/device.rs +++ b/src/device.rs @@ -1899,6 +1899,45 @@ impl DeviceRef { } } + /// Only available on (macos(13.0), ios(16.0)) + pub fn new_mesh_render_pipeline_state_with_reflection( + &self, + descriptor: &MeshRenderPipelineDescriptorRef, + reflection_options: MTLPipelineOption, + ) -> Result<(RenderPipelineState, RenderPipelineReflection), String> { + unsafe { + let mut reflection: *mut Object = ptr::null_mut(); + let pipeline_state: *mut MTLRenderPipelineState = try_objc! { err => + msg_send![self, newRenderPipelineStateWithMeshDescriptor:descriptor + options:reflection_options + reflection:&mut reflection + error:&mut err] + }; + + let state = RenderPipelineState::from_ptr(pipeline_state); + + let () = msg_send![reflection, retain]; + let reflection = RenderPipelineReflection::from_ptr(reflection as _); + + Ok((state, reflection)) + } + } + + /// Only available on (macos(13.0), ios(16.0)) + pub fn new_mesh_render_pipeline_state( + &self, + descriptor: &MeshRenderPipelineDescriptorRef, + ) -> Result { + unsafe { + let pipeline_state: *mut MTLRenderPipelineState = try_objc! { err => + msg_send![self, newRenderPipelineStateWithMeshDescriptor:descriptor + error:&mut err] + }; + + Ok(RenderPipelineState::from_ptr(pipeline_state)) + } + } + pub fn new_compute_pipeline_state_with_function( &self, function: &FunctionRef, diff --git a/src/encoder.rs b/src/encoder.rs index 0616d2c..09e5e6e 100644 --- a/src/encoder.rs +++ b/src/encoder.rs @@ -440,6 +440,278 @@ impl RenderCommandEncoderRef { } } + // Specifying Resources for a Object Shader Function + + /// Only available in (macos(13.0), ios(16.0)) + pub fn set_object_buffer( + &self, + index: NSUInteger, + buffer: Option<&BufferRef>, + offset: NSUInteger, + ) { + unsafe { + msg_send![self, + setObjectBuffer:buffer + offset:offset + atIndex:index + ] + } + } + + /// Only available in (macos(13.0), ios(16.0)) + pub fn set_object_buffer_offset(&self, index: NSUInteger, offset: NSUInteger) { + unsafe { + msg_send![self, + setObjectBufferOffset:offset + atIndex:index + ] + } + } + + /// Only available in (macos(13.0), ios(16.0)) + pub fn set_object_bytes( + &self, + index: NSUInteger, + length: NSUInteger, + bytes: *const std::ffi::c_void, + ) { + unsafe { + msg_send![self, + setObjectBytes:bytes + length:length + atIndex:index + ] + } + } + + /// Only available in (macos(13.0), ios(16.0)) + pub fn set_object_sampler_state(&self, index: NSUInteger, sampler: Option<&SamplerStateRef>) { + unsafe { + msg_send![self, + setObjectSamplerState:sampler + atIndex:index + ] + } + } + + /// Only available in (macos(13.0), ios(16.0)) + pub fn set_object_sampler_state_with_lod( + &self, + index: NSUInteger, + sampler: Option<&SamplerStateRef>, + lod_clamp: Range, + ) { + unsafe { + msg_send![self, + setObjectSamplerState:sampler + lodMinClamp:lod_clamp.start + lodMaxClamp:lod_clamp.end + atIndex:index + ] + } + } + + /// Only available in (macos(13.0), ios(16.0)) + pub fn set_object_texture(&self, index: NSUInteger, texture: Option<&TextureRef>) { + unsafe { + msg_send![self, + setObjectTexture:texture + atIndex:index + ] + } + } + + /// Only available in (macos(13.0), ios(16.0)) + pub fn set_object_threadgroup_memory_length(&self, index: NSUInteger, length: NSUInteger) { + unsafe { + msg_send![self, + setObjectThreadgroupMemoryLength: length + atIndex: index + ] + } + } + + /// Only available in (macos(13.0), ios(16.0)) + pub fn set_object_buffers( + &self, + start_index: NSUInteger, + data: &[Option<&BufferRef>], + offsets: &[NSUInteger], + ) { + debug_assert_eq!(offsets.len(), data.len()); + unsafe { + msg_send![self, + setObjectBuffers: data.as_ptr() + offsets: offsets.as_ptr() + withRange: NSRange { + location: start_index, + length: data.len() as _, + } + ] + } + } + + /// Only available in (macos(13.0), ios(16.0)) + pub fn set_object_sampler_states( + &self, + start_index: NSUInteger, + data: &[Option<&SamplerStateRef>], + ) { + unsafe { + msg_send![self, + setObjectSamplerStates: data.as_ptr() + withRange: NSRange { + location: start_index, + length: data.len() as _, + } + ] + } + } + + /// Only available in (macos(13.0), ios(16.0)) + pub fn set_object_textures(&self, start_index: NSUInteger, data: &[Option<&TextureRef>]) { + unsafe { + msg_send![self, + setObjectTextures: data.as_ptr() + withRange: NSRange { + location: start_index, + length: data.len() as _, + } + ] + } + } + + // Specifying Resources for a Mesh Shader + + /// Only available in (macos(13.0), ios(16.0)) + pub fn set_mesh_buffer( + &self, + index: NSUInteger, + buffer: Option<&BufferRef>, + offset: NSUInteger, + ) { + unsafe { + msg_send![self, + setMeshBuffer:buffer + offset:offset + atIndex:index + ] + } + } + + /// Only available in (macos(13.0), ios(16.0)) + pub fn set_mesh_buffer_offset(&self, index: NSUInteger, offset: NSUInteger) { + unsafe { + msg_send![self, + setMeshBufferOffset:offset + atIndex:index + ] + } + } + + /// Only available in (macos(13.0), ios(16.0)) + pub fn set_mesh_bytes( + &self, + index: NSUInteger, + length: NSUInteger, + bytes: *const std::ffi::c_void, + ) { + unsafe { + msg_send![self, + setMeshBytes:bytes + length:length + atIndex:index + ] + } + } + + /// Only available in (macos(13.0), ios(16.0)) + pub fn set_mesh_sampler_state(&self, index: NSUInteger, sampler: Option<&SamplerStateRef>) { + unsafe { + msg_send![self, + setMeshSamplerState:sampler + atIndex:index + ] + } + } + + /// Only available in (macos(13.0), ios(16.0)) + pub fn set_mesh_sampler_state_with_lod( + &self, + index: NSUInteger, + sampler: Option<&SamplerStateRef>, + lod_clamp: Range, + ) { + unsafe { + msg_send![self, + setMeshSamplerState:sampler + lodMinClamp:lod_clamp.start + lodMaxClamp:lod_clamp.end + atIndex:index + ] + } + } + + /// Only available in (macos(13.0), ios(16.0)) + pub fn set_mesh_texture(&self, index: NSUInteger, texture: Option<&TextureRef>) { + unsafe { + msg_send![self, + setMeshTexture:texture + atIndex:index + ] + } + } + + /// Only available in (macos(13.0), ios(16.0)) + pub fn set_mesh_buffers( + &self, + start_index: NSUInteger, + data: &[Option<&BufferRef>], + offsets: &[NSUInteger], + ) { + debug_assert_eq!(offsets.len(), data.len()); + unsafe { + msg_send![self, + setMeshBuffers: data.as_ptr() + offsets: offsets.as_ptr() + withRange: NSRange { + location: start_index, + length: data.len() as _, + } + ] + } + } + + /// Only available in (macos(13.0), ios(16.0)) + pub fn set_mesh_sampler_states( + &self, + start_index: NSUInteger, + data: &[Option<&SamplerStateRef>], + ) { + unsafe { + msg_send![self, + setMeshSamplerStates: data.as_ptr() + withRange: NSRange { + location: start_index, + length: data.len() as _, + } + ] + } + } + + /// Only available in (macos(13.0), ios(16.0)) + pub fn set_mesh_textures(&self, start_index: NSUInteger, data: &[Option<&TextureRef>]) { + unsafe { + msg_send![self, + setMeshTextures: data.as_ptr() + withRange: NSRange { + location: start_index, + length: data.len() as _, + } + ] + } + } + // Specifying Resources for a Fragment Shader Function pub fn set_fragment_bytes( @@ -749,6 +1021,56 @@ impl RenderCommandEncoderRef { // fn setVertexBuffers_offsets_withRange(self, buffers: *const id, offsets: *const NSUInteger, range: NSRange); // fn setVertexSamplerStates_lodMinClamps_lodMaxClamps_withRange(self, samplers: *const id, lodMinClamps: *const f32, lodMaxClamps: *const f32, range: NSRange); + /// Only available in (macos(13.0), ios(16.0)) + pub fn draw_mesh_threadgroups( + &self, + threadgroups_per_grid: MTLSize, + threads_per_object_threadgroup: MTLSize, + threads_per_mesh_threadgroup: MTLSize, + ) { + unsafe { + msg_send![self, + drawMeshThreadgroups: threadgroups_per_grid + threadsPerObjectThreadgroup: threads_per_object_threadgroup + threadsPerMeshThreadgroup: threads_per_mesh_threadgroup + ] + } + } + + /// Only available in (macos(13.0), ios(16.0)) + pub fn draw_mesh_threadgroups_with_indirect_buffer( + &self, + indirect_buffer: &BufferRef, + indirect_buffer_offset: NSUInteger, + threads_per_object_threadgroup: MTLSize, + threads_per_mesh_threadgroup: MTLSize, + ) { + unsafe { + msg_send![self, + drawMeshThreadgroupsWithIndirectBuffer: indirect_buffer + indirectBufferOffset: indirect_buffer_offset + threadsPerObjectThreadgroup: threads_per_object_threadgroup + threadsPerMeshThreadgroup: threads_per_mesh_threadgroup + ] + } + } + + /// Only available in (macos(13.0), ios(16.0)) + pub fn draw_mesh_threads( + &self, + threads_per_grid: MTLSize, + threads_per_object_threadgroup: MTLSize, + threads_per_mesh_threadgroup: MTLSize, + ) { + unsafe { + msg_send![self, + drawMeshThreads: threads_per_grid + threadsPerObjectThreadgroup: threads_per_object_threadgroup + threadsPerMeshThreadgroup: threads_per_mesh_threadgroup + ] + } + } + /// Adds an untracked resource to the render pass. /// /// Availability: iOS 11.0+, macOS 10.13+ diff --git a/src/pipeline/render.rs b/src/pipeline/render.rs index b6ecdd5..6731c15 100644 --- a/src/pipeline/render.rs +++ b/src/pipeline/render.rs @@ -246,6 +246,268 @@ impl ComputePipelineReflectionRef { } } +/// See +/// Only available in (macos(13.0), ios(16.0)) +pub enum MTLMeshRenderPipelineDescriptor {} + +foreign_obj_type! { + type CType = MTLMeshRenderPipelineDescriptor; + pub struct MeshRenderPipelineDescriptor; +} + +impl MeshRenderPipelineDescriptor { + pub fn new() -> Self { + unsafe { + let class = class!(MTLMeshRenderPipelineDescriptor); + msg_send![class, new] + } + } +} + +impl MeshRenderPipelineDescriptorRef { + pub fn color_attachments(&self) -> &RenderPipelineColorAttachmentDescriptorArrayRef { + unsafe { msg_send![self, colorAttachments] } + } + + pub fn depth_attachment_pixel_format(&self) -> MTLPixelFormat { + unsafe { msg_send![self, depthAttachmentPixelFormat] } + } + + pub fn set_depth_attachment_pixel_format(&self, pixel_format: MTLPixelFormat) { + unsafe { msg_send![self, setDepthAttachmentPixelFormat: pixel_format] } + } + + pub fn fragment_buffers(&self) -> Option<&PipelineBufferDescriptorArrayRef> { + unsafe { msg_send![self, fragmentBuffers] } + } + + pub fn fragment_function(&self) -> Option<&FunctionRef> { + unsafe { msg_send![self, fragmentFunction] } + } + + pub fn set_fragment_function(&self, function: Option<&FunctionRef>) { + unsafe { msg_send![self, setFragmentFunction: function] } + } + + pub fn is_alpha_to_coverage_enabled(&self) -> bool { + unsafe { + match msg_send![self, isAlphaToCoverageEnabled] { + YES => true, + NO => false, + _ => unreachable!(), + } + } + } + + pub fn set_alpha_to_coverage_enabled(&self, enabled: bool) { + unsafe { msg_send![self, setAlphaToCoverageEnabled: enabled] } + } + + pub fn is_alpha_to_one_enabled(&self) -> bool { + unsafe { + match msg_send![self, isAlphaToOneEnabled] { + YES => true, + NO => false, + _ => unreachable!(), + } + } + } + + pub fn set_alpha_to_one_enabled(&self, enabled: bool) { + unsafe { msg_send![self, setAlphaToOneEnabled: enabled] } + } + + pub fn is_rasterization_enabled(&self) -> bool { + unsafe { + match msg_send![self, isRasterizationEnabled] { + YES => true, + NO => false, + _ => unreachable!(), + } + } + } + + pub fn set_rasterization_enabled(&self, enabled: bool) { + unsafe { msg_send![self, setRasterizationEnabled: enabled] } + } + + pub fn label(&self) -> &str { + unsafe { + let label = msg_send![self, label]; + crate::nsstring_as_str(label) + } + } + + pub fn set_label(&self, label: &str) { + unsafe { + let nslabel = crate::nsstring_from_str(label); + let () = msg_send![self, setLabel: nslabel]; + } + } + + pub fn max_total_threadgroups_per_mesh_grid(&self) -> NSUInteger { + unsafe { msg_send![self, maxTotalThreadgroupsPerMeshGrid] } + } + + pub fn set_max_total_threadgroups_per_mesh_grid( + &self, + max_total_threadgroups_per_mesh_grid: NSUInteger, + ) { + unsafe { + msg_send![ + self, + setMaxTotalThreadgroupsPerMeshGrid: max_total_threadgroups_per_mesh_grid + ] + } + } + + pub fn max_total_threads_per_mesh_threadgroup(&self) -> NSUInteger { + unsafe { msg_send![self, maxTotalThreadsPerMeshThreadgroup] } + } + + pub fn set_max_total_threads_per_mesh_threadgroup( + &self, + max_total_threads_per_mesh_threadgroup: NSUInteger, + ) { + unsafe { + msg_send![ + self, + setMaxTotalThreadsPerMeshThreadgroup: max_total_threads_per_mesh_threadgroup + ] + } + } + + pub fn max_total_threads_per_object_threadgroup(&self) -> NSUInteger { + unsafe { msg_send![self, maxTotalThreadsPerObjectThreadgroup] } + } + + pub fn set_max_total_threads_per_object_threadgroup( + &self, + max_total_threads_per_object_threadgroup: NSUInteger, + ) { + unsafe { + msg_send![ + self, + setMaxTotalThreadsPerObjectThreadgroup: max_total_threads_per_object_threadgroup + ] + } + } + + pub fn max_vertex_amplification_count(&self) -> NSUInteger { + unsafe { msg_send![self, maxVertexAmplificationCount] } + } + + pub fn set_max_vertex_amplification_count(&self, max_vertex_amplification_count: NSUInteger) { + unsafe { + msg_send![ + self, + setMaxVertexAmplificationCount: max_vertex_amplification_count + ] + } + } + + pub fn mesh_buffers(&self) -> Option<&PipelineBufferDescriptorArrayRef> { + unsafe { msg_send![self, meshBuffers] } + } + + pub fn mesh_function(&self) -> Option<&FunctionRef> { + unsafe { msg_send![self, meshFunction] } + } + + pub fn set_mesh_function(&self, function: Option<&FunctionRef>) { + unsafe { msg_send![self, setMeshFunction: function] } + } + + pub fn mesh_threadgroup_size_is_multiple_of_thread_execution_width(&self) -> bool { + unsafe { + match msg_send![self, isMeshThreadgroupSizeIsMultipleOfThreadExecutionWidth] { + YES => true, + NO => false, + _ => unreachable!(), + } + } + } + + pub fn set_mesh_threadgroup_size_is_multiple_of_thread_execution_width( + &self, + mesh_threadgroup_size_is_multiple_of_thread_execution_width: bool, + ) { + unsafe { + msg_send![ + self, + setMeshThreadgroupSizeIsMultipleOfThreadExecutionWidth: + mesh_threadgroup_size_is_multiple_of_thread_execution_width + ] + } + } + + pub fn object_buffers(&self) -> Option<&PipelineBufferDescriptorArrayRef> { + unsafe { msg_send![self, objectBuffers] } + } + + pub fn object_function(&self) -> Option<&FunctionRef> { + unsafe { msg_send![self, objectFunction] } + } + + pub fn set_object_function(&self, function: Option<&FunctionRef>) { + unsafe { msg_send![self, setObjectFunction: function] } + } + + pub fn object_threadgroup_size_is_multiple_of_thread_execution_width(&self) -> bool { + unsafe { + match msg_send![ + self, + isObjectThreadgroupSizeIsMultipleOfThreadExecutionWidth + ] { + YES => true, + NO => false, + _ => unreachable!(), + } + } + } + + pub fn set_object_threadgroup_size_is_multiple_of_thread_execution_width( + &self, + object_threadgroup_size_is_multiple_of_thread_execution_width: bool, + ) { + unsafe { + msg_send![ + self, + setObjectThreadgroupSizeIsMultipleOfThreadExecutionWidth: + object_threadgroup_size_is_multiple_of_thread_execution_width + ] + } + } + + pub fn payload_memory_length(&self) -> NSUInteger { + unsafe { msg_send![self, payloadMemoryLength] } + } + + pub fn set_payload_memory_length(&self, payload_memory_length: NSUInteger) { + unsafe { msg_send![self, setPayloadMemoryLength: payload_memory_length] } + } + + pub fn raster_sample_count(&self) -> NSUInteger { + unsafe { msg_send![self, rasterSampleCount] } + } + + pub fn set_raster_sample_count(&self, raster_sample_count: NSUInteger) { + unsafe { msg_send![self, setRasterSampleCount: raster_sample_count] } + } + + pub fn stencil_attachment_pixel_format(&self) -> MTLPixelFormat { + unsafe { msg_send![self, stencilAttachmentPixelFormat] } + } + + pub fn set_stencil_attachment_pixel_format(&self, pixel_format: MTLPixelFormat) { + unsafe { msg_send![self, setStencilAttachmentPixelFormat: pixel_format] } + } + + pub fn reset(&self) { + unsafe { msg_send![self, reset] } + } +} + /// See pub enum MTLRenderPipelineDescriptor {}