Harbor

Changelog d3c4a8bf7fd9

pin gpu parity proof examples

@sky · 1 month ago · parent 62e01d9167dd
2 added 0 modified 0 deleted
gpu/examples/parity_backend_features/main.odin +227 -0 added
1 + package main
2 +
3 + import "core:fmt"
4 + import "core:mem"
5 + import "core:os"
6 +
7 + import app "../../app"
8 + import bk "../../backend"
9 + import compiler "../../compiler"
10 + import gpu "../.."
11 + import ir "../../render_ir"
12 + import resource "../../resource"
13 +
14 + WIDTH :: u32(256)
15 + HEIGHT :: u32(256)
16 +
17 + Instance_PNUC :: struct {
18 + position: [3]f32,
19 + normal: [3]f32,
20 + uv: [2]f32,
21 + color: [4]f32,
22 + }
23 +
24 + VERTEX_SHADER :: `
25 + vertex main
26 + in position: vec3 @location(0)
27 + in normal: vec3 @location(1)
28 + in uv: vec2 @location(2)
29 + out position: vec4 @builtin(position)
30 + do
31 + position = vec4(position, 0.0, 1.0)
32 + end
33 + `
34 +
35 + INSTANCE_VERTEX_SHADER :: `
36 + vertex main
37 + in position: vec3 @location(0)
38 + in normal: vec3 @location(1)
39 + in uv: vec2 @location(2)
40 + in instance_position: vec3 @location(3)
41 + in instance_normal: vec3 @location(4)
42 + in instance_uv: vec2 @location(5)
43 + in instance_color: vec4 @location(6)
44 + out position: vec4 @builtin(position)
45 + out color: vec4
46 + do
47 + position = vec4(position + instance_position, 0.0, 1.0)
48 + out.color = instance_color
49 + end
50 + `
51 +
52 + RED_FRAGMENT_SHADER :: `
53 + fragment main
54 + out color: vec4
55 + do
56 + color = vec4(240.0 / 255.0, 64.0 / 255.0, 64.0 / 255.0, 1.0)
57 + end
58 + `
59 +
60 + INSTANCE_FRAGMENT_SHADER :: `
61 + fragment main
62 + in color: vec4
63 + out out_color: vec4
64 + do
65 + out_color = color
66 + end
67 + `
68 +
69 + quad_vertices :: proc(x0, y0, x1, y1: f32) -> [6]resource.Mesh_Vertex_PNU {
70 + n := [3]f32{0, 0, 1}
71 + return {
72 + {position = {x0, y0, 0}, normal = n, tex_coord = {0, 1}},
73 + {position = {x1, y0, 0}, normal = n, tex_coord = {1, 1}},
74 + {position = {x1, y1, 0}, normal = n, tex_coord = {1, 0}},
75 + {position = {x0, y0, 0}, normal = n, tex_coord = {0, 1}},
76 + {position = {x1, y1, 0}, normal = n, tex_coord = {1, 0}},
77 + {position = {x0, y1, 0}, normal = n, tex_coord = {0, 0}},
78 + }
79 + }
80 +
81 + write_bytes :: proc(backend: ^bk.Backend, buffer: bk.Buffer_Handle, data: rawptr, size: int) -> bool {
82 + mapped := backend.map_buffer(buffer)
83 + if mapped == nil {
84 + return false
85 + }
86 + mem.copy(mapped, data, size)
87 + backend.unmap_buffer(buffer)
88 + return true
89 + }
90 +
91 + write_ppm_rgba8 :: proc(path: string, rgba: []u8, width, height: u32) -> bool {
92 + if len(path) == 0 {
93 + return false
94 + }
95 + header := fmt.aprintf("P6\n%d %d\n255\n", width, height, allocator = context.temp_allocator)
96 + out := make([]u8, len(header) + int(width * height * 3))
97 + defer delete(out)
98 + copy(out[:len(header)], transmute([]u8)header)
99 + dst := len(header)
100 + for i in 0 ..< int(width * height) {
101 + out[dst + i * 3 + 0] = rgba[i * 4 + 0]
102 + out[dst + i * 3 + 1] = rgba[i * 4 + 1]
103 + out[dst + i * 3 + 2] = rgba[i * 4 + 2]
104 + }
105 + return os.write_entire_file(path, out) == nil
106 + }
107 +
108 + fail :: proc(message: string) {
109 + fmt.println(message)
110 + os.exit(1)
111 + }
112 +
113 + make_frame :: proc() -> (
114 + frame: ir.Frame_IR,
115 + color: ir.Resource_Handle,
116 + mrt_geometry: ir.Resource_Handle,
117 + indirect_geometry: ir.Resource_Handle,
118 + instance_data: ir.Resource_Handle,
119 + indirect_args: ir.Resource_Handle,
120 + ) {
121 + frame = ir.init_frame_ir()
122 + color = ir.add_texture(&frame, "proof-color", WIDTH, HEIGHT, .R8G8B8A8_UNORM, {.Color_Attachment, .Sampled}, .Transient)
123 + secondary := ir.add_texture(&frame, "mrt-secondary", WIDTH, HEIGHT, .R8G8B8A8_UNORM, {.Color_Attachment, .Sampled}, .Transient)
124 + mrt_geometry = ir.add_buffer(&frame, "mrt-vertices", u64(6 * size_of(resource.Mesh_Vertex_PNU)), {.Vertex}, {.Host_Visible, .Host_Coherent}, .Transient)
125 + indirect_geometry = ir.add_buffer(&frame, "indirect-vertices", u64(6 * size_of(resource.Mesh_Vertex_PNU)), {.Vertex}, {.Host_Visible, .Host_Coherent}, .Transient)
126 + instance_data = ir.add_buffer(&frame, "instances", u64(size_of(Instance_PNUC)), {.Vertex}, {.Host_Visible, .Host_Coherent}, .Transient)
127 + indirect_args = ir.add_buffer(&frame, "indirect-args", u64(size_of(bk.Indirect_Draw_Args)), {.Indirect_Argument}, {.Host_Visible, .Host_Coherent}, .Transient)
128 +
129 + pass_desc := ir.Pass_Desc{kind = .Render, name = "backend-feature-proof"}
130 + _ = ir.pass_add_color_target(&pass_desc, ir.make_color_target(color, .R8G8B8A8_UNORM, .Clear, .Store, {16.0 / 255.0, 16.0 / 255.0, 24.0 / 255.0, 1}))
131 + _ = ir.pass_add_color_target(&pass_desc, ir.make_color_target(secondary, .R8G8B8A8_UNORM, .Clear, .Store, {0, 0, 0, 1}))
132 + ir.pass_set_viewport(&pass_desc, {x = 0, y = 0, width = f32(WIDTH), height = f32(HEIGHT), min_depth = 0, max_depth = 1})
133 + ir.pass_set_scissor(&pass_desc, {x = 0, y = 0, width = WIDTH, height = HEIGHT})
134 + pass := ir.add_pass(&frame, pass_desc)
135 + target := ir.add_target(&frame, {kind = .Offscreen, name = "proof-target", width = WIDTH, height = HEIGHT, pass = pass, color_format = .R8G8B8A8_UNORM, color_resource = color})
136 +
137 + vs := ir.add_shader_source(&frame, "parity_backend_features.vert", .Vertex, VERTEX_SHADER)
138 + red_fs := ir.add_shader_source(&frame, "parity_backend_features_red.frag", .Fragment, RED_FRAGMENT_SHADER)
139 + red_state := ir.default_graphics_pipeline_state()
140 + red_state.vertex_shader = vs
141 + red_state.fragment_shader = red_fs
142 + red_pipeline := ir.add_graphics_pipeline(&frame, "mrt-red", red_state)
143 + _ = ir.add_draw_packet(&frame, pass, red_pipeline, .Vertex_Stream, {
144 + target = target,
145 + geometry = {kind = .Vertex_Stream, resource = mrt_geometry, vertex_count = 6},
146 + order = .Strict,
147 + })
148 +
149 + instance_vs := ir.add_shader_source(&frame, "parity_backend_features_instance.vert", .Vertex, INSTANCE_VERTEX_SHADER)
150 + instance_fs := ir.add_shader_source(&frame, "parity_backend_features_instance.frag", .Fragment, INSTANCE_FRAGMENT_SHADER)
151 + instance_state := ir.default_graphics_pipeline_state()
152 + instance_state.vertex_shader = instance_vs
153 + instance_state.fragment_shader = instance_fs
154 + instance_state.has_instance_layout = true
155 + instance_state.instance_layout_variant = .PNUC
156 + instance_pipeline := ir.add_graphics_pipeline(&frame, "indirect-instance", instance_state)
157 + _ = ir.add_draw_packet(&frame, pass, instance_pipeline, .Indirect, {
158 + target = target,
159 + geometry = {kind = .Vertex_Stream, resource = indirect_geometry, vertex_count = 6},
160 + instances = {
161 + resource = instance_data,
162 + indirect = indirect_args,
163 + count = 1,
164 + stride = u32(size_of(bk.Indirect_Draw_Args)),
165 + },
166 + order = .Strict,
167 + })
168 + return
169 + }
170 +
171 + main :: proc() {
172 + state, ok := app.init_window(i32(WIDTH), i32(HEIGHT), "GPU parity backend features")
173 + if !ok {
174 + fail("parity_backend_features: failed to initialize window/backend")
175 + }
176 + defer app.shutdown(&state)
177 +
178 + backend := gpu.get_backend()
179 + if backend == nil || backend.read_texture_rgba8 == nil {
180 + fail("parity_backend_features: backend readback unsupported")
181 + }
182 +
183 + frame, color, mrt_geometry, indirect_geometry, instance_data, indirect_args := make_frame()
184 + defer ir.destroy_frame_ir(&frame)
185 + exec := compiler.init_execution_state(backend)
186 + defer compiler.destroy_execution_state(&exec)
187 + if !compiler.prepare_planned_frame(&exec, &frame) {
188 + fail("parity_backend_features: failed to prepare planned frame")
189 + }
190 + mrt_buffer, mrt_ok := compiler.prepared_buffer(&exec, mrt_geometry)
191 + indirect_buffer, indirect_ok := compiler.prepared_buffer(&exec, indirect_geometry)
192 + instance_buffer, instance_ok := compiler.prepared_buffer(&exec, instance_data)
193 + args_buffer, args_ok := compiler.prepared_buffer(&exec, indirect_args)
194 + if !mrt_ok || !indirect_ok || !instance_ok || !args_ok {
195 + fail("parity_backend_features: failed to resolve prepared buffers")
196 + }
197 + mrt_vertices := quad_vertices(-0.85, -0.5, -0.35, 0.5)
198 + indirect_vertices := quad_vertices(-0.25, -0.5, 0.25, 0.5)
199 + instance := Instance_PNUC{normal = {0, 0, 1}, color = {64.0 / 255.0, 240.0 / 255.0, 64.0 / 255.0, 1}}
200 + args := bk.Indirect_Draw_Args{vertex_count = 6, instance_count = 1}
201 + if !write_bytes(backend, mrt_buffer, raw_data(mrt_vertices[:]), len(mrt_vertices) * size_of(resource.Mesh_Vertex_PNU)) ||
202 + !write_bytes(backend, indirect_buffer, raw_data(indirect_vertices[:]), len(indirect_vertices) * size_of(resource.Mesh_Vertex_PNU)) ||
203 + !write_bytes(backend, instance_buffer, &instance, size_of(Instance_PNUC)) ||
204 + !write_bytes(backend, args_buffer, &args, size_of(bk.Indirect_Draw_Args)) {
205 + fail("parity_backend_features: failed to upload proof buffers")
206 + }
207 + if !compiler.execute_prepared_planned_frame(&exec, &frame, {0, 0, 0, 1}) {
208 + fail("parity_backend_features: failed to execute planned frame")
209 + }
210 + texture, tex_ok := compiler.prepared_texture(&exec, color)
211 + if !tex_ok {
212 + fail("parity_backend_features: failed to resolve readback target")
213 + }
214 + rgba := make([]u8, int(WIDTH * HEIGHT * 4))
215 + defer delete(rgba)
216 + if !backend.read_texture_rgba8({texture = texture, width = WIDTH, height = HEIGHT, current_layout = .Shader_Read_Only}, rgba) {
217 + fail("parity_backend_features: readback failed")
218 + }
219 + readback_path := os.get_env("GPU_PROOF_READBACK", context.allocator)
220 + if !write_ppm_rgba8(readback_path, rgba, WIDTH, HEIGHT) {
221 + fail("parity_backend_features: failed to write readback")
222 + }
223 + profile_path := os.get_env("GPU_PROFILE_JSON", context.allocator)
224 + if len(profile_path) > 0 {
225 + _ = gpu.write_profile(profile_path, 0, 1)
226 + }
227 + }
gpu/examples/parity_resource_sync/main.odin +193 -0 added
1 + package main
2 +
3 + import "core:fmt"
4 + import "core:os"
5 +
6 + import app "../../app"
7 + import compiler "../../compiler"
8 + import gpu "../.."
9 + import ir "../../render_ir"
10 + import resource "../../resource"
11 +
12 + WIDTH :: u32(256)
13 + HEIGHT :: u32(256)
14 +
15 + COMPUTE_SHADER :: `
16 + struct Vertex
17 + position: vec3
18 + normal: vec3
19 + uv: vec2
20 + end
21 +
22 + struct Vertices
23 + data: [6]Vertex
24 + end
25 +
26 + @group(0) @binding(0)
27 + buffer vertices: Vertices
28 +
29 + @entry(compute)
30 + @workgroup_size(1, 1, 1)
31 + function main(@builtin(global_invocation_id) gid: uvec3)
32 + let z = 0.0
33 + vertices.data[0].position = vec3(-1.0, -1.0, z)
34 + vertices.data[1].position = vec3( 1.0, -1.0, z)
35 + vertices.data[2].position = vec3( 1.0, 1.0, z)
36 + vertices.data[3].position = vec3(-1.0, -1.0, z)
37 + vertices.data[4].position = vec3( 1.0, 1.0, z)
38 + vertices.data[5].position = vec3(-1.0, 1.0, z)
39 + for i in 0..6
40 + vertices.data[i].normal = vec3(0.0, 0.0, 1.0)
41 + vertices.data[i].uv = vec2(0.0, 0.0)
42 + end
43 + end
44 + `
45 +
46 + VERTEX_SHADER :: `
47 + vertex main
48 + in position: vec3 @location(0)
49 + in normal: vec3 @location(1)
50 + in uv: vec2 @location(2)
51 + out position: vec4 @builtin(position)
52 + do
53 + position = vec4(position, 0.0, 1.0)
54 + end
55 + `
56 +
57 + FRAGMENT_SHADER :: `
58 + fragment main
59 + out color: vec4
60 + do
61 + color = vec4(48.0 / 255.0, 224.0 / 255.0, 128.0 / 255.0, 1.0)
62 + end
63 + `
64 +
65 + write_ppm_rgba8 :: proc(path: string, rgba: []u8, width, height: u32) -> bool {
66 + if len(path) == 0 {
67 + return false
68 + }
69 + header := fmt.aprintf("P6\n%d %d\n255\n", width, height, allocator = context.temp_allocator)
70 + out := make([]u8, len(header) + int(width * height * 3))
71 + defer delete(out)
72 + copy(out[:len(header)], transmute([]u8)header)
73 + dst := len(header)
74 + for i in 0 ..< int(width * height) {
75 + out[dst + i * 3 + 0] = rgba[i * 4 + 0]
76 + out[dst + i * 3 + 1] = rgba[i * 4 + 1]
77 + out[dst + i * 3 + 2] = rgba[i * 4 + 2]
78 + }
79 + return os.write_entire_file(path, out) == nil
80 + }
81 +
82 + fail :: proc(message: string) {
83 + fmt.println(message)
84 + os.exit(1)
85 + }
86 +
87 + make_frame :: proc() -> (frame: ir.Frame_IR, color: ir.Resource_Handle) {
88 + frame = ir.init_frame_ir()
89 + color = ir.add_texture(&frame, "proof-color", WIDTH, HEIGHT, .R8G8B8A8_UNORM, {.Color_Attachment, .Sampled}, .Transient)
90 + vertices := ir.add_buffer(
91 + &frame,
92 + "compute-written-vertices",
93 + u64(6 * size_of(resource.Mesh_Vertex_PNU)),
94 + {.Storage, .Vertex},
95 + {.Device_Local},
96 + .Transient,
97 + )
98 + compute_shader := ir.add_shader_source(&frame, "parity_resource_sync.comp", .Compute, COMPUTE_SHADER)
99 + compute_pipeline := ir.add_compute_pipeline(
100 + &frame,
101 + "write-vertices",
102 + ir.default_compute_pipeline_state(compute_shader),
103 + )
104 + compute_pass := ir.add_pass(&frame, {kind = .Compute, name = "compute"})
105 + storage := [?]ir.Descriptor_Resource_Binding{{
106 + binding = 0,
107 + type = .Storage_Buffer,
108 + resource = vertices,
109 + size = u64(6 * size_of(resource.Mesh_Vertex_PNU)),
110 + }}
111 + _ = ir.add_compute_dispatch(&frame, compute_pass, compute_pipeline, 0, {1, 1, 1}, storage[:])
112 +
113 + pass_desc := ir.Pass_Desc{kind = .Render, name = "draw-compute-output"}
114 + _ = ir.pass_add_color_target(
115 + &pass_desc,
116 + ir.make_color_target(color, .R8G8B8A8_UNORM, .Clear, .Store, {16.0 / 255.0, 16.0 / 255.0, 24.0 / 255.0, 1}),
117 + )
118 + ir.pass_set_viewport(&pass_desc, {x = 0, y = 0, width = f32(WIDTH), height = f32(HEIGHT), min_depth = 0, max_depth = 1})
119 + ir.pass_set_scissor(&pass_desc, {x = 0, y = 0, width = WIDTH, height = HEIGHT})
120 + pass := ir.add_pass(&frame, pass_desc)
121 + target := ir.add_target(
122 + &frame,
123 + {
124 + kind = .Offscreen,
125 + name = "proof-target",
126 + width = WIDTH,
127 + height = HEIGHT,
128 + pass = pass,
129 + color_format = .R8G8B8A8_UNORM,
130 + color_resource = color,
131 + clear_color = {16.0 / 255.0, 16.0 / 255.0, 24.0 / 255.0, 1},
132 + },
133 + )
134 + vs := ir.add_shader_source(&frame, "parity_resource_sync.vert", .Vertex, VERTEX_SHADER)
135 + fs := ir.add_shader_source(&frame, "parity_resource_sync.frag", .Fragment, FRAGMENT_SHADER)
136 + state := ir.default_graphics_pipeline_state()
137 + state.vertex_shader = vs
138 + state.fragment_shader = fs
139 + pipeline := ir.add_graphics_pipeline(&frame, "draw", state)
140 + _ = ir.add_draw_packet(
141 + &frame,
142 + pass,
143 + pipeline,
144 + .Vertex_Stream,
145 + {
146 + target = target,
147 + geometry = {kind = .Vertex_Stream, resource = vertices, vertex_count = 6},
148 + order = .Strict,
149 + },
150 + )
151 + return
152 + }
153 +
154 + main :: proc() {
155 + state, ok := app.init_window(i32(WIDTH), i32(HEIGHT), "GPU parity resource sync")
156 + if !ok {
157 + fail("parity_resource_sync: failed to initialize window/backend")
158 + }
159 + defer app.shutdown(&state)
160 +
161 + backend := gpu.get_backend()
162 + if backend == nil || backend.read_texture_rgba8 == nil {
163 + fail("parity_resource_sync: backend readback unsupported")
164 + }
165 +
166 + frame, color := make_frame()
167 + defer ir.destroy_frame_ir(&frame)
168 + exec := compiler.init_execution_state(backend)
169 + defer compiler.destroy_execution_state(&exec)
170 + if !compiler.prepare_planned_frame(&exec, &frame) {
171 + fail("parity_resource_sync: failed to prepare planned frame")
172 + }
173 + if !compiler.execute_prepared_planned_frame(&exec, &frame, {0, 0, 0, 1}) {
174 + fail("parity_resource_sync: failed to execute planned frame")
175 + }
176 + texture, tex_ok := compiler.prepared_texture(&exec, color)
177 + if !tex_ok {
178 + fail("parity_resource_sync: failed to resolve readback target")
179 + }
180 + rgba := make([]u8, int(WIDTH * HEIGHT * 4))
181 + defer delete(rgba)
182 + if !backend.read_texture_rgba8({texture = texture, width = WIDTH, height = HEIGHT, current_layout = .Shader_Read_Only}, rgba) {
183 + fail("parity_resource_sync: readback failed")
184 + }
185 + readback_path := os.get_env("GPU_PROOF_READBACK", context.allocator)
186 + if !write_ppm_rgba8(readback_path, rgba, WIDTH, HEIGHT) {
187 + fail("parity_resource_sync: failed to write readback")
188 + }
189 + profile_path := os.get_env("GPU_PROFILE_JSON", context.allocator)
190 + if len(profile_path) > 0 {
191 + _ = gpu.write_profile(profile_path, 0, 1)
192 + }
193 + }