|
1 |
+ |
package main |
|
2 |
+ |
|
|
3 |
+ |
import "core:fmt" |
|
4 |
+ |
import "core:mem" |
|
5 |
+ |
import "core:os" |
|
6 |
+ |
|
|
7 |
+ |
import bk "../../backend" |
|
8 |
+ |
import compiler "../../compiler" |
|
9 |
+ |
import app "../../app" |
|
10 |
+ |
import gpu "../.." |
|
11 |
+ |
import ir "../../render_ir" |
|
12 |
+ |
import resource "../../resource" |
|
13 |
+ |
|
|
14 |
+ |
WIDTH :: u32(256) |
|
15 |
+ |
HEIGHT :: u32(256) |
|
16 |
+ |
|
|
17 |
+ |
VERTEX_SHADER :: ` |
|
18 |
+ |
vertex main |
|
19 |
+ |
in position: vec3 @location(0) |
|
20 |
+ |
in normal: vec3 @location(1) |
|
21 |
+ |
in uv: vec2 @location(2) |
|
22 |
+ |
out position: vec4 @builtin(position) |
|
23 |
+ |
do |
|
24 |
+ |
position = vec4(position.x, position.y, 0.0, 1.0) |
|
25 |
+ |
end |
|
26 |
+ |
` |
|
27 |
+ |
|
|
28 |
+ |
FRAGMENT_SHADER :: ` |
|
29 |
+ |
fragment main |
|
30 |
+ |
out color: vec4 |
|
31 |
+ |
do |
|
32 |
+ |
color = vec4(240.0 / 255.0, 48.0 / 255.0, 96.0 / 255.0, 1.0) |
|
33 |
+ |
end |
|
34 |
+ |
` |
|
35 |
+ |
|
|
36 |
+ |
quad_vertices :: proc(x0, y0, x1, y1: f32) -> [6]resource.Mesh_Vertex_PNU { |
|
37 |
+ |
n := [3]f32{0, 0, 1} |
|
38 |
+ |
return { |
|
39 |
+ |
{position = {x0, y0, 0}, normal = n, tex_coord = {0, 0}}, |
|
40 |
+ |
{position = {x1, y0, 0}, normal = n, tex_coord = {1, 0}}, |
|
41 |
+ |
{position = {x1, y1, 0}, normal = n, tex_coord = {1, 1}}, |
|
42 |
+ |
{position = {x0, y0, 0}, normal = n, tex_coord = {0, 0}}, |
|
43 |
+ |
{position = {x1, y1, 0}, normal = n, tex_coord = {1, 1}}, |
|
44 |
+ |
{position = {x0, y1, 0}, normal = n, tex_coord = {0, 1}}, |
|
45 |
+ |
} |
|
46 |
+ |
} |
|
47 |
+ |
|
|
48 |
+ |
write_vertices :: proc(backend: ^bk.Backend, buffer: bk.Buffer_Handle, vertices: []resource.Mesh_Vertex_PNU) -> bool { |
|
49 |
+ |
ptr := backend.map_buffer(buffer) |
|
50 |
+ |
if ptr == nil { |
|
51 |
+ |
return false |
|
52 |
+ |
} |
|
53 |
+ |
defer backend.unmap_buffer(buffer) |
|
54 |
+ |
mem.copy(ptr, raw_data(vertices), len(vertices) * size_of(resource.Mesh_Vertex_PNU)) |
|
55 |
+ |
return true |
|
56 |
+ |
} |
|
57 |
+ |
|
|
58 |
+ |
write_ppm_rgba8 :: proc(path: string, rgba: []u8, width, height: u32) -> bool { |
|
59 |
+ |
if len(path) == 0 || len(rgba) < int(width * height * 4) { |
|
60 |
+ |
return false |
|
61 |
+ |
} |
|
62 |
+ |
header := "P6\n256 256\n255\n" |
|
63 |
+ |
out := make([]u8, len(header) + int(width * height * 3)) |
|
64 |
+ |
defer delete(out) |
|
65 |
+ |
for i in 0..<len(header) { |
|
66 |
+ |
out[i] = header[i] |
|
67 |
+ |
} |
|
68 |
+ |
dst := len(header) |
|
69 |
+ |
for i in 0..<int(width * height) { |
|
70 |
+ |
out[dst + i * 3 + 0] = rgba[i * 4 + 0] |
|
71 |
+ |
out[dst + i * 3 + 1] = rgba[i * 4 + 1] |
|
72 |
+ |
out[dst + i * 3 + 2] = rgba[i * 4 + 2] |
|
73 |
+ |
} |
|
74 |
+ |
err := os.write_entire_file(path, out) |
|
75 |
+ |
return err == nil |
|
76 |
+ |
} |
|
77 |
+ |
|
|
78 |
+ |
fail :: proc(message: string) { |
|
79 |
+ |
fmt.println(message) |
|
80 |
+ |
os.exit(1) |
|
81 |
+ |
} |
|
82 |
+ |
|
|
83 |
+ |
make_frame :: proc(negative: bool) -> ( |
|
84 |
+ |
frame: ir.Frame_IR, |
|
85 |
+ |
color: ir.Resource_Handle, |
|
86 |
+ |
geometry: ir.Resource_Handle, |
|
87 |
+ |
coverage: ir.Resource_Handle, |
|
88 |
+ |
) { |
|
89 |
+ |
frame = ir.init_frame_ir() |
|
90 |
+ |
color = ir.add_texture(&frame, "proof-color", WIDTH, HEIGHT, .R8G8B8A8_UNORM, {.Color_Attachment, .Sampled}, .Transient) |
|
91 |
+ |
depth := ir.add_texture(&frame, "proof-depth-stencil", WIDTH, HEIGHT, .D24_UNORM_S8_UINT, {.Depth_Stencil_Attachment}, .Transient) |
|
92 |
+ |
geometry = ir.add_buffer( |
|
93 |
+ |
&frame, |
|
94 |
+ |
"foreground-geometry", |
|
95 |
+ |
u64(6 * size_of(resource.Mesh_Vertex_PNU)), |
|
96 |
+ |
{.Vertex}, |
|
97 |
+ |
{.Host_Visible, .Host_Coherent}, |
|
98 |
+ |
.Transient, |
|
99 |
+ |
) |
|
100 |
+ |
coverage = ir.add_buffer( |
|
101 |
+ |
&frame, |
|
102 |
+ |
"mask-coverage", |
|
103 |
+ |
u64(6 * size_of(resource.Mesh_Vertex_PNU)), |
|
104 |
+ |
{.Vertex}, |
|
105 |
+ |
{.Host_Visible, .Host_Coherent}, |
|
106 |
+ |
.Transient, |
|
107 |
+ |
) |
|
108 |
+ |
pass_desc := ir.Pass_Desc{kind = .Render, name = "stencil-proof"} |
|
109 |
+ |
_ = ir.pass_add_color_target( |
|
110 |
+ |
&pass_desc, |
|
111 |
+ |
ir.make_color_target(color, .R8G8B8A8_UNORM, .Clear, .Store, {16.0 / 255.0, 16.0 / 255.0, 24.0 / 255.0, 1}), |
|
112 |
+ |
) |
|
113 |
+ |
ir.pass_set_depth_target(&pass_desc, ir.make_depth_target(depth, .D24_UNORM_S8_UINT, .Clear, .Store, 1)) |
|
114 |
+ |
ir.pass_set_viewport(&pass_desc, {x = 0, y = 0, width = f32(WIDTH), height = f32(HEIGHT), min_depth = 0, max_depth = 1}) |
|
115 |
+ |
ir.pass_set_scissor(&pass_desc, {x = 0, y = 0, width = WIDTH, height = HEIGHT}) |
|
116 |
+ |
pass := ir.add_pass(&frame, pass_desc) |
|
117 |
+ |
target := ir.add_target( |
|
118 |
+ |
&frame, |
|
119 |
+ |
{ |
|
120 |
+ |
kind = .Offscreen, |
|
121 |
+ |
name = "stencil-proof-target", |
|
122 |
+ |
width = WIDTH, |
|
123 |
+ |
height = HEIGHT, |
|
124 |
+ |
pass = pass, |
|
125 |
+ |
color_format = .R8G8B8A8_UNORM, |
|
126 |
+ |
depth_format = .D24_UNORM_S8_UINT, |
|
127 |
+ |
color_resource = color, |
|
128 |
+ |
depth_resource = depth, |
|
129 |
+ |
clear_color = {16.0 / 255.0, 16.0 / 255.0, 24.0 / 255.0, 1}, |
|
130 |
+ |
clear_depth = 1, |
|
131 |
+ |
}, |
|
132 |
+ |
) |
|
133 |
+ |
vs := ir.add_shader_source(&frame, "parity_stencil_clip.vert", .Vertex, VERTEX_SHADER) |
|
134 |
+ |
fs := ir.add_shader_source(&frame, "parity_stencil_clip.frag", .Fragment, FRAGMENT_SHADER) |
|
135 |
+ |
state := ir.default_graphics_pipeline_state() |
|
136 |
+ |
state.vertex_shader = vs |
|
137 |
+ |
state.fragment_shader = fs |
|
138 |
+ |
state.enable_blending = false |
|
139 |
+ |
pipeline := ir.add_graphics_pipeline(&frame, "foreground", state) |
|
140 |
+ |
mask := ir.add_mask( |
|
141 |
+ |
&frame, |
|
142 |
+ |
{ |
|
143 |
+ |
target = target, |
|
144 |
+ |
name = "center-mask", |
|
145 |
+ |
source_kind = .Geometry_Coverage, |
|
146 |
+ |
layout_variant = .PNU, |
|
147 |
+ |
coverage_resource = coverage, |
|
148 |
+ |
vertex_count = 6, |
|
149 |
+ |
}, |
|
150 |
+ |
) |
|
151 |
+ |
clip := ir.INVALID_CLIP |
|
152 |
+ |
if !negative { |
|
153 |
+ |
clip = ir.add_clip(&frame, {target = target, kind = .Mask, mask = mask}) |
|
154 |
+ |
} |
|
155 |
+ |
_ = ir.add_draw_packet( |
|
156 |
+ |
&frame, |
|
157 |
+ |
pass, |
|
158 |
+ |
pipeline, |
|
159 |
+ |
.Vertex_Stream, |
|
160 |
+ |
{ |
|
161 |
+ |
target = target, |
|
162 |
+ |
clip = clip, |
|
163 |
+ |
geometry = {kind = .Vertex_Stream, resource = geometry, vertex_count = 6}, |
|
164 |
+ |
order = .Strict, |
|
165 |
+ |
}, |
|
166 |
+ |
) |
|
167 |
+ |
return |
|
168 |
+ |
} |
|
169 |
+ |
|
|
170 |
+ |
main :: proc() { |
|
171 |
+ |
state, ok := app.init_window(i32(WIDTH), i32(HEIGHT), "GPU parity stencil clip") |
|
172 |
+ |
if !ok { |
|
173 |
+ |
fail("parity_stencil_clip: failed to initialize window/backend") |
|
174 |
+ |
} |
|
175 |
+ |
defer app.shutdown(&state) |
|
176 |
+ |
|
|
177 |
+ |
backend := gpu.get_backend() |
|
178 |
+ |
if backend == nil || backend.read_texture_rgba8 == nil { |
|
179 |
+ |
fail("parity_stencil_clip: backend readback unsupported") |
|
180 |
+ |
} |
|
181 |
+ |
backend.capabilities.features += {.Stencil_Clips} |
|
182 |
+ |
|
|
183 |
+ |
mode := os.get_env("GPU_PROOF_MODE", context.allocator) |
|
184 |
+ |
readback_path := os.get_env("GPU_PROOF_READBACK", context.allocator) |
|
185 |
+ |
profile_path := os.get_env("GPU_PROFILE_JSON", context.allocator) |
|
186 |
+ |
negative := mode == "negative" |
|
187 |
+ |
|
|
188 |
+ |
frame, color, geometry, coverage := make_frame(negative) |
|
189 |
+ |
defer ir.destroy_frame_ir(&frame) |
|
190 |
+ |
|
|
191 |
+ |
exec := compiler.init_execution_state(backend) |
|
192 |
+ |
defer compiler.destroy_execution_state(&exec) |
|
193 |
+ |
if !compiler.prepare_planned_frame(&exec, &frame) { |
|
194 |
+ |
fail("parity_stencil_clip: failed to prepare planned frame") |
|
195 |
+ |
} |
|
196 |
+ |
foreground := quad_vertices(-1, -1, 1, 1) |
|
197 |
+ |
mask := quad_vertices(-0.5, -0.5, 0.5, 0.5) |
|
198 |
+ |
foreground_buffer, fg_ok := compiler.prepared_buffer(&exec, geometry) |
|
199 |
+ |
mask_buffer, mask_ok := compiler.prepared_buffer(&exec, coverage) |
|
200 |
+ |
if !fg_ok || !mask_ok { |
|
201 |
+ |
fail("parity_stencil_clip: failed to resolve prepared buffers") |
|
202 |
+ |
} |
|
203 |
+ |
if !write_vertices(backend, foreground_buffer, foreground[:]) || |
|
204 |
+ |
!write_vertices(backend, mask_buffer, mask[:]) { |
|
205 |
+ |
fail("parity_stencil_clip: failed to upload vertices") |
|
206 |
+ |
} |
|
207 |
+ |
if !compiler.execute_prepared_planned_frame(&exec, &frame, {0, 0, 0, 1}) { |
|
208 |
+ |
fail("parity_stencil_clip: failed to execute planned frame") |
|
209 |
+ |
} |
|
210 |
+ |
|
|
211 |
+ |
texture, tex_ok := compiler.prepared_texture(&exec, color) |
|
212 |
+ |
if !tex_ok { |
|
213 |
+ |
fail("parity_stencil_clip: failed to resolve readback target") |
|
214 |
+ |
} |
|
215 |
+ |
rgba := make([]u8, int(WIDTH * HEIGHT * 4)) |
|
216 |
+ |
defer delete(rgba) |
|
217 |
+ |
if !backend.read_texture_rgba8({texture = texture, width = WIDTH, height = HEIGHT, current_layout = .Shader_Read_Only}, rgba) { |
|
218 |
+ |
fail("parity_stencil_clip: readback failed") |
|
219 |
+ |
} |
|
220 |
+ |
if !write_ppm_rgba8(readback_path, rgba, WIDTH, HEIGHT) { |
|
221 |
+ |
fail("parity_stencil_clip: failed to write readback") |
|
222 |
+ |
} |
|
223 |
+ |
if len(profile_path) > 0 { |
|
224 |
+ |
_ = gpu.write_profile(profile_path, 0, 1) |
|
225 |
+ |
} |
|
226 |
+ |
} |