Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
RasterPipelineBuilderTest.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2022 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
14#include "tests/Test.h"
15
18 program.dump(&stream);
19 return stream.detachAsData();
20}
21
22static std::string_view as_string_view(const sk_sp<SkData>& dump) {
23 return std::string_view(static_cast<const char*>(dump->data()), dump->size());
24}
25
26static void check(skiatest::Reporter* r, SkSL::RP::Program& program, std::string_view expected) {
27 // Verify that the program matches expectations.
29 REPORTER_ASSERT(r, as_string_view(dump) == expected,
30 "Output did not match expectation:\n%.*s",
31 (int)dump->size(), static_cast<const char*>(dump->data()));
32}
33
35 return SkSL::RP::SlotRange{index, 1};
36}
37
39 return SkSL::RP::SlotRange{index, 2};
40}
41
45
49
53
55 return SkSL::RP::SlotRange{index, 10};
56}
57
58DEF_TEST(RasterPipelineBuilder, r) {
59 // Create a very simple nonsense program.
60 SkSL::RP::Builder builder;
61 builder.store_src_rg(two_slots_at(0));
62 builder.store_src(four_slots_at(2));
63 builder.store_dst(four_slots_at(4));
64 builder.store_device_xy01(four_slots_at(6));
65 builder.init_lane_masks();
66 builder.enableExecutionMaskWrites();
67 builder.mask_off_return_mask();
68 builder.mask_off_loop_mask();
69 builder.reenable_loop_mask(one_slot_at(4));
70 builder.disableExecutionMaskWrites();
71 builder.load_src(four_slots_at(1));
72 builder.load_dst(four_slots_at(3));
73 std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/10,
74 /*numUniformSlots=*/0,
75 /*numImmutableSlots=*/0);
76 check(r, *program,
77R"(store_src_rg v0..1 = src.rg
78store_src v2..5 = src.rgba
79store_dst v4..7 = dst.rgba
80store_device_xy01 v6..9 = DeviceCoords.xy01
81init_lane_masks CondMask = LoopMask = RetMask = true
82mask_off_return_mask RetMask &= ~(CondMask & LoopMask & RetMask)
83mask_off_loop_mask LoopMask &= ~(CondMask & LoopMask & RetMask)
84reenable_loop_mask LoopMask |= v4
85load_src src.rgba = v1..4
86load_dst dst.rgba = v3..6
87)");
88}
89
90DEF_TEST(RasterPipelineBuilderPushPopMaskRegisters, r) {
91 // Create a very simple nonsense program.
93
94 REPORTER_ASSERT(r, !builder.executionMaskWritesAreEnabled());
95 builder.enableExecutionMaskWrites();
96 REPORTER_ASSERT(r, builder.executionMaskWritesAreEnabled());
97
98 builder.push_condition_mask(); // push into 0
99 builder.push_loop_mask(); // push into 1
100 builder.push_return_mask(); // push into 2
101 builder.merge_condition_mask(); // set the condition-mask to 1 & 2
102 builder.merge_inv_condition_mask(); // set the condition-mask to 1 & ~2
103 builder.pop_condition_mask(); // pop from 2
104 builder.merge_loop_mask(); // mask off the loop-mask against 1
105 builder.push_condition_mask(); // push into 2
106 builder.pop_condition_mask(); // pop from 2
107 builder.pop_loop_mask(); // pop from 1
108 builder.pop_return_mask(); // pop from 0
109 builder.push_condition_mask(); // push into 0
110 builder.pop_and_reenable_loop_mask(); // pop from 0
111
112 REPORTER_ASSERT(r, builder.executionMaskWritesAreEnabled());
113 builder.disableExecutionMaskWrites();
114 REPORTER_ASSERT(r, !builder.executionMaskWritesAreEnabled());
115
116 std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/0,
117 /*numUniformSlots=*/0,
118 /*numImmutableSlots=*/0);
119 check(r, *program,
120R"(store_condition_mask $0 = CondMask
121store_loop_mask $1 = LoopMask
122store_return_mask $2 = RetMask
123merge_condition_mask CondMask = $1 & $2
124merge_inv_condition_mask CondMask = $1 & ~$2
125load_condition_mask CondMask = $2
126merge_loop_mask LoopMask &= $1
127store_condition_mask $2 = CondMask
128load_condition_mask CondMask = $2
129load_loop_mask LoopMask = $1
130load_return_mask RetMask = $0
131store_condition_mask $0 = CondMask
132reenable_loop_mask LoopMask |= $0
133)");
134}
135
136
137DEF_TEST(RasterPipelineBuilderCaseOp, r) {
138 // Create a very simple nonsense program.
140
141 builder.push_constant_i(123); // push a test value
142 builder.push_constant_i(~0); // push an all-on default mask
143 builder.case_op(123); // do `case 123:`
144 builder.case_op(124); // do `case 124:`
145 builder.discard_stack(2);
146
147 std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/0,
148 /*numUniformSlots=*/0,
149 /*numImmutableSlots=*/0);
150 check(r, *program,
151R"(copy_constant $0 = 0x0000007B (1.723597e-43)
152copy_constant $1 = 0xFFFFFFFF
153case_op if ($0 == 0x0000007B) { LoopMask = true; $1 = false; }
154case_op if ($0 == 0x0000007C) { LoopMask = true; $1 = false; }
155)");
156}
157
158DEF_TEST(RasterPipelineBuilderPushPopSrcDst, r) {
159 // Create a very simple nonsense program.
161
162 builder.push_src_rgba();
163 builder.push_dst_rgba();
164 builder.pop_src_rgba();
165 builder.exchange_src();
166 builder.exchange_src();
167 builder.exchange_src();
168 builder.pop_dst_rgba();
169
170 std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/0,
171 /*numUniformSlots=*/0,
172 /*numImmutableSlots=*/0);
173 check(r, *program,
174R"(store_src $0..3 = src.rgba
175store_dst $4..7 = dst.rgba
176load_src src.rgba = $4..7
177exchange_src swap(src.rgba, $0..3)
178load_dst dst.rgba = $0..3
179)");
180}
181
182DEF_TEST(RasterPipelineBuilderInvokeChild, r) {
183 // Create a very simple nonsense program.
185
186 builder.invoke_shader(1);
187 builder.invoke_color_filter(2);
188 builder.invoke_blender(3);
189
190 std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/0,
191 /*numUniformSlots=*/0,
192 /*numImmutableSlots=*/0);
193check(r, *program,
194R"(invoke_shader invoke_shader 0x00000001
195invoke_color_filter invoke_color_filter 0x00000002
196invoke_blender invoke_blender 0x00000003
197)");
198}
199
200DEF_TEST(RasterPipelineBuilderPushPopTempImmediates, r) {
201 // Create a very simple nonsense program.
203 builder.set_current_stack(1);
204 builder.push_constant_i(999); // push into 2
205 builder.set_current_stack(0);
206 builder.push_constant_f(13.5f); // push into 0
207 builder.push_clone_from_stack(one_slot_at(0), /*otherStackID=*/1, /*offsetFromStackTop=*/1);
208 // push into 1 from 2
209 builder.discard_stack(1); // discard 2
210 builder.push_constant_u(357); // push into 2
211 builder.set_current_stack(1);
212 builder.push_clone_from_stack(one_slot_at(0), /*otherStackID=*/0, /*offsetFromStackTop=*/1);
213 // push into 3 from 0
214 builder.discard_stack(2); // discard 2 and 3
215 builder.set_current_stack(0);
216 builder.push_constant_f(1.2f); // push into 2
217 builder.pad_stack(3); // pad slots 3,4,5
218 builder.push_constant_f(3.4f); // push into 6
219 builder.discard_stack(7); // discard 0 through 6
220 std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/1,
221 /*numUniformSlots=*/0,
222 /*numImmutableSlots=*/0);
223 check(r, *program,
224R"(copy_constant $2 = 0x000003E7 (1.399897e-42)
225copy_constant $0 = 0x41580000 (13.5)
226copy_constant $1 = 0x00000165 (5.002636e-43)
227)");
228}
230DEF_TEST(RasterPipelineBuilderPushPopIndirect, r) {
231 // Create a very simple nonsense program.
232 SkSL::RP::Builder builder;
233 builder.set_current_stack(1);
234 builder.push_constant_i(3);
235 builder.set_current_stack(0);
236 builder.push_slots_indirect(two_slots_at(0), /*dynamicStack=*/1, ten_slots_at(0));
237 builder.push_slots_indirect(four_slots_at(10), /*dynamicStack=*/1, ten_slots_at(10));
238 builder.push_uniform_indirect(one_slot_at(0), /*dynamicStack=*/1, five_slots_at(0));
239 builder.push_uniform_indirect(three_slots_at(5), /*dynamicStack=*/1, five_slots_at(5));
240 builder.swizzle_copy_stack_to_slots_indirect(three_slots_at(6), /*dynamicStackID=*/1,
241 ten_slots_at(0), {2, 1, 0},
242 /*offsetFromStackTop=*/3);
243 builder.copy_stack_to_slots_indirect(three_slots_at(4), /*dynamicStackID=*/1, ten_slots_at(0));
244 builder.pop_slots_indirect(five_slots_at(0), /*dynamicStackID=*/1, ten_slots_at(0));
245 builder.pop_slots_indirect(five_slots_at(10), /*dynamicStackID=*/1, ten_slots_at(10));
246 builder.set_current_stack(1);
247 builder.discard_stack(1);
248 std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/20,
249 /*numUniformSlots=*/10,
250 /*numImmutableSlots=*/0);
251 check(r, *program,
252R"(copy_constant $10 = 0x00000003 (4.203895e-45)
253copy_from_indirect_unmasked $0..1 = Indirect(v0..1 + $10)
254copy_from_indirect_unmasked $2..5 = Indirect(v10..13 + $10)
255copy_from_indirect_uniform_unm $6 = Indirect(u0 + $10)
256copy_from_indirect_uniform_unm $7..9 = Indirect(u5..7 + $10)
257swizzle_copy_to_indirect_maske Indirect(v6..8 + $10).zyx = Mask($7..9)
258copy_to_indirect_masked Indirect(v4..6 + $10) = Mask($7..9)
259copy_to_indirect_masked Indirect(v0..4 + $10) = Mask($5..9)
260copy_to_indirect_masked Indirect(v10..14 + $10) = Mask($0..4)
261)");
262}
263
264DEF_TEST(RasterPipelineBuilderCopySlotsMasked, r) {
265 // Create a very simple nonsense program.
267 builder.copy_slots_masked(two_slots_at(0), two_slots_at(2));
268 builder.copy_slots_masked(four_slots_at(1), four_slots_at(5));
269 std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/9,
270 /*numUniformSlots=*/0,
271 /*numImmutableSlots=*/0);
272 check(r, *program,
273R"(copy_2_slots_masked v0..1 = Mask(v2..3)
274copy_4_slots_masked v1..4 = Mask(v5..8)
275)");
276}
277
278DEF_TEST(RasterPipelineBuilderCopySlotsUnmasked, r) {
279 // Create a very simple nonsense program.
281 builder.copy_slots_unmasked(three_slots_at(0), three_slots_at(2));
282 builder.copy_slots_unmasked(five_slots_at(1), five_slots_at(5));
283 std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/10,
284 /*numUniformSlots=*/0,
285 /*numImmutableSlots=*/0);
286 check(r, *program,
287R"(copy_3_slots_unmasked v0..2 = v2..4
288copy_4_slots_unmasked v1..4 = v5..8
289copy_slot_unmasked v5 = v9
290)");
291}
292
293DEF_TEST(RasterPipelineBuilderPushPopSlots, r) {
294 // Create a very simple nonsense program.
296 builder.push_slots(four_slots_at(10)); // push from 10~13 into $0~$3
297 builder.copy_stack_to_slots(one_slot_at(5), 3); // copy from $1 into 5
298 builder.pop_slots_unmasked(two_slots_at(20)); // pop from $2~$3 into 20~21 (unmasked)
299 builder.enableExecutionMaskWrites();
300 builder.copy_stack_to_slots_unmasked(one_slot_at(4), 2); // copy from $0 into 4
301 builder.push_slots(three_slots_at(30)); // push from 30~32 into $2~$4
302 builder.pop_slots(five_slots_at(0)); // pop from $0~$4 into 0~4 (masked)
303 builder.disableExecutionMaskWrites();
304
305 std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/50,
306 /*numUniformSlots=*/0,
307 /*numImmutableSlots=*/0);
308 check(r, *program,
309R"(copy_4_slots_unmasked $0..3 = v10..13
310copy_slot_unmasked v5 = $1
311copy_2_slots_unmasked v20..21 = $2..3
312copy_slot_unmasked v4 = $0
313copy_3_slots_unmasked $2..4 = v30..32
314copy_4_slots_masked v0..3 = Mask($0..3)
315copy_slot_masked v4 = Mask($4)
316)");
317}
318
319DEF_TEST(RasterPipelineBuilderDuplicateSelectAndSwizzleSlots, r) {
320 // Create a very simple nonsense program.
322 builder.push_constant_f(1.0f); // push into 0
323 builder.push_duplicates(1); // duplicate into 1
324 builder.push_duplicates(2); // duplicate into 2~3
325 builder.push_duplicates(3); // duplicate into 4~6
326 builder.push_duplicates(5); // duplicate into 7~11
327 builder.select(4); // select from 4~7 and 8~11 into 4~7
328 builder.select(3); // select from 2~4 and 5~7 into 2~4
329 builder.select(1); // select from 3 and 4 into 3
330 builder.swizzle_copy_stack_to_slots(four_slots_at(1), {3, 2, 1, 0}, 4);
331 builder.swizzle_copy_stack_to_slots(four_slots_at(0), {0, 1, 3}, 3);
332 builder.swizzle(4, {3, 2, 1, 0}); // reverse the order of 0~3 (value.wzyx)
333 builder.swizzle(4, {1, 2}); // eliminate elements 0 and 3 (value.yz)
334 builder.swizzle(2, {0}); // eliminate element 1 (value.x)
335 builder.discard_stack(1); // balance stack
336 std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/6,
337 /*numUniformSlots=*/0,
338 /*numImmutableSlots=*/0);
339 check(r, *program,
340R"(splat_4_constants $0..3 = 0x3F800000 (1.0)
341splat_4_constants $4..7 = 0x3F800000 (1.0)
342splat_4_constants $8..11 = 0x3F800000 (1.0)
343copy_4_slots_masked $4..7 = Mask($8..11)
344copy_3_slots_masked $2..4 = Mask($5..7)
345copy_slot_masked $3 = Mask($4)
346swizzle_copy_4_slots_masked (v1..4).wzyx = Mask($0..3)
347swizzle_copy_3_slots_masked (v0..3).xyw = Mask($1..3)
348swizzle_4 $0..3 = ($0..3).wzyx
349swizzle_2 $0..1 = ($0..2).yz
350)");
351}
352
353DEF_TEST(RasterPipelineBuilderTransposeMatrix, r) {
354 // Create a very simple nonsense program.
356 builder.push_constant_f(1.0f); // push into 0
357 builder.push_duplicates(15); // duplicate into 1~15
358 builder.transpose(2, 2); // transpose a 2x2 matrix
359 builder.transpose(3, 3); // transpose a 3x3 matrix
360 builder.transpose(4, 4); // transpose a 4x4 matrix
361 builder.transpose(2, 4); // transpose a 2x4 matrix
362 builder.transpose(4, 3); // transpose a 4x3 matrix
363 builder.discard_stack(16); // balance stack
364 std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/0,
365 /*numUniformSlots=*/0,
366 /*numImmutableSlots=*/0);
367 check(r, *program,
368R"(splat_4_constants $0..3 = 0x3F800000 (1.0)
369splat_4_constants $4..7 = 0x3F800000 (1.0)
370splat_4_constants $8..11 = 0x3F800000 (1.0)
371splat_4_constants $12..15 = 0x3F800000 (1.0)
372swizzle_3 $13..15 = ($13..15).yxz
373shuffle $8..15 = ($8..15)[2 5 0 3 6 1 4 7]
374shuffle $1..15 = ($1..15)[3 7 11 0 4 8 12 1 5 9 13 2 6 10 14]
375shuffle $9..15 = ($9..15)[3 0 4 1 5 2 6]
376shuffle $5..15 = ($5..15)[2 5 8 0 3 6 9 1 4 7 10]
377)");
378}
379
380DEF_TEST(RasterPipelineBuilderDiagonalMatrix, r) {
381 // Create a very simple nonsense program.
383 builder.push_constant_f(0.0f); // push into 0
384 builder.push_constant_f(1.0f); // push into 1
385 builder.diagonal_matrix(2, 2); // generate a 2x2 diagonal matrix
386 builder.discard_stack(4); // balance stack
387 builder.push_constant_f(0.0f); // push into 0
388 builder.push_constant_f(2.0f); // push into 1
389 builder.diagonal_matrix(4, 4); // generate a 4x4 diagonal matrix
390 builder.discard_stack(16); // balance stack
391 builder.push_constant_f(0.0f); // push into 0
392 builder.push_constant_f(3.0f); // push into 1
393 builder.diagonal_matrix(2, 3); // generate a 2x3 diagonal matrix
394 builder.discard_stack(6); // balance stack
395 std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/0,
396 /*numUniformSlots=*/0,
397 /*numImmutableSlots=*/0);
398 check(r, *program,
399R"(copy_constant $0 = 0
400copy_constant $1 = 0x3F800000 (1.0)
401swizzle_4 $0..3 = ($0..3).yxxy
402copy_constant $0 = 0
403copy_constant $1 = 0x40000000 (2.0)
404shuffle $0..15 = ($0..15)[1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1]
405copy_constant $0 = 0
406copy_constant $1 = 0x40400000 (3.0)
407shuffle $0..5 = ($0..5)[1 0 0 0 1 0]
408)");
409}
410
411DEF_TEST(RasterPipelineBuilderMatrixResize, r) {
412 // Create a very simple nonsense program.
414 builder.push_constant_f(1.0f); // synthesize a 2x2 matrix
415 builder.push_constant_f(2.0f);
416 builder.push_constant_f(3.0f);
417 builder.push_constant_f(4.0f);
418 builder.matrix_resize(2, 2, 4, 4); // resize 2x2 matrix into 4x4
419 builder.matrix_resize(4, 4, 2, 2); // resize 4x4 matrix back into 2x2
420 builder.matrix_resize(2, 2, 2, 4); // resize 2x2 matrix into 2x4
421 builder.matrix_resize(2, 4, 4, 2); // resize 2x4 matrix into 4x2
422 builder.matrix_resize(4, 2, 3, 3); // resize 4x2 matrix into 3x3
423 builder.discard_stack(9); // balance stack
424 std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/0,
425 /*numUniformSlots=*/0,
426 /*numImmutableSlots=*/0);
427 check(r, *program,
428R"(copy_constant $0 = 0x3F800000 (1.0)
429copy_constant $1 = 0x40000000 (2.0)
430copy_constant $2 = 0x40400000 (3.0)
431copy_constant $3 = 0x40800000 (4.0)
432copy_constant $4 = 0
433copy_constant $5 = 0x3F800000 (1.0)
434shuffle $2..15 = ($2..15)[2 2 0 1 2 2 2 2 3 2 2 2 2 3]
435shuffle $2..3 = ($2..3)[2 3]
436copy_constant $4 = 0
437shuffle $2..7 = ($2..7)[2 2 0 1 2 2]
438copy_constant $8 = 0
439shuffle $2..7 = ($2..7)[2 3 6 6 6 6]
440copy_constant $8 = 0
441copy_constant $9 = 0x3F800000 (1.0)
442shuffle $2..8 = ($2..8)[6 0 1 6 2 3 7]
443)");
444}
445
446DEF_TEST(RasterPipelineBuilderBranches, r) {
447#if SK_HAS_MUSTTAIL
448 // We have guaranteed tail-calling, and don't need to rewind the stack.
449 static constexpr char kExpectationWithKnownExecutionMask[] =
450R"(jump jump +9 (label 3 at #10)
451label label 0
452copy_constant v0 = 0
453label label 0x00000001
454copy_constant v1 = 0
455jump jump -4 (label 0 at #2)
456label label 0x00000002
457copy_constant v2 = 0
458jump jump -7 (label 0 at #2)
459label label 0x00000003
460branch_if_no_active_lanes_eq branch -4 (label 2 at #7) if no lanes of v2 == 0
461branch_if_no_active_lanes_eq branch -10 (label 0 at #2) if no lanes of v2 == 0x00000001 (1.401298e-45)
462)";
463 static constexpr char kExpectationWithExecutionMaskWrites[] =
464R"(jump jump +10 (label 3 at #11)
465label label 0
466copy_constant v0 = 0
467label label 0x00000001
468copy_constant v1 = 0
469branch_if_no_lanes_active branch_if_no_lanes_active -2 (label 1 at #4)
470branch_if_all_lanes_active branch_if_all_lanes_active -5 (label 0 at #2)
471label label 0x00000002
472copy_constant v2 = 0
473branch_if_any_lanes_active branch_if_any_lanes_active -8 (label 0 at #2)
474label label 0x00000003
475branch_if_no_active_lanes_eq branch -4 (label 2 at #8) if no lanes of v2 == 0
476branch_if_no_active_lanes_eq branch -11 (label 0 at #2) if no lanes of v2 == 0x00000001 (1.401298e-45)
477)";
478#else
479 // We don't have guaranteed tail-calling, so we rewind the stack immediately before any backward
480 // branches.
481 static constexpr char kExpectationWithKnownExecutionMask[] =
482R"(jump jump +11 (label 3 at #12)
483label label 0
484copy_constant v0 = 0
485label label 0x00000001
486copy_constant v1 = 0
487stack_rewind
488jump jump -5 (label 0 at #2)
489label label 0x00000002
490copy_constant v2 = 0
491stack_rewind
492jump jump -9 (label 0 at #2)
493label label 0x00000003
494stack_rewind
495branch_if_no_active_lanes_eq branch -6 (label 2 at #8) if no lanes of v2 == 0
496stack_rewind
497branch_if_no_active_lanes_eq branch -14 (label 0 at #2) if no lanes of v2 == 0x00000001 (1.401298e-45)
498)";
499 static constexpr char kExpectationWithExecutionMaskWrites[] =
500R"(jump jump +13 (label 3 at #14)
501label label 0
502copy_constant v0 = 0
503label label 0x00000001
504copy_constant v1 = 0
505stack_rewind
506branch_if_no_lanes_active branch_if_no_lanes_active -3 (label 1 at #4)
507stack_rewind
508branch_if_all_lanes_active branch_if_all_lanes_active -7 (label 0 at #2)
509label label 0x00000002
510copy_constant v2 = 0
511stack_rewind
512branch_if_any_lanes_active branch_if_any_lanes_active -11 (label 0 at #2)
513label label 0x00000003
514stack_rewind
515branch_if_no_active_lanes_eq branch -6 (label 2 at #10) if no lanes of v2 == 0
516stack_rewind
517branch_if_no_active_lanes_eq branch -16 (label 0 at #2) if no lanes of v2 == 0x00000001 (1.401298e-45)
518)";
519#endif
520
521 for (bool enableExecutionMaskWrites : {false, true}) {
522 // Create a very simple nonsense program.
524 int label1 = builder.nextLabelID();
525 int label2 = builder.nextLabelID();
526 int label3 = builder.nextLabelID();
527 int label4 = builder.nextLabelID();
528
529 if (enableExecutionMaskWrites) {
530 builder.enableExecutionMaskWrites();
531 }
532
533 builder.jump(label4);
534 builder.label(label1);
535 builder.zero_slots_unmasked(one_slot_at(0));
536 builder.label(label2);
537 builder.zero_slots_unmasked(one_slot_at(1));
538 builder.branch_if_no_lanes_active(label2);
539 builder.branch_if_no_lanes_active(label3);
540 builder.branch_if_all_lanes_active(label1);
541 builder.label(label3);
542 builder.zero_slots_unmasked(one_slot_at(2));
543 builder.branch_if_any_lanes_active(label1);
544 builder.branch_if_any_lanes_active(label1);
545 builder.label(label4);
546 builder.branch_if_no_active_lanes_on_stack_top_equal(0, label3);
547 builder.branch_if_no_active_lanes_on_stack_top_equal(0, label2);
548 builder.branch_if_no_active_lanes_on_stack_top_equal(1, label1);
549 builder.branch_if_no_active_lanes_on_stack_top_equal(1, label4);
550
551 if (enableExecutionMaskWrites) {
552 builder.disableExecutionMaskWrites();
553 }
554
555 std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/3,
556 /*numUniformSlots=*/0,
557 /*numImmutableSlots=*/0);
558
559 check(r, *program, enableExecutionMaskWrites ? kExpectationWithExecutionMaskWrites
560 : kExpectationWithKnownExecutionMask);
562}
563
564DEF_TEST(RasterPipelineBuilderBinaryFloatOps, r) {
566
568 builder.push_constant_f(10.0f);
569 builder.push_duplicates(30);
570 builder.binary_op(BuilderOp::add_n_floats, 1);
571 builder.binary_op(BuilderOp::sub_n_floats, 2);
572 builder.binary_op(BuilderOp::mul_n_floats, 3);
573 builder.binary_op(BuilderOp::div_n_floats, 4);
574 builder.binary_op(BuilderOp::max_n_floats, 3);
575 builder.binary_op(BuilderOp::min_n_floats, 2);
576 builder.binary_op(BuilderOp::cmplt_n_floats, 5);
577 builder.binary_op(BuilderOp::cmple_n_floats, 4);
578 builder.binary_op(BuilderOp::cmpeq_n_floats, 3);
579 builder.binary_op(BuilderOp::cmpne_n_floats, 2);
580 builder.discard_stack(2);
581 std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/0,
582 /*numUniformSlots=*/0,
583 /*numImmutableSlots=*/0);
584 check(r, *program,
585R"(splat_4_constants $0..3 = 0x41200000 (10.0)
586splat_4_constants $4..7 = 0x41200000 (10.0)
587splat_4_constants $8..11 = 0x41200000 (10.0)
588splat_4_constants $12..15 = 0x41200000 (10.0)
589splat_4_constants $16..19 = 0x41200000 (10.0)
590splat_4_constants $20..23 = 0x41200000 (10.0)
591splat_4_constants $24..27 = 0x41200000 (10.0)
592splat_2_constants $28..29 = 0x41200000 (10.0)
593add_imm_float $29 += 0x41200000 (10.0)
594sub_2_floats $26..27 -= $28..29
595mul_3_floats $22..24 *= $25..27
596div_4_floats $17..20 /= $21..24
597max_3_floats $15..17 = max($15..17, $18..20)
598min_2_floats $14..15 = min($14..15, $16..17)
599cmplt_n_floats $6..10 = lessThan($6..10, $11..15)
600cmple_4_floats $3..6 = lessThanEqual($3..6, $7..10)
601cmpeq_3_floats $1..3 = equal($1..3, $4..6)
602cmpne_2_floats $0..1 = notEqual($0..1, $2..3)
603)");
604}
605
606DEF_TEST(RasterPipelineBuilderBinaryIntOps, r) {
608
610 builder.push_constant_i(123);
611 builder.push_duplicates(40);
612 builder.binary_op(BuilderOp::bitwise_and_n_ints, 1);
613 builder.binary_op(BuilderOp::bitwise_xor_n_ints, 2);
614 builder.binary_op(BuilderOp::bitwise_or_n_ints, 3);
615 builder.binary_op(BuilderOp::add_n_ints, 2);
616 builder.binary_op(BuilderOp::sub_n_ints, 3);
617 builder.binary_op(BuilderOp::mul_n_ints, 4);
618 builder.binary_op(BuilderOp::div_n_ints, 5);
619 builder.binary_op(BuilderOp::max_n_ints, 4);
620 builder.binary_op(BuilderOp::min_n_ints, 3);
621 builder.binary_op(BuilderOp::cmplt_n_ints, 1);
622 builder.binary_op(BuilderOp::cmple_n_ints, 2);
623 builder.binary_op(BuilderOp::cmpeq_n_ints, 3);
624 builder.binary_op(BuilderOp::cmpne_n_ints, 4);
625 builder.discard_stack(4);
626 std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/0,
627 /*numUniformSlots=*/0,
628 /*numImmutableSlots=*/0);
629 check(r, *program,
630R"(splat_4_constants $0..3 = 0x0000007B (1.723597e-43)
631splat_4_constants $4..7 = 0x0000007B (1.723597e-43)
632splat_4_constants $8..11 = 0x0000007B (1.723597e-43)
633splat_4_constants $12..15 = 0x0000007B (1.723597e-43)
634splat_4_constants $16..19 = 0x0000007B (1.723597e-43)
635splat_4_constants $20..23 = 0x0000007B (1.723597e-43)
636splat_4_constants $24..27 = 0x0000007B (1.723597e-43)
637splat_4_constants $28..31 = 0x0000007B (1.723597e-43)
638splat_4_constants $32..35 = 0x0000007B (1.723597e-43)
639splat_4_constants $36..39 = 0x0000007B (1.723597e-43)
640bitwise_and_imm_int $39 &= 0x0000007B
641bitwise_xor_2_ints $36..37 ^= $38..39
642bitwise_or_3_ints $32..34 |= $35..37
643add_2_ints $31..32 += $33..34
644sub_3_ints $27..29 -= $30..32
645mul_4_ints $22..25 *= $26..29
646div_n_ints $16..20 /= $21..25
647max_4_ints $13..16 = max($13..16, $17..20)
648min_3_ints $11..13 = min($11..13, $14..16)
649cmplt_int $12 = lessThan($12, $13)
650cmple_2_ints $9..10 = lessThanEqual($9..10, $11..12)
651cmpeq_3_ints $5..7 = equal($5..7, $8..10)
652cmpne_4_ints $0..3 = notEqual($0..3, $4..7)
653)");
654}
655
656DEF_TEST(RasterPipelineBuilderBinaryUIntOps, r) {
658
660 builder.push_constant_u(456);
661 builder.push_duplicates(21);
662 builder.binary_op(BuilderOp::div_n_uints, 6);
663 builder.binary_op(BuilderOp::cmplt_n_uints, 5);
664 builder.binary_op(BuilderOp::cmple_n_uints, 4);
665 builder.binary_op(BuilderOp::max_n_uints, 3);
666 builder.binary_op(BuilderOp::min_n_uints, 2);
667 builder.discard_stack(2);
668 std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/0,
669 /*numUniformSlots=*/0,
670 /*numImmutableSlots=*/0);
671 check(r, *program,
672R"(splat_4_constants $0..3 = 0x000001C8 (6.389921e-43)
673splat_4_constants $4..7 = 0x000001C8 (6.389921e-43)
674splat_4_constants $8..11 = 0x000001C8 (6.389921e-43)
675splat_4_constants $12..15 = 0x000001C8 (6.389921e-43)
676splat_4_constants $16..19 = 0x000001C8 (6.389921e-43)
677splat_2_constants $20..21 = 0x000001C8 (6.389921e-43)
678div_n_uints $10..15 /= $16..21
679cmplt_n_uints $6..10 = lessThan($6..10, $11..15)
680cmple_4_uints $3..6 = lessThanEqual($3..6, $7..10)
681max_3_uints $1..3 = max($1..3, $4..6)
682min_2_uints $0..1 = min($0..1, $2..3)
683)");
684}
685
686DEF_TEST(RasterPipelineBuilderUnaryOps, r) {
688
690 builder.push_constant_i(456);
691 builder.push_duplicates(4);
692 builder.unary_op(BuilderOp::cast_to_float_from_int, 1);
693 builder.unary_op(BuilderOp::cast_to_float_from_uint, 2);
694 builder.unary_op(BuilderOp::cast_to_int_from_float, 3);
695 builder.unary_op(BuilderOp::cast_to_uint_from_float, 4);
696 builder.unary_op(BuilderOp::cos_float, 4);
697 builder.unary_op(BuilderOp::tan_float, 3);
698 builder.unary_op(BuilderOp::sin_float, 2);
699 builder.unary_op(BuilderOp::sqrt_float, 1);
700 builder.unary_op(BuilderOp::abs_int, 2);
701 builder.unary_op(BuilderOp::floor_float, 3);
702 builder.unary_op(BuilderOp::ceil_float, 4);
703 builder.discard_stack(5);
704 std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/0,
705 /*numUniformSlots=*/0,
706 /*numImmutableSlots=*/0);
707 check(r, *program,
708R"(splat_4_constants $0..3 = 0x000001C8 (6.389921e-43)
709copy_constant $4 = 0x000001C8 (6.389921e-43)
710cast_to_float_from_int $4 = IntToFloat($4)
711cast_to_float_from_2_uints $3..4 = UintToFloat($3..4)
712cast_to_int_from_3_floats $2..4 = FloatToInt($2..4)
713cast_to_uint_from_4_floats $1..4 = FloatToUint($1..4)
714cos_float $1 = cos($1)
715cos_float $2 = cos($2)
716cos_float $3 = cos($3)
717cos_float $4 = cos($4)
718tan_float $2 = tan($2)
719tan_float $3 = tan($3)
720tan_float $4 = tan($4)
721sin_float $3 = sin($3)
722sin_float $4 = sin($4)
723sqrt_float $4 = sqrt($4)
724abs_2_ints $3..4 = abs($3..4)
725floor_3_floats $2..4 = floor($2..4)
726ceil_4_floats $1..4 = ceil($1..4)
727)");
728}
729
730DEF_TEST(RasterPipelineBuilderUniforms, r) {
732
733 // Create a very simple nonsense program.
735 builder.push_uniform(one_slot_at(0)); // push into 0
736 builder.push_uniform(two_slots_at(1)); // push into 1~2
737 builder.push_uniform(three_slots_at(3)); // push into 3~5
738 builder.push_uniform(four_slots_at(6)); // push into 6~9
739 builder.push_uniform(five_slots_at(0)); // push into 10~14
740 builder.unary_op(BuilderOp::abs_int, 1); // perform work so the program isn't eliminated
741 builder.discard_stack(15); // balance stack
742 std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/0,
743 /*numUniformSlots=*/10,
744 /*numImmutableSlots=*/0);
745 check(r, *program,
746R"(copy_4_uniforms $0..3 = u0..3
747copy_4_uniforms $4..7 = u4..7
748copy_2_uniforms $8..9 = u8..9
749copy_4_uniforms $10..13 = u0..3
750copy_uniform $14 = u4
751abs_int $14 = abs($14)
752)");
753}
754
755DEF_TEST(RasterPipelineBuilderPushZeros, r) {
757
758 // Create a very simple nonsense program.
760 builder.push_zeros(1); // push into 0
761 builder.push_zeros(2); // push into 1~2
762 builder.push_zeros(3); // push into 3~5
763 builder.push_zeros(4); // push into 6~9
764 builder.push_zeros(5); // push into 10~14
765 builder.unary_op(BuilderOp::abs_int, 1); // perform work so the program isn't eliminated
766 builder.discard_stack(15); // balance stack
767 std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/0,
768 /*numUniformSlots=*/10,
769 /*numImmutableSlots=*/0);
770 check(r, *program,
771R"(splat_4_constants $0..3 = 0
772splat_4_constants $4..7 = 0
773splat_4_constants $8..11 = 0
774splat_3_constants $12..14 = 0
775abs_int $14 = abs($14)
776)");
777}
778
779DEF_TEST(RasterPipelineBuilderTernaryFloatOps, r) {
781
783 builder.push_constant_f(0.75f);
784 builder.push_duplicates(8);
785 builder.ternary_op(BuilderOp::mix_n_floats, 3);
786 builder.discard_stack(3);
787 std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/0,
788 /*numUniformSlots=*/0,
789 /*numImmutableSlots=*/0);
790 check(r, *program,
791R"(splat_4_constants $0..3 = 0x3F400000 (0.75)
792splat_4_constants $4..7 = 0x3F400000 (0.75)
793copy_constant $8 = 0x3F400000 (0.75)
794mix_3_floats $0..2 = mix($3..5, $6..8, $0..2)
795)");
796}
797
798DEF_TEST(RasterPipelineBuilderAutomaticStackRewinding, r) {
800
802 builder.push_constant_i(1);
803 builder.push_duplicates(2000);
804 builder.unary_op(BuilderOp::abs_int, 1); // perform work so the program isn't eliminated
805 builder.discard_stack(2001);
806 std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/0,
807 /*numUniformSlots=*/0,
808 /*numImmutableSlots=*/0);
810
811#if SK_HAS_MUSTTAIL
812 // We have guaranteed tail-calling, so we never use `stack_rewind`.
813 REPORTER_ASSERT(r, !skstd::contains(as_string_view(dump), "stack_rewind"));
814#else
815 // We can't guarantee tail-calling, so we should automatically insert `stack_rewind` stages into
816 // long programs.
818#endif
819}
820
821DEF_TEST(RasterPipelineBuilderTraceOps, r) {
822 for (bool provideDebugTrace : {false, true}) {
824 // Create a trace mask stack on stack-ID 123.
825 builder.set_current_stack(123);
826 builder.push_constant_i(~0);
827 // Emit trace ops.
828 builder.trace_enter(123, 2);
829 builder.trace_scope(123, +1);
830 builder.trace_line(123, 456);
831 builder.trace_var(123, two_slots_at(3));
832 builder.trace_scope(123, -1);
833 builder.trace_exit(123, 2);
834 // Discard the trace mask.
835 builder.discard_stack(1);
836
837 if (!provideDebugTrace) {
838 // Test the output when no DebugTrace info is provided.
839 std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/20,
840 /*numUniformSlots=*/0,
841 /*numImmutableSlots=*/0);
842 check(r, *program,
843R"(copy_constant $0 = 0xFFFFFFFF
844trace_enter TraceEnter(???) when $0 is true
845trace_scope TraceScope(+1) when $0 is true
846trace_line TraceLine(456) when $0 is true
847trace_var TraceVar(v3..4) when $0 is true
848trace_scope TraceScope(-1) when $0 is true
849trace_exit TraceExit(???) when $0 is true
850)");
851 } else {
852 // Test the output when we supply a populated DebugTrace.
854 trace.fFuncInfo = {{"FunctionA"}, {"FunctionB"}, {"FunctionC"}, {"FunctionD"}};
855
857 slot.name = "Var0";
858 trace.fSlotInfo.push_back(slot);
859 slot.name = "Var1";
860 trace.fSlotInfo.push_back(slot);
861 slot.name = "Var2";
862 trace.fSlotInfo.push_back(slot);
863 slot.name = "Var3";
864 trace.fSlotInfo.push_back(slot);
865 slot.name = "Var4";
866 trace.fSlotInfo.push_back(slot);
867
868 std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/20,
869 /*numUniformSlots=*/0,
870 /*numImmutableSlots=*/0,
871 &trace);
872 check(r, *program,
873R"(copy_constant $0 = 0xFFFFFFFF
874trace_enter TraceEnter(FunctionC) when $0 is true
875trace_scope TraceScope(+1) when $0 is true
876trace_line TraceLine(456) when $0 is true
877trace_var TraceVar(Var3, Var4) when $0 is true
878trace_scope TraceScope(-1) when $0 is true
879trace_exit TraceExit(FunctionC) when $0 is true
880)");
881 }
882 }
883}
static SkSL::RP::SlotRange ten_slots_at(SkSL::RP::Slot index)
static SkSL::RP::SlotRange one_slot_at(SkSL::RP::Slot index)
static SkSL::RP::SlotRange three_slots_at(SkSL::RP::Slot index)
static SkSL::RP::SlotRange four_slots_at(SkSL::RP::Slot index)
static std::string_view as_string_view(const sk_sp< SkData > &dump)
static sk_sp< SkData > get_program_dump(SkSL::RP::Program &program)
static SkSL::RP::SlotRange five_slots_at(SkSL::RP::Slot index)
static SkSL::RP::SlotRange two_slots_at(SkSL::RP::Slot index)
#define check(reporter, ref, unref, make, kill)
static void dump(const float m[20], SkYUVColorSpace cs, bool rgb2yuv)
#define DEF_TEST(name, reporter)
Definition Test.h:312
#define REPORTER_ASSERT(r, cond,...)
Definition Test.h:286
std::vector< FunctionDebugInfo > fFuncInfo
std::vector< SlotDebugInfo > fSlotInfo
void dump(SkWStream *out, bool writeInstructionCount=false) const
constexpr bool contains(std::string_view str, std::string_view needle)