metal/learn_metal/01-primitive macOS

Code

01-primitive.odin ¶
176 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
12build_shaders :: proc(device: ^MTL.Device) -> (library: ^MTL.Library, pso: ^MTL.RenderPipelineState, err: ^NS.Error) {
13	shader_src := `
14	#include <metal_stdlib>
15	using namespace metal;
16
17	struct v2f {
18		float4 position [[position]];
19		half3 color;
20	};
21
22	v2f vertex vertex_main(uint vertex_id                        [[vertex_id]],
23	                       device const packed_float3* positions [[buffer(0)]],
24	                       device const packed_float3* colors    [[buffer(1)]]) {
25		v2f o;
26		o.position = float4(positions[vertex_id], 1.0);
27		o.color = half3(colors[vertex_id]);
28		return o;
29	}
30
31	half4 fragment fragment_main(v2f in [[stage_in]]) {
32		return half4(in.color, 1.0);
33	}
34	`
35	shader_src_str := NS.String.alloc()->initWithOdinString(shader_src)
36	defer shader_src_str->release()
37
38	library = device->newLibraryWithSource(shader_src_str, nil) or_return
39
40	vertex_function   := library->newFunctionWithName(NS.AT("vertex_main"))
41	fragment_function := library->newFunctionWithName(NS.AT("fragment_main"))
42	defer vertex_function->release()
43	defer fragment_function->release()
44
45	desc := MTL.RenderPipelineDescriptor.alloc()->init()
46	defer desc->release()
47
48	desc->setVertexFunction(vertex_function)
49	desc->setFragmentFunction(fragment_function)
50	desc->colorAttachments()->object(0)->setPixelFormat(.BGRA8Unorm_sRGB)
51
52	pso = device->newRenderPipelineStateWithDescriptor(desc) or_return
53	return
54}
55
56build_buffers :: proc(device: ^MTL.Device) -> (vertex_positions_buffer, vertex_colors_buffer: ^MTL.Buffer) {
57	NUM_VERTICES :: 3
58	positions := [NUM_VERTICES][3]f32{
59		{-0.8,  0.8, 0.0},
60		{ 0.0, -0.8, 0.0},
61		{+0.8,  0.8, 0.0},
62	}
63	colors := [NUM_VERTICES][3]f32{
64		{1.0, 0.3, 0.2},
65		{0.8, 1.0, 0.0},
66		{0.8, 0.0, 1.0},
67	}
68
69	vertex_positions_buffer = device->newBufferWithSlice(positions[:], {.StorageModeManaged})
70	vertex_colors_buffer    = device->newBufferWithSlice(colors[:],    {.StorageModeManaged})
71	return
72}
73
74metal_main :: proc() -> (err: ^NS.Error) {
75	SDL.SetHint(SDL.HINT_RENDER_DRIVER, "metal")
76	SDL.setenv("METAL_DEVICE_WRAPPER_TYPE", "1", 0)
77	SDL.Init({.VIDEO})
78	defer SDL.Quit()
79
80	window := SDL.CreateWindow("Metal in Odin - 01 primitive",
81		SDL.WINDOWPOS_CENTERED, SDL.WINDOWPOS_CENTERED,
82		854, 480,
83		{.ALLOW_HIGHDPI, .HIDDEN, .RESIZABLE},
84	)
85	defer SDL.DestroyWindow(window)
86
87	window_system_info: SDL.SysWMinfo
88	SDL.GetVersion(&window_system_info.version)
89	SDL.GetWindowWMInfo(window, &window_system_info)
90	assert(window_system_info.subsystem == .COCOA)
91
92	native_window := (^NS.Window)(window_system_info.info.cocoa.window)
93
94	device := MTL.CreateSystemDefaultDevice()
95	defer device->release()
96
97	fmt.println(device->name()->odinString())
98
99	swapchain := CA.MetalLayer.layer()
100	defer swapchain->release()
101
102	swapchain->setDevice(device)
103	swapchain->setPixelFormat(.BGRA8Unorm_sRGB)
104	swapchain->setFramebufferOnly(true)
105	swapchain->setFrame(native_window->frame())
106
107	native_window->contentView()->setLayer(swapchain)
108	native_window->setOpaque(true)
109	native_window->setBackgroundColor(nil)
110
111	library, pso := build_shaders(device) or_return
112	defer library->release()
113	defer pso->release()
114
115	vertex_positions_buffer, vertex_colors_buffer := build_buffers(device)
116	defer vertex_positions_buffer->release()
117	defer vertex_colors_buffer->release()
118
119	command_queue := device->newCommandQueue()
120	defer command_queue->release()
121
122	SDL.ShowWindow(window)
123	for quit := false; !quit;  {
124		for e: SDL.Event; SDL.PollEvent(&e); {
125			#partial switch e.type {
126			case .QUIT:
127				quit = true
128			case .KEYDOWN:
129				if e.key.keysym.sym == .ESCAPE {
130					quit = true
131				}
132			}
133		}
134
135		drawable := swapchain->nextDrawable()
136		assert(drawable != nil)
137		defer drawable->release()
138
139		pass := MTL.RenderPassDescriptor.renderPassDescriptor()
140		defer pass->release()
141
142		color_attachment := pass->colorAttachments()->object(0)
143		assert(color_attachment != nil)
144		color_attachment->setClearColor(MTL.ClearColor{0.25, 0.5, 1.0, 1.0})
145		color_attachment->setLoadAction(.Clear)
146		color_attachment->setStoreAction(.Store)
147		color_attachment->setTexture(drawable->texture())
148
149
150		command_buffer := command_queue->commandBuffer()
151		defer command_buffer->release()
152
153		render_encoder := command_buffer->renderCommandEncoderWithDescriptor(pass)
154		defer render_encoder->release()
155
156		render_encoder->setRenderPipelineState(pso)
157		render_encoder->setVertexBuffer(vertex_positions_buffer, 0, 0)
158		render_encoder->setVertexBuffer(vertex_colors_buffer,    0, 1)
159		render_encoder->drawPrimitives(.Triangle, 0, 3)
160
161		render_encoder->endEncoding()
162
163		command_buffer->presentDrawable(drawable)
164		command_buffer->commit()
165	}
166
167	return nil
168}
169
170main :: proc() {
171	err := metal_main()
172	if err != nil {
173		fmt.eprintln(err->localizedDescription()->odinString())
174		os.exit(1)
175	}
176}

Declarations Used 30