// Copyright 2016 metal-rs developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0 > or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT >, 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::OneMinusSourc
eAlpha);
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 .0 f32, 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::<f32>()) as u64,
MTLResourceOptions::CPUCacheModeDefaultCache | MTLResourceOptions::StorageModeManaged,
)
};
let mut r = 0 .0 f32;
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::<ClearRect>() 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 .0 f32,
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::<f32>()) as usize,
);
}
vbuf.did_modify_range(crate ::NSRange::new(
0 as u64,
(vertex_data.len() * mem::size_of::<f32>()) 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 .01 f32;
}
_ => (),
},
_ => {}
}
});
})
.unwrap();
}
Messung V0.5 in Prozent C=82 H=93 G=87
¤ Dauer der Verarbeitung: 0.11 Sekunden
(vorverarbeitet am 2026-06-19)
¤
*© Formatika GbR, Deutschland