Skip to content

Commit

Permalink
Add support for object and mesh shader (#264)
Browse files Browse the repository at this point in the history
* Add support for object and mesh shader

* Remove unnecessary unsafe block
  • Loading branch information
zmarlon authored Apr 1, 2023
1 parent b04fec3 commit 50fc105
Show file tree
Hide file tree
Showing 6 changed files with 771 additions and 0 deletions.
118 changes: 118 additions & 0 deletions examples/mesh-shader/main.rs
Original file line number Diff line number Diff line change
@@ -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();
}
_ => {}
}
});
});
}
30 changes: 30 additions & 0 deletions examples/mesh-shader/shaders.metal
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#include <metal_stdlib>

using namespace metal;

struct VertexOut {
float4 position [[position]];
};

using mesh_t = mesh<VertexOut, void, 3, 1, topology::triangle>;

[[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);
}
Binary file added examples/mesh-shader/shaders.metallib
Binary file not shown.
39 changes: 39 additions & 0 deletions src/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<RenderPipelineState, String> {
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,
Expand Down
Loading

0 comments on commit 50fc105

Please sign in to comment.