|
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 |
+ |
} |