metal/learn_metal/02-argbuffers macOS

Code

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

Declarations Used 31