blob: 438cb4a6610f49fcfc12b474e56ddb571d2807f9 [file] [log] [blame]
dan sinclairf15c1c32024-09-05 21:41:20 +00001// Copyright 2024 The Dawn & Tint Authors
2//
3// Redistribution and use in source and binary forms, with or without
4// modification, are permitted provided that the following conditions are met:
5//
6// 1. Redistributions of source code must retain the above copyright notice, this
7// list of conditions and the following disclaimer.
8//
9// 2. Redistributions in binary form must reproduce the above copyright notice,
10// this list of conditions and the following disclaimer in the documentation
11// and/or other materials provided with the distribution.
12//
13// 3. Neither the name of the copyright holder nor the names of its
14// contributors may be used to endorse or promote products derived from
15// this software without specific prior written permission.
16//
17// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
21// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28#include <utility>
29
30#include "src/tint/lang/core/ir/transform/helper_test.h"
31#include "src/tint/lang/core/type/struct.h"
32#include "src/tint/lang/glsl/writer/raise/shader_io.h"
33
34namespace tint::glsl::writer::raise {
35namespace {
36
37using namespace tint::core::fluent_types; // NOLINT
38using namespace tint::core::number_suffixes; // NOLINT
39
40using GlslWriter_ShaderIOTest = core::ir::transform::TransformTest;
41
42TEST_F(GlslWriter_ShaderIOTest, NoInputsOrOutputs) {
dan sinclair75742452024-10-29 03:34:53 +000043 auto* ep = b.ComputeFunction("foo");
dan sinclairf15c1c32024-09-05 21:41:20 +000044
45 b.Append(ep->Block(), [&] { //
46 b.Return(ep);
47 });
48
49 auto* src = R"(
dan sinclair75742452024-10-29 03:34:53 +000050%foo = @compute @workgroup_size(1u, 1u, 1u) func():void {
dan sinclairf15c1c32024-09-05 21:41:20 +000051 $B1: {
52 ret
53 }
54}
55)";
56 EXPECT_EQ(src, str());
57
58 auto* expect = src;
59
James Price978af552024-10-08 06:05:52 +000060 core::ir::transform::PushConstantLayout push_constants;
61 ShaderIOConfig config{push_constants};
dan sinclairf15c1c32024-09-05 21:41:20 +000062 Run(ShaderIO, config);
63
64 EXPECT_EQ(expect, str());
65}
66
67TEST_F(GlslWriter_ShaderIOTest, Parameters_NonStruct) {
68 auto* ep = b.Function("foo", ty.void_());
69 auto* front_facing = b.FunctionParam("front_facing", ty.bool_());
70 front_facing->SetBuiltin(core::BuiltinValue::kFrontFacing);
71 auto* position = b.FunctionParam("position", ty.vec4<f32>());
72 position->SetBuiltin(core::BuiltinValue::kPosition);
73 position->SetInvariant(true);
74 auto* color1 = b.FunctionParam("color1", ty.f32());
75 color1->SetLocation(0);
76 auto* color2 = b.FunctionParam("color2", ty.f32());
77 color2->SetLocation(1);
78 color2->SetInterpolation(core::Interpolation{core::InterpolationType::kLinear,
79 core::InterpolationSampling::kSample});
80
81 ep->SetParams({front_facing, position, color1, color2});
82 ep->SetStage(core::ir::Function::PipelineStage::kFragment);
83
84 b.Append(ep->Block(), [&] {
85 auto* ifelse = b.If(front_facing);
86 b.Append(ifelse->True(), [&] {
87 b.Multiply(ty.vec4<f32>(), position, b.Add(ty.f32(), color1, color2));
88 b.ExitIf(ifelse);
89 });
90 b.Return(ep);
91 });
92
93 auto* src = R"(
94%foo = @fragment func(%front_facing:bool [@front_facing], %position:vec4<f32> [@invariant, @position], %color1:f32 [@location(0)], %color2:f32 [@location(1), @interpolate(linear, sample)]):void {
95 $B1: {
96 if %front_facing [t: $B2] { # if_1
97 $B2: { # true
98 %6:f32 = add %color1, %color2
99 %7:vec4<f32> = mul %position, %6
100 exit_if # if_1
101 }
102 }
103 ret
104 }
105}
106)";
107 EXPECT_EQ(src, str());
108
109 auto* expect = R"(
110$B1: { # root
111 %gl_FrontFacing:ptr<__in, bool, read> = var @builtin(front_facing)
112 %gl_FragCoord:ptr<__in, vec4<f32>, read> = var @invariant @builtin(position)
113 %foo_loc0_Input:ptr<__in, f32, read> = var @location(0)
114 %foo_loc1_Input:ptr<__in, f32, read> = var @location(1) @interpolate(linear, sample)
115}
116
117%foo_inner = func(%front_facing:bool, %position:vec4<f32>, %color1:f32, %color2:f32):void {
118 $B2: {
119 if %front_facing [t: $B3] { # if_1
120 $B3: { # true
121 %10:f32 = add %color1, %color2
122 %11:vec4<f32> = mul %position, %10
123 exit_if # if_1
124 }
125 }
126 ret
127 }
128}
129%foo = @fragment func():void {
130 $B4: {
131 %13:bool = load %gl_FrontFacing
132 %14:vec4<f32> = load %gl_FragCoord
133 %15:f32 = load %foo_loc0_Input
134 %16:f32 = load %foo_loc1_Input
135 %17:void = call %foo_inner, %13, %14, %15, %16
136 ret
137 }
138}
139)";
140
James Price978af552024-10-08 06:05:52 +0000141 core::ir::transform::PushConstantLayout push_constants;
142 ShaderIOConfig config{push_constants};
dan sinclairf15c1c32024-09-05 21:41:20 +0000143 Run(ShaderIO, config);
144
145 EXPECT_EQ(expect, str());
146}
147
148TEST_F(GlslWriter_ShaderIOTest, Parameters_Struct) {
149 auto* str_ty = ty.Struct(mod.symbols.New("Inputs"),
150 {
151 {
152 mod.symbols.New("front_facing"),
153 ty.bool_(),
154 core::IOAttributes{
155 /* location */ std::nullopt,
156 /* blend_src */ std::nullopt,
157 /* color */ std::nullopt,
158 /* builtin */ core::BuiltinValue::kFrontFacing,
159 /* interpolation */ std::nullopt,
160 /* invariant */ false,
161 },
162 },
163 {
164 mod.symbols.New("position"),
165 ty.vec4<f32>(),
166 core::IOAttributes{
167 /* location */ std::nullopt,
168 /* blend_src */ std::nullopt,
169 /* color */ std::nullopt,
170 /* builtin */ core::BuiltinValue::kPosition,
171 /* interpolation */ std::nullopt,
172 /* invariant */ true,
173 },
174 },
175 {
176 mod.symbols.New("color1"),
177 ty.f32(),
178 core::IOAttributes{
179 /* location */ 0u,
180 /* blend_src */ std::nullopt,
181 /* color */ std::nullopt,
182 /* builtin */ std::nullopt,
183 /* interpolation */ std::nullopt,
184 /* invariant */ false,
185 },
186 },
187 {
188 mod.symbols.New("color2"),
189 ty.f32(),
190 core::IOAttributes{
191 /* location */ 1u,
192 /* blend_src */ std::nullopt,
193 /* color */ std::nullopt,
194 /* builtin */ std::nullopt,
195 /* interpolation */
196 core::Interpolation{
197 core::InterpolationType::kLinear,
198 core::InterpolationSampling::kSample,
199 },
200 /* invariant */ false,
201 },
202 },
203 });
204
205 auto* ep = b.Function("foo", ty.void_());
206 auto* str_param = b.FunctionParam("inputs", str_ty);
207 ep->SetParams({str_param});
208 ep->SetStage(core::ir::Function::PipelineStage::kFragment);
209
210 b.Append(ep->Block(), [&] {
211 auto* ifelse = b.If(b.Access(ty.bool_(), str_param, 0_i));
212 b.Append(ifelse->True(), [&] {
213 auto* position = b.Access(ty.vec4<f32>(), str_param, 1_i);
214 auto* color1 = b.Access(ty.f32(), str_param, 2_i);
215 auto* color2 = b.Access(ty.f32(), str_param, 3_i);
216 b.Multiply(ty.vec4<f32>(), position, b.Add(ty.f32(), color1, color2));
217 b.ExitIf(ifelse);
218 });
219 b.Return(ep);
220 });
221
222 auto* src = R"(
223Inputs = struct @align(16) {
224 front_facing:bool @offset(0), @builtin(front_facing)
225 position:vec4<f32> @offset(16), @invariant, @builtin(position)
226 color1:f32 @offset(32), @location(0)
227 color2:f32 @offset(36), @location(1), @interpolate(linear, sample)
228}
229
230%foo = @fragment func(%inputs:Inputs):void {
231 $B1: {
232 %3:bool = access %inputs, 0i
233 if %3 [t: $B2] { # if_1
234 $B2: { # true
235 %4:vec4<f32> = access %inputs, 1i
236 %5:f32 = access %inputs, 2i
237 %6:f32 = access %inputs, 3i
238 %7:f32 = add %5, %6
239 %8:vec4<f32> = mul %4, %7
240 exit_if # if_1
241 }
242 }
243 ret
244 }
245}
246)";
247 EXPECT_EQ(src, str());
248
249 auto* expect = R"(
250Inputs = struct @align(16) {
251 front_facing:bool @offset(0)
252 position:vec4<f32> @offset(16)
253 color1:f32 @offset(32)
254 color2:f32 @offset(36)
255}
256
257$B1: { # root
258 %gl_FrontFacing:ptr<__in, bool, read> = var @builtin(front_facing)
259 %gl_FragCoord:ptr<__in, vec4<f32>, read> = var @invariant @builtin(position)
260 %foo_loc0_Input:ptr<__in, f32, read> = var @location(0)
261 %foo_loc1_Input:ptr<__in, f32, read> = var @location(1) @interpolate(linear, sample)
262}
263
264%foo_inner = func(%inputs:Inputs):void {
265 $B2: {
266 %7:bool = access %inputs, 0i
267 if %7 [t: $B3] { # if_1
268 $B3: { # true
269 %8:vec4<f32> = access %inputs, 1i
270 %9:f32 = access %inputs, 2i
271 %10:f32 = access %inputs, 3i
272 %11:f32 = add %9, %10
273 %12:vec4<f32> = mul %8, %11
274 exit_if # if_1
275 }
276 }
277 ret
278 }
279}
280%foo = @fragment func():void {
281 $B4: {
282 %14:bool = load %gl_FrontFacing
283 %15:vec4<f32> = load %gl_FragCoord
284 %16:f32 = load %foo_loc0_Input
285 %17:f32 = load %foo_loc1_Input
286 %18:Inputs = construct %14, %15, %16, %17
287 %19:void = call %foo_inner, %18
288 ret
289 }
290}
291)";
292
James Price978af552024-10-08 06:05:52 +0000293 core::ir::transform::PushConstantLayout push_constants;
294 ShaderIOConfig config{push_constants};
dan sinclairf15c1c32024-09-05 21:41:20 +0000295 Run(ShaderIO, config);
296
297 EXPECT_EQ(expect, str());
298}
299
300TEST_F(GlslWriter_ShaderIOTest, Parameters_Mixed) {
301 auto* str_ty = ty.Struct(mod.symbols.New("Inputs"),
302 {
303 {
304 mod.symbols.New("position"),
305 ty.vec4<f32>(),
306 core::IOAttributes{
307 /* location */ std::nullopt,
308 /* blend_src */ std::nullopt,
309 /* color */ std::nullopt,
310 /* builtin */ core::BuiltinValue::kPosition,
311 /* interpolation */ std::nullopt,
312 /* invariant */ true,
313 },
314 },
315 {
316 mod.symbols.New("color1"),
317 ty.f32(),
318 core::IOAttributes{
319 /* location */ 0u,
320 /* blend_src */ std::nullopt,
321 /* color */ std::nullopt,
322 /* builtin */ std::nullopt,
323 /* interpolation */ std::nullopt,
324 /* invariant */ false,
325 },
326 },
327 });
328
329 auto* ep = b.Function("foo", ty.void_());
330 auto* front_facing = b.FunctionParam("front_facing", ty.bool_());
331 front_facing->SetBuiltin(core::BuiltinValue::kFrontFacing);
332 auto* str_param = b.FunctionParam("inputs", str_ty);
333 auto* color2 = b.FunctionParam("color2", ty.f32());
334 color2->SetLocation(1);
335 color2->SetInterpolation(core::Interpolation{core::InterpolationType::kLinear,
336 core::InterpolationSampling::kSample});
337
338 ep->SetParams({front_facing, str_param, color2});
339 ep->SetStage(core::ir::Function::PipelineStage::kFragment);
340
341 b.Append(ep->Block(), [&] {
342 auto* ifelse = b.If(front_facing);
343 b.Append(ifelse->True(), [&] {
344 auto* position = b.Access(ty.vec4<f32>(), str_param, 0_i);
345 auto* color1 = b.Access(ty.f32(), str_param, 1_i);
346 b.Multiply(ty.vec4<f32>(), position, b.Add(ty.f32(), color1, color2));
347 b.ExitIf(ifelse);
348 });
349 b.Return(ep);
350 });
351
352 auto* src = R"(
353Inputs = struct @align(16) {
354 position:vec4<f32> @offset(0), @invariant, @builtin(position)
355 color1:f32 @offset(16), @location(0)
356}
357
358%foo = @fragment func(%front_facing:bool [@front_facing], %inputs:Inputs, %color2:f32 [@location(1), @interpolate(linear, sample)]):void {
359 $B1: {
360 if %front_facing [t: $B2] { # if_1
361 $B2: { # true
362 %5:vec4<f32> = access %inputs, 0i
363 %6:f32 = access %inputs, 1i
364 %7:f32 = add %6, %color2
365 %8:vec4<f32> = mul %5, %7
366 exit_if # if_1
367 }
368 }
369 ret
370 }
371}
372)";
373 EXPECT_EQ(src, str());
374
375 auto* expect = R"(
376Inputs = struct @align(16) {
377 position:vec4<f32> @offset(0)
378 color1:f32 @offset(16)
379}
380
381$B1: { # root
382 %gl_FrontFacing:ptr<__in, bool, read> = var @builtin(front_facing)
383 %gl_FragCoord:ptr<__in, vec4<f32>, read> = var @invariant @builtin(position)
384 %foo_loc0_Input:ptr<__in, f32, read> = var @location(0)
385 %foo_loc1_Input:ptr<__in, f32, read> = var @location(1) @interpolate(linear, sample)
386}
387
388%foo_inner = func(%front_facing:bool, %inputs:Inputs, %color2:f32):void {
389 $B2: {
390 if %front_facing [t: $B3] { # if_1
391 $B3: { # true
392 %9:vec4<f32> = access %inputs, 0i
393 %10:f32 = access %inputs, 1i
394 %11:f32 = add %10, %color2
395 %12:vec4<f32> = mul %9, %11
396 exit_if # if_1
397 }
398 }
399 ret
400 }
401}
402%foo = @fragment func():void {
403 $B4: {
404 %14:bool = load %gl_FrontFacing
405 %15:vec4<f32> = load %gl_FragCoord
406 %16:f32 = load %foo_loc0_Input
407 %17:Inputs = construct %15, %16
408 %18:f32 = load %foo_loc1_Input
409 %19:void = call %foo_inner, %14, %17, %18
410 ret
411 }
412}
413)";
414
James Price978af552024-10-08 06:05:52 +0000415 core::ir::transform::PushConstantLayout push_constants;
416 ShaderIOConfig config{push_constants};
dan sinclairf15c1c32024-09-05 21:41:20 +0000417 Run(ShaderIO, config);
418
419 EXPECT_EQ(expect, str());
420}
421
422TEST_F(GlslWriter_ShaderIOTest, ReturnValue_NonStructBuiltin) {
423 auto* ep = b.Function("foo", ty.vec4<f32>());
424 ep->SetReturnBuiltin(core::BuiltinValue::kPosition);
425 ep->SetReturnInvariant(true);
426 ep->SetStage(core::ir::Function::PipelineStage::kVertex);
427
428 b.Append(ep->Block(), [&] { //
429 b.Return(ep, b.Construct(ty.vec4<f32>(), 0.5_f));
430 });
431
432 auto* src = R"(
433%foo = @vertex func():vec4<f32> [@invariant, @position] {
434 $B1: {
435 %2:vec4<f32> = construct 0.5f
436 ret %2
437 }
438}
439)";
440 EXPECT_EQ(src, str());
441
442 auto* expect = R"(
443$B1: { # root
444 %gl_Position:ptr<__out, vec4<f32>, write> = var @invariant @builtin(position)
445 %gl_PointSize:ptr<__out, f32, write> = var @builtin(__point_size)
446}
447
448%foo_inner = func():vec4<f32> {
449 $B2: {
450 %4:vec4<f32> = construct 0.5f
451 ret %4
452 }
453}
454%foo = @vertex func():void {
455 $B3: {
456 %6:vec4<f32> = call %foo_inner
457 store %gl_Position, %6
458 %7:f32 = swizzle %gl_Position, y
459 %8:f32 = negation %7
460 store_vector_element %gl_Position, 1u, %8
461 %9:f32 = swizzle %gl_Position, z
462 %10:f32 = swizzle %gl_Position, w
463 %11:f32 = mul 2.0f, %9
464 %12:f32 = sub %11, %10
465 store_vector_element %gl_Position, 2u, %12
466 store %gl_PointSize, 1.0f
467 ret
468 }
469}
470)";
471
James Price978af552024-10-08 06:05:52 +0000472 core::ir::transform::PushConstantLayout push_constants;
473 ShaderIOConfig config{push_constants};
dan sinclairf15c1c32024-09-05 21:41:20 +0000474 Run(ShaderIO, config);
475
476 EXPECT_EQ(expect, str());
477}
478
479TEST_F(GlslWriter_ShaderIOTest, ReturnValue_NonStructLocation) {
480 auto* ep = b.Function("foo", ty.vec4<f32>());
481 ep->SetReturnLocation(1u);
482 ep->SetStage(core::ir::Function::PipelineStage::kFragment);
483
484 b.Append(ep->Block(), [&] { //
485 b.Return(ep, b.Construct(ty.vec4<f32>(), 0.5_f));
486 });
487
488 auto* src = R"(
489%foo = @fragment func():vec4<f32> [@location(1)] {
490 $B1: {
491 %2:vec4<f32> = construct 0.5f
492 ret %2
493 }
494}
495)";
496 EXPECT_EQ(src, str());
497
498 auto* expect = R"(
499$B1: { # root
500 %foo_loc1_Output:ptr<__out, vec4<f32>, write> = var @location(1)
501}
502
503%foo_inner = func():vec4<f32> {
504 $B2: {
505 %3:vec4<f32> = construct 0.5f
506 ret %3
507 }
508}
509%foo = @fragment func():void {
510 $B3: {
511 %5:vec4<f32> = call %foo_inner
512 store %foo_loc1_Output, %5
513 ret
514 }
515}
516)";
517
James Price978af552024-10-08 06:05:52 +0000518 core::ir::transform::PushConstantLayout push_constants;
519 ShaderIOConfig config{push_constants};
dan sinclairf15c1c32024-09-05 21:41:20 +0000520 Run(ShaderIO, config);
521
522 EXPECT_EQ(expect, str());
523}
524
525TEST_F(GlslWriter_ShaderIOTest, ReturnValue_Struct) {
526 auto* str_ty = ty.Struct(mod.symbols.New("Outputs"),
527 {
528 {
529 mod.symbols.New("position"),
530 ty.vec4<f32>(),
531 core::IOAttributes{
532 /* location */ std::nullopt,
533 /* blend_src */ std::nullopt,
534 /* color */ std::nullopt,
535 /* builtin */ core::BuiltinValue::kPosition,
536 /* interpolation */ std::nullopt,
537 /* invariant */ true,
538 },
539 },
540 {
541 mod.symbols.New("color1"),
542 ty.f32(),
543 core::IOAttributes{
544 /* location */ 0u,
545 /* blend_src */ std::nullopt,
546 /* color */ std::nullopt,
547 /* builtin */ std::nullopt,
548 /* interpolation */ std::nullopt,
549 /* invariant */ false,
550 },
551 },
552 {
553 mod.symbols.New("color2"),
554 ty.f32(),
555 core::IOAttributes{
556 /* location */ 1u,
557 /* blend_src */ std::nullopt,
558 /* color */ std::nullopt,
559 /* builtin */ std::nullopt,
560 /* interpolation */
561 core::Interpolation{
562 core::InterpolationType::kLinear,
563 core::InterpolationSampling::kSample,
564 },
565 /* invariant */ false,
566 },
567 },
568 });
569
570 auto* ep = b.Function("foo", str_ty);
571 ep->SetStage(core::ir::Function::PipelineStage::kVertex);
572
573 b.Append(ep->Block(), [&] { //
574 b.Return(ep, b.Construct(str_ty, b.Construct(ty.vec4<f32>(), 0_f), 0.25_f, 0.75_f));
575 });
576
577 auto* src = R"(
578Outputs = struct @align(16) {
579 position:vec4<f32> @offset(0), @invariant, @builtin(position)
580 color1:f32 @offset(16), @location(0)
581 color2:f32 @offset(20), @location(1), @interpolate(linear, sample)
582}
583
584%foo = @vertex func():Outputs {
585 $B1: {
586 %2:vec4<f32> = construct 0.0f
587 %3:Outputs = construct %2, 0.25f, 0.75f
588 ret %3
589 }
590}
591)";
592 EXPECT_EQ(src, str());
593
594 auto* expect = R"(
595Outputs = struct @align(16) {
596 position:vec4<f32> @offset(0)
597 color1:f32 @offset(16)
598 color2:f32 @offset(20)
599}
600
601$B1: { # root
602 %gl_Position:ptr<__out, vec4<f32>, write> = var @invariant @builtin(position)
603 %foo_loc0_Output:ptr<__out, f32, write> = var @location(0)
604 %foo_loc1_Output:ptr<__out, f32, write> = var @location(1) @interpolate(linear, sample)
605 %gl_PointSize:ptr<__out, f32, write> = var @builtin(__point_size)
606}
607
608%foo_inner = func():Outputs {
609 $B2: {
610 %6:vec4<f32> = construct 0.0f
611 %7:Outputs = construct %6, 0.25f, 0.75f
612 ret %7
613 }
614}
615%foo = @vertex func():void {
616 $B3: {
617 %9:Outputs = call %foo_inner
618 %10:vec4<f32> = access %9, 0u
619 store %gl_Position, %10
620 %11:f32 = swizzle %gl_Position, y
621 %12:f32 = negation %11
622 store_vector_element %gl_Position, 1u, %12
623 %13:f32 = swizzle %gl_Position, z
624 %14:f32 = swizzle %gl_Position, w
625 %15:f32 = mul 2.0f, %13
626 %16:f32 = sub %15, %14
627 store_vector_element %gl_Position, 2u, %16
628 %17:f32 = access %9, 1u
629 store %foo_loc0_Output, %17
630 %18:f32 = access %9, 2u
631 store %foo_loc1_Output, %18
632 store %gl_PointSize, 1.0f
633 ret
634 }
635}
636)";
637
James Price978af552024-10-08 06:05:52 +0000638 core::ir::transform::PushConstantLayout push_constants;
639 ShaderIOConfig config{push_constants};
dan sinclairf15c1c32024-09-05 21:41:20 +0000640 Run(ShaderIO, config);
641
642 EXPECT_EQ(expect, str());
643}
644
645TEST_F(GlslWriter_ShaderIOTest, ReturnValue_DualSourceBlending) {
646 auto* str_ty =
647 ty.Struct(mod.symbols.New("Output"), {
648 {
649 mod.symbols.New("color1"),
650 ty.f32(),
651 core::IOAttributes{
652 /* location */ 0u,
653 /* blend_src */ 0u,
654 /* color */ std::nullopt,
655 /* builtin */ std::nullopt,
656 /* interpolation */ std::nullopt,
657 /* invariant */ false,
658 },
659 },
660 {
661 mod.symbols.New("color2"),
662 ty.f32(),
663 core::IOAttributes{
664 /* location */ 0u,
665 /* blend_src */ 1u,
666 /* color */ std::nullopt,
667 /* builtin */ std::nullopt,
668 /* interpolation */ std::nullopt,
669 /* invariant */ false,
670 },
671 },
672 });
673
674 auto* ep = b.Function("foo", str_ty);
675 ep->SetStage(core::ir::Function::PipelineStage::kFragment);
676
677 b.Append(ep->Block(), [&] { //
678 b.Return(ep, b.Construct(str_ty, 0.25_f, 0.75_f));
679 });
680
681 auto* src = R"(
682Output = struct @align(4) {
Jiawei Shaoc6442902024-10-09 01:10:08 +0000683 color1:f32 @offset(0), @location(0), @blend_src(0)
684 color2:f32 @offset(4), @location(0), @blend_src(1)
dan sinclairf15c1c32024-09-05 21:41:20 +0000685}
686
687%foo = @fragment func():Output {
688 $B1: {
689 %2:Output = construct 0.25f, 0.75f
690 ret %2
691 }
692}
693)";
694 EXPECT_EQ(src, str());
695
696 auto* expect = R"(
697Output = struct @align(4) {
698 color1:f32 @offset(0)
699 color2:f32 @offset(4)
700}
701
702$B1: { # root
703 %foo_loc0_idx0_Output:ptr<__out, f32, write> = var @location(0) @blend_src(0)
704 %foo_loc0_idx1_Output:ptr<__out, f32, write> = var @location(0) @blend_src(1)
705}
706
707%foo_inner = func():Output {
708 $B2: {
709 %4:Output = construct 0.25f, 0.75f
710 ret %4
711 }
712}
713%foo = @fragment func():void {
714 $B3: {
715 %6:Output = call %foo_inner
716 %7:f32 = access %6, 0u
717 store %foo_loc0_idx0_Output, %7
718 %8:f32 = access %6, 1u
719 store %foo_loc0_idx1_Output, %8
720 ret
721 }
722}
723)";
724
James Price978af552024-10-08 06:05:52 +0000725 core::ir::transform::PushConstantLayout push_constants;
726 ShaderIOConfig config{push_constants};
dan sinclairf15c1c32024-09-05 21:41:20 +0000727 Run(ShaderIO, config);
728
729 EXPECT_EQ(expect, str());
730}
731
732TEST_F(GlslWriter_ShaderIOTest, Struct_SharedByVertexAndFragment) {
733 auto* vec4f = ty.vec4<f32>();
734 auto* str_ty = ty.Struct(mod.symbols.New("Interface"),
735 {
736 {
737 mod.symbols.New("position"),
738 vec4f,
739 core::IOAttributes{
740 /* location */ std::nullopt,
741 /* blend_src */ std::nullopt,
742 /* color */ std::nullopt,
743 /* builtin */ core::BuiltinValue::kPosition,
744 /* interpolation */ std::nullopt,
745 /* invariant */ false,
746 },
747 },
748 {
749 mod.symbols.New("color"),
750 vec4f,
751 core::IOAttributes{
752 /* location */ 0u,
753 /* blend_src */ std::nullopt,
754 /* color */ std::nullopt,
755 /* builtin */ std::nullopt,
756 /* interpolation */ std::nullopt,
757 /* invariant */ false,
758 },
759 },
760 });
761
762 // Vertex shader.
763 {
764 auto* ep = b.Function("vert", str_ty);
765 ep->SetStage(core::ir::Function::PipelineStage::kVertex);
766
767 b.Append(ep->Block(), [&] { //
768 auto* position = b.Construct(vec4f, 0_f);
769 auto* color = b.Construct(vec4f, 1_f);
770 b.Return(ep, b.Construct(str_ty, position, color));
771 });
772 }
773
774 // Fragment shader.
775 {
776 auto* ep = b.Function("frag", vec4f);
777 auto* inputs = b.FunctionParam("inputs", str_ty);
778 ep->SetStage(core::ir::Function::PipelineStage::kFragment);
779 ep->SetParams({inputs});
780 ep->SetReturnLocation(0u);
781
782 b.Append(ep->Block(), [&] { //
783 auto* position = b.Access(vec4f, inputs, 0_u);
784 auto* color = b.Access(vec4f, inputs, 1_u);
785 b.Return(ep, b.Add(vec4f, position, color));
786 });
787 }
788
789 auto* src = R"(
790Interface = struct @align(16) {
791 position:vec4<f32> @offset(0), @builtin(position)
792 color:vec4<f32> @offset(16), @location(0)
793}
794
795%vert = @vertex func():Interface {
796 $B1: {
797 %2:vec4<f32> = construct 0.0f
798 %3:vec4<f32> = construct 1.0f
799 %4:Interface = construct %2, %3
800 ret %4
801 }
802}
803%frag = @fragment func(%inputs:Interface):vec4<f32> [@location(0)] {
804 $B2: {
805 %7:vec4<f32> = access %inputs, 0u
806 %8:vec4<f32> = access %inputs, 1u
807 %9:vec4<f32> = add %7, %8
808 ret %9
809 }
810}
811)";
812 EXPECT_EQ(src, str());
813
814 auto* expect = R"(
815Interface = struct @align(16) {
816 position:vec4<f32> @offset(0)
817 color:vec4<f32> @offset(16)
818}
819
820$B1: { # root
821 %gl_Position:ptr<__out, vec4<f32>, write> = var @builtin(position)
822 %vert_loc0_Output:ptr<__out, vec4<f32>, write> = var @location(0)
823 %gl_PointSize:ptr<__out, f32, write> = var @builtin(__point_size)
824 %gl_FragCoord:ptr<__in, vec4<f32>, read> = var @builtin(position)
825 %frag_loc0_Input:ptr<__in, vec4<f32>, read> = var @location(0)
826 %frag_loc0_Output:ptr<__out, vec4<f32>, write> = var @location(0)
827}
828
829%vert_inner = func():Interface {
830 $B2: {
831 %8:vec4<f32> = construct 0.0f
832 %9:vec4<f32> = construct 1.0f
833 %10:Interface = construct %8, %9
834 ret %10
835 }
836}
837%frag_inner = func(%inputs:Interface):vec4<f32> {
838 $B3: {
839 %13:vec4<f32> = access %inputs, 0u
840 %14:vec4<f32> = access %inputs, 1u
841 %15:vec4<f32> = add %13, %14
842 ret %15
843 }
844}
845%vert = @vertex func():void {
846 $B4: {
847 %17:Interface = call %vert_inner
848 %18:vec4<f32> = access %17, 0u
849 store %gl_Position, %18
850 %19:f32 = swizzle %gl_Position, y
851 %20:f32 = negation %19
852 store_vector_element %gl_Position, 1u, %20
853 %21:f32 = swizzle %gl_Position, z
854 %22:f32 = swizzle %gl_Position, w
855 %23:f32 = mul 2.0f, %21
856 %24:f32 = sub %23, %22
857 store_vector_element %gl_Position, 2u, %24
858 %25:vec4<f32> = access %17, 1u
859 store %vert_loc0_Output, %25
860 store %gl_PointSize, 1.0f
861 ret
862 }
863}
864%frag = @fragment func():void {
865 $B5: {
866 %27:vec4<f32> = load %gl_FragCoord
867 %28:vec4<f32> = load %frag_loc0_Input
868 %29:Interface = construct %27, %28
869 %30:vec4<f32> = call %frag_inner, %29
870 store %frag_loc0_Output, %30
871 ret
872 }
873}
874)";
875
James Price978af552024-10-08 06:05:52 +0000876 core::ir::transform::PushConstantLayout push_constants;
877 ShaderIOConfig config{push_constants};
dan sinclairf15c1c32024-09-05 21:41:20 +0000878 Run(ShaderIO, config);
879
880 EXPECT_EQ(expect, str());
881}
882
883TEST_F(GlslWriter_ShaderIOTest, Struct_SharedWithBuffer) {
884 auto* vec4f = ty.vec4<f32>();
885 auto* str_ty = ty.Struct(mod.symbols.New("Outputs"),
886 {
887 {
888 mod.symbols.New("position"),
889 vec4f,
890 core::IOAttributes{
891 /* location */ std::nullopt,
892 /* blend_src */ std::nullopt,
893 /* color */ std::nullopt,
894 /* builtin */ core::BuiltinValue::kPosition,
895 /* interpolation */ std::nullopt,
896 /* invariant */ false,
897 },
898 },
899 {
900 mod.symbols.New("color"),
901 vec4f,
902 core::IOAttributes{
903 /* location */ 0u,
904 /* blend_src */ std::nullopt,
905 /* color */ std::nullopt,
906 /* builtin */ std::nullopt,
907 /* interpolation */ std::nullopt,
908 /* invariant */ false,
909 },
910 },
911 });
912
913 auto* var = b.Var(ty.ptr(storage, str_ty, read));
914 var->SetBindingPoint(0, 0);
915 auto* buffer = mod.root_block->Append(var);
916
917 auto* ep = b.Function("vert", str_ty);
918 ep->SetStage(core::ir::Function::PipelineStage::kVertex);
919
920 b.Append(ep->Block(), [&] { //
921 b.Return(ep, b.Load(buffer));
922 });
923
924 auto* src = R"(
925Outputs = struct @align(16) {
926 position:vec4<f32> @offset(0), @builtin(position)
927 color:vec4<f32> @offset(16), @location(0)
928}
929
930$B1: { # root
931 %1:ptr<storage, Outputs, read> = var @binding_point(0, 0)
932}
933
934%vert = @vertex func():Outputs {
935 $B2: {
936 %3:Outputs = load %1
937 ret %3
938 }
939}
940)";
941 EXPECT_EQ(src, str());
942
943 auto* expect = R"(
944Outputs = struct @align(16) {
945 position:vec4<f32> @offset(0)
946 color:vec4<f32> @offset(16)
947}
948
949$B1: { # root
950 %1:ptr<storage, Outputs, read> = var @binding_point(0, 0)
951 %gl_Position:ptr<__out, vec4<f32>, write> = var @builtin(position)
952 %vert_loc0_Output:ptr<__out, vec4<f32>, write> = var @location(0)
953 %gl_PointSize:ptr<__out, f32, write> = var @builtin(__point_size)
954}
955
956%vert_inner = func():Outputs {
957 $B2: {
958 %6:Outputs = load %1
959 ret %6
960 }
961}
962%vert = @vertex func():void {
963 $B3: {
964 %8:Outputs = call %vert_inner
965 %9:vec4<f32> = access %8, 0u
966 store %gl_Position, %9
967 %10:f32 = swizzle %gl_Position, y
968 %11:f32 = negation %10
969 store_vector_element %gl_Position, 1u, %11
970 %12:f32 = swizzle %gl_Position, z
971 %13:f32 = swizzle %gl_Position, w
972 %14:f32 = mul 2.0f, %12
973 %15:f32 = sub %14, %13
974 store_vector_element %gl_Position, 2u, %15
975 %16:vec4<f32> = access %8, 1u
976 store %vert_loc0_Output, %16
977 store %gl_PointSize, 1.0f
978 ret
979 }
980}
981)";
982
James Price978af552024-10-08 06:05:52 +0000983 core::ir::transform::PushConstantLayout push_constants;
984 ShaderIOConfig config{push_constants};
dan sinclairf15c1c32024-09-05 21:41:20 +0000985 Run(ShaderIO, config);
986
987 EXPECT_EQ(expect, str());
988}
989
dan sinclairfc7d3462024-09-28 18:29:17 +0000990// Test that we change the type of the sample mask builtin to an array for GLSL
dan sinclairf15c1c32024-09-05 21:41:20 +0000991TEST_F(GlslWriter_ShaderIOTest, SampleMask) {
992 auto* str_ty = ty.Struct(mod.symbols.New("Outputs"),
993 {
994 {
995 mod.symbols.New("color"),
996 ty.f32(),
997 core::IOAttributes{
998 /* location */ 0u,
999 /* blend_src */ std::nullopt,
1000 /* color */ std::nullopt,
1001 /* builtin */ std::nullopt,
1002 /* interpolation */ std::nullopt,
1003 /* invariant */ false,
1004 },
1005 },
1006 {
1007 mod.symbols.New("mask"),
1008 ty.u32(),
1009 core::IOAttributes{
1010 /* location */ std::nullopt,
1011 /* blend_src */ std::nullopt,
1012 /* color */ std::nullopt,
1013 /* builtin */ core::BuiltinValue::kSampleMask,
1014 /* interpolation */ std::nullopt,
1015 /* invariant */ false,
1016 },
1017 },
1018 });
1019
1020 auto* mask_in = b.FunctionParam("mask_in", ty.u32());
1021 mask_in->SetBuiltin(core::BuiltinValue::kSampleMask);
1022
1023 auto* ep = b.Function("foo", str_ty);
1024 ep->SetStage(core::ir::Function::PipelineStage::kFragment);
1025 ep->SetParams({mask_in});
1026
1027 b.Append(ep->Block(), [&] { //
1028 b.Return(ep, b.Construct(str_ty, 0.5_f, mask_in));
1029 });
1030
1031 auto* src = R"(
1032Outputs = struct @align(4) {
1033 color:f32 @offset(0), @location(0)
1034 mask:u32 @offset(4), @builtin(sample_mask)
1035}
1036
1037%foo = @fragment func(%mask_in:u32 [@sample_mask]):Outputs {
1038 $B1: {
1039 %3:Outputs = construct 0.5f, %mask_in
1040 ret %3
1041 }
1042}
1043)";
1044 EXPECT_EQ(src, str());
1045
1046 auto* expect = R"(
1047Outputs = struct @align(4) {
1048 color:f32 @offset(0)
1049 mask:u32 @offset(4)
1050}
1051
1052$B1: { # root
dan sinclairfc7d3462024-09-28 18:29:17 +00001053 %gl_SampleMaskIn:ptr<__in, array<i32, 1>, read> = var @builtin(sample_mask)
dan sinclairf15c1c32024-09-05 21:41:20 +00001054 %foo_loc0_Output:ptr<__out, f32, write> = var @location(0)
dan sinclairfc7d3462024-09-28 18:29:17 +00001055 %gl_SampleMask:ptr<__out, array<i32, 1>, write> = var @builtin(sample_mask)
dan sinclairf15c1c32024-09-05 21:41:20 +00001056}
1057
1058%foo_inner = func(%mask_in:u32):Outputs {
1059 $B2: {
1060 %6:Outputs = construct 0.5f, %mask_in
1061 ret %6
1062 }
1063}
1064%foo = @fragment func():void {
1065 $B3: {
dan sinclairfc7d3462024-09-28 18:29:17 +00001066 %8:array<i32, 1> = load %gl_SampleMaskIn
1067 %9:i32 = access %8, 0u
1068 %10:u32 = convert %9
1069 %11:Outputs = call %foo_inner, %10
1070 %12:f32 = access %11, 0u
1071 store %foo_loc0_Output, %12
1072 %13:u32 = access %11, 1u
1073 %14:ptr<__out, i32, write> = access %gl_SampleMask, 0u
1074 %15:i32 = convert %13
1075 store %14, %15
dan sinclairf15c1c32024-09-05 21:41:20 +00001076 ret
1077 }
1078}
1079)";
1080
James Price978af552024-10-08 06:05:52 +00001081 core::ir::transform::PushConstantLayout push_constants;
1082 ShaderIOConfig config{push_constants};
dan sinclairf15c1c32024-09-05 21:41:20 +00001083 Run(ShaderIO, config);
1084
1085 EXPECT_EQ(expect, str());
1086}
1087
1088// Test that interpolation attributes are stripped from vertex inputs and fragment outputs.
1089TEST_F(GlslWriter_ShaderIOTest, InterpolationOnVertexInputOrFragmentOutput) {
1090 auto* str_ty =
1091 ty.Struct(mod.symbols.New("MyStruct"), {
1092 {
1093 mod.symbols.New("color"),
1094 ty.f32(),
1095 core::IOAttributes{
1096 /* location */ 1u,
1097 /* blend_src */ std::nullopt,
1098 /* color */ std::nullopt,
1099 /* builtin */ std::nullopt,
1100 /* interpolation */
1101 core::Interpolation{
1102 core::InterpolationType::kLinear,
1103 core::InterpolationSampling::kSample,
1104 },
1105 /* invariant */ false,
1106 },
1107 },
1108 });
1109
1110 // Vertex shader.
1111 {
1112 auto* ep = b.Function("vert", ty.vec4<f32>());
1113 ep->SetReturnBuiltin(core::BuiltinValue::kPosition);
1114 ep->SetReturnInvariant(true);
1115 ep->SetStage(core::ir::Function::PipelineStage::kVertex);
1116
1117 auto* str_param = b.FunctionParam("input", str_ty);
1118 auto* ival = b.FunctionParam("ival", ty.i32());
1119 ival->SetLocation(1);
1120 ival->SetInterpolation(core::Interpolation{core::InterpolationType::kFlat});
1121 ep->SetParams({str_param, ival});
1122
1123 b.Append(ep->Block(), [&] { //
1124 b.Return(ep, b.Construct(ty.vec4<f32>(), 0.5_f));
1125 });
1126 }
1127
1128 // Fragment shader with struct output.
1129 {
1130 auto* ep = b.Function("frag1", str_ty);
1131 ep->SetStage(core::ir::Function::PipelineStage::kFragment);
1132
1133 b.Append(ep->Block(), [&] { //
1134 b.Return(ep, b.Construct(str_ty, 0.5_f));
1135 });
1136 }
1137
1138 // Fragment shader with non-struct output.
1139 {
1140 auto* ep = b.Function("frag2", ty.i32());
1141 ep->SetStage(core::ir::Function::PipelineStage::kFragment);
1142 ep->SetReturnLocation(0);
1143 ep->SetReturnInterpolation(core::Interpolation{core::InterpolationType::kFlat});
1144
1145 b.Append(ep->Block(), [&] { //
1146 b.Return(ep, b.Constant(42_i));
1147 });
1148 }
1149
1150 auto* src = R"(
1151MyStruct = struct @align(4) {
1152 color:f32 @offset(0), @location(1), @interpolate(linear, sample)
1153}
1154
1155%vert = @vertex func(%input:MyStruct, %ival:i32 [@location(1), @interpolate(flat)]):vec4<f32> [@invariant, @position] {
1156 $B1: {
1157 %4:vec4<f32> = construct 0.5f
1158 ret %4
1159 }
1160}
1161%frag1 = @fragment func():MyStruct {
1162 $B2: {
1163 %6:MyStruct = construct 0.5f
1164 ret %6
1165 }
1166}
1167%frag2 = @fragment func():i32 [@location(0), @interpolate(flat)] {
1168 $B3: {
1169 ret 42i
1170 }
1171}
1172)";
1173 EXPECT_EQ(src, str());
1174
1175 auto* expect = R"(
1176MyStruct = struct @align(4) {
1177 color:f32 @offset(0)
1178}
1179
1180$B1: { # root
1181 %vert_loc1_Input:ptr<__in, f32, read> = var @location(1)
1182 %vert_loc1_Input_1:ptr<__in, i32, read> = var @location(1) # %vert_loc1_Input_1: 'vert_loc1_Input'
1183 %gl_Position:ptr<__out, vec4<f32>, write> = var @invariant @builtin(position)
1184 %gl_PointSize:ptr<__out, f32, write> = var @builtin(__point_size)
1185 %frag1_loc1_Output:ptr<__out, f32, write> = var @location(1)
1186 %frag2_loc0_Output:ptr<__out, i32, write> = var @location(0)
1187}
1188
1189%vert_inner = func(%input:MyStruct, %ival:i32):vec4<f32> {
1190 $B2: {
1191 %10:vec4<f32> = construct 0.5f
1192 ret %10
1193 }
1194}
1195%frag1_inner = func():MyStruct {
1196 $B3: {
1197 %12:MyStruct = construct 0.5f
1198 ret %12
1199 }
1200}
1201%frag2_inner = func():i32 {
1202 $B4: {
1203 ret 42i
1204 }
1205}
1206%vert = @vertex func():void {
1207 $B5: {
1208 %15:f32 = load %vert_loc1_Input
1209 %16:MyStruct = construct %15
1210 %17:i32 = load %vert_loc1_Input_1
1211 %18:vec4<f32> = call %vert_inner, %16, %17
1212 store %gl_Position, %18
1213 %19:f32 = swizzle %gl_Position, y
1214 %20:f32 = negation %19
1215 store_vector_element %gl_Position, 1u, %20
1216 %21:f32 = swizzle %gl_Position, z
1217 %22:f32 = swizzle %gl_Position, w
1218 %23:f32 = mul 2.0f, %21
1219 %24:f32 = sub %23, %22
1220 store_vector_element %gl_Position, 2u, %24
1221 store %gl_PointSize, 1.0f
1222 ret
1223 }
1224}
1225%frag1 = @fragment func():void {
1226 $B6: {
1227 %26:MyStruct = call %frag1_inner
1228 %27:f32 = access %26, 0u
1229 store %frag1_loc1_Output, %27
1230 ret
1231 }
1232}
1233%frag2 = @fragment func():void {
1234 $B7: {
1235 %29:i32 = call %frag2_inner
1236 store %frag2_loc0_Output, %29
1237 ret
1238 }
1239}
1240)";
1241
James Price978af552024-10-08 06:05:52 +00001242 core::ir::transform::PushConstantLayout push_constants;
1243 ShaderIOConfig config{push_constants};
dan sinclairf15c1c32024-09-05 21:41:20 +00001244 Run(ShaderIO, config);
1245
1246 EXPECT_EQ(expect, str());
1247}
1248
James Price978af552024-10-08 06:05:52 +00001249TEST_F(GlslWriter_ShaderIOTest, ClampFragDepth) {
dan sinclairf15c1c32024-09-05 21:41:20 +00001250 auto* str_ty = ty.Struct(mod.symbols.New("Outputs"),
1251 {
1252 {
1253 mod.symbols.New("color"),
1254 ty.f32(),
1255 core::IOAttributes{
1256 /* location */ 0u,
1257 /* blend_src */ std::nullopt,
1258 /* color */ std::nullopt,
1259 /* builtin */ std::nullopt,
1260 /* interpolation */ std::nullopt,
1261 /* invariant */ false,
1262 },
1263 },
1264 {
1265 mod.symbols.New("depth"),
1266 ty.f32(),
1267 core::IOAttributes{
1268 /* location */ std::nullopt,
1269 /* blend_src */ std::nullopt,
1270 /* color */ std::nullopt,
1271 /* builtin */ core::BuiltinValue::kFragDepth,
1272 /* interpolation */ std::nullopt,
1273 /* invariant */ false,
1274 },
1275 },
1276 });
1277
1278 auto* ep = b.Function("foo", str_ty);
1279 ep->SetStage(core::ir::Function::PipelineStage::kFragment);
1280
1281 b.Append(ep->Block(), [&] { //
1282 b.Return(ep, b.Construct(str_ty, 0.5_f, 2_f));
1283 });
1284
1285 auto* src = R"(
1286Outputs = struct @align(4) {
1287 color:f32 @offset(0), @location(0)
1288 depth:f32 @offset(4), @builtin(frag_depth)
1289}
1290
1291%foo = @fragment func():Outputs {
1292 $B1: {
1293 %2:Outputs = construct 0.5f, 2.0f
1294 ret %2
1295 }
1296}
1297)";
1298 EXPECT_EQ(src, str());
1299
1300 auto* expect = R"(
1301Outputs = struct @align(4) {
1302 color:f32 @offset(0)
1303 depth:f32 @offset(4)
1304}
1305
James Price978af552024-10-08 06:05:52 +00001306tint_push_constant_struct = struct @align(4), @block {
1307 depth_min:f32 @offset(4)
1308 depth_max:f32 @offset(8)
1309}
1310
dan sinclairf15c1c32024-09-05 21:41:20 +00001311$B1: { # root
James Price978af552024-10-08 06:05:52 +00001312 %tint_push_constants:ptr<push_constant, tint_push_constant_struct, read> = var
dan sinclairf15c1c32024-09-05 21:41:20 +00001313 %foo_loc0_Output:ptr<__out, f32, write> = var @location(0)
1314 %gl_FragDepth:ptr<__out, f32, write> = var @builtin(frag_depth)
1315}
1316
1317%foo_inner = func():Outputs {
1318 $B2: {
James Price978af552024-10-08 06:05:52 +00001319 %5:Outputs = construct 0.5f, 2.0f
1320 ret %5
dan sinclairf15c1c32024-09-05 21:41:20 +00001321 }
1322}
1323%foo = @fragment func():void {
1324 $B3: {
James Price978af552024-10-08 06:05:52 +00001325 %7:Outputs = call %foo_inner
1326 %8:f32 = access %7, 0u
1327 store %foo_loc0_Output, %8
1328 %9:f32 = access %7, 1u
1329 %10:ptr<push_constant, f32, read> = access %tint_push_constants, 0u
1330 %11:f32 = load %10
1331 %12:ptr<push_constant, f32, read> = access %tint_push_constants, 1u
1332 %13:f32 = load %12
1333 %14:f32 = clamp %9, %11, %13
1334 store %gl_FragDepth, %14
dan sinclairf15c1c32024-09-05 21:41:20 +00001335 ret
1336 }
1337}
1338)";
1339
James Price978af552024-10-08 06:05:52 +00001340 core::ir::transform::PreparePushConstantsConfig push_constants_config;
1341 push_constants_config.AddInternalConstant(4, mod.symbols.New("depth_min"), ty.f32());
1342 push_constants_config.AddInternalConstant(8, mod.symbols.New("depth_max"), ty.f32());
1343 auto push_constants = PreparePushConstants(mod, push_constants_config);
1344 EXPECT_EQ(push_constants, Success);
1345
1346 ShaderIOConfig config{push_constants.Get()};
1347 config.depth_range_offsets = {4, 8};
dan sinclairf15c1c32024-09-05 21:41:20 +00001348 Run(ShaderIO, config);
1349
1350 EXPECT_EQ(expect, str());
1351}
1352
James Price978af552024-10-08 06:05:52 +00001353TEST_F(GlslWriter_ShaderIOTest, ClampFragDepth_MultipleFragmentShaders) {
dan sinclairf15c1c32024-09-05 21:41:20 +00001354 auto* str_ty = ty.Struct(mod.symbols.New("Outputs"),
1355 {
1356 {
1357 mod.symbols.New("color"),
1358 ty.f32(),
1359 core::IOAttributes{
1360 /* location */ 0u,
1361 /* blend_src */ std::nullopt,
1362 /* color */ std::nullopt,
1363 /* builtin */ std::nullopt,
1364 /* interpolation */ std::nullopt,
1365 /* invariant */ false,
1366 },
1367 },
1368 {
1369 mod.symbols.New("depth"),
1370 ty.f32(),
1371 core::IOAttributes{
1372 /* location */ std::nullopt,
1373 /* blend_src */ std::nullopt,
1374 /* color */ std::nullopt,
1375 /* builtin */ core::BuiltinValue::kFragDepth,
1376 /* interpolation */ std::nullopt,
1377 /* invariant */ false,
1378 },
1379 },
1380 });
1381
1382 auto make_entry_point = [&](std::string_view name) {
1383 auto* ep = b.Function(name, str_ty);
1384 ep->SetStage(core::ir::Function::PipelineStage::kFragment);
1385 b.Append(ep->Block(), [&] { //
1386 b.Return(ep, b.Construct(str_ty, 0.5_f, 2_f));
1387 });
1388 };
1389 make_entry_point("ep1");
1390 make_entry_point("ep2");
1391 make_entry_point("ep3");
1392
1393 auto* src = R"(
1394Outputs = struct @align(4) {
1395 color:f32 @offset(0), @location(0)
1396 depth:f32 @offset(4), @builtin(frag_depth)
1397}
1398
1399%ep1 = @fragment func():Outputs {
1400 $B1: {
1401 %2:Outputs = construct 0.5f, 2.0f
1402 ret %2
1403 }
1404}
1405%ep2 = @fragment func():Outputs {
1406 $B2: {
1407 %4:Outputs = construct 0.5f, 2.0f
1408 ret %4
1409 }
1410}
1411%ep3 = @fragment func():Outputs {
1412 $B3: {
1413 %6:Outputs = construct 0.5f, 2.0f
1414 ret %6
1415 }
1416}
1417)";
1418 EXPECT_EQ(src, str());
1419
1420 auto* expect = R"(
1421Outputs = struct @align(4) {
1422 color:f32 @offset(0)
1423 depth:f32 @offset(4)
1424}
1425
James Price978af552024-10-08 06:05:52 +00001426tint_push_constant_struct = struct @align(4), @block {
1427 depth_min:f32 @offset(4)
1428 depth_max:f32 @offset(8)
1429}
1430
dan sinclairf15c1c32024-09-05 21:41:20 +00001431$B1: { # root
James Price978af552024-10-08 06:05:52 +00001432 %tint_push_constants:ptr<push_constant, tint_push_constant_struct, read> = var
dan sinclairf15c1c32024-09-05 21:41:20 +00001433 %ep1_loc0_Output:ptr<__out, f32, write> = var @location(0)
1434 %gl_FragDepth:ptr<__out, f32, write> = var @builtin(frag_depth)
1435 %ep2_loc0_Output:ptr<__out, f32, write> = var @location(0)
1436 %gl_FragDepth_1:ptr<__out, f32, write> = var @builtin(frag_depth) # %gl_FragDepth_1: 'gl_FragDepth'
1437 %ep3_loc0_Output:ptr<__out, f32, write> = var @location(0)
1438 %gl_FragDepth_2:ptr<__out, f32, write> = var @builtin(frag_depth) # %gl_FragDepth_2: 'gl_FragDepth'
1439}
1440
1441%ep1_inner = func():Outputs {
1442 $B2: {
James Price978af552024-10-08 06:05:52 +00001443 %9:Outputs = construct 0.5f, 2.0f
1444 ret %9
dan sinclairf15c1c32024-09-05 21:41:20 +00001445 }
1446}
1447%ep2_inner = func():Outputs {
1448 $B3: {
James Price978af552024-10-08 06:05:52 +00001449 %11:Outputs = construct 0.5f, 2.0f
1450 ret %11
dan sinclairf15c1c32024-09-05 21:41:20 +00001451 }
1452}
1453%ep3_inner = func():Outputs {
1454 $B4: {
James Price978af552024-10-08 06:05:52 +00001455 %13:Outputs = construct 0.5f, 2.0f
1456 ret %13
dan sinclairf15c1c32024-09-05 21:41:20 +00001457 }
1458}
1459%ep1 = @fragment func():void {
1460 $B5: {
James Price978af552024-10-08 06:05:52 +00001461 %15:Outputs = call %ep1_inner
1462 %16:f32 = access %15, 0u
1463 store %ep1_loc0_Output, %16
1464 %17:f32 = access %15, 1u
1465 %18:ptr<push_constant, f32, read> = access %tint_push_constants, 0u
1466 %19:f32 = load %18
1467 %20:ptr<push_constant, f32, read> = access %tint_push_constants, 1u
1468 %21:f32 = load %20
1469 %22:f32 = clamp %17, %19, %21
1470 store %gl_FragDepth, %22
dan sinclairf15c1c32024-09-05 21:41:20 +00001471 ret
1472 }
1473}
1474%ep2 = @fragment func():void {
1475 $B6: {
James Price978af552024-10-08 06:05:52 +00001476 %24:Outputs = call %ep2_inner
1477 %25:f32 = access %24, 0u
1478 store %ep2_loc0_Output, %25
1479 %26:f32 = access %24, 1u
1480 %27:ptr<push_constant, f32, read> = access %tint_push_constants, 0u
1481 %28:f32 = load %27
1482 %29:ptr<push_constant, f32, read> = access %tint_push_constants, 1u
1483 %30:f32 = load %29
1484 %31:f32 = clamp %26, %28, %30
1485 store %gl_FragDepth_1, %31
dan sinclairf15c1c32024-09-05 21:41:20 +00001486 ret
1487 }
1488}
1489%ep3 = @fragment func():void {
1490 $B7: {
James Price978af552024-10-08 06:05:52 +00001491 %33:Outputs = call %ep3_inner
1492 %34:f32 = access %33, 0u
1493 store %ep3_loc0_Output, %34
1494 %35:f32 = access %33, 1u
1495 %36:ptr<push_constant, f32, read> = access %tint_push_constants, 0u
1496 %37:f32 = load %36
1497 %38:ptr<push_constant, f32, read> = access %tint_push_constants, 1u
1498 %39:f32 = load %38
1499 %40:f32 = clamp %35, %37, %39
1500 store %gl_FragDepth_2, %40
dan sinclairf15c1c32024-09-05 21:41:20 +00001501 ret
1502 }
1503}
1504)";
1505
James Price978af552024-10-08 06:05:52 +00001506 core::ir::transform::PreparePushConstantsConfig push_constants_config;
1507 push_constants_config.AddInternalConstant(4, mod.symbols.New("depth_min"), ty.f32());
1508 push_constants_config.AddInternalConstant(8, mod.symbols.New("depth_max"), ty.f32());
1509 auto push_constants = PreparePushConstants(mod, push_constants_config);
1510 EXPECT_EQ(push_constants, Success);
1511
1512 ShaderIOConfig config{push_constants.Get()};
1513 config.depth_range_offsets = {4, 8};
dan sinclairf15c1c32024-09-05 21:41:20 +00001514 Run(ShaderIO, config);
1515
1516 EXPECT_EQ(expect, str());
1517}
1518
1519} // namespace
1520} // namespace tint::glsl::writer::raise