// Copyright 2016 metal-rs developers // // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be // copied, modified, or distributed except according to those terms. 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::{ event::{Event, WindowEvent}, event_loop::ControlFlow, raw_window_handle::{HasWindowHandle, RawWindowHandle}, }; #[repr(C)] struct Rect { pub x: f32, pub y: f32, pub w: f32, pub h: f32, } #[repr(C)] struct Color { pub r: f32, pub g: f32, pub b: f32, pub a: f32, } #[repr(C)] struct ClearRect { pub rect: Rect, pub color: Color, } fn prepare_pipeline_state<'a>( device: &DeviceRef, library: &LibraryRef, vertex_shader: &str, fragment_shader: &str, ) -> RenderPipelineState { let vert = library.get_function(vertex_shader, None).unwrap(); let frag = library.get_function(fragment_shader, None).unwrap(); let pipeline_state_descriptor = RenderPipelineDescriptor::new(); pipeline_state_descriptor.set_vertex_function(Some(&vert)); pipeline_state_descriptor.set_fragment_function(Some(&frag)); let attachment = pipeline_state_descriptor .color_attachments() .object_at(0) .unwrap(); attachment.set_pixel_format(MTLPixelFormat::BGRA8Unorm); attachment.set_blending_enabled(true); attachment.set_rgb_blend_operation(metal::MTLBlendOperation::Add); attachment.set_alpha_blend_operation(metal::MTLBlendOperation::Add); attachment.set_source_rgb_blend_factor(metal::MTLBlendFactor::SourceAlpha); attachment.set_source_alpha_blend_factor(metal::MTLBlendFactor::SourceAlpha); attachment.set_destination_rgb_blend_factor(metal::MTLBlendFactor::OneMinusSourceAlpha); attachment.set_destination_alpha_blend_factor(metal::MTLBlendFactor::OneMinusSourceAlpha); device .new_render_pipeline_state(&pipeline_state_descriptor) .unwrap() } fn prepare_render_pass_descriptor(descriptor: &RenderPassDescriptorRef, texture: &TextureRef) { //descriptor.color_attachments().set_object_at(0, MTLRenderPassColorAttachmentDescriptor::alloc()); //let color_attachment: MTLRenderPassColorAttachmentDescriptor = unsafe { msg_send![descriptor.color_attachments().0, _descriptorAtIndex:0] };//descriptor.color_attachments().object_at(0); 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 event_loop = winit::event_loop::EventLoop::new().unwrap(); let size = winit::dpi::LogicalSize::new(800, 600); let window = winit::window::WindowBuilder::new() .with_inner_size(size) .with_title("Metal Window Example".to_string()) .build(&event_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 { if let Ok(RawWindowHandle::AppKit(rw)) = window.window_handle().map(|wh| wh.as_raw()) { let view = rw.ns_view.as_ptr() 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/window/shaders.metallib"); let library = device.new_library_with_file(library_path).unwrap(); let triangle_pipeline_state = prepare_pipeline_state(&device, &library, "triangle_vertex", "triangle_fragment"); let clear_rect_pipeline_state = prepare_pipeline_state( &device, &library, "clear_rect_vertex", "clear_rect_fragment", ); let command_queue = device.new_command_queue(); //let nc: () = msg_send![command_queue.0, setExecutionEnabled:true]; let vbuf = { let vertex_data = [ 0.0f32, 0.5, 1.0, 0.0, 0.0, -0.5, -0.5, 0.0, 1.0, 0.0, 0.5, 0.5, 0.0, 0.0, 1.0, ]; device.new_buffer_with_data( vertex_data.as_ptr() as *const _, (vertex_data.len() * mem::size_of::()) as u64, MTLResourceOptions::CPUCacheModeDefaultCache | MTLResourceOptions::StorageModeManaged, ) }; let mut r = 0.0f32; let clear_rect = vec![ClearRect { rect: Rect { x: -1.0, y: -1.0, w: 2.0, h: 2.0, }, color: Color { r: 0.5, g: 0.8, b: 0.5, a: 1.0, }, }]; let clear_rect_buffer = device.new_buffer_with_data( clear_rect.as_ptr() as *const _, mem::size_of::() as u64, MTLResourceOptions::CPUCacheModeDefaultCache | MTLResourceOptions::StorageModeManaged, ); event_loop .run(move |event, event_loop| { autoreleasepool(|| { event_loop.set_control_flow(ControlFlow::Poll); match event { Event::AboutToWait => window.request_redraw(), Event::WindowEvent { event, .. } => match event { WindowEvent::CloseRequested => event_loop.exit(), WindowEvent::Resized(size) => { layer.set_drawable_size(CGSize::new( size.width as f64, size.height as f64, )); } WindowEvent::RedrawRequested => { let p = vbuf.contents(); let vertex_data = [ 0.0f32, 0.5, 1.0, 0.0, 0.0, -0.5 + (r.cos() / 2. + 0.5), -0.5, 0.0, 1.0, 0.0, 0.5 - (r.cos() / 2. + 0.5), -0.5, 0.0, 0.0, 1.0, ]; unsafe { std::ptr::copy( vertex_data.as_ptr(), p as *mut f32, (vertex_data.len() * mem::size_of::()) as usize, ); } vbuf.did_modify_range(crate::NSRange::new( 0 as u64, (vertex_data.len() * mem::size_of::()) as u64, )); 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_scissor_rect(MTLScissorRect { x: 20, y: 20, width: 100, height: 100, }); encoder.set_render_pipeline_state(&clear_rect_pipeline_state); encoder.set_vertex_buffer(0, Some(&clear_rect_buffer), 0); encoder.draw_primitives_instanced( metal::MTLPrimitiveType::TriangleStrip, 0, 4, 1, ); let physical_size = window.inner_size(); encoder.set_scissor_rect(MTLScissorRect { x: 0, y: 0, width: physical_size.width as _, height: physical_size.height as _, }); encoder.set_render_pipeline_state(&triangle_pipeline_state); encoder.set_vertex_buffer(0, Some(&vbuf), 0); encoder.draw_primitives(MTLPrimitiveType::Triangle, 0, 3); encoder.end_encoding(); command_buffer.present_drawable(&drawable); command_buffer.commit(); r += 0.01f32; } _ => (), }, _ => {} } }); }) .unwrap(); }