metal/learn_metal/03-animation macOS

Code

03-animation.odin ¶
215 linesSource

1package main
2
3import NS  "core:sys/darwin/Foundation"
4import MTL "vendor:darwin/Metal"
5import CA  "vendor:darwin/QuartzCore"
6
7import SDL "vendor:sdl2"
8
9import "core:fmt"
10import "core:os"
11
12
13Frame_Data :: struct {
14	angle: f32,
15}
16
17build_shaders :: proc(device: ^MTL.Device) -> (library: ^MTL.Library, pso: ^MTL.RenderPipelineState, err: ^NS.Error) {
18	shader_src := `
19	#include <metal_stdlib>
20	using namespace metal;
21
22	struct v2f {
23		float4 position [[position]];
24		half3 color;
25	};
26
27	struct Vertex_Data {
28		device packed_float3* positions [[id(0)]];
29		device packed_float3* colors    [[id(1)]];
30	};
31
32	struct Frame_Data {
33		float angle;
34	};
35
36	v2f vertex vertex_main(device const Vertex_Data* vertex_data [[buffer(0)]],
37	                       device const Frame_Data*  frame_data  [[buffer(1)]],
38	                       uint vertex_id                        [[vertex_id]]) {
39		float a = frame_data->angle;
40		float3x3 rotation_matrix = float3x3(sin(a), cos(a), 0.0, cos(a), -sin(a), 0.0, 0.0, 0.0, 1.0);
41		float3 position = float3(vertex_data->positions[vertex_id]);
42		v2f o;
43		o.position = float4(rotation_matrix * position, 1.0);
44		o.color = half3(vertex_data->colors[vertex_id]);
45		return o;
46	}
47
48	half4 fragment fragment_main(v2f in [[stage_in]]) {
49		return half4(in.color, 1.0);
50	}
51	`
52	shader_src_str := NS.String.alloc()->initWithOdinString(shader_src)
53	defer shader_src_str->release()
54
55	library = device->newLibraryWithSource(shader_src_str, nil) or_return
56
57	vertex_function   := library->newFunctionWithName(NS.AT("vertex_main"))
58	fragment_function := library->newFunctionWithName(NS.AT("fragment_main"))
59	defer vertex_function->release()
60	defer fragment_function->release()
61
62	desc := MTL.RenderPipelineDescriptor.alloc()->init()
63	defer desc->release()
64
65	desc->setVertexFunction(vertex_function)
66	desc->setFragmentFunction(fragment_function)
67	desc->colorAttachments()->object(0)->setPixelFormat(.BGRA8Unorm_sRGB)
68
69	pso = device->newRenderPipelineStateWithDescriptor(desc) or_return
70	return
71}
72
73build_buffers :: proc(device: ^MTL.Device, library: ^MTL.Library) -> (vertex_positions_buffer, vertex_colors_buffer, arg_buffer: ^MTL.Buffer) {
74	NUM_VERTICES :: 3
75	positions := [NUM_VERTICES][3]f32{
76		{-0.8,  0.8, 0.0},
77		{ 0.0, -0.8, 0.0},
78		{+0.8,  0.8, 0.0},
79	}
80	colors := [NUM_VERTICES][3]f32{
81		{1.0, 0.3, 0.2},
82		{0.8, 1.0, 0.0},
83		{0.8, 0.0, 1.0},
84	}
85
86	vertex_positions_buffer = device->newBufferWithSlice(positions[:], {.StorageModeManaged})
87	vertex_colors_buffer    = device->newBufferWithSlice(colors[:],    {.StorageModeManaged})
88
89	vertex_function := library->newFunctionWithName(NS.AT("vertex_main"))
90	defer vertex_function->release()
91
92	arg_encoder := vertex_function->newArgumentEncoder(0)
93	defer arg_encoder->release()
94
95	arg_buffer = device->newBuffer(arg_encoder->encodedLength(), {.StorageModeManaged})
96	arg_encoder->setArgumentBufferWithOffset(arg_buffer, 0)
97	arg_encoder->setBuffer(vertex_positions_buffer, 0, 0)
98	arg_encoder->setBuffer(vertex_colors_buffer,    0, 1)
99	arg_buffer->didModifyRange(NS.Range_Make(0, arg_buffer->length()))
100
101	return
102}
103
104metal_main :: proc() -> (err: ^NS.Error) {
105	SDL.SetHint(SDL.HINT_RENDER_DRIVER, "metal")
106	SDL.setenv("METAL_DEVICE_WRAPPER_TYPE", "1", 0)
107	SDL.Init({.VIDEO})
108	defer SDL.Quit()
109
110	window := SDL.CreateWindow("Metal in Odin - 03 animation",
111		SDL.WINDOWPOS_CENTERED, SDL.WINDOWPOS_CENTERED,
112		854, 480,
113		{.ALLOW_HIGHDPI, .HIDDEN, .RESIZABLE},
114	)
115	defer SDL.DestroyWindow(window)
116
117	window_system_info: SDL.SysWMinfo
118	SDL.GetVersion(&window_system_info.version)
119	SDL.GetWindowWMInfo(window, &window_system_info)
120	assert(window_system_info.subsystem == .COCOA)
121
122	native_window := (^NS.Window)(window_system_info.info.cocoa.window)
123
124	device := MTL.CreateSystemDefaultDevice()
125	defer device->release()
126
127	fmt.println(device->name()->odinString())
128
129	swapchain := CA.MetalLayer.layer()
130	defer swapchain->release()
131
132	swapchain->setDevice(device)
133	swapchain->setPixelFormat(.BGRA8Unorm_sRGB)
134	swapchain->setFramebufferOnly(true)
135	swapchain->setFrame(native_window->frame())
136
137	native_window->contentView()->setLayer(swapchain)
138	native_window->setOpaque(true)
139	native_window->setBackgroundColor(nil)
140
141	library, pso := build_shaders(device) or_return
142	defer library->release()
143	defer pso->release()
144
145	vertex_positions_buffer, vertex_colors_buffer, arg_buffer := build_buffers(device, library)
146	defer arg_buffer->release()
147
148	frame_data_buffer := device->newBuffer(size_of(Frame_Data), {.StorageModeManaged})
149	defer frame_data_buffer->release()
150
151	command_queue := device->newCommandQueue()
152	defer command_queue->release()
153
154	SDL.ShowWindow(window)
155	for quit := false; !quit;  {
156		for e: SDL.Event; SDL.PollEvent(&e); {
157			#partial switch e.type {
158			case .QUIT:
159				quit = true
160			case .KEYDOWN:
161				if e.key.keysym.sym == .ESCAPE {
162					quit = true
163				}
164			}
165		}
166
167		@static angle: f32
168		frame_data := (^Frame_Data)(frame_data_buffer->contentsPointer())
169		frame_data.angle = angle
170		angle += 0.01
171		frame_data_buffer->didModifyRange(NS.Range_Make(0, size_of(Frame_Data)))
172
173		drawable := swapchain->nextDrawable()
174		assert(drawable != nil)
175		defer drawable->release()
176
177		pass := MTL.RenderPassDescriptor.renderPassDescriptor()
178		defer pass->release()
179
180		color_attachment := pass->colorAttachments()->object(0)
181		assert(color_attachment != nil)
182		color_attachment->setClearColor(MTL.ClearColor{0.25, 0.5, 1.0, 1.0})
183		color_attachment->setLoadAction(.Clear)
184		color_attachment->setStoreAction(.Store)
185		color_attachment->setTexture(drawable->texture())
186
187		command_buffer := command_queue->commandBuffer()
188		defer command_buffer->release()
189
190		render_encoder := command_buffer->renderCommandEncoderWithDescriptor(pass)
191		defer render_encoder->release()
192
193		render_encoder->setRenderPipelineState(pso)
194		render_encoder->setVertexBuffer(arg_buffer,        0, 0)
195		render_encoder->setVertexBuffer(frame_data_buffer, 0, 1)
196		render_encoder->useResource(vertex_positions_buffer, {.Read})
197		render_encoder->useResource(vertex_colors_buffer, {.Read})
198		render_encoder->drawPrimitives(.Triangle, 0, 3)
199
200		render_encoder->endEncoding()
201
202		command_buffer->presentDrawable(drawable)
203		command_buffer->commit()
204	}
205
206	return nil
207}
208
209main :: proc() {
210	err := metal_main()
211	if err != nil {
212		fmt.eprintln(err->localizedDescription()->odinString())
213		os.exit(1)
214	}
215}

Declarations Used 31