xref: /aosp_15_r20/external/skia/tests/SkRasterPipelineTest.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2016 Google Inc.
3*c8dee2aaSAndroid Build Coastguard Worker  *
4*c8dee2aaSAndroid Build Coastguard Worker  * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker  * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker  */
7*c8dee2aaSAndroid Build Coastguard Worker 
8*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTo.h"
9*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkHalf.h"
10*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkUtils.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkOpts.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkRasterPipeline.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkRasterPipelineContextUtils.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/Swizzle.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/tracing/SkSLTraceHook.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "tests/Test.h"
17*c8dee2aaSAndroid Build Coastguard Worker 
18*c8dee2aaSAndroid Build Coastguard Worker #include <cmath>
19*c8dee2aaSAndroid Build Coastguard Worker #include <numeric>
20*c8dee2aaSAndroid Build Coastguard Worker 
21*c8dee2aaSAndroid Build Coastguard Worker using namespace skia_private;
22*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRasterPipeline,r)23*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRasterPipeline, r) {
24*c8dee2aaSAndroid Build Coastguard Worker     // Build and run a simple pipeline to exercise SkRasterPipeline,
25*c8dee2aaSAndroid Build Coastguard Worker     // drawing 50% transparent blue over opaque red in half-floats.
26*c8dee2aaSAndroid Build Coastguard Worker     uint64_t red  = 0x3c00000000003c00ull,
27*c8dee2aaSAndroid Build Coastguard Worker              blue = 0x3800380000000000ull,
28*c8dee2aaSAndroid Build Coastguard Worker              result;
29*c8dee2aaSAndroid Build Coastguard Worker 
30*c8dee2aaSAndroid Build Coastguard Worker     SkRasterPipeline_MemoryCtx load_s_ctx = { &blue, 0 },
31*c8dee2aaSAndroid Build Coastguard Worker                                load_d_ctx = { &red, 0 },
32*c8dee2aaSAndroid Build Coastguard Worker                                store_ctx  = { &result, 0 };
33*c8dee2aaSAndroid Build Coastguard Worker 
34*c8dee2aaSAndroid Build Coastguard Worker     SkRasterPipeline_<256> p;
35*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::load_f16,     &load_s_ctx);
36*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::load_f16_dst, &load_d_ctx);
37*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::srcover);
38*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::store_f16, &store_ctx);
39*c8dee2aaSAndroid Build Coastguard Worker     p.run(0,0,1,1);
40*c8dee2aaSAndroid Build Coastguard Worker 
41*c8dee2aaSAndroid Build Coastguard Worker     // We should see half-intensity magenta.
42*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, ((result >>  0) & 0xffff) == 0x3800);
43*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, ((result >> 16) & 0xffff) == 0x0000);
44*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, ((result >> 32) & 0xffff) == 0x3800);
45*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, ((result >> 48) & 0xffff) == 0x3c00);
46*c8dee2aaSAndroid Build Coastguard Worker }
47*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRasterPipeline_PackSmallContext,r)48*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRasterPipeline_PackSmallContext, r) {
49*c8dee2aaSAndroid Build Coastguard Worker     struct PackableObject {
50*c8dee2aaSAndroid Build Coastguard Worker         std::array<uint8_t, sizeof(void*)> data;
51*c8dee2aaSAndroid Build Coastguard Worker     };
52*c8dee2aaSAndroid Build Coastguard Worker 
53*c8dee2aaSAndroid Build Coastguard Worker     // Create an arena with storage.
54*c8dee2aaSAndroid Build Coastguard Worker     using StorageArray = std::array<char, 128>;
55*c8dee2aaSAndroid Build Coastguard Worker     StorageArray storage = {};
56*c8dee2aaSAndroid Build Coastguard Worker     SkArenaAllocWithReset alloc(storage.data(), storage.size(), 500);
57*c8dee2aaSAndroid Build Coastguard Worker 
58*c8dee2aaSAndroid Build Coastguard Worker     // Construct and pack one PackableObject.
59*c8dee2aaSAndroid Build Coastguard Worker     PackableObject object;
60*c8dee2aaSAndroid Build Coastguard Worker     std::fill(object.data.begin(), object.data.end(), 123);
61*c8dee2aaSAndroid Build Coastguard Worker 
62*c8dee2aaSAndroid Build Coastguard Worker     const void* packed = SkRPCtxUtils::Pack(object, &alloc);
63*c8dee2aaSAndroid Build Coastguard Worker 
64*c8dee2aaSAndroid Build Coastguard Worker     // The alloc should still be empty.
65*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, alloc.isEmpty());
66*c8dee2aaSAndroid Build Coastguard Worker 
67*c8dee2aaSAndroid Build Coastguard Worker     // `packed` should now contain a bitwise cast of the raw object data.
68*c8dee2aaSAndroid Build Coastguard Worker     uintptr_t objectBits = sk_bit_cast<uintptr_t>(packed);
69*c8dee2aaSAndroid Build Coastguard Worker     for (size_t index = 0; index < sizeof(void*); ++index) {
70*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, (objectBits & 0xFF) == 123);
71*c8dee2aaSAndroid Build Coastguard Worker         objectBits >>= 8;
72*c8dee2aaSAndroid Build Coastguard Worker     }
73*c8dee2aaSAndroid Build Coastguard Worker 
74*c8dee2aaSAndroid Build Coastguard Worker     // Now unpack it.
75*c8dee2aaSAndroid Build Coastguard Worker     auto unpacked = SkRPCtxUtils::Unpack((const PackableObject*)packed);
76*c8dee2aaSAndroid Build Coastguard Worker 
77*c8dee2aaSAndroid Build Coastguard Worker     // The data should be identical to the original.
78*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, unpacked.data == object.data);
79*c8dee2aaSAndroid Build Coastguard Worker }
80*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRasterPipeline_PackBigContext,r)81*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRasterPipeline_PackBigContext, r) {
82*c8dee2aaSAndroid Build Coastguard Worker     struct BigObject {
83*c8dee2aaSAndroid Build Coastguard Worker         std::array<uint8_t, sizeof(void*) + 1> data;
84*c8dee2aaSAndroid Build Coastguard Worker     };
85*c8dee2aaSAndroid Build Coastguard Worker 
86*c8dee2aaSAndroid Build Coastguard Worker     // Create an arena with storage.
87*c8dee2aaSAndroid Build Coastguard Worker     using StorageArray = std::array<char, 128>;
88*c8dee2aaSAndroid Build Coastguard Worker     StorageArray storage = {};
89*c8dee2aaSAndroid Build Coastguard Worker     SkArenaAllocWithReset alloc(storage.data(), storage.size(), 500);
90*c8dee2aaSAndroid Build Coastguard Worker 
91*c8dee2aaSAndroid Build Coastguard Worker     // Construct and pack one BigObject.
92*c8dee2aaSAndroid Build Coastguard Worker     BigObject object;
93*c8dee2aaSAndroid Build Coastguard Worker     std::fill(object.data.begin(), object.data.end(), 123);
94*c8dee2aaSAndroid Build Coastguard Worker 
95*c8dee2aaSAndroid Build Coastguard Worker     const void* packed = SkRPCtxUtils::Pack(object, &alloc);
96*c8dee2aaSAndroid Build Coastguard Worker 
97*c8dee2aaSAndroid Build Coastguard Worker     // The alloc should not be empty any longer.
98*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, !alloc.isEmpty());
99*c8dee2aaSAndroid Build Coastguard Worker 
100*c8dee2aaSAndroid Build Coastguard Worker     // Now unpack it.
101*c8dee2aaSAndroid Build Coastguard Worker     auto unpacked = SkRPCtxUtils::Unpack((const BigObject*)packed);
102*c8dee2aaSAndroid Build Coastguard Worker 
103*c8dee2aaSAndroid Build Coastguard Worker     // The data should be identical to the original.
104*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, unpacked.data == object.data);
105*c8dee2aaSAndroid Build Coastguard Worker }
106*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRasterPipeline_LoadStoreConditionMask,reporter)107*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRasterPipeline_LoadStoreConditionMask, reporter) {
108*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) int32_t mask[16]  = {~0, 0, ~0, 0, ~0, ~0, ~0, 0, ~0, 0, ~0, 0, ~0, ~0, ~0, 0};
109*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) int32_t maskCopy[SkRasterPipeline_kMaxStride_highp] = {};
110*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) int32_t src[4 * SkRasterPipeline_kMaxStride_highp] = {};
111*c8dee2aaSAndroid Build Coastguard Worker 
112*c8dee2aaSAndroid Build Coastguard Worker     static_assert(std::size(mask) == SkRasterPipeline_kMaxStride_highp);
113*c8dee2aaSAndroid Build Coastguard Worker 
114*c8dee2aaSAndroid Build Coastguard Worker     SkRasterPipeline_<256> p;
115*c8dee2aaSAndroid Build Coastguard Worker     SkRasterPipeline_InitLaneMasksCtx initLaneMasksCtx;
116*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::init_lane_masks, &initLaneMasksCtx);
117*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::load_condition_mask, mask);
118*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::store_condition_mask, maskCopy);
119*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::store_src, src);
120*c8dee2aaSAndroid Build Coastguard Worker     p.run(0,0,SkOpts::raster_pipeline_highp_stride,1);
121*c8dee2aaSAndroid Build Coastguard Worker 
122*c8dee2aaSAndroid Build Coastguard Worker     {
123*c8dee2aaSAndroid Build Coastguard Worker         // `maskCopy` should be populated with `mask` in the frontmost positions
124*c8dee2aaSAndroid Build Coastguard Worker         // (depending on the architecture that SkRasterPipeline is targeting).
125*c8dee2aaSAndroid Build Coastguard Worker         size_t index = 0;
126*c8dee2aaSAndroid Build Coastguard Worker         for (; index < SkOpts::raster_pipeline_highp_stride; ++index) {
127*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(reporter, maskCopy[index] == mask[index]);
128*c8dee2aaSAndroid Build Coastguard Worker         }
129*c8dee2aaSAndroid Build Coastguard Worker 
130*c8dee2aaSAndroid Build Coastguard Worker         // The remaining slots should have been left alone.
131*c8dee2aaSAndroid Build Coastguard Worker         for (; index < std::size(maskCopy); ++index) {
132*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(reporter, maskCopy[index] == 0);
133*c8dee2aaSAndroid Build Coastguard Worker         }
134*c8dee2aaSAndroid Build Coastguard Worker     }
135*c8dee2aaSAndroid Build Coastguard Worker     {
136*c8dee2aaSAndroid Build Coastguard Worker         // `r` and `a` should be populated with `mask`.
137*c8dee2aaSAndroid Build Coastguard Worker         // `g` and `b` should remain initialized to true.
138*c8dee2aaSAndroid Build Coastguard Worker         const int r = 0 * SkOpts::raster_pipeline_highp_stride;
139*c8dee2aaSAndroid Build Coastguard Worker         const int g = 1 * SkOpts::raster_pipeline_highp_stride;
140*c8dee2aaSAndroid Build Coastguard Worker         const int b = 2 * SkOpts::raster_pipeline_highp_stride;
141*c8dee2aaSAndroid Build Coastguard Worker         const int a = 3 * SkOpts::raster_pipeline_highp_stride;
142*c8dee2aaSAndroid Build Coastguard Worker         for (size_t index = 0; index < SkOpts::raster_pipeline_highp_stride; ++index) {
143*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(reporter, src[r + index] == mask[index]);
144*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(reporter, src[g + index] == ~0);
145*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(reporter, src[b + index] == ~0);
146*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(reporter, src[a + index] == mask[index]);
147*c8dee2aaSAndroid Build Coastguard Worker         }
148*c8dee2aaSAndroid Build Coastguard Worker     }
149*c8dee2aaSAndroid Build Coastguard Worker }
150*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRasterPipeline_LoadStoreLoopMask,reporter)151*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRasterPipeline_LoadStoreLoopMask, reporter) {
152*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) int32_t mask[16]  = {~0, 0, ~0, 0, ~0, ~0, ~0, 0, ~0, 0, ~0, 0, ~0, ~0, ~0, 0};
153*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) int32_t maskCopy[SkRasterPipeline_kMaxStride_highp] = {};
154*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) int32_t src[4 * SkRasterPipeline_kMaxStride_highp] = {};
155*c8dee2aaSAndroid Build Coastguard Worker 
156*c8dee2aaSAndroid Build Coastguard Worker     static_assert(std::size(mask) == SkRasterPipeline_kMaxStride_highp);
157*c8dee2aaSAndroid Build Coastguard Worker 
158*c8dee2aaSAndroid Build Coastguard Worker     SkRasterPipeline_<256> p;
159*c8dee2aaSAndroid Build Coastguard Worker     SkRasterPipeline_InitLaneMasksCtx initLaneMasksCtx;
160*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::init_lane_masks, &initLaneMasksCtx);
161*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::load_loop_mask, mask);
162*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::store_loop_mask, maskCopy);
163*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::store_src, src);
164*c8dee2aaSAndroid Build Coastguard Worker     p.run(0,0,SkOpts::raster_pipeline_highp_stride,1);
165*c8dee2aaSAndroid Build Coastguard Worker 
166*c8dee2aaSAndroid Build Coastguard Worker     {
167*c8dee2aaSAndroid Build Coastguard Worker         // `maskCopy` should be populated with `mask` in the frontmost positions
168*c8dee2aaSAndroid Build Coastguard Worker         // (depending on the architecture that SkRasterPipeline is targeting).
169*c8dee2aaSAndroid Build Coastguard Worker         size_t index = 0;
170*c8dee2aaSAndroid Build Coastguard Worker         for (; index < SkOpts::raster_pipeline_highp_stride; ++index) {
171*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(reporter, maskCopy[index] == mask[index]);
172*c8dee2aaSAndroid Build Coastguard Worker         }
173*c8dee2aaSAndroid Build Coastguard Worker 
174*c8dee2aaSAndroid Build Coastguard Worker         // The remaining slots should have been left alone.
175*c8dee2aaSAndroid Build Coastguard Worker         for (; index < std::size(maskCopy); ++index) {
176*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(reporter, maskCopy[index] == 0);
177*c8dee2aaSAndroid Build Coastguard Worker         }
178*c8dee2aaSAndroid Build Coastguard Worker     }
179*c8dee2aaSAndroid Build Coastguard Worker     {
180*c8dee2aaSAndroid Build Coastguard Worker         // `g` and `a` should be populated with `mask`.
181*c8dee2aaSAndroid Build Coastguard Worker         // `r` and `b` should remain initialized to true.
182*c8dee2aaSAndroid Build Coastguard Worker         const int r = 0 * SkOpts::raster_pipeline_highp_stride;
183*c8dee2aaSAndroid Build Coastguard Worker         const int g = 1 * SkOpts::raster_pipeline_highp_stride;
184*c8dee2aaSAndroid Build Coastguard Worker         const int b = 2 * SkOpts::raster_pipeline_highp_stride;
185*c8dee2aaSAndroid Build Coastguard Worker         const int a = 3 * SkOpts::raster_pipeline_highp_stride;
186*c8dee2aaSAndroid Build Coastguard Worker         for (size_t index = 0; index < SkOpts::raster_pipeline_highp_stride; ++index) {
187*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(reporter, src[r + index] == ~0);
188*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(reporter, src[g + index] == mask[index]);
189*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(reporter, src[b + index] == ~0);
190*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(reporter, src[a + index] == mask[index]);
191*c8dee2aaSAndroid Build Coastguard Worker         }
192*c8dee2aaSAndroid Build Coastguard Worker     }
193*c8dee2aaSAndroid Build Coastguard Worker }
194*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRasterPipeline_LoadStoreReturnMask,reporter)195*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRasterPipeline_LoadStoreReturnMask, reporter) {
196*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) int32_t mask[16]  = {~0, 0, ~0, 0, ~0, ~0, ~0, 0, ~0, 0, ~0, 0, ~0, ~0, ~0, 0};
197*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) int32_t maskCopy[SkRasterPipeline_kMaxStride_highp] = {};
198*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) int32_t src[4 * SkRasterPipeline_kMaxStride_highp] = {};
199*c8dee2aaSAndroid Build Coastguard Worker 
200*c8dee2aaSAndroid Build Coastguard Worker     static_assert(std::size(mask) == SkRasterPipeline_kMaxStride_highp);
201*c8dee2aaSAndroid Build Coastguard Worker 
202*c8dee2aaSAndroid Build Coastguard Worker     SkRasterPipeline_<256> p;
203*c8dee2aaSAndroid Build Coastguard Worker     SkRasterPipeline_InitLaneMasksCtx initLaneMasksCtx;
204*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::init_lane_masks, &initLaneMasksCtx);
205*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::load_return_mask, mask);
206*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::store_return_mask, maskCopy);
207*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::store_src, src);
208*c8dee2aaSAndroid Build Coastguard Worker     p.run(0,0,SkOpts::raster_pipeline_highp_stride,1);
209*c8dee2aaSAndroid Build Coastguard Worker 
210*c8dee2aaSAndroid Build Coastguard Worker     {
211*c8dee2aaSAndroid Build Coastguard Worker         // `maskCopy` should be populated with `mask` in the frontmost positions
212*c8dee2aaSAndroid Build Coastguard Worker         // (depending on the architecture that SkRasterPipeline is targeting).
213*c8dee2aaSAndroid Build Coastguard Worker         size_t index = 0;
214*c8dee2aaSAndroid Build Coastguard Worker         for (; index < SkOpts::raster_pipeline_highp_stride; ++index) {
215*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(reporter, maskCopy[index] == mask[index]);
216*c8dee2aaSAndroid Build Coastguard Worker         }
217*c8dee2aaSAndroid Build Coastguard Worker 
218*c8dee2aaSAndroid Build Coastguard Worker         // The remaining slots should have been left alone.
219*c8dee2aaSAndroid Build Coastguard Worker         for (; index < std::size(maskCopy); ++index) {
220*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(reporter, maskCopy[index] == 0);
221*c8dee2aaSAndroid Build Coastguard Worker         }
222*c8dee2aaSAndroid Build Coastguard Worker     }
223*c8dee2aaSAndroid Build Coastguard Worker     {
224*c8dee2aaSAndroid Build Coastguard Worker         // `b` and `a` should be populated with `mask`.
225*c8dee2aaSAndroid Build Coastguard Worker         // `r` and `g` should remain initialized to true.
226*c8dee2aaSAndroid Build Coastguard Worker         const int r = 0 * SkOpts::raster_pipeline_highp_stride;
227*c8dee2aaSAndroid Build Coastguard Worker         const int g = 1 * SkOpts::raster_pipeline_highp_stride;
228*c8dee2aaSAndroid Build Coastguard Worker         const int b = 2 * SkOpts::raster_pipeline_highp_stride;
229*c8dee2aaSAndroid Build Coastguard Worker         const int a = 3 * SkOpts::raster_pipeline_highp_stride;
230*c8dee2aaSAndroid Build Coastguard Worker         for (size_t index = 0; index < SkOpts::raster_pipeline_highp_stride; ++index) {
231*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(reporter, src[r + index] == ~0);
232*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(reporter, src[g + index] == ~0);
233*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(reporter, src[b + index] == mask[index]);
234*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(reporter, src[a + index] == mask[index]);
235*c8dee2aaSAndroid Build Coastguard Worker         }
236*c8dee2aaSAndroid Build Coastguard Worker     }
237*c8dee2aaSAndroid Build Coastguard Worker }
238*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRasterPipeline_MergeConditionMask,reporter)239*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRasterPipeline_MergeConditionMask, reporter) {
240*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) int32_t mask[32]  = { 0, 0, ~0, ~0, 0, ~0, 0, ~0,
241*c8dee2aaSAndroid Build Coastguard Worker                                       ~0, ~0, ~0, ~0, 0, 0, 0, 0,
242*c8dee2aaSAndroid Build Coastguard Worker                                       0, 0, ~0, ~0, 0, ~0, 0, ~0,
243*c8dee2aaSAndroid Build Coastguard Worker                                       ~0, ~0, ~0, ~0, 0, 0, 0, 0};
244*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) int32_t src[4 * SkRasterPipeline_kMaxStride_highp] = {};
245*c8dee2aaSAndroid Build Coastguard Worker     static_assert(std::size(mask) == (2 * SkRasterPipeline_kMaxStride_highp));
246*c8dee2aaSAndroid Build Coastguard Worker 
247*c8dee2aaSAndroid Build Coastguard Worker     SkRasterPipeline_<256> p;
248*c8dee2aaSAndroid Build Coastguard Worker     SkRasterPipeline_InitLaneMasksCtx initLaneMasksCtx;
249*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::init_lane_masks, &initLaneMasksCtx);
250*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::merge_condition_mask, mask);
251*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::store_src, src);
252*c8dee2aaSAndroid Build Coastguard Worker     p.run(0,0,SkOpts::raster_pipeline_highp_stride,1);
253*c8dee2aaSAndroid Build Coastguard Worker 
254*c8dee2aaSAndroid Build Coastguard Worker     // `r` and `a` should be populated with `mask[x] & mask[y]` in the frontmost positions.
255*c8dee2aaSAndroid Build Coastguard Worker     // `g` and `b` should remain initialized to true.
256*c8dee2aaSAndroid Build Coastguard Worker     const int r = 0 * SkOpts::raster_pipeline_highp_stride;
257*c8dee2aaSAndroid Build Coastguard Worker     const int g = 1 * SkOpts::raster_pipeline_highp_stride;
258*c8dee2aaSAndroid Build Coastguard Worker     const int b = 2 * SkOpts::raster_pipeline_highp_stride;
259*c8dee2aaSAndroid Build Coastguard Worker     const int a = 3 * SkOpts::raster_pipeline_highp_stride;
260*c8dee2aaSAndroid Build Coastguard Worker     for (size_t index = 0; index < SkOpts::raster_pipeline_highp_stride; ++index) {
261*c8dee2aaSAndroid Build Coastguard Worker         int32_t expected = mask[index] & mask[index + SkOpts::raster_pipeline_highp_stride];
262*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, src[r + index] == expected);
263*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, src[g + index] == ~0);
264*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, src[b + index] == ~0);
265*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, src[a + index] == expected);
266*c8dee2aaSAndroid Build Coastguard Worker     }
267*c8dee2aaSAndroid Build Coastguard Worker }
268*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRasterPipeline_MergeLoopMask,reporter)269*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRasterPipeline_MergeLoopMask, reporter) {
270*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) int32_t initial[64]  = {~0, ~0, ~0, ~0, ~0,  0, ~0, ~0,  // r (condition)
271*c8dee2aaSAndroid Build Coastguard Worker                                         ~0,  0, ~0,  0, ~0, ~0, ~0, ~0,
272*c8dee2aaSAndroid Build Coastguard Worker                                         ~0, ~0, ~0, ~0, ~0, ~0,  0, ~0,  // g (loop)
273*c8dee2aaSAndroid Build Coastguard Worker                                         ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0,
274*c8dee2aaSAndroid Build Coastguard Worker                                         ~0, ~0, ~0, ~0, ~0,  0, ~0, ~0,  // b (return)
275*c8dee2aaSAndroid Build Coastguard Worker                                         ~0,  0, ~0,  0, ~0, ~0, ~0, ~0,
276*c8dee2aaSAndroid Build Coastguard Worker                                         ~0, ~0, ~0, ~0, ~0, ~0,  0, ~0,  // a (combined)
277*c8dee2aaSAndroid Build Coastguard Worker                                         ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0};
278*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) int32_t mask[16]     = {0, ~0, ~0, 0, ~0, ~0, ~0, ~0, 0, ~0, ~0, 0, ~0, ~0, ~0, ~0};
279*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) int32_t src[4 * SkRasterPipeline_kMaxStride_highp] = {};
280*c8dee2aaSAndroid Build Coastguard Worker     static_assert(std::size(initial) == (4 * SkRasterPipeline_kMaxStride_highp));
281*c8dee2aaSAndroid Build Coastguard Worker 
282*c8dee2aaSAndroid Build Coastguard Worker     SkRasterPipeline_<256> p;
283*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::load_src, initial);
284*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::merge_loop_mask, mask);
285*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::store_src, src);
286*c8dee2aaSAndroid Build Coastguard Worker     p.run(0,0,SkOpts::raster_pipeline_highp_stride,1);
287*c8dee2aaSAndroid Build Coastguard Worker 
288*c8dee2aaSAndroid Build Coastguard Worker     const int r = 0 * SkOpts::raster_pipeline_highp_stride;
289*c8dee2aaSAndroid Build Coastguard Worker     const int g = 1 * SkOpts::raster_pipeline_highp_stride;
290*c8dee2aaSAndroid Build Coastguard Worker     const int b = 2 * SkOpts::raster_pipeline_highp_stride;
291*c8dee2aaSAndroid Build Coastguard Worker     const int a = 3 * SkOpts::raster_pipeline_highp_stride;
292*c8dee2aaSAndroid Build Coastguard Worker     for (size_t index = 0; index < SkOpts::raster_pipeline_highp_stride; ++index) {
293*c8dee2aaSAndroid Build Coastguard Worker         // `g` should contain `g & mask` in each lane.
294*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, src[g + index] == (initial[g + index] & mask[index]));
295*c8dee2aaSAndroid Build Coastguard Worker 
296*c8dee2aaSAndroid Build Coastguard Worker         // `r` and `b` should be unchanged.
297*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, src[r + index] == initial[r + index]);
298*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, src[b + index] == initial[b + index]);
299*c8dee2aaSAndroid Build Coastguard Worker 
300*c8dee2aaSAndroid Build Coastguard Worker         // `a` should contain `r & g & b`.
301*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, src[a + index] == (src[r+index] & src[g+index] & src[b+index]));
302*c8dee2aaSAndroid Build Coastguard Worker     }
303*c8dee2aaSAndroid Build Coastguard Worker }
304*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRasterPipeline_ReenableLoopMask,reporter)305*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRasterPipeline_ReenableLoopMask, reporter) {
306*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) int32_t initial[64]  = {~0, ~0, ~0, ~0, ~0,  0, ~0, ~0,  // r (condition)
307*c8dee2aaSAndroid Build Coastguard Worker                                         ~0,  0, ~0,  0, ~0, ~0,  0, ~0,
308*c8dee2aaSAndroid Build Coastguard Worker                                          0, ~0, ~0, ~0,  0,  0,  0, ~0,  // g (loop)
309*c8dee2aaSAndroid Build Coastguard Worker                                          0,  0, ~0,  0,  0,  0,  0, ~0,
310*c8dee2aaSAndroid Build Coastguard Worker                                         ~0, ~0, ~0, ~0, ~0,  0, ~0, ~0,  // b (return)
311*c8dee2aaSAndroid Build Coastguard Worker                                         ~0,  0, ~0,  0, ~0, ~0,  0, ~0,
312*c8dee2aaSAndroid Build Coastguard Worker                                          0, ~0, ~0, ~0,  0,  0,  0, ~0,  // a (combined)
313*c8dee2aaSAndroid Build Coastguard Worker                                          0,  0, ~0,  0,  0,  0,  0, ~0};
314*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) int32_t mask[16]     = { 0, ~0, 0, 0, 0, 0, ~0, 0, 0, ~0, 0, 0, 0, 0, ~0, 0};
315*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) int32_t src[4 * SkRasterPipeline_kMaxStride_highp] = {};
316*c8dee2aaSAndroid Build Coastguard Worker     static_assert(std::size(initial) == (4 * SkRasterPipeline_kMaxStride_highp));
317*c8dee2aaSAndroid Build Coastguard Worker 
318*c8dee2aaSAndroid Build Coastguard Worker     SkRasterPipeline_<256> p;
319*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::load_src, initial);
320*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::reenable_loop_mask, mask);
321*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::store_src, src);
322*c8dee2aaSAndroid Build Coastguard Worker     p.run(0,0,SkOpts::raster_pipeline_highp_stride,1);
323*c8dee2aaSAndroid Build Coastguard Worker 
324*c8dee2aaSAndroid Build Coastguard Worker     const int r = 0 * SkOpts::raster_pipeline_highp_stride;
325*c8dee2aaSAndroid Build Coastguard Worker     const int g = 1 * SkOpts::raster_pipeline_highp_stride;
326*c8dee2aaSAndroid Build Coastguard Worker     const int b = 2 * SkOpts::raster_pipeline_highp_stride;
327*c8dee2aaSAndroid Build Coastguard Worker     const int a = 3 * SkOpts::raster_pipeline_highp_stride;
328*c8dee2aaSAndroid Build Coastguard Worker     for (size_t index = 0; index < SkOpts::raster_pipeline_highp_stride; ++index) {
329*c8dee2aaSAndroid Build Coastguard Worker         // `g` should contain `g | mask` in each lane.
330*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, src[g + index] == (initial[g + index] | mask[index]));
331*c8dee2aaSAndroid Build Coastguard Worker 
332*c8dee2aaSAndroid Build Coastguard Worker         // `r` and `b` should be unchanged.
333*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, src[r + index] == initial[r + index]);
334*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, src[b + index] == initial[b + index]);
335*c8dee2aaSAndroid Build Coastguard Worker 
336*c8dee2aaSAndroid Build Coastguard Worker         // `a` should contain `r & g & b`.
337*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, src[a + index] == (src[r+index] & src[g+index] & src[b+index]));
338*c8dee2aaSAndroid Build Coastguard Worker     }
339*c8dee2aaSAndroid Build Coastguard Worker }
340*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRasterPipeline_CaseOp,reporter)341*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRasterPipeline_CaseOp, reporter) {
342*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) int32_t initial[64]        = {~0, ~0, ~0, ~0, ~0,  0, ~0, ~0,  // r (condition)
343*c8dee2aaSAndroid Build Coastguard Worker                                                0, ~0, ~0,  0, ~0, ~0,  0, ~0,
344*c8dee2aaSAndroid Build Coastguard Worker                                               ~0,  0, ~0, ~0,  0,  0,  0, ~0,  // g (loop)
345*c8dee2aaSAndroid Build Coastguard Worker                                                0,  0, ~0,  0,  0,  0,  0, ~0,
346*c8dee2aaSAndroid Build Coastguard Worker                                               ~0, ~0, ~0, ~0, ~0,  0, ~0, ~0,  // b (return)
347*c8dee2aaSAndroid Build Coastguard Worker                                                0, ~0, ~0,  0, ~0, ~0,  0, ~0,
348*c8dee2aaSAndroid Build Coastguard Worker                                               ~0,  0, ~0, ~0,  0,  0,  0, ~0,  // a (combined)
349*c8dee2aaSAndroid Build Coastguard Worker                                                0,  0, ~0,  0,  0,  0,  0, ~0};
350*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) int32_t src[4 * SkRasterPipeline_kMaxStride_highp] = {};
351*c8dee2aaSAndroid Build Coastguard Worker     static_assert(std::size(initial) == (4 * SkRasterPipeline_kMaxStride_highp));
352*c8dee2aaSAndroid Build Coastguard Worker 
353*c8dee2aaSAndroid Build Coastguard Worker     constexpr int32_t actualValues[16] = { 2,  1,  2,  4,  5,  2,  2,  8};
354*c8dee2aaSAndroid Build Coastguard Worker     static_assert(std::size(actualValues) == SkRasterPipeline_kMaxStride_highp);
355*c8dee2aaSAndroid Build Coastguard Worker 
356*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) int32_t caseOpData[2 * SkRasterPipeline_kMaxStride_highp];
357*c8dee2aaSAndroid Build Coastguard Worker     for (size_t index = 0; index < SkOpts::raster_pipeline_highp_stride; ++index) {
358*c8dee2aaSAndroid Build Coastguard Worker         caseOpData[0 * SkOpts::raster_pipeline_highp_stride + index] = actualValues[index];
359*c8dee2aaSAndroid Build Coastguard Worker         caseOpData[1 * SkOpts::raster_pipeline_highp_stride + index] = ~0;
360*c8dee2aaSAndroid Build Coastguard Worker     }
361*c8dee2aaSAndroid Build Coastguard Worker 
362*c8dee2aaSAndroid Build Coastguard Worker     SkRasterPipeline_CaseOpCtx ctx;
363*c8dee2aaSAndroid Build Coastguard Worker     ctx.offset = 0;
364*c8dee2aaSAndroid Build Coastguard Worker     ctx.expectedValue = 2;
365*c8dee2aaSAndroid Build Coastguard Worker 
366*c8dee2aaSAndroid Build Coastguard Worker     SkArenaAlloc alloc(/*firstHeapAllocation=*/256);
367*c8dee2aaSAndroid Build Coastguard Worker     SkRasterPipeline p(&alloc);
368*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::load_src, initial);
369*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::set_base_pointer, &caseOpData[0]);
370*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::case_op, SkRPCtxUtils::Pack(ctx, &alloc));
371*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::store_src, src);
372*c8dee2aaSAndroid Build Coastguard Worker     p.run(0,0,SkOpts::raster_pipeline_highp_stride,1);
373*c8dee2aaSAndroid Build Coastguard Worker 
374*c8dee2aaSAndroid Build Coastguard Worker     const int r = 0 * SkOpts::raster_pipeline_highp_stride;
375*c8dee2aaSAndroid Build Coastguard Worker     const int g = 1 * SkOpts::raster_pipeline_highp_stride;
376*c8dee2aaSAndroid Build Coastguard Worker     const int b = 2 * SkOpts::raster_pipeline_highp_stride;
377*c8dee2aaSAndroid Build Coastguard Worker     const int a = 3 * SkOpts::raster_pipeline_highp_stride;
378*c8dee2aaSAndroid Build Coastguard Worker     const int actualValueIdx = 0 * SkOpts::raster_pipeline_highp_stride;
379*c8dee2aaSAndroid Build Coastguard Worker     const int defaultMaskIdx = 1 * SkOpts::raster_pipeline_highp_stride;
380*c8dee2aaSAndroid Build Coastguard Worker 
381*c8dee2aaSAndroid Build Coastguard Worker     for (size_t index = 0; index < SkOpts::raster_pipeline_highp_stride; ++index) {
382*c8dee2aaSAndroid Build Coastguard Worker         // `g` should have been set to true for each lane containing 2.
383*c8dee2aaSAndroid Build Coastguard Worker         int32_t expected = (actualValues[index] == 2) ? ~0 : initial[g + index];
384*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, src[g + index] == expected);
385*c8dee2aaSAndroid Build Coastguard Worker 
386*c8dee2aaSAndroid Build Coastguard Worker         // `r` and `b` should be unchanged.
387*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, src[r + index] == initial[r + index]);
388*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, src[b + index] == initial[b + index]);
389*c8dee2aaSAndroid Build Coastguard Worker 
390*c8dee2aaSAndroid Build Coastguard Worker         // `a` should contain `r & g & b`.
391*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, src[a + index] == (src[r+index] & src[g+index] & src[b+index]));
392*c8dee2aaSAndroid Build Coastguard Worker 
393*c8dee2aaSAndroid Build Coastguard Worker         // The actual-value part of `caseOpData` should be unchanged from the inputs.
394*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, caseOpData[actualValueIdx + index] == actualValues[index]);
395*c8dee2aaSAndroid Build Coastguard Worker 
396*c8dee2aaSAndroid Build Coastguard Worker         // The default-mask part of `caseOpData` should have been zeroed where the values matched.
397*c8dee2aaSAndroid Build Coastguard Worker         expected = (actualValues[index] == 2) ? 0 : ~0;
398*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, caseOpData[defaultMaskIdx + index] == expected);
399*c8dee2aaSAndroid Build Coastguard Worker     }
400*c8dee2aaSAndroid Build Coastguard Worker }
401*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRasterPipeline_MaskOffLoopMask,reporter)402*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRasterPipeline_MaskOffLoopMask, reporter) {
403*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) int32_t initial[64]  = {~0, ~0, ~0, ~0, ~0,  0, ~0, ~0,  // r (condition)
404*c8dee2aaSAndroid Build Coastguard Worker                                         ~0,  0, ~0, ~0,  0,  0,  0, ~0,
405*c8dee2aaSAndroid Build Coastguard Worker                                         ~0, ~0,  0, ~0,  0,  0, ~0, ~0,  // g (loop)
406*c8dee2aaSAndroid Build Coastguard Worker                                         ~0,  0,  0, ~0,  0,  0,  0, ~0,
407*c8dee2aaSAndroid Build Coastguard Worker                                         ~0, ~0, ~0, ~0, ~0,  0, ~0, ~0,  // b (return)
408*c8dee2aaSAndroid Build Coastguard Worker                                         ~0,  0, ~0, ~0,  0,  0,  0, ~0,
409*c8dee2aaSAndroid Build Coastguard Worker                                         ~0, ~0,  0, ~0,  0,  0, ~0, ~0,  // a (combined)
410*c8dee2aaSAndroid Build Coastguard Worker                                         ~0,  0,  0, ~0,  0,  0,  0, ~0};
411*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) int32_t src[4 * SkRasterPipeline_kMaxStride_highp] = {};
412*c8dee2aaSAndroid Build Coastguard Worker     static_assert(std::size(initial) == (4 * SkRasterPipeline_kMaxStride_highp));
413*c8dee2aaSAndroid Build Coastguard Worker 
414*c8dee2aaSAndroid Build Coastguard Worker     SkRasterPipeline_<256> p;
415*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::load_src, initial);
416*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::mask_off_loop_mask);
417*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::store_src, src);
418*c8dee2aaSAndroid Build Coastguard Worker     p.run(0,0,SkOpts::raster_pipeline_highp_stride,1);
419*c8dee2aaSAndroid Build Coastguard Worker 
420*c8dee2aaSAndroid Build Coastguard Worker     const int r = 0 * SkOpts::raster_pipeline_highp_stride;
421*c8dee2aaSAndroid Build Coastguard Worker     const int g = 1 * SkOpts::raster_pipeline_highp_stride;
422*c8dee2aaSAndroid Build Coastguard Worker     const int b = 2 * SkOpts::raster_pipeline_highp_stride;
423*c8dee2aaSAndroid Build Coastguard Worker     const int a = 3 * SkOpts::raster_pipeline_highp_stride;
424*c8dee2aaSAndroid Build Coastguard Worker     for (size_t index = 0; index < SkOpts::raster_pipeline_highp_stride; ++index) {
425*c8dee2aaSAndroid Build Coastguard Worker         // `g` should have masked off any lanes that are currently executing.
426*c8dee2aaSAndroid Build Coastguard Worker         int32_t expected = initial[g + index] & ~initial[a + index];
427*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, src[g + index] == expected);
428*c8dee2aaSAndroid Build Coastguard Worker 
429*c8dee2aaSAndroid Build Coastguard Worker         // `a` should contain `r & g & b`.
430*c8dee2aaSAndroid Build Coastguard Worker         expected = src[r + index] & src[g + index] & src[b + index];
431*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, src[a + index] == expected);
432*c8dee2aaSAndroid Build Coastguard Worker     }
433*c8dee2aaSAndroid Build Coastguard Worker }
434*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRasterPipeline_MaskOffReturnMask,reporter)435*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRasterPipeline_MaskOffReturnMask, reporter) {
436*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) int32_t initial[64]  = {~0, ~0, ~0, ~0, ~0,  0, ~0, ~0,  // r (condition)
437*c8dee2aaSAndroid Build Coastguard Worker                                         ~0,  0, ~0, ~0,  0,  0,  0, ~0,
438*c8dee2aaSAndroid Build Coastguard Worker                                         ~0, ~0,  0, ~0,  0,  0, ~0, ~0,  // g (loop)
439*c8dee2aaSAndroid Build Coastguard Worker                                         ~0,  0,  0, ~0,  0,  0,  0, ~0,
440*c8dee2aaSAndroid Build Coastguard Worker                                         ~0, ~0, ~0, ~0, ~0,  0, ~0, ~0,  // b (return)
441*c8dee2aaSAndroid Build Coastguard Worker                                         ~0,  0, ~0, ~0,  0,  0,  0, ~0,
442*c8dee2aaSAndroid Build Coastguard Worker                                         ~0, ~0,  0, ~0,  0,  0, ~0, ~0,  // a (combined)
443*c8dee2aaSAndroid Build Coastguard Worker                                         ~0,  0,  0, ~0,  0,  0,  0, ~0};
444*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) int32_t src[4 * SkRasterPipeline_kMaxStride_highp] = {};
445*c8dee2aaSAndroid Build Coastguard Worker     static_assert(std::size(initial) == (4 * SkRasterPipeline_kMaxStride_highp));
446*c8dee2aaSAndroid Build Coastguard Worker 
447*c8dee2aaSAndroid Build Coastguard Worker     SkRasterPipeline_<256> p;
448*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::load_src, initial);
449*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::mask_off_return_mask);
450*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::store_src, src);
451*c8dee2aaSAndroid Build Coastguard Worker     p.run(0,0,SkOpts::raster_pipeline_highp_stride,1);
452*c8dee2aaSAndroid Build Coastguard Worker 
453*c8dee2aaSAndroid Build Coastguard Worker     const int r = 0 * SkOpts::raster_pipeline_highp_stride;
454*c8dee2aaSAndroid Build Coastguard Worker     const int g = 1 * SkOpts::raster_pipeline_highp_stride;
455*c8dee2aaSAndroid Build Coastguard Worker     const int b = 2 * SkOpts::raster_pipeline_highp_stride;
456*c8dee2aaSAndroid Build Coastguard Worker     const int a = 3 * SkOpts::raster_pipeline_highp_stride;
457*c8dee2aaSAndroid Build Coastguard Worker     for (size_t index = 0; index < SkOpts::raster_pipeline_highp_stride; ++index) {
458*c8dee2aaSAndroid Build Coastguard Worker         // `b` should have masked off any lanes that are currently executing.
459*c8dee2aaSAndroid Build Coastguard Worker         int32_t expected = initial[b + index] & ~initial[a + index];
460*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, src[b + index] == expected);
461*c8dee2aaSAndroid Build Coastguard Worker 
462*c8dee2aaSAndroid Build Coastguard Worker         // `a` should contain `r & g & b`.
463*c8dee2aaSAndroid Build Coastguard Worker         expected = src[r + index] & src[g + index] & src[b + index];
464*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, src[a + index] == expected);
465*c8dee2aaSAndroid Build Coastguard Worker     }
466*c8dee2aaSAndroid Build Coastguard Worker }
467*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRasterPipeline_InitLaneMasks,reporter)468*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRasterPipeline_InitLaneMasks, reporter) {
469*c8dee2aaSAndroid Build Coastguard Worker     for (size_t width = 1; width <= SkOpts::raster_pipeline_highp_stride; ++width) {
470*c8dee2aaSAndroid Build Coastguard Worker         SkArenaAlloc alloc(/*firstHeapAllocation=*/256);
471*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline p(&alloc);
472*c8dee2aaSAndroid Build Coastguard Worker 
473*c8dee2aaSAndroid Build Coastguard Worker         // Initialize RGBA to unrelated values.
474*c8dee2aaSAndroid Build Coastguard Worker         alignas(64) static constexpr float kArbitraryColor[4] = {0.0f, 0.25f, 0.50f, 0.75f};
475*c8dee2aaSAndroid Build Coastguard Worker         p.appendConstantColor(&alloc, kArbitraryColor);
476*c8dee2aaSAndroid Build Coastguard Worker 
477*c8dee2aaSAndroid Build Coastguard Worker         // Overwrite RGBA with lane masks up to the tail width.
478*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline_InitLaneMasksCtx ctx;
479*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::init_lane_masks, &ctx);
480*c8dee2aaSAndroid Build Coastguard Worker 
481*c8dee2aaSAndroid Build Coastguard Worker         // Use the store_src command to write out RGBA for inspection.
482*c8dee2aaSAndroid Build Coastguard Worker         alignas(64) int32_t RGBA[4 * SkRasterPipeline_kMaxStride_highp] = {};
483*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::store_src, RGBA);
484*c8dee2aaSAndroid Build Coastguard Worker 
485*c8dee2aaSAndroid Build Coastguard Worker         // Execute our program.
486*c8dee2aaSAndroid Build Coastguard Worker         p.run(0,0,width,1);
487*c8dee2aaSAndroid Build Coastguard Worker 
488*c8dee2aaSAndroid Build Coastguard Worker         // Initialized data should look like on/on/on/on (RGBA are all set) and is
489*c8dee2aaSAndroid Build Coastguard Worker         // striped by the raster pipeline stride because we wrote it using store_src.
490*c8dee2aaSAndroid Build Coastguard Worker         size_t index = 0;
491*c8dee2aaSAndroid Build Coastguard Worker         int32_t* channelR = RGBA;
492*c8dee2aaSAndroid Build Coastguard Worker         int32_t* channelG = channelR + SkOpts::raster_pipeline_highp_stride;
493*c8dee2aaSAndroid Build Coastguard Worker         int32_t* channelB = channelG + SkOpts::raster_pipeline_highp_stride;
494*c8dee2aaSAndroid Build Coastguard Worker         int32_t* channelA = channelB + SkOpts::raster_pipeline_highp_stride;
495*c8dee2aaSAndroid Build Coastguard Worker         for (; index < width; ++index) {
496*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(reporter, *channelR++ == ~0);
497*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(reporter, *channelG++ == ~0);
498*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(reporter, *channelB++ == ~0);
499*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(reporter, *channelA++ == ~0);
500*c8dee2aaSAndroid Build Coastguard Worker         }
501*c8dee2aaSAndroid Build Coastguard Worker 
502*c8dee2aaSAndroid Build Coastguard Worker         // The rest of the output array should be untouched (all zero).
503*c8dee2aaSAndroid Build Coastguard Worker         for (; index < SkOpts::raster_pipeline_highp_stride; ++index) {
504*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(reporter, *channelR++ == 0);
505*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(reporter, *channelG++ == 0);
506*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(reporter, *channelB++ == 0);
507*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(reporter, *channelA++ == 0);
508*c8dee2aaSAndroid Build Coastguard Worker         }
509*c8dee2aaSAndroid Build Coastguard Worker     }
510*c8dee2aaSAndroid Build Coastguard Worker }
511*c8dee2aaSAndroid Build Coastguard Worker 
512*c8dee2aaSAndroid Build Coastguard Worker // This is the bit pattern of the "largest" signaling NaN. The next integer is a quiet NaN.
513*c8dee2aaSAndroid Build Coastguard Worker // We use this as the starting point for various memory-shuffling tests below, to ensure that our
514*c8dee2aaSAndroid Build Coastguard Worker // code doesn't interpret values as float when they might be integral. Using floats can cause
515*c8dee2aaSAndroid Build Coastguard Worker // signaling NaN values to change (becoming quiet), even with the most innocuous operations
516*c8dee2aaSAndroid Build Coastguard Worker // (particularly on 32-bit x86, where floats are often passed around in the x87 FPU).
517*c8dee2aaSAndroid Build Coastguard Worker static constexpr int kLastSignalingNaN    = 0x7fbfffff;
518*c8dee2aaSAndroid Build Coastguard Worker 
519*c8dee2aaSAndroid Build Coastguard Worker // Similarly, this is the "smallest" (in magnitude) negative signaling NaN. The next integer is
520*c8dee2aaSAndroid Build Coastguard Worker // a quiet negative NaN. Only used when testing operations that need two distinct integer sequences
521*c8dee2aaSAndroid Build Coastguard Worker // as input, and the logic is asymmetric enough that we want NaNs fed into both sides.
522*c8dee2aaSAndroid Build Coastguard Worker static constexpr int kLastSignalingNegNaN = 0xffbfffff;
523*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRasterPipeline_CopyFromIndirectUnmasked,r)524*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRasterPipeline_CopyFromIndirectUnmasked, r) {
525*c8dee2aaSAndroid Build Coastguard Worker     // Allocate space for 5 source slots, and 5 dest slots.
526*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) int src[5 * SkRasterPipeline_kMaxStride_highp];
527*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) int dst[5 * SkRasterPipeline_kMaxStride_highp];
528*c8dee2aaSAndroid Build Coastguard Worker 
529*c8dee2aaSAndroid Build Coastguard Worker     // Test with various mixes of indirect offsets.
530*c8dee2aaSAndroid Build Coastguard Worker     static_assert(SkRasterPipeline_kMaxStride_highp == 16);
531*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) const uint32_t kOffsets1[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
532*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) const uint32_t kOffsets2[16] = {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2};
533*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) const uint32_t kOffsets3[16] = {0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2};
534*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) const uint32_t kOffsets4[16] = {99, 99, 0, 0, 99, 99, 0, 0,
535*c8dee2aaSAndroid Build Coastguard Worker                                                 99, 99, 0, 0, 99, 99, 0, 0};
536*c8dee2aaSAndroid Build Coastguard Worker 
537*c8dee2aaSAndroid Build Coastguard Worker     const int N = SkOpts::raster_pipeline_highp_stride;
538*c8dee2aaSAndroid Build Coastguard Worker 
539*c8dee2aaSAndroid Build Coastguard Worker     for (const uint32_t* offsets : {kOffsets1, kOffsets2, kOffsets3, kOffsets4}) {
540*c8dee2aaSAndroid Build Coastguard Worker         for (int copySize = 1; copySize <= 5; ++copySize) {
541*c8dee2aaSAndroid Build Coastguard Worker             // Initialize the destination slots to 0,1,2.. and the source slots to various NaNs
542*c8dee2aaSAndroid Build Coastguard Worker             std::iota(&dst[0], &dst[5 * N], 0);
543*c8dee2aaSAndroid Build Coastguard Worker             std::iota(&src[0], &src[5 * N], kLastSignalingNaN);
544*c8dee2aaSAndroid Build Coastguard Worker 
545*c8dee2aaSAndroid Build Coastguard Worker             // Run `copy_from_indirect_unmasked` over our data.
546*c8dee2aaSAndroid Build Coastguard Worker             SkArenaAlloc alloc(/*firstHeapAllocation=*/256);
547*c8dee2aaSAndroid Build Coastguard Worker             SkRasterPipeline p(&alloc);
548*c8dee2aaSAndroid Build Coastguard Worker             auto* ctx = alloc.make<SkRasterPipeline_CopyIndirectCtx>();
549*c8dee2aaSAndroid Build Coastguard Worker             ctx->dst = &dst[0];
550*c8dee2aaSAndroid Build Coastguard Worker             ctx->src = &src[0];
551*c8dee2aaSAndroid Build Coastguard Worker             ctx->indirectOffset = offsets;
552*c8dee2aaSAndroid Build Coastguard Worker             ctx->indirectLimit = 5 - copySize;
553*c8dee2aaSAndroid Build Coastguard Worker             ctx->slots = copySize;
554*c8dee2aaSAndroid Build Coastguard Worker 
555*c8dee2aaSAndroid Build Coastguard Worker             p.append(SkRasterPipelineOp::copy_from_indirect_unmasked, ctx);
556*c8dee2aaSAndroid Build Coastguard Worker             p.run(0,0,N,1);
557*c8dee2aaSAndroid Build Coastguard Worker 
558*c8dee2aaSAndroid Build Coastguard Worker             // If the offset plus copy-size would overflow the source data, the results don't
559*c8dee2aaSAndroid Build Coastguard Worker             // matter; indexing off the end of the buffer is UB, and we don't make any promises
560*c8dee2aaSAndroid Build Coastguard Worker             // about the values you get. If we didn't crash, that's success. (In practice, we
561*c8dee2aaSAndroid Build Coastguard Worker             // will have clamped the source pointer so that we don't read past the end.)
562*c8dee2aaSAndroid Build Coastguard Worker             int maxOffset = *std::max_element(offsets, offsets + N);
563*c8dee2aaSAndroid Build Coastguard Worker             if (copySize + maxOffset > 5) {
564*c8dee2aaSAndroid Build Coastguard Worker                 continue;
565*c8dee2aaSAndroid Build Coastguard Worker             }
566*c8dee2aaSAndroid Build Coastguard Worker 
567*c8dee2aaSAndroid Build Coastguard Worker             // Verify that the destination has been overwritten in the mask-on fields, and has
568*c8dee2aaSAndroid Build Coastguard Worker             // not been overwritten in the mask-off fields, for each destination slot.
569*c8dee2aaSAndroid Build Coastguard Worker             int expectedUnchanged = 0;
570*c8dee2aaSAndroid Build Coastguard Worker             int expectedFromZero = src[0 * N], expectedFromTwo = src[2 * N];
571*c8dee2aaSAndroid Build Coastguard Worker             int* destPtr = dst;
572*c8dee2aaSAndroid Build Coastguard Worker             for (int checkSlot = 0; checkSlot < 5; ++checkSlot) {
573*c8dee2aaSAndroid Build Coastguard Worker                 for (int checkLane = 0; checkLane < N; ++checkLane) {
574*c8dee2aaSAndroid Build Coastguard Worker                     if (checkSlot < copySize) {
575*c8dee2aaSAndroid Build Coastguard Worker                         if (offsets[checkLane] == 0) {
576*c8dee2aaSAndroid Build Coastguard Worker                             REPORTER_ASSERT(r, *destPtr == expectedFromZero);
577*c8dee2aaSAndroid Build Coastguard Worker                         } else if (offsets[checkLane] == 2) {
578*c8dee2aaSAndroid Build Coastguard Worker                             REPORTER_ASSERT(r, *destPtr == expectedFromTwo);
579*c8dee2aaSAndroid Build Coastguard Worker                         } else {
580*c8dee2aaSAndroid Build Coastguard Worker                             ERRORF(r, "unexpected offset value");
581*c8dee2aaSAndroid Build Coastguard Worker                         }
582*c8dee2aaSAndroid Build Coastguard Worker                     } else {
583*c8dee2aaSAndroid Build Coastguard Worker                         REPORTER_ASSERT(r, *destPtr == expectedUnchanged);
584*c8dee2aaSAndroid Build Coastguard Worker                     }
585*c8dee2aaSAndroid Build Coastguard Worker 
586*c8dee2aaSAndroid Build Coastguard Worker                     ++destPtr;
587*c8dee2aaSAndroid Build Coastguard Worker                     expectedUnchanged += 1;
588*c8dee2aaSAndroid Build Coastguard Worker                     expectedFromZero += 1;
589*c8dee2aaSAndroid Build Coastguard Worker                     expectedFromTwo += 1;
590*c8dee2aaSAndroid Build Coastguard Worker                 }
591*c8dee2aaSAndroid Build Coastguard Worker             }
592*c8dee2aaSAndroid Build Coastguard Worker         }
593*c8dee2aaSAndroid Build Coastguard Worker     }
594*c8dee2aaSAndroid Build Coastguard Worker }
595*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRasterPipeline_CopyFromIndirectUniformUnmasked,r)596*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRasterPipeline_CopyFromIndirectUniformUnmasked, r) {
597*c8dee2aaSAndroid Build Coastguard Worker     // Allocate space for 5 source uniform values, and 5 dest slots.
598*c8dee2aaSAndroid Build Coastguard Worker     // (Note that unlike slots, uniforms don't use multiple lanes per value.)
599*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) int src[5];
600*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) int dst[5 * SkRasterPipeline_kMaxStride_highp];
601*c8dee2aaSAndroid Build Coastguard Worker 
602*c8dee2aaSAndroid Build Coastguard Worker     // Test with various mixes of indirect offsets.
603*c8dee2aaSAndroid Build Coastguard Worker     static_assert(SkRasterPipeline_kMaxStride_highp == 16);
604*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) const uint32_t kOffsets1[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
605*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) const uint32_t kOffsets2[16] = {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2};
606*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) const uint32_t kOffsets3[16] = {0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2};
607*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) const uint32_t kOffsets4[16] = {99, ~99u, 0, 0, ~99u, 99, 0, 0,
608*c8dee2aaSAndroid Build Coastguard Worker                                                 99, ~99u, 0, 0, ~99u, 99, 0, 0};
609*c8dee2aaSAndroid Build Coastguard Worker 
610*c8dee2aaSAndroid Build Coastguard Worker     const int N = SkOpts::raster_pipeline_highp_stride;
611*c8dee2aaSAndroid Build Coastguard Worker 
612*c8dee2aaSAndroid Build Coastguard Worker     for (const uint32_t* offsets : {kOffsets1, kOffsets2, kOffsets3, kOffsets4}) {
613*c8dee2aaSAndroid Build Coastguard Worker         for (int copySize = 1; copySize <= 5; ++copySize) {
614*c8dee2aaSAndroid Build Coastguard Worker             // Initialize the destination slots to 0,1,2.. and the source uniforms to various NaNs
615*c8dee2aaSAndroid Build Coastguard Worker             std::iota(&dst[0], &dst[5 * N], 0);
616*c8dee2aaSAndroid Build Coastguard Worker             std::iota(&src[0], &src[5], kLastSignalingNaN);
617*c8dee2aaSAndroid Build Coastguard Worker 
618*c8dee2aaSAndroid Build Coastguard Worker             // Run `copy_from_indirect_unmasked` over our data.
619*c8dee2aaSAndroid Build Coastguard Worker             SkArenaAlloc alloc(/*firstHeapAllocation=*/256);
620*c8dee2aaSAndroid Build Coastguard Worker             SkRasterPipeline p(&alloc);
621*c8dee2aaSAndroid Build Coastguard Worker             auto* ctx = alloc.make<SkRasterPipeline_CopyIndirectCtx>();
622*c8dee2aaSAndroid Build Coastguard Worker             ctx->dst = &dst[0];
623*c8dee2aaSAndroid Build Coastguard Worker             ctx->src = &src[0];
624*c8dee2aaSAndroid Build Coastguard Worker             ctx->indirectOffset = offsets;
625*c8dee2aaSAndroid Build Coastguard Worker             ctx->indirectLimit = 5 - copySize;
626*c8dee2aaSAndroid Build Coastguard Worker             ctx->slots = copySize;
627*c8dee2aaSAndroid Build Coastguard Worker 
628*c8dee2aaSAndroid Build Coastguard Worker             p.append(SkRasterPipelineOp::copy_from_indirect_uniform_unmasked, ctx);
629*c8dee2aaSAndroid Build Coastguard Worker             p.run(0,0,N,1);
630*c8dee2aaSAndroid Build Coastguard Worker 
631*c8dee2aaSAndroid Build Coastguard Worker             // If the offset plus copy-size would overflow the source data, the results don't
632*c8dee2aaSAndroid Build Coastguard Worker             // matter; indexing off the end of the buffer is UB, and we don't make any promises
633*c8dee2aaSAndroid Build Coastguard Worker             // about the values you get. If we didn't crash, that's success. (In practice, we
634*c8dee2aaSAndroid Build Coastguard Worker             // will have clamped the source pointer so that we don't read past the end.)
635*c8dee2aaSAndroid Build Coastguard Worker             uint32_t maxOffset = *std::max_element(offsets, offsets + N);
636*c8dee2aaSAndroid Build Coastguard Worker             if (copySize + maxOffset > 5) {
637*c8dee2aaSAndroid Build Coastguard Worker                 continue;
638*c8dee2aaSAndroid Build Coastguard Worker             }
639*c8dee2aaSAndroid Build Coastguard Worker 
640*c8dee2aaSAndroid Build Coastguard Worker             // Verify that the destination has been overwritten in each slot.
641*c8dee2aaSAndroid Build Coastguard Worker             int expectedUnchanged = 0;
642*c8dee2aaSAndroid Build Coastguard Worker             int expectedFromZero = src[0], expectedFromTwo = src[2];
643*c8dee2aaSAndroid Build Coastguard Worker             int* destPtr = dst;
644*c8dee2aaSAndroid Build Coastguard Worker             for (int checkSlot = 0; checkSlot < 5; ++checkSlot) {
645*c8dee2aaSAndroid Build Coastguard Worker                 for (int checkLane = 0; checkLane < N; ++checkLane) {
646*c8dee2aaSAndroid Build Coastguard Worker                     if (checkSlot < copySize) {
647*c8dee2aaSAndroid Build Coastguard Worker                         if (offsets[checkLane] == 0) {
648*c8dee2aaSAndroid Build Coastguard Worker                             REPORTER_ASSERT(r, *destPtr == expectedFromZero);
649*c8dee2aaSAndroid Build Coastguard Worker                         } else if (offsets[checkLane] == 2) {
650*c8dee2aaSAndroid Build Coastguard Worker                             REPORTER_ASSERT(r, *destPtr == expectedFromTwo);
651*c8dee2aaSAndroid Build Coastguard Worker                         } else {
652*c8dee2aaSAndroid Build Coastguard Worker                             ERRORF(r, "unexpected offset value");
653*c8dee2aaSAndroid Build Coastguard Worker                         }
654*c8dee2aaSAndroid Build Coastguard Worker                     } else {
655*c8dee2aaSAndroid Build Coastguard Worker                         REPORTER_ASSERT(r, *destPtr == expectedUnchanged);
656*c8dee2aaSAndroid Build Coastguard Worker                     }
657*c8dee2aaSAndroid Build Coastguard Worker 
658*c8dee2aaSAndroid Build Coastguard Worker                     ++destPtr;
659*c8dee2aaSAndroid Build Coastguard Worker                     expectedUnchanged += 1;
660*c8dee2aaSAndroid Build Coastguard Worker                 }
661*c8dee2aaSAndroid Build Coastguard Worker                 expectedFromZero += 1;
662*c8dee2aaSAndroid Build Coastguard Worker                 expectedFromTwo += 1;
663*c8dee2aaSAndroid Build Coastguard Worker             }
664*c8dee2aaSAndroid Build Coastguard Worker         }
665*c8dee2aaSAndroid Build Coastguard Worker     }
666*c8dee2aaSAndroid Build Coastguard Worker }
667*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRasterPipeline_CopyToIndirectMasked,r)668*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRasterPipeline_CopyToIndirectMasked, r) {
669*c8dee2aaSAndroid Build Coastguard Worker     // Allocate space for 5 source slots, and 5 dest slots.
670*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) int src[5 * SkRasterPipeline_kMaxStride_highp];
671*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) int dst[5 * SkRasterPipeline_kMaxStride_highp];
672*c8dee2aaSAndroid Build Coastguard Worker 
673*c8dee2aaSAndroid Build Coastguard Worker     // Test with various mixes of indirect offsets.
674*c8dee2aaSAndroid Build Coastguard Worker     static_assert(SkRasterPipeline_kMaxStride_highp == 16);
675*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) const uint32_t kOffsets1[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
676*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) const uint32_t kOffsets2[16] = {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2};
677*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) const uint32_t kOffsets3[16] = {0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2};
678*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) const uint32_t kOffsets4[16] = {99, ~99u, 0, 0, ~99u, 99, 0, 0,
679*c8dee2aaSAndroid Build Coastguard Worker                                                 99, ~99u, 0, 0, ~99u, 99, 0, 0};
680*c8dee2aaSAndroid Build Coastguard Worker 
681*c8dee2aaSAndroid Build Coastguard Worker     // Test with various masks.
682*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) const int32_t kMask1[16]  = {~0, ~0, ~0, ~0, ~0,  0, ~0, ~0,
683*c8dee2aaSAndroid Build Coastguard Worker                                              ~0, ~0, ~0, ~0, ~0,  0, ~0, ~0};
684*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) const int32_t kMask2[16]  = {~0,  0, ~0, ~0,  0,  0,  0, ~0,
685*c8dee2aaSAndroid Build Coastguard Worker                                              ~0,  0, ~0, ~0,  0,  0,  0, ~0};
686*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) const int32_t kMask3[16]  = {~0, ~0,  0, ~0,  0,  0, ~0, ~0,
687*c8dee2aaSAndroid Build Coastguard Worker                                              ~0, ~0,  0, ~0,  0,  0, ~0, ~0};
688*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) const int32_t kMask4[16]  = { 0,  0,  0,  0,  0,  0,  0,  0,
689*c8dee2aaSAndroid Build Coastguard Worker                                               0,  0,  0,  0,  0,  0,  0,  0};
690*c8dee2aaSAndroid Build Coastguard Worker 
691*c8dee2aaSAndroid Build Coastguard Worker     const int N = SkOpts::raster_pipeline_highp_stride;
692*c8dee2aaSAndroid Build Coastguard Worker 
693*c8dee2aaSAndroid Build Coastguard Worker     for (const int32_t* mask : {kMask1, kMask2, kMask3, kMask4}) {
694*c8dee2aaSAndroid Build Coastguard Worker         for (const uint32_t* offsets : {kOffsets1, kOffsets2, kOffsets3, kOffsets4}) {
695*c8dee2aaSAndroid Build Coastguard Worker             for (int copySize = 1; copySize <= 5; ++copySize) {
696*c8dee2aaSAndroid Build Coastguard Worker                 // Initialize the destination slots to 0,1,2.. and the source slots to various NaNs
697*c8dee2aaSAndroid Build Coastguard Worker                 std::iota(&dst[0], &dst[5 * N], 0);
698*c8dee2aaSAndroid Build Coastguard Worker                 std::iota(&src[0], &src[5 * N], kLastSignalingNaN);
699*c8dee2aaSAndroid Build Coastguard Worker 
700*c8dee2aaSAndroid Build Coastguard Worker                 // Run `copy_to_indirect_masked` over our data.
701*c8dee2aaSAndroid Build Coastguard Worker                 SkArenaAlloc alloc(/*firstHeapAllocation=*/256);
702*c8dee2aaSAndroid Build Coastguard Worker                 SkRasterPipeline p(&alloc);
703*c8dee2aaSAndroid Build Coastguard Worker                 auto* ctx = alloc.make<SkRasterPipeline_CopyIndirectCtx>();
704*c8dee2aaSAndroid Build Coastguard Worker                 ctx->dst = &dst[0];
705*c8dee2aaSAndroid Build Coastguard Worker                 ctx->src = &src[0];
706*c8dee2aaSAndroid Build Coastguard Worker                 ctx->indirectOffset = offsets;
707*c8dee2aaSAndroid Build Coastguard Worker                 ctx->indirectLimit = 5 - copySize;
708*c8dee2aaSAndroid Build Coastguard Worker                 ctx->slots = copySize;
709*c8dee2aaSAndroid Build Coastguard Worker 
710*c8dee2aaSAndroid Build Coastguard Worker                 SkRasterPipeline_InitLaneMasksCtx initLaneMasksCtx;
711*c8dee2aaSAndroid Build Coastguard Worker                 p.append(SkRasterPipelineOp::init_lane_masks, &initLaneMasksCtx);
712*c8dee2aaSAndroid Build Coastguard Worker                 p.append(SkRasterPipelineOp::load_condition_mask, mask);
713*c8dee2aaSAndroid Build Coastguard Worker                 p.append(SkRasterPipelineOp::copy_to_indirect_masked, ctx);
714*c8dee2aaSAndroid Build Coastguard Worker                 p.run(0,0,N,1);
715*c8dee2aaSAndroid Build Coastguard Worker 
716*c8dee2aaSAndroid Build Coastguard Worker                 // If the offset plus copy-size would overflow the destination, the results don't
717*c8dee2aaSAndroid Build Coastguard Worker                 // matter; indexing off the end of the buffer is UB, and we don't make any promises
718*c8dee2aaSAndroid Build Coastguard Worker                 // about the values you get. If we didn't crash, that's success. (In practice, we
719*c8dee2aaSAndroid Build Coastguard Worker                 // will have clamped the destination pointer so that we don't read past the end.)
720*c8dee2aaSAndroid Build Coastguard Worker                 uint32_t maxOffset = *std::max_element(offsets, offsets + N);
721*c8dee2aaSAndroid Build Coastguard Worker                 if (copySize + maxOffset > 5) {
722*c8dee2aaSAndroid Build Coastguard Worker                     continue;
723*c8dee2aaSAndroid Build Coastguard Worker                 }
724*c8dee2aaSAndroid Build Coastguard Worker 
725*c8dee2aaSAndroid Build Coastguard Worker                 // Verify that the destination has been overwritten in the mask-on fields, and has
726*c8dee2aaSAndroid Build Coastguard Worker                 // not been overwritten in the mask-off fields, for each destination slot.
727*c8dee2aaSAndroid Build Coastguard Worker                 int expectedUnchanged = 0;
728*c8dee2aaSAndroid Build Coastguard Worker                 int expectedFromZero = src[0], expectedFromTwo = src[0] - (2 * N);
729*c8dee2aaSAndroid Build Coastguard Worker                 int* destPtr = dst;
730*c8dee2aaSAndroid Build Coastguard Worker                 int pos = 0;
731*c8dee2aaSAndroid Build Coastguard Worker                 for (int checkSlot = 0; checkSlot < 5; ++checkSlot) {
732*c8dee2aaSAndroid Build Coastguard Worker                     for (int checkLane = 0; checkLane < N; ++checkLane) {
733*c8dee2aaSAndroid Build Coastguard Worker                         int rangeStart = offsets[checkLane] * N;
734*c8dee2aaSAndroid Build Coastguard Worker                         int rangeEnd   = (offsets[checkLane] + copySize) * N;
735*c8dee2aaSAndroid Build Coastguard Worker                         if (mask[checkLane] && pos >= rangeStart && pos < rangeEnd) {
736*c8dee2aaSAndroid Build Coastguard Worker                             if (offsets[checkLane] == 0) {
737*c8dee2aaSAndroid Build Coastguard Worker                                 REPORTER_ASSERT(r, *destPtr == expectedFromZero);
738*c8dee2aaSAndroid Build Coastguard Worker                             } else if (offsets[checkLane] == 2) {
739*c8dee2aaSAndroid Build Coastguard Worker                                 REPORTER_ASSERT(r, *destPtr == expectedFromTwo);
740*c8dee2aaSAndroid Build Coastguard Worker                             } else {
741*c8dee2aaSAndroid Build Coastguard Worker                                 ERRORF(r, "unexpected offset value");
742*c8dee2aaSAndroid Build Coastguard Worker                             }
743*c8dee2aaSAndroid Build Coastguard Worker                         } else {
744*c8dee2aaSAndroid Build Coastguard Worker                             REPORTER_ASSERT(r, *destPtr == expectedUnchanged);
745*c8dee2aaSAndroid Build Coastguard Worker                         }
746*c8dee2aaSAndroid Build Coastguard Worker 
747*c8dee2aaSAndroid Build Coastguard Worker                         ++pos;
748*c8dee2aaSAndroid Build Coastguard Worker                         ++destPtr;
749*c8dee2aaSAndroid Build Coastguard Worker                         expectedUnchanged += 1;
750*c8dee2aaSAndroid Build Coastguard Worker                         expectedFromZero += 1;
751*c8dee2aaSAndroid Build Coastguard Worker                         expectedFromTwo += 1;
752*c8dee2aaSAndroid Build Coastguard Worker                     }
753*c8dee2aaSAndroid Build Coastguard Worker                 }
754*c8dee2aaSAndroid Build Coastguard Worker             }
755*c8dee2aaSAndroid Build Coastguard Worker         }
756*c8dee2aaSAndroid Build Coastguard Worker     }
757*c8dee2aaSAndroid Build Coastguard Worker }
758*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRasterPipeline_SwizzleCopyToIndirectMasked,r)759*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRasterPipeline_SwizzleCopyToIndirectMasked, r) {
760*c8dee2aaSAndroid Build Coastguard Worker     // Allocate space for 5 source slots, and 5 dest slots.
761*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) int src[5 * SkRasterPipeline_kMaxStride_highp];
762*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) int dst[5 * SkRasterPipeline_kMaxStride_highp];
763*c8dee2aaSAndroid Build Coastguard Worker 
764*c8dee2aaSAndroid Build Coastguard Worker     // Test with various mixes of indirect offsets.
765*c8dee2aaSAndroid Build Coastguard Worker     static_assert(SkRasterPipeline_kMaxStride_highp == 16);
766*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) const uint32_t kOffsets1[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
767*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) const uint32_t kOffsets2[16] = {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2};
768*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) const uint32_t kOffsets3[16] = {0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2};
769*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) const uint32_t kOffsets4[16] = {99, ~99u, 0, 0, ~99u, 99, 0, 0,
770*c8dee2aaSAndroid Build Coastguard Worker                                                 99, ~99u, 0, 0, ~99u, 99, 0, 0};
771*c8dee2aaSAndroid Build Coastguard Worker 
772*c8dee2aaSAndroid Build Coastguard Worker     // Test with various masks.
773*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) const int32_t kMask1[16]  = {~0, ~0, ~0, ~0, ~0,  0, ~0, ~0,
774*c8dee2aaSAndroid Build Coastguard Worker                                              ~0, ~0, ~0, ~0, ~0,  0, ~0, ~0};
775*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) const int32_t kMask2[16]  = {~0,  0, ~0, ~0,  0,  0,  0, ~0,
776*c8dee2aaSAndroid Build Coastguard Worker                                              ~0,  0, ~0, ~0,  0,  0,  0, ~0};
777*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) const int32_t kMask3[16]  = {~0, ~0,  0, ~0,  0,  0, ~0, ~0,
778*c8dee2aaSAndroid Build Coastguard Worker                                              ~0, ~0,  0, ~0,  0,  0, ~0, ~0};
779*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) const int32_t kMask4[16]  = { 0,  0,  0,  0,  0,  0,  0,  0,
780*c8dee2aaSAndroid Build Coastguard Worker                                               0,  0,  0,  0,  0,  0,  0,  0};
781*c8dee2aaSAndroid Build Coastguard Worker 
782*c8dee2aaSAndroid Build Coastguard Worker     // Test with various swizzle permutations.
783*c8dee2aaSAndroid Build Coastguard Worker     struct TestPattern {
784*c8dee2aaSAndroid Build Coastguard Worker         int swizzleSize;
785*c8dee2aaSAndroid Build Coastguard Worker         int swizzleUpperBound;
786*c8dee2aaSAndroid Build Coastguard Worker         uint16_t swizzle[4];
787*c8dee2aaSAndroid Build Coastguard Worker     };
788*c8dee2aaSAndroid Build Coastguard Worker 
789*c8dee2aaSAndroid Build Coastguard Worker     static const TestPattern kPatterns[] = {
790*c8dee2aaSAndroid Build Coastguard Worker         {1, 4, {3}},          // v.w    = (1)
791*c8dee2aaSAndroid Build Coastguard Worker         {2, 2, {1, 0}},       // v.yx   = (1,2)
792*c8dee2aaSAndroid Build Coastguard Worker         {3, 3, {2, 1, 0}},    // v.zyx  = (1,2,3)
793*c8dee2aaSAndroid Build Coastguard Worker         {4, 4, {3, 0, 1, 2}}, // v.wxyz = (1,2,3,4)
794*c8dee2aaSAndroid Build Coastguard Worker     };
795*c8dee2aaSAndroid Build Coastguard Worker 
796*c8dee2aaSAndroid Build Coastguard Worker     enum Result {
797*c8dee2aaSAndroid Build Coastguard Worker         kOutOfBounds = 0,
798*c8dee2aaSAndroid Build Coastguard Worker         kUnchanged = 1,
799*c8dee2aaSAndroid Build Coastguard Worker         S0 = 2,
800*c8dee2aaSAndroid Build Coastguard Worker         S1 = 3,
801*c8dee2aaSAndroid Build Coastguard Worker         S2 = 4,
802*c8dee2aaSAndroid Build Coastguard Worker         S3 = 5,
803*c8dee2aaSAndroid Build Coastguard Worker         S4 = 6,
804*c8dee2aaSAndroid Build Coastguard Worker     };
805*c8dee2aaSAndroid Build Coastguard Worker 
806*c8dee2aaSAndroid Build Coastguard Worker #define __ kUnchanged
807*c8dee2aaSAndroid Build Coastguard Worker #define XX kOutOfBounds
808*c8dee2aaSAndroid Build Coastguard Worker     static const Result kExpectationsAtZero[4][5] = {
809*c8dee2aaSAndroid Build Coastguard Worker     //  d[0].w = 1        d[0].yx = (1,2)   d[0].zyx = (1,2,3) d[0].wxyz = (1,2,3,4)
810*c8dee2aaSAndroid Build Coastguard Worker         {__,__,__,S0,__}, {S1,S0,__,__,__}, {S2,S1,S0,__,__},  {S1,S2,S3,S0,__},
811*c8dee2aaSAndroid Build Coastguard Worker     };
812*c8dee2aaSAndroid Build Coastguard Worker     static const Result kExpectationsAtTwo[4][5] = {
813*c8dee2aaSAndroid Build Coastguard Worker     //  d[2].w = 1        d[2].yx = (1,2)   d[2].zyx = (1,2,3) d[2].wxyz = (1,2,3,4)
814*c8dee2aaSAndroid Build Coastguard Worker         {XX,XX,XX,XX,XX}, {__,__,S1,S0,__}, {__,__,S2,S1,S0},  {XX,XX,XX,XX,XX},
815*c8dee2aaSAndroid Build Coastguard Worker     };
816*c8dee2aaSAndroid Build Coastguard Worker #undef __
817*c8dee2aaSAndroid Build Coastguard Worker #undef XX
818*c8dee2aaSAndroid Build Coastguard Worker 
819*c8dee2aaSAndroid Build Coastguard Worker     const int N = SkOpts::raster_pipeline_highp_stride;
820*c8dee2aaSAndroid Build Coastguard Worker 
821*c8dee2aaSAndroid Build Coastguard Worker     for (const int32_t* mask : {kMask1, kMask2, kMask3, kMask4}) {
822*c8dee2aaSAndroid Build Coastguard Worker         for (const uint32_t* offsets : {kOffsets1, kOffsets2, kOffsets3, kOffsets4}) {
823*c8dee2aaSAndroid Build Coastguard Worker             for (size_t patternIndex = 0; patternIndex < std::size(kPatterns); ++patternIndex) {
824*c8dee2aaSAndroid Build Coastguard Worker                 const TestPattern& pattern = kPatterns[patternIndex];
825*c8dee2aaSAndroid Build Coastguard Worker 
826*c8dee2aaSAndroid Build Coastguard Worker                 // Initialize the destination slots to 0,1,2.. and the source slots to various NaNs
827*c8dee2aaSAndroid Build Coastguard Worker                 std::iota(&dst[0], &dst[5 * N], 0);
828*c8dee2aaSAndroid Build Coastguard Worker                 std::iota(&src[0], &src[5 * N], kLastSignalingNaN);
829*c8dee2aaSAndroid Build Coastguard Worker 
830*c8dee2aaSAndroid Build Coastguard Worker                 // Run `swizzle_copy_to_indirect_masked` over our data.
831*c8dee2aaSAndroid Build Coastguard Worker                 SkArenaAlloc alloc(/*firstHeapAllocation=*/256);
832*c8dee2aaSAndroid Build Coastguard Worker                 SkRasterPipeline p(&alloc);
833*c8dee2aaSAndroid Build Coastguard Worker                 auto* ctx = alloc.make<SkRasterPipeline_SwizzleCopyIndirectCtx>();
834*c8dee2aaSAndroid Build Coastguard Worker                 ctx->dst = &dst[0];
835*c8dee2aaSAndroid Build Coastguard Worker                 ctx->src = &src[0];
836*c8dee2aaSAndroid Build Coastguard Worker                 ctx->indirectOffset = offsets;
837*c8dee2aaSAndroid Build Coastguard Worker                 ctx->indirectLimit = 5 - pattern.swizzleUpperBound;
838*c8dee2aaSAndroid Build Coastguard Worker                 ctx->slots = pattern.swizzleSize;
839*c8dee2aaSAndroid Build Coastguard Worker                 ctx->offsets[0] = pattern.swizzle[0] * N * sizeof(float);
840*c8dee2aaSAndroid Build Coastguard Worker                 ctx->offsets[1] = pattern.swizzle[1] * N * sizeof(float);
841*c8dee2aaSAndroid Build Coastguard Worker                 ctx->offsets[2] = pattern.swizzle[2] * N * sizeof(float);
842*c8dee2aaSAndroid Build Coastguard Worker                 ctx->offsets[3] = pattern.swizzle[3] * N * sizeof(float);
843*c8dee2aaSAndroid Build Coastguard Worker 
844*c8dee2aaSAndroid Build Coastguard Worker                 SkRasterPipeline_InitLaneMasksCtx initLaneMasksCtx;
845*c8dee2aaSAndroid Build Coastguard Worker                 p.append(SkRasterPipelineOp::init_lane_masks, &initLaneMasksCtx);
846*c8dee2aaSAndroid Build Coastguard Worker                 p.append(SkRasterPipelineOp::load_condition_mask, mask);
847*c8dee2aaSAndroid Build Coastguard Worker                 p.append(SkRasterPipelineOp::swizzle_copy_to_indirect_masked, ctx);
848*c8dee2aaSAndroid Build Coastguard Worker                 p.run(0,0,N,1);
849*c8dee2aaSAndroid Build Coastguard Worker 
850*c8dee2aaSAndroid Build Coastguard Worker                 // If the offset plus copy-size would overflow the destination, the results don't
851*c8dee2aaSAndroid Build Coastguard Worker                 // matter; indexing off the end of the buffer is UB, and we don't make any promises
852*c8dee2aaSAndroid Build Coastguard Worker                 // about the values you get. If we didn't crash, that's success. (In practice, we
853*c8dee2aaSAndroid Build Coastguard Worker                 // will have clamped the destination pointer so that we don't read past the end.)
854*c8dee2aaSAndroid Build Coastguard Worker                 uint32_t maxOffset = *std::max_element(offsets, offsets + N);
855*c8dee2aaSAndroid Build Coastguard Worker                 if (pattern.swizzleUpperBound + maxOffset > 5) {
856*c8dee2aaSAndroid Build Coastguard Worker                     continue;
857*c8dee2aaSAndroid Build Coastguard Worker                 }
858*c8dee2aaSAndroid Build Coastguard Worker 
859*c8dee2aaSAndroid Build Coastguard Worker                 // Verify that the destination has been overwritten in the mask-on fields, and has
860*c8dee2aaSAndroid Build Coastguard Worker                 // not been overwritten in the mask-off fields, for each destination slot.
861*c8dee2aaSAndroid Build Coastguard Worker                 int expectedUnchanged = 0;
862*c8dee2aaSAndroid Build Coastguard Worker                 int* destPtr = dst;
863*c8dee2aaSAndroid Build Coastguard Worker                 for (int checkSlot = 0; checkSlot < 5; ++checkSlot) {
864*c8dee2aaSAndroid Build Coastguard Worker                     for (int checkLane = 0; checkLane < N; ++checkLane) {
865*c8dee2aaSAndroid Build Coastguard Worker                         Result expectedType = kUnchanged;
866*c8dee2aaSAndroid Build Coastguard Worker                         if (offsets[checkLane] == 0) {
867*c8dee2aaSAndroid Build Coastguard Worker                             expectedType = kExpectationsAtZero[patternIndex][checkSlot];
868*c8dee2aaSAndroid Build Coastguard Worker                         } else if (offsets[checkLane] == 2) {
869*c8dee2aaSAndroid Build Coastguard Worker                             expectedType = kExpectationsAtTwo[patternIndex][checkSlot];
870*c8dee2aaSAndroid Build Coastguard Worker                         }
871*c8dee2aaSAndroid Build Coastguard Worker                         if (!mask[checkLane]) {
872*c8dee2aaSAndroid Build Coastguard Worker                             expectedType = kUnchanged;
873*c8dee2aaSAndroid Build Coastguard Worker                         }
874*c8dee2aaSAndroid Build Coastguard Worker                         switch (expectedType) {
875*c8dee2aaSAndroid Build Coastguard Worker                             case kOutOfBounds: // out of bounds; ignore result
876*c8dee2aaSAndroid Build Coastguard Worker                                 break;
877*c8dee2aaSAndroid Build Coastguard Worker                             case kUnchanged:
878*c8dee2aaSAndroid Build Coastguard Worker                                 REPORTER_ASSERT(r, *destPtr == expectedUnchanged);
879*c8dee2aaSAndroid Build Coastguard Worker                                 break;
880*c8dee2aaSAndroid Build Coastguard Worker                             case S0: // destination should match source 0
881*c8dee2aaSAndroid Build Coastguard Worker                                 REPORTER_ASSERT(r, *destPtr == src[0*N + checkLane]);
882*c8dee2aaSAndroid Build Coastguard Worker                                 break;
883*c8dee2aaSAndroid Build Coastguard Worker                             case S1: // destination should match source 1
884*c8dee2aaSAndroid Build Coastguard Worker                                 REPORTER_ASSERT(r, *destPtr == src[1*N + checkLane]);
885*c8dee2aaSAndroid Build Coastguard Worker                                 break;
886*c8dee2aaSAndroid Build Coastguard Worker                             case S2: // destination should match source 2
887*c8dee2aaSAndroid Build Coastguard Worker                                 REPORTER_ASSERT(r, *destPtr == src[2*N + checkLane]);
888*c8dee2aaSAndroid Build Coastguard Worker                                 break;
889*c8dee2aaSAndroid Build Coastguard Worker                             case S3: // destination should match source 3
890*c8dee2aaSAndroid Build Coastguard Worker                                 REPORTER_ASSERT(r, *destPtr == src[3*N + checkLane]);
891*c8dee2aaSAndroid Build Coastguard Worker                                 break;
892*c8dee2aaSAndroid Build Coastguard Worker                             case S4: // destination should match source 4
893*c8dee2aaSAndroid Build Coastguard Worker                                 REPORTER_ASSERT(r, *destPtr == src[4*N + checkLane]);
894*c8dee2aaSAndroid Build Coastguard Worker                                 break;
895*c8dee2aaSAndroid Build Coastguard Worker                         }
896*c8dee2aaSAndroid Build Coastguard Worker 
897*c8dee2aaSAndroid Build Coastguard Worker                         ++destPtr;
898*c8dee2aaSAndroid Build Coastguard Worker                         expectedUnchanged += 1;
899*c8dee2aaSAndroid Build Coastguard Worker                     }
900*c8dee2aaSAndroid Build Coastguard Worker                 }
901*c8dee2aaSAndroid Build Coastguard Worker             }
902*c8dee2aaSAndroid Build Coastguard Worker         }
903*c8dee2aaSAndroid Build Coastguard Worker     }
904*c8dee2aaSAndroid Build Coastguard Worker }
905*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRasterPipeline_TraceVar,r)906*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRasterPipeline_TraceVar, r) {
907*c8dee2aaSAndroid Build Coastguard Worker     const int N = SkOpts::raster_pipeline_highp_stride;
908*c8dee2aaSAndroid Build Coastguard Worker 
909*c8dee2aaSAndroid Build Coastguard Worker     class TestTraceHook : public SkSL::TraceHook {
910*c8dee2aaSAndroid Build Coastguard Worker     public:
911*c8dee2aaSAndroid Build Coastguard Worker         void line(int) override                  { fBuffer.push_back(-9999999); }
912*c8dee2aaSAndroid Build Coastguard Worker         void enter(int) override                 { fBuffer.push_back(-9999999); }
913*c8dee2aaSAndroid Build Coastguard Worker         void exit(int) override                  { fBuffer.push_back(-9999999); }
914*c8dee2aaSAndroid Build Coastguard Worker         void scope(int) override                 { fBuffer.push_back(-9999999); }
915*c8dee2aaSAndroid Build Coastguard Worker         void var(int slot, int32_t val) override {
916*c8dee2aaSAndroid Build Coastguard Worker             fBuffer.push_back(slot);
917*c8dee2aaSAndroid Build Coastguard Worker             fBuffer.push_back(val);
918*c8dee2aaSAndroid Build Coastguard Worker         }
919*c8dee2aaSAndroid Build Coastguard Worker 
920*c8dee2aaSAndroid Build Coastguard Worker         TArray<int> fBuffer;
921*c8dee2aaSAndroid Build Coastguard Worker     };
922*c8dee2aaSAndroid Build Coastguard Worker 
923*c8dee2aaSAndroid Build Coastguard Worker     static_assert(SkRasterPipeline_kMaxStride_highp == 16);
924*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) static constexpr int32_t  kMaskOn   [16] = {~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0,
925*c8dee2aaSAndroid Build Coastguard Worker                                                             ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0};
926*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) static constexpr int32_t  kMaskOff  [16] = { 0,  0,  0,  0,  0,  0,  0,  0,
927*c8dee2aaSAndroid Build Coastguard Worker                                                              0,  0,  0,  0,  0,  0,  0,  0};
928*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) static constexpr uint32_t kIndirect0[16] = { 0,  0,  0,  0,  0,  0,  0,  0,
929*c8dee2aaSAndroid Build Coastguard Worker                                                              0,  0,  0,  0,  0,  0,  0,  0};
930*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) static constexpr uint32_t kIndirect1[16] = { 1,  1,  1,  1,  1,  1,  1,  1,
931*c8dee2aaSAndroid Build Coastguard Worker                                                              1,  1,  1,  1,  1,  1,  1,  1};
932*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) int32_t kData333[16];
933*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) int32_t kData555[16];
934*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) int32_t kData666[16];
935*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) int32_t kData777[32];
936*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) int32_t kData999[32];
937*c8dee2aaSAndroid Build Coastguard Worker     std::fill(kData333,     kData333 + N,   333);
938*c8dee2aaSAndroid Build Coastguard Worker     std::fill(kData555,     kData555 + N,   555);
939*c8dee2aaSAndroid Build Coastguard Worker     std::fill(kData666,     kData666 + N,   666);
940*c8dee2aaSAndroid Build Coastguard Worker     std::fill(kData777,     kData777 + N,   777);
941*c8dee2aaSAndroid Build Coastguard Worker     std::fill(kData777 + N, kData777 + 2*N, 707);
942*c8dee2aaSAndroid Build Coastguard Worker     std::fill(kData999,     kData999 + N,   999);
943*c8dee2aaSAndroid Build Coastguard Worker     std::fill(kData999 + N, kData999 + 2*N, 909);
944*c8dee2aaSAndroid Build Coastguard Worker 
945*c8dee2aaSAndroid Build Coastguard Worker     TestTraceHook trace;
946*c8dee2aaSAndroid Build Coastguard Worker     SkArenaAlloc alloc(/*firstHeapAllocation=*/256);
947*c8dee2aaSAndroid Build Coastguard Worker     SkRasterPipeline p(&alloc);
948*c8dee2aaSAndroid Build Coastguard Worker     SkRasterPipeline_InitLaneMasksCtx initLaneMasksCtx;
949*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::init_lane_masks, &initLaneMasksCtx);
950*c8dee2aaSAndroid Build Coastguard Worker     const SkRasterPipeline_TraceVarCtx kTraceVar1 = {/*traceMask=*/kMaskOff,
951*c8dee2aaSAndroid Build Coastguard Worker                                                      &trace, 2, 1, kData333,
952*c8dee2aaSAndroid Build Coastguard Worker                                                      /*indirectOffset=*/nullptr,
953*c8dee2aaSAndroid Build Coastguard Worker                                                      /*indirectLimit=*/0};
954*c8dee2aaSAndroid Build Coastguard Worker     const SkRasterPipeline_TraceVarCtx kTraceVar2 = {/*traceMask=*/kMaskOn,
955*c8dee2aaSAndroid Build Coastguard Worker                                                      &trace, 4, 1, kData555,
956*c8dee2aaSAndroid Build Coastguard Worker                                                      /*indirectOffset=*/nullptr,
957*c8dee2aaSAndroid Build Coastguard Worker                                                      /*indirectLimit=*/0};
958*c8dee2aaSAndroid Build Coastguard Worker     const SkRasterPipeline_TraceVarCtx kTraceVar3 = {/*traceMask=*/kMaskOff,
959*c8dee2aaSAndroid Build Coastguard Worker                                                      &trace, 5, 1, kData666,
960*c8dee2aaSAndroid Build Coastguard Worker                                                      /*indirectOffset=*/nullptr,
961*c8dee2aaSAndroid Build Coastguard Worker                                                      /*indirectLimit=*/0};
962*c8dee2aaSAndroid Build Coastguard Worker     const SkRasterPipeline_TraceVarCtx kTraceVar4 = {/*traceMask=*/kMaskOn,
963*c8dee2aaSAndroid Build Coastguard Worker                                                      &trace, 6, 2, kData777,
964*c8dee2aaSAndroid Build Coastguard Worker                                                      /*indirectOffset=*/nullptr,
965*c8dee2aaSAndroid Build Coastguard Worker                                                      /*indirectLimit=*/0};
966*c8dee2aaSAndroid Build Coastguard Worker     const SkRasterPipeline_TraceVarCtx kTraceVar5 = {/*traceMask=*/kMaskOn,
967*c8dee2aaSAndroid Build Coastguard Worker                                                      &trace, 8, 2, kData999,
968*c8dee2aaSAndroid Build Coastguard Worker                                                      /*indirectOffset=*/nullptr,
969*c8dee2aaSAndroid Build Coastguard Worker                                                      /*indirectLimit=*/0};
970*c8dee2aaSAndroid Build Coastguard Worker     const SkRasterPipeline_TraceVarCtx kTraceVar6 = {/*traceMask=*/kMaskOn,
971*c8dee2aaSAndroid Build Coastguard Worker                                                      &trace, 9, 1, kData999,
972*c8dee2aaSAndroid Build Coastguard Worker                                                      /*indirectOffset=*/kIndirect0,
973*c8dee2aaSAndroid Build Coastguard Worker                                                      /*indirectLimit=*/1};
974*c8dee2aaSAndroid Build Coastguard Worker     const SkRasterPipeline_TraceVarCtx kTraceVar7 = {/*traceMask=*/kMaskOn,
975*c8dee2aaSAndroid Build Coastguard Worker                                                      &trace, 9, 1, kData999,
976*c8dee2aaSAndroid Build Coastguard Worker                                                      /*indirectOffset=*/kIndirect1,
977*c8dee2aaSAndroid Build Coastguard Worker                                                      /*indirectLimit=*/1};
978*c8dee2aaSAndroid Build Coastguard Worker 
979*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::load_condition_mask, kMaskOn);
980*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::trace_var, &kTraceVar1);
981*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::load_condition_mask, kMaskOn);
982*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::trace_var, &kTraceVar2);
983*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::load_condition_mask, kMaskOff);
984*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::trace_var, &kTraceVar3);
985*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::load_condition_mask, kMaskOn);
986*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::trace_var, &kTraceVar4);
987*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::load_condition_mask, kMaskOff);
988*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::trace_var, &kTraceVar5);
989*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::load_condition_mask, kMaskOn);
990*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::trace_var, &kTraceVar6);
991*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::load_condition_mask, kMaskOn);
992*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::trace_var, &kTraceVar7);
993*c8dee2aaSAndroid Build Coastguard Worker     p.run(0,0,N,1);
994*c8dee2aaSAndroid Build Coastguard Worker 
995*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, (trace.fBuffer == TArray<int>{4, 555, 6, 777, 7, 707, 9, 999, 10, 909}));
996*c8dee2aaSAndroid Build Coastguard Worker }
997*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRasterPipeline_TraceLine,r)998*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRasterPipeline_TraceLine, r) {
999*c8dee2aaSAndroid Build Coastguard Worker     const int N = SkOpts::raster_pipeline_highp_stride;
1000*c8dee2aaSAndroid Build Coastguard Worker 
1001*c8dee2aaSAndroid Build Coastguard Worker     class TestTraceHook : public SkSL::TraceHook {
1002*c8dee2aaSAndroid Build Coastguard Worker     public:
1003*c8dee2aaSAndroid Build Coastguard Worker         void var(int, int32_t) override { fBuffer.push_back(-9999999); }
1004*c8dee2aaSAndroid Build Coastguard Worker         void enter(int) override        { fBuffer.push_back(-9999999); }
1005*c8dee2aaSAndroid Build Coastguard Worker         void exit(int) override         { fBuffer.push_back(-9999999); }
1006*c8dee2aaSAndroid Build Coastguard Worker         void scope(int) override        { fBuffer.push_back(-9999999); }
1007*c8dee2aaSAndroid Build Coastguard Worker         void line(int lineNum) override { fBuffer.push_back(lineNum); }
1008*c8dee2aaSAndroid Build Coastguard Worker 
1009*c8dee2aaSAndroid Build Coastguard Worker         TArray<int> fBuffer;
1010*c8dee2aaSAndroid Build Coastguard Worker     };
1011*c8dee2aaSAndroid Build Coastguard Worker 
1012*c8dee2aaSAndroid Build Coastguard Worker     static_assert(SkRasterPipeline_kMaxStride_highp == 16);
1013*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) static constexpr int32_t kMaskOn [16] = {~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0,
1014*c8dee2aaSAndroid Build Coastguard Worker                                                          ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0};
1015*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) static constexpr int32_t kMaskOff[16] = { 0,  0,  0,  0,  0,  0,  0,  0,
1016*c8dee2aaSAndroid Build Coastguard Worker                                                           0,  0,  0,  0,  0,  0,  0,  0};
1017*c8dee2aaSAndroid Build Coastguard Worker 
1018*c8dee2aaSAndroid Build Coastguard Worker     TestTraceHook trace;
1019*c8dee2aaSAndroid Build Coastguard Worker     SkArenaAlloc alloc(/*firstHeapAllocation=*/256);
1020*c8dee2aaSAndroid Build Coastguard Worker     SkRasterPipeline p(&alloc);
1021*c8dee2aaSAndroid Build Coastguard Worker     SkRasterPipeline_InitLaneMasksCtx initLaneMasksCtx;
1022*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::init_lane_masks, &initLaneMasksCtx);
1023*c8dee2aaSAndroid Build Coastguard Worker     const SkRasterPipeline_TraceLineCtx kTraceLine1 = {/*traceMask=*/kMaskOn,  &trace, 123};
1024*c8dee2aaSAndroid Build Coastguard Worker     const SkRasterPipeline_TraceLineCtx kTraceLine2 = {/*traceMask=*/kMaskOff, &trace, 456};
1025*c8dee2aaSAndroid Build Coastguard Worker     const SkRasterPipeline_TraceLineCtx kTraceLine3 = {/*traceMask=*/kMaskOn,  &trace, 567};
1026*c8dee2aaSAndroid Build Coastguard Worker     const SkRasterPipeline_TraceLineCtx kTraceLine4 = {/*traceMask=*/kMaskOff, &trace, 678};
1027*c8dee2aaSAndroid Build Coastguard Worker     const SkRasterPipeline_TraceLineCtx kTraceLine5 = {/*traceMask=*/kMaskOn,  &trace, 789};
1028*c8dee2aaSAndroid Build Coastguard Worker 
1029*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::load_condition_mask, kMaskOn);
1030*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::trace_line, &kTraceLine1);
1031*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::load_condition_mask, kMaskOn);
1032*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::trace_line, &kTraceLine2);
1033*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::load_condition_mask, kMaskOff);
1034*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::trace_line, &kTraceLine3);
1035*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::load_condition_mask, kMaskOff);
1036*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::trace_line, &kTraceLine4);
1037*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::load_condition_mask, kMaskOn);
1038*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::trace_line, &kTraceLine5);
1039*c8dee2aaSAndroid Build Coastguard Worker     p.run(0,0,N,1);
1040*c8dee2aaSAndroid Build Coastguard Worker 
1041*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, (trace.fBuffer == TArray<int>{123, 789}));
1042*c8dee2aaSAndroid Build Coastguard Worker }
1043*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRasterPipeline_TraceEnterExit,r)1044*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRasterPipeline_TraceEnterExit, r) {
1045*c8dee2aaSAndroid Build Coastguard Worker     const int N = SkOpts::raster_pipeline_highp_stride;
1046*c8dee2aaSAndroid Build Coastguard Worker 
1047*c8dee2aaSAndroid Build Coastguard Worker     class TestTraceHook : public SkSL::TraceHook {
1048*c8dee2aaSAndroid Build Coastguard Worker     public:
1049*c8dee2aaSAndroid Build Coastguard Worker         void line(int) override         { fBuffer.push_back(-9999999); }
1050*c8dee2aaSAndroid Build Coastguard Worker         void var(int, int32_t) override { fBuffer.push_back(-9999999); }
1051*c8dee2aaSAndroid Build Coastguard Worker         void scope(int) override        { fBuffer.push_back(-9999999); }
1052*c8dee2aaSAndroid Build Coastguard Worker         void enter(int fnIdx) override  {
1053*c8dee2aaSAndroid Build Coastguard Worker             fBuffer.push_back(fnIdx);
1054*c8dee2aaSAndroid Build Coastguard Worker             fBuffer.push_back(1);
1055*c8dee2aaSAndroid Build Coastguard Worker         }
1056*c8dee2aaSAndroid Build Coastguard Worker         void exit(int fnIdx) override {
1057*c8dee2aaSAndroid Build Coastguard Worker             fBuffer.push_back(fnIdx);
1058*c8dee2aaSAndroid Build Coastguard Worker             fBuffer.push_back(0);
1059*c8dee2aaSAndroid Build Coastguard Worker         }
1060*c8dee2aaSAndroid Build Coastguard Worker 
1061*c8dee2aaSAndroid Build Coastguard Worker         TArray<int> fBuffer;
1062*c8dee2aaSAndroid Build Coastguard Worker     };
1063*c8dee2aaSAndroid Build Coastguard Worker 
1064*c8dee2aaSAndroid Build Coastguard Worker     static_assert(SkRasterPipeline_kMaxStride_highp == 16);
1065*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) static constexpr int32_t kMaskOn [16] = {~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0,
1066*c8dee2aaSAndroid Build Coastguard Worker                                                          ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0};
1067*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) static constexpr int32_t kMaskOff[16] = { 0,  0,  0,  0,  0,  0,  0,  0,
1068*c8dee2aaSAndroid Build Coastguard Worker                                                           0,  0,  0,  0,  0,  0,  0,  0};
1069*c8dee2aaSAndroid Build Coastguard Worker 
1070*c8dee2aaSAndroid Build Coastguard Worker     TestTraceHook trace;
1071*c8dee2aaSAndroid Build Coastguard Worker     SkArenaAlloc alloc(/*firstHeapAllocation=*/256);
1072*c8dee2aaSAndroid Build Coastguard Worker     SkRasterPipeline p(&alloc);
1073*c8dee2aaSAndroid Build Coastguard Worker     SkRasterPipeline_InitLaneMasksCtx initLaneMasksCtx;
1074*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::init_lane_masks, &initLaneMasksCtx);
1075*c8dee2aaSAndroid Build Coastguard Worker     const SkRasterPipeline_TraceFuncCtx kTraceFunc1 = {/*traceMask=*/kMaskOff, &trace, 99};
1076*c8dee2aaSAndroid Build Coastguard Worker     const SkRasterPipeline_TraceFuncCtx kTraceFunc2 = {/*traceMask=*/kMaskOn,  &trace, 12};
1077*c8dee2aaSAndroid Build Coastguard Worker     const SkRasterPipeline_TraceFuncCtx kTraceFunc3 = {/*traceMask=*/kMaskOff, &trace, 34};
1078*c8dee2aaSAndroid Build Coastguard Worker     const SkRasterPipeline_TraceFuncCtx kTraceFunc4 = {/*traceMask=*/kMaskOn,  &trace, 56};
1079*c8dee2aaSAndroid Build Coastguard Worker     const SkRasterPipeline_TraceFuncCtx kTraceFunc5 = {/*traceMask=*/kMaskOn,  &trace, 78};
1080*c8dee2aaSAndroid Build Coastguard Worker     const SkRasterPipeline_TraceFuncCtx kTraceFunc6 = {/*traceMask=*/kMaskOff, &trace, 90};
1081*c8dee2aaSAndroid Build Coastguard Worker 
1082*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::load_condition_mask, kMaskOff);
1083*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::trace_enter, &kTraceFunc1);
1084*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::load_condition_mask, kMaskOn);
1085*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::trace_enter, &kTraceFunc2);
1086*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::trace_enter, &kTraceFunc3);
1087*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::trace_exit, &kTraceFunc4);
1088*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::load_condition_mask, kMaskOff);
1089*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::trace_exit, &kTraceFunc5);
1090*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::trace_exit, &kTraceFunc6);
1091*c8dee2aaSAndroid Build Coastguard Worker     p.run(0,0,N,1);
1092*c8dee2aaSAndroid Build Coastguard Worker 
1093*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, (trace.fBuffer == TArray<int>{12, 1, 56, 0}));
1094*c8dee2aaSAndroid Build Coastguard Worker }
1095*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRasterPipeline_TraceScope,r)1096*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRasterPipeline_TraceScope, r) {
1097*c8dee2aaSAndroid Build Coastguard Worker     const int N = SkOpts::raster_pipeline_highp_stride;
1098*c8dee2aaSAndroid Build Coastguard Worker 
1099*c8dee2aaSAndroid Build Coastguard Worker     class TestTraceHook : public SkSL::TraceHook {
1100*c8dee2aaSAndroid Build Coastguard Worker     public:
1101*c8dee2aaSAndroid Build Coastguard Worker         void line(int) override         { fBuffer.push_back(-9999999); }
1102*c8dee2aaSAndroid Build Coastguard Worker         void var(int, int32_t) override { fBuffer.push_back(-9999999); }
1103*c8dee2aaSAndroid Build Coastguard Worker         void enter(int) override        { fBuffer.push_back(-9999999); }
1104*c8dee2aaSAndroid Build Coastguard Worker         void exit(int) override         { fBuffer.push_back(-9999999); }
1105*c8dee2aaSAndroid Build Coastguard Worker         void scope(int delta) override  { fBuffer.push_back(delta); }
1106*c8dee2aaSAndroid Build Coastguard Worker 
1107*c8dee2aaSAndroid Build Coastguard Worker         TArray<int> fBuffer;
1108*c8dee2aaSAndroid Build Coastguard Worker     };
1109*c8dee2aaSAndroid Build Coastguard Worker 
1110*c8dee2aaSAndroid Build Coastguard Worker     static_assert(SkRasterPipeline_kMaxStride_highp == 16);
1111*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) static constexpr int32_t kMaskOn [16] = {~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0,
1112*c8dee2aaSAndroid Build Coastguard Worker                                                          ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0};
1113*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) static constexpr int32_t kMaskOff[16] = { 0,  0,  0,  0,  0,  0,  0,  0,
1114*c8dee2aaSAndroid Build Coastguard Worker                                                           0,  0,  0,  0,  0,  0,  0,  0};
1115*c8dee2aaSAndroid Build Coastguard Worker 
1116*c8dee2aaSAndroid Build Coastguard Worker     TestTraceHook trace;
1117*c8dee2aaSAndroid Build Coastguard Worker     SkArenaAlloc alloc(/*firstHeapAllocation=*/256);
1118*c8dee2aaSAndroid Build Coastguard Worker     SkRasterPipeline p(&alloc);
1119*c8dee2aaSAndroid Build Coastguard Worker     SkRasterPipeline_InitLaneMasksCtx initLaneMasksCtx;
1120*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::init_lane_masks, &initLaneMasksCtx);
1121*c8dee2aaSAndroid Build Coastguard Worker     const SkRasterPipeline_TraceScopeCtx kTraceScope1  = {/*traceMask=*/kMaskOn,  &trace, +1};
1122*c8dee2aaSAndroid Build Coastguard Worker     const SkRasterPipeline_TraceScopeCtx kTraceScope2  = {/*traceMask=*/kMaskOff, &trace, -2};
1123*c8dee2aaSAndroid Build Coastguard Worker     const SkRasterPipeline_TraceScopeCtx kTraceScope3  = {/*traceMask=*/kMaskOff, &trace, +3};
1124*c8dee2aaSAndroid Build Coastguard Worker     const SkRasterPipeline_TraceScopeCtx kTraceScope4  = {/*traceMask=*/kMaskOn,  &trace, +4};
1125*c8dee2aaSAndroid Build Coastguard Worker     const SkRasterPipeline_TraceScopeCtx kTraceScope5  = {/*traceMask=*/kMaskOn,  &trace, -5};
1126*c8dee2aaSAndroid Build Coastguard Worker 
1127*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::load_condition_mask, kMaskOn);
1128*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::trace_scope, &kTraceScope1);
1129*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::trace_scope, &kTraceScope2);
1130*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::load_condition_mask, kMaskOff);
1131*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::trace_scope, &kTraceScope3);
1132*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::trace_scope, &kTraceScope4);
1133*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::load_condition_mask, kMaskOn);
1134*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::trace_scope, &kTraceScope5);
1135*c8dee2aaSAndroid Build Coastguard Worker     p.run(0,0,N,1);
1136*c8dee2aaSAndroid Build Coastguard Worker 
1137*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, (trace.fBuffer == TArray<int>{+1, +4, -5}));
1138*c8dee2aaSAndroid Build Coastguard Worker }
1139*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRasterPipeline_CopySlotsMasked,r)1140*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRasterPipeline_CopySlotsMasked, r) {
1141*c8dee2aaSAndroid Build Coastguard Worker     // Allocate space for 5 source slots and 5 dest slots.
1142*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) int slots[10 * SkRasterPipeline_kMaxStride_highp];
1143*c8dee2aaSAndroid Build Coastguard Worker     const int srcIndex = 0, dstIndex = 5;
1144*c8dee2aaSAndroid Build Coastguard Worker 
1145*c8dee2aaSAndroid Build Coastguard Worker     struct CopySlotsOp {
1146*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipelineOp stage;
1147*c8dee2aaSAndroid Build Coastguard Worker         int numSlotsAffected;
1148*c8dee2aaSAndroid Build Coastguard Worker     };
1149*c8dee2aaSAndroid Build Coastguard Worker 
1150*c8dee2aaSAndroid Build Coastguard Worker     static const CopySlotsOp kCopyOps[] = {
1151*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::copy_slot_masked,    1},
1152*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::copy_2_slots_masked, 2},
1153*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::copy_3_slots_masked, 3},
1154*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::copy_4_slots_masked, 4},
1155*c8dee2aaSAndroid Build Coastguard Worker     };
1156*c8dee2aaSAndroid Build Coastguard Worker 
1157*c8dee2aaSAndroid Build Coastguard Worker     static_assert(SkRasterPipeline_kMaxStride_highp == 16);
1158*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) const int32_t kMask1[16] = {~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0,
1159*c8dee2aaSAndroid Build Coastguard Worker                                             ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0};
1160*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) const int32_t kMask2[16] = { 0,  0,  0,  0,  0,  0,  0,  0,
1161*c8dee2aaSAndroid Build Coastguard Worker                                              0,  0,  0,  0,  0,  0,  0,  0};
1162*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) const int32_t kMask3[16] = {~0,  0, ~0, ~0, ~0, ~0,  0, ~0,
1163*c8dee2aaSAndroid Build Coastguard Worker                                             ~0,  0, ~0, ~0, ~0, ~0,  0, ~0};
1164*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) const int32_t kMask4[16] = { 0, ~0,  0,  0,  0, ~0, ~0,  0,
1165*c8dee2aaSAndroid Build Coastguard Worker                                              0, ~0,  0,  0,  0, ~0, ~0,  0};
1166*c8dee2aaSAndroid Build Coastguard Worker 
1167*c8dee2aaSAndroid Build Coastguard Worker     const int N = SkOpts::raster_pipeline_highp_stride;
1168*c8dee2aaSAndroid Build Coastguard Worker 
1169*c8dee2aaSAndroid Build Coastguard Worker     for (const CopySlotsOp& op : kCopyOps) {
1170*c8dee2aaSAndroid Build Coastguard Worker         for (const int32_t* mask : {kMask1, kMask2, kMask3, kMask4}) {
1171*c8dee2aaSAndroid Build Coastguard Worker             // Initialize the destination slots to 0,1,2.. and the source slots to various NaNs
1172*c8dee2aaSAndroid Build Coastguard Worker             std::iota(&slots[N * dstIndex],  &slots[N * (dstIndex + 5)], 0);
1173*c8dee2aaSAndroid Build Coastguard Worker             std::iota(&slots[N * srcIndex],  &slots[N * (srcIndex + 5)], kLastSignalingNaN);
1174*c8dee2aaSAndroid Build Coastguard Worker 
1175*c8dee2aaSAndroid Build Coastguard Worker             // Run `copy_slots_masked` over our data.
1176*c8dee2aaSAndroid Build Coastguard Worker             SkArenaAlloc alloc(/*firstHeapAllocation=*/256);
1177*c8dee2aaSAndroid Build Coastguard Worker             SkRasterPipeline p(&alloc);
1178*c8dee2aaSAndroid Build Coastguard Worker             SkRasterPipeline_BinaryOpCtx ctx;
1179*c8dee2aaSAndroid Build Coastguard Worker             ctx.dst = N * dstIndex * sizeof(float);
1180*c8dee2aaSAndroid Build Coastguard Worker             ctx.src = N * srcIndex * sizeof(float);
1181*c8dee2aaSAndroid Build Coastguard Worker 
1182*c8dee2aaSAndroid Build Coastguard Worker             SkRasterPipeline_InitLaneMasksCtx initLaneMasksCtx;
1183*c8dee2aaSAndroid Build Coastguard Worker             p.append(SkRasterPipelineOp::init_lane_masks, &initLaneMasksCtx);
1184*c8dee2aaSAndroid Build Coastguard Worker             p.append(SkRasterPipelineOp::set_base_pointer, &slots[0]);
1185*c8dee2aaSAndroid Build Coastguard Worker             p.append(SkRasterPipelineOp::load_condition_mask, mask);
1186*c8dee2aaSAndroid Build Coastguard Worker             p.append(op.stage, SkRPCtxUtils::Pack(ctx, &alloc));
1187*c8dee2aaSAndroid Build Coastguard Worker             p.run(0,0,N,1);
1188*c8dee2aaSAndroid Build Coastguard Worker 
1189*c8dee2aaSAndroid Build Coastguard Worker             // Verify that the destination has been overwritten in the mask-on fields, and has not
1190*c8dee2aaSAndroid Build Coastguard Worker             // been overwritten in the mask-off fields, for each destination slot.
1191*c8dee2aaSAndroid Build Coastguard Worker             int expectedUnchanged = 0, expectedChanged = kLastSignalingNaN;
1192*c8dee2aaSAndroid Build Coastguard Worker             int* destPtr = &slots[N * dstIndex];
1193*c8dee2aaSAndroid Build Coastguard Worker             for (int checkSlot = 0; checkSlot < 5; ++checkSlot) {
1194*c8dee2aaSAndroid Build Coastguard Worker                 for (int checkMask = 0; checkMask < N; ++checkMask) {
1195*c8dee2aaSAndroid Build Coastguard Worker                     if (checkSlot < op.numSlotsAffected && mask[checkMask]) {
1196*c8dee2aaSAndroid Build Coastguard Worker                         REPORTER_ASSERT(r, *destPtr == expectedChanged);
1197*c8dee2aaSAndroid Build Coastguard Worker                     } else {
1198*c8dee2aaSAndroid Build Coastguard Worker                         REPORTER_ASSERT(r, *destPtr == expectedUnchanged);
1199*c8dee2aaSAndroid Build Coastguard Worker                     }
1200*c8dee2aaSAndroid Build Coastguard Worker 
1201*c8dee2aaSAndroid Build Coastguard Worker                     ++destPtr;
1202*c8dee2aaSAndroid Build Coastguard Worker                     expectedUnchanged += 1;
1203*c8dee2aaSAndroid Build Coastguard Worker                     expectedChanged += 1;
1204*c8dee2aaSAndroid Build Coastguard Worker                 }
1205*c8dee2aaSAndroid Build Coastguard Worker             }
1206*c8dee2aaSAndroid Build Coastguard Worker         }
1207*c8dee2aaSAndroid Build Coastguard Worker     }
1208*c8dee2aaSAndroid Build Coastguard Worker }
1209*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRasterPipeline_CopySlotsUnmasked,r)1210*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRasterPipeline_CopySlotsUnmasked, r) {
1211*c8dee2aaSAndroid Build Coastguard Worker     // Allocate space for 5 source slots and 5 dest slots.
1212*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) int slots[10 * SkRasterPipeline_kMaxStride_highp];
1213*c8dee2aaSAndroid Build Coastguard Worker     const int srcIndex = 0, dstIndex = 5;
1214*c8dee2aaSAndroid Build Coastguard Worker     const int N = SkOpts::raster_pipeline_highp_stride;
1215*c8dee2aaSAndroid Build Coastguard Worker 
1216*c8dee2aaSAndroid Build Coastguard Worker     struct CopySlotsOp {
1217*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipelineOp stage;
1218*c8dee2aaSAndroid Build Coastguard Worker         int numSlotsAffected;
1219*c8dee2aaSAndroid Build Coastguard Worker     };
1220*c8dee2aaSAndroid Build Coastguard Worker 
1221*c8dee2aaSAndroid Build Coastguard Worker     static const CopySlotsOp kCopyOps[] = {
1222*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::copy_slot_unmasked,    1},
1223*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::copy_2_slots_unmasked, 2},
1224*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::copy_3_slots_unmasked, 3},
1225*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::copy_4_slots_unmasked, 4},
1226*c8dee2aaSAndroid Build Coastguard Worker     };
1227*c8dee2aaSAndroid Build Coastguard Worker 
1228*c8dee2aaSAndroid Build Coastguard Worker     for (const CopySlotsOp& op : kCopyOps) {
1229*c8dee2aaSAndroid Build Coastguard Worker         // Initialize the destination slots to 0,1,2.. and the source slots to various NaNs
1230*c8dee2aaSAndroid Build Coastguard Worker         std::iota(&slots[N * dstIndex],  &slots[N * (dstIndex + 5)], 0);
1231*c8dee2aaSAndroid Build Coastguard Worker         std::iota(&slots[N * srcIndex],  &slots[N * (srcIndex + 5)], kLastSignalingNaN);
1232*c8dee2aaSAndroid Build Coastguard Worker 
1233*c8dee2aaSAndroid Build Coastguard Worker         // Run `copy_slots_unmasked` over our data.
1234*c8dee2aaSAndroid Build Coastguard Worker         SkArenaAlloc alloc(/*firstHeapAllocation=*/256);
1235*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline p(&alloc);
1236*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline_BinaryOpCtx ctx;
1237*c8dee2aaSAndroid Build Coastguard Worker         ctx.dst = N * dstIndex * sizeof(float);
1238*c8dee2aaSAndroid Build Coastguard Worker         ctx.src = N * srcIndex * sizeof(float);
1239*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::set_base_pointer, &slots[0]);
1240*c8dee2aaSAndroid Build Coastguard Worker         p.append(op.stage, SkRPCtxUtils::Pack(ctx, &alloc));
1241*c8dee2aaSAndroid Build Coastguard Worker         p.run(0,0,1,1);
1242*c8dee2aaSAndroid Build Coastguard Worker 
1243*c8dee2aaSAndroid Build Coastguard Worker         // Verify that the destination has been overwritten in each slot.
1244*c8dee2aaSAndroid Build Coastguard Worker         int expectedUnchanged = 0, expectedChanged = kLastSignalingNaN;
1245*c8dee2aaSAndroid Build Coastguard Worker         int* destPtr = &slots[N * dstIndex];
1246*c8dee2aaSAndroid Build Coastguard Worker         for (int checkSlot = 0; checkSlot < 5; ++checkSlot) {
1247*c8dee2aaSAndroid Build Coastguard Worker             for (int checkLane = 0; checkLane < N; ++checkLane) {
1248*c8dee2aaSAndroid Build Coastguard Worker                 if (checkSlot < op.numSlotsAffected) {
1249*c8dee2aaSAndroid Build Coastguard Worker                     REPORTER_ASSERT(r, *destPtr == expectedChanged);
1250*c8dee2aaSAndroid Build Coastguard Worker                 } else {
1251*c8dee2aaSAndroid Build Coastguard Worker                     REPORTER_ASSERT(r, *destPtr == expectedUnchanged);
1252*c8dee2aaSAndroid Build Coastguard Worker                 }
1253*c8dee2aaSAndroid Build Coastguard Worker 
1254*c8dee2aaSAndroid Build Coastguard Worker                 ++destPtr;
1255*c8dee2aaSAndroid Build Coastguard Worker                 expectedUnchanged += 1;
1256*c8dee2aaSAndroid Build Coastguard Worker                 expectedChanged += 1;
1257*c8dee2aaSAndroid Build Coastguard Worker             }
1258*c8dee2aaSAndroid Build Coastguard Worker         }
1259*c8dee2aaSAndroid Build Coastguard Worker     }
1260*c8dee2aaSAndroid Build Coastguard Worker }
1261*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRasterPipeline_CopyUniforms,r)1262*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRasterPipeline_CopyUniforms, r) {
1263*c8dee2aaSAndroid Build Coastguard Worker     // Allocate space for 5 dest slots.
1264*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) int slots[5 * SkRasterPipeline_kMaxStride_highp];
1265*c8dee2aaSAndroid Build Coastguard Worker     int uniforms[5];
1266*c8dee2aaSAndroid Build Coastguard Worker     const int N = SkOpts::raster_pipeline_highp_stride;
1267*c8dee2aaSAndroid Build Coastguard Worker 
1268*c8dee2aaSAndroid Build Coastguard Worker     struct CopyUniformsOp {
1269*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipelineOp stage;
1270*c8dee2aaSAndroid Build Coastguard Worker         int numSlotsAffected;
1271*c8dee2aaSAndroid Build Coastguard Worker     };
1272*c8dee2aaSAndroid Build Coastguard Worker 
1273*c8dee2aaSAndroid Build Coastguard Worker     static const CopyUniformsOp kCopyOps[] = {
1274*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::copy_uniform,    1},
1275*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::copy_2_uniforms, 2},
1276*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::copy_3_uniforms, 3},
1277*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::copy_4_uniforms, 4},
1278*c8dee2aaSAndroid Build Coastguard Worker     };
1279*c8dee2aaSAndroid Build Coastguard Worker 
1280*c8dee2aaSAndroid Build Coastguard Worker     for (const CopyUniformsOp& op : kCopyOps) {
1281*c8dee2aaSAndroid Build Coastguard Worker         // Initialize the destination slots to 1,2,3...
1282*c8dee2aaSAndroid Build Coastguard Worker         std::iota(&slots[0], &slots[5 * N], 1);
1283*c8dee2aaSAndroid Build Coastguard Worker         // Initialize the uniform buffer to various NaNs
1284*c8dee2aaSAndroid Build Coastguard Worker         std::iota(&uniforms[0], &uniforms[5], kLastSignalingNaN);
1285*c8dee2aaSAndroid Build Coastguard Worker 
1286*c8dee2aaSAndroid Build Coastguard Worker         // Run `copy_n_uniforms` over our data.
1287*c8dee2aaSAndroid Build Coastguard Worker         SkArenaAlloc alloc(/*firstHeapAllocation=*/256);
1288*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline p(&alloc);
1289*c8dee2aaSAndroid Build Coastguard Worker         auto* ctx = alloc.make<SkRasterPipeline_UniformCtx>();
1290*c8dee2aaSAndroid Build Coastguard Worker         ctx->dst = slots;
1291*c8dee2aaSAndroid Build Coastguard Worker         ctx->src = uniforms;
1292*c8dee2aaSAndroid Build Coastguard Worker         p.append(op.stage, ctx);
1293*c8dee2aaSAndroid Build Coastguard Worker         p.run(0,0,1,1);
1294*c8dee2aaSAndroid Build Coastguard Worker 
1295*c8dee2aaSAndroid Build Coastguard Worker         // Verify that our uniforms have been broadcast into each slot.
1296*c8dee2aaSAndroid Build Coastguard Worker         int expectedUnchanged = 1;
1297*c8dee2aaSAndroid Build Coastguard Worker         int expectedChanged = kLastSignalingNaN;
1298*c8dee2aaSAndroid Build Coastguard Worker         int* destPtr = &slots[0];
1299*c8dee2aaSAndroid Build Coastguard Worker         for (int checkSlot = 0; checkSlot < 5; ++checkSlot) {
1300*c8dee2aaSAndroid Build Coastguard Worker             for (int checkLane = 0; checkLane < N; ++checkLane) {
1301*c8dee2aaSAndroid Build Coastguard Worker                 if (checkSlot < op.numSlotsAffected) {
1302*c8dee2aaSAndroid Build Coastguard Worker                     REPORTER_ASSERT(r, *destPtr == expectedChanged);
1303*c8dee2aaSAndroid Build Coastguard Worker                 } else {
1304*c8dee2aaSAndroid Build Coastguard Worker                     REPORTER_ASSERT(r, *destPtr == expectedUnchanged);
1305*c8dee2aaSAndroid Build Coastguard Worker                 }
1306*c8dee2aaSAndroid Build Coastguard Worker 
1307*c8dee2aaSAndroid Build Coastguard Worker                 ++destPtr;
1308*c8dee2aaSAndroid Build Coastguard Worker                 expectedUnchanged += 1;
1309*c8dee2aaSAndroid Build Coastguard Worker             }
1310*c8dee2aaSAndroid Build Coastguard Worker             expectedChanged += 1;
1311*c8dee2aaSAndroid Build Coastguard Worker         }
1312*c8dee2aaSAndroid Build Coastguard Worker     }
1313*c8dee2aaSAndroid Build Coastguard Worker }
1314*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRasterPipeline_CopyConstant,r)1315*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRasterPipeline_CopyConstant, r) {
1316*c8dee2aaSAndroid Build Coastguard Worker     // Allocate space for 5 dest slots.
1317*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) int slots[5 * SkRasterPipeline_kMaxStride_highp];
1318*c8dee2aaSAndroid Build Coastguard Worker     const int N = SkOpts::raster_pipeline_highp_stride;
1319*c8dee2aaSAndroid Build Coastguard Worker 
1320*c8dee2aaSAndroid Build Coastguard Worker     for (int index = 0; index < 5; ++index) {
1321*c8dee2aaSAndroid Build Coastguard Worker         // Initialize the destination slots to 1,2,3...
1322*c8dee2aaSAndroid Build Coastguard Worker         std::iota(&slots[0], &slots[5 * N], 1);
1323*c8dee2aaSAndroid Build Coastguard Worker 
1324*c8dee2aaSAndroid Build Coastguard Worker         // Overwrite one destination slot with a constant (some NaN based on slot number).
1325*c8dee2aaSAndroid Build Coastguard Worker         SkArenaAlloc alloc(/*firstHeapAllocation=*/256);
1326*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline p(&alloc);
1327*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline_ConstantCtx ctx;
1328*c8dee2aaSAndroid Build Coastguard Worker         ctx.dst = N * index * sizeof(float);
1329*c8dee2aaSAndroid Build Coastguard Worker         ctx.value = kLastSignalingNaN + index;
1330*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::set_base_pointer, &slots[0]);
1331*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::copy_constant, SkRPCtxUtils::Pack(ctx, &alloc));
1332*c8dee2aaSAndroid Build Coastguard Worker         p.run(0,0,1,1);
1333*c8dee2aaSAndroid Build Coastguard Worker 
1334*c8dee2aaSAndroid Build Coastguard Worker         // Verify that our constant value has been broadcast into exactly one slot.
1335*c8dee2aaSAndroid Build Coastguard Worker         int expectedUnchanged = 1;
1336*c8dee2aaSAndroid Build Coastguard Worker         int* destPtr = &slots[0];
1337*c8dee2aaSAndroid Build Coastguard Worker         for (int checkSlot = 0; checkSlot < 5; ++checkSlot) {
1338*c8dee2aaSAndroid Build Coastguard Worker             for (int checkLane = 0; checkLane < N; ++checkLane) {
1339*c8dee2aaSAndroid Build Coastguard Worker                 if (checkSlot == index) {
1340*c8dee2aaSAndroid Build Coastguard Worker                     REPORTER_ASSERT(r, *destPtr == ctx.value);
1341*c8dee2aaSAndroid Build Coastguard Worker                 } else {
1342*c8dee2aaSAndroid Build Coastguard Worker                     REPORTER_ASSERT(r, *destPtr == expectedUnchanged);
1343*c8dee2aaSAndroid Build Coastguard Worker                 }
1344*c8dee2aaSAndroid Build Coastguard Worker 
1345*c8dee2aaSAndroid Build Coastguard Worker                 ++destPtr;
1346*c8dee2aaSAndroid Build Coastguard Worker                 expectedUnchanged += 1;
1347*c8dee2aaSAndroid Build Coastguard Worker             }
1348*c8dee2aaSAndroid Build Coastguard Worker         }
1349*c8dee2aaSAndroid Build Coastguard Worker     }
1350*c8dee2aaSAndroid Build Coastguard Worker }
1351*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRasterPipeline_Swizzle,r)1352*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRasterPipeline_Swizzle, r) {
1353*c8dee2aaSAndroid Build Coastguard Worker     // Allocate space for 4 dest slots.
1354*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) int slots[4 * SkRasterPipeline_kMaxStride_highp];
1355*c8dee2aaSAndroid Build Coastguard Worker     const int N = SkOpts::raster_pipeline_highp_stride;
1356*c8dee2aaSAndroid Build Coastguard Worker 
1357*c8dee2aaSAndroid Build Coastguard Worker     struct TestPattern {
1358*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipelineOp stage;
1359*c8dee2aaSAndroid Build Coastguard Worker         uint8_t swizzle[4];
1360*c8dee2aaSAndroid Build Coastguard Worker         uint8_t expectation[4];
1361*c8dee2aaSAndroid Build Coastguard Worker     };
1362*c8dee2aaSAndroid Build Coastguard Worker     static const TestPattern kPatterns[] = {
1363*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::swizzle_1, {3},          {3, 1, 2, 3}}, // (1,2,3,4).w    = (4)
1364*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::swizzle_2, {1, 0},       {1, 0, 2, 3}}, // (1,2,3,4).yx   = (2,1)
1365*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::swizzle_3, {2, 2, 2},    {2, 2, 2, 3}}, // (1,2,3,4).zzz  = (3,3,3)
1366*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::swizzle_4, {0, 0, 1, 2}, {0, 0, 1, 2}}, // (1,2,3,4).xxyz = (1,1,2,3)
1367*c8dee2aaSAndroid Build Coastguard Worker     };
1368*c8dee2aaSAndroid Build Coastguard Worker     static_assert(sizeof(TestPattern::swizzle) == sizeof(SkRasterPipeline_SwizzleCtx::offsets));
1369*c8dee2aaSAndroid Build Coastguard Worker 
1370*c8dee2aaSAndroid Build Coastguard Worker     for (const TestPattern& pattern : kPatterns) {
1371*c8dee2aaSAndroid Build Coastguard Worker         // Initialize the destination slots to various NaNs
1372*c8dee2aaSAndroid Build Coastguard Worker         std::iota(&slots[0], &slots[4 * N], kLastSignalingNaN);
1373*c8dee2aaSAndroid Build Coastguard Worker 
1374*c8dee2aaSAndroid Build Coastguard Worker         // Apply the test-pattern swizzle.
1375*c8dee2aaSAndroid Build Coastguard Worker         SkArenaAlloc alloc(/*firstHeapAllocation=*/256);
1376*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline p(&alloc);
1377*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline_SwizzleCtx ctx;
1378*c8dee2aaSAndroid Build Coastguard Worker         ctx.dst = 0;
1379*c8dee2aaSAndroid Build Coastguard Worker         for (size_t index = 0; index < std::size(ctx.offsets); ++index) {
1380*c8dee2aaSAndroid Build Coastguard Worker             ctx.offsets[index] = pattern.swizzle[index] * N * sizeof(float);
1381*c8dee2aaSAndroid Build Coastguard Worker         }
1382*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::set_base_pointer, &slots[0]);
1383*c8dee2aaSAndroid Build Coastguard Worker         p.append(pattern.stage, SkRPCtxUtils::Pack(ctx, &alloc));
1384*c8dee2aaSAndroid Build Coastguard Worker         p.run(0,0,1,1);
1385*c8dee2aaSAndroid Build Coastguard Worker 
1386*c8dee2aaSAndroid Build Coastguard Worker         // Verify that the swizzle has been applied in each slot.
1387*c8dee2aaSAndroid Build Coastguard Worker         int* destPtr = &slots[0];
1388*c8dee2aaSAndroid Build Coastguard Worker         for (int checkSlot = 0; checkSlot < 4; ++checkSlot) {
1389*c8dee2aaSAndroid Build Coastguard Worker             int expected = pattern.expectation[checkSlot] * N + kLastSignalingNaN;
1390*c8dee2aaSAndroid Build Coastguard Worker             for (int checkLane = 0; checkLane < N; ++checkLane) {
1391*c8dee2aaSAndroid Build Coastguard Worker                 REPORTER_ASSERT(r, *destPtr == expected);
1392*c8dee2aaSAndroid Build Coastguard Worker 
1393*c8dee2aaSAndroid Build Coastguard Worker                 ++destPtr;
1394*c8dee2aaSAndroid Build Coastguard Worker                 expected += 1;
1395*c8dee2aaSAndroid Build Coastguard Worker             }
1396*c8dee2aaSAndroid Build Coastguard Worker         }
1397*c8dee2aaSAndroid Build Coastguard Worker     }
1398*c8dee2aaSAndroid Build Coastguard Worker }
1399*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRasterPipeline_SwizzleCopy,r)1400*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRasterPipeline_SwizzleCopy, r) {
1401*c8dee2aaSAndroid Build Coastguard Worker     const int N = SkOpts::raster_pipeline_highp_stride;
1402*c8dee2aaSAndroid Build Coastguard Worker 
1403*c8dee2aaSAndroid Build Coastguard Worker     struct TestPattern {
1404*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipelineOp op;
1405*c8dee2aaSAndroid Build Coastguard Worker         uint16_t swizzle[4];
1406*c8dee2aaSAndroid Build Coastguard Worker         uint16_t expectation[4];
1407*c8dee2aaSAndroid Build Coastguard Worker     };
1408*c8dee2aaSAndroid Build Coastguard Worker     constexpr uint16_t _ = ~0;
1409*c8dee2aaSAndroid Build Coastguard Worker     static const TestPattern kPatterns[] = {
1410*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::swizzle_copy_slot_masked,    {3,_,_,_}, {_,_,_,0}},//v.w    = (1)
1411*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::swizzle_copy_2_slots_masked, {1,0,_,_}, {1,0,_,_}},//v.yx   = (1,2)
1412*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::swizzle_copy_3_slots_masked, {2,3,0,_}, {2,_,0,1}},//v.zwy  = (1,2,3)
1413*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::swizzle_copy_4_slots_masked, {3,0,1,2}, {1,2,3,0}},//v.wxyz = (1,2,3,4)
1414*c8dee2aaSAndroid Build Coastguard Worker     };
1415*c8dee2aaSAndroid Build Coastguard Worker     static_assert(sizeof(TestPattern::swizzle) == sizeof(SkRasterPipeline_SwizzleCopyCtx::offsets));
1416*c8dee2aaSAndroid Build Coastguard Worker 
1417*c8dee2aaSAndroid Build Coastguard Worker     for (const TestPattern& pattern : kPatterns) {
1418*c8dee2aaSAndroid Build Coastguard Worker         // Allocate space for 4 dest slots, and initialize them to zero.
1419*c8dee2aaSAndroid Build Coastguard Worker         alignas(64) int dest[4 * SkRasterPipeline_kMaxStride_highp] = {};
1420*c8dee2aaSAndroid Build Coastguard Worker 
1421*c8dee2aaSAndroid Build Coastguard Worker         // Allocate 4 source slots and initialize them to various NaNs
1422*c8dee2aaSAndroid Build Coastguard Worker         alignas(64) int source[4 * SkRasterPipeline_kMaxStride_highp] = {};
1423*c8dee2aaSAndroid Build Coastguard Worker         std::iota(&source[0 * N], &source[4 * N], kLastSignalingNaN);
1424*c8dee2aaSAndroid Build Coastguard Worker 
1425*c8dee2aaSAndroid Build Coastguard Worker         // Apply the dest-swizzle pattern.
1426*c8dee2aaSAndroid Build Coastguard Worker         SkArenaAlloc alloc(/*firstHeapAllocation=*/256);
1427*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline p(&alloc);
1428*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline_InitLaneMasksCtx initLaneMasksCtx;
1429*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline_SwizzleCopyCtx ctx = {};
1430*c8dee2aaSAndroid Build Coastguard Worker         ctx.src = source;
1431*c8dee2aaSAndroid Build Coastguard Worker         ctx.dst = dest;
1432*c8dee2aaSAndroid Build Coastguard Worker         for (size_t index = 0; index < std::size(ctx.offsets); ++index) {
1433*c8dee2aaSAndroid Build Coastguard Worker             if (pattern.swizzle[index] != _) {
1434*c8dee2aaSAndroid Build Coastguard Worker                 ctx.offsets[index] = pattern.swizzle[index] * N * sizeof(float);
1435*c8dee2aaSAndroid Build Coastguard Worker             }
1436*c8dee2aaSAndroid Build Coastguard Worker         }
1437*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::init_lane_masks, &initLaneMasksCtx);
1438*c8dee2aaSAndroid Build Coastguard Worker         p.append(pattern.op, &ctx);
1439*c8dee2aaSAndroid Build Coastguard Worker         p.run(0,0,N,1);
1440*c8dee2aaSAndroid Build Coastguard Worker 
1441*c8dee2aaSAndroid Build Coastguard Worker         // Verify that the swizzle has been applied in each slot.
1442*c8dee2aaSAndroid Build Coastguard Worker         int* destPtr = &dest[0];
1443*c8dee2aaSAndroid Build Coastguard Worker         for (int checkSlot = 0; checkSlot < 4; ++checkSlot) {
1444*c8dee2aaSAndroid Build Coastguard Worker             for (int checkLane = 0; checkLane < N; ++checkLane) {
1445*c8dee2aaSAndroid Build Coastguard Worker                 if (pattern.expectation[checkSlot] == _) {
1446*c8dee2aaSAndroid Build Coastguard Worker                     REPORTER_ASSERT(r, *destPtr == 0);
1447*c8dee2aaSAndroid Build Coastguard Worker                 } else {
1448*c8dee2aaSAndroid Build Coastguard Worker                     int expectedIdx = pattern.expectation[checkSlot] * N + checkLane;
1449*c8dee2aaSAndroid Build Coastguard Worker                     REPORTER_ASSERT(r, *destPtr == source[expectedIdx]);
1450*c8dee2aaSAndroid Build Coastguard Worker                 }
1451*c8dee2aaSAndroid Build Coastguard Worker 
1452*c8dee2aaSAndroid Build Coastguard Worker                 ++destPtr;
1453*c8dee2aaSAndroid Build Coastguard Worker             }
1454*c8dee2aaSAndroid Build Coastguard Worker         }
1455*c8dee2aaSAndroid Build Coastguard Worker     }
1456*c8dee2aaSAndroid Build Coastguard Worker }
1457*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRasterPipeline_Shuffle,r)1458*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRasterPipeline_Shuffle, r) {
1459*c8dee2aaSAndroid Build Coastguard Worker     // Allocate space for 16 dest slots.
1460*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) int slots[16 * SkRasterPipeline_kMaxStride_highp];
1461*c8dee2aaSAndroid Build Coastguard Worker     const int N = SkOpts::raster_pipeline_highp_stride;
1462*c8dee2aaSAndroid Build Coastguard Worker 
1463*c8dee2aaSAndroid Build Coastguard Worker     struct TestPattern {
1464*c8dee2aaSAndroid Build Coastguard Worker         int count;
1465*c8dee2aaSAndroid Build Coastguard Worker         uint16_t shuffle[16];
1466*c8dee2aaSAndroid Build Coastguard Worker         uint16_t expectation[16];
1467*c8dee2aaSAndroid Build Coastguard Worker     };
1468*c8dee2aaSAndroid Build Coastguard Worker     static const TestPattern kPatterns[] = {
1469*c8dee2aaSAndroid Build Coastguard Worker         {9,  { 0,  3,  6,
1470*c8dee2aaSAndroid Build Coastguard Worker                1,  4,  7,
1471*c8dee2aaSAndroid Build Coastguard Worker                2,  5,  8, /* past end: */  0,  0,  0,  0,  0,  0,  0},
1472*c8dee2aaSAndroid Build Coastguard Worker              { 0,  3,  6,
1473*c8dee2aaSAndroid Build Coastguard Worker                1,  4,  7,
1474*c8dee2aaSAndroid Build Coastguard Worker                2,  5,  8, /* unchanged: */ 9, 10, 11, 12, 13, 14, 15}},
1475*c8dee2aaSAndroid Build Coastguard Worker         {16, { 0,  4,  8, 12,
1476*c8dee2aaSAndroid Build Coastguard Worker                1,  5,  9, 13,
1477*c8dee2aaSAndroid Build Coastguard Worker                2,  6, 10, 14,
1478*c8dee2aaSAndroid Build Coastguard Worker                3,  7, 11, 15},
1479*c8dee2aaSAndroid Build Coastguard Worker              { 0,  4,  8, 12,
1480*c8dee2aaSAndroid Build Coastguard Worker                1,  5,  9, 13,
1481*c8dee2aaSAndroid Build Coastguard Worker                2,  6, 10, 14,
1482*c8dee2aaSAndroid Build Coastguard Worker                3,  7, 11, 15}},
1483*c8dee2aaSAndroid Build Coastguard Worker     };
1484*c8dee2aaSAndroid Build Coastguard Worker     static_assert(sizeof(TestPattern::shuffle) == sizeof(SkRasterPipeline_ShuffleCtx::offsets));
1485*c8dee2aaSAndroid Build Coastguard Worker 
1486*c8dee2aaSAndroid Build Coastguard Worker     for (const TestPattern& pattern : kPatterns) {
1487*c8dee2aaSAndroid Build Coastguard Worker         // Initialize the destination slots to various NaNs
1488*c8dee2aaSAndroid Build Coastguard Worker         std::iota(&slots[0], &slots[16 * N], kLastSignalingNaN);
1489*c8dee2aaSAndroid Build Coastguard Worker 
1490*c8dee2aaSAndroid Build Coastguard Worker         // Apply the shuffle.
1491*c8dee2aaSAndroid Build Coastguard Worker         SkArenaAlloc alloc(/*firstHeapAllocation=*/256);
1492*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline p(&alloc);
1493*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline_ShuffleCtx ctx;
1494*c8dee2aaSAndroid Build Coastguard Worker         ctx.ptr = slots;
1495*c8dee2aaSAndroid Build Coastguard Worker         ctx.count = pattern.count;
1496*c8dee2aaSAndroid Build Coastguard Worker         for (size_t index = 0; index < std::size(ctx.offsets); ++index) {
1497*c8dee2aaSAndroid Build Coastguard Worker             ctx.offsets[index] = pattern.shuffle[index] * N * sizeof(float);
1498*c8dee2aaSAndroid Build Coastguard Worker         }
1499*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::shuffle, &ctx);
1500*c8dee2aaSAndroid Build Coastguard Worker         p.run(0,0,1,1);
1501*c8dee2aaSAndroid Build Coastguard Worker 
1502*c8dee2aaSAndroid Build Coastguard Worker         // Verify that the shuffle has been applied in each slot.
1503*c8dee2aaSAndroid Build Coastguard Worker         int* destPtr = &slots[0];
1504*c8dee2aaSAndroid Build Coastguard Worker         for (int checkSlot = 0; checkSlot < 16; ++checkSlot) {
1505*c8dee2aaSAndroid Build Coastguard Worker             int expected = pattern.expectation[checkSlot] * N + kLastSignalingNaN;
1506*c8dee2aaSAndroid Build Coastguard Worker             for (int checkLane = 0; checkLane < N; ++checkLane) {
1507*c8dee2aaSAndroid Build Coastguard Worker                 REPORTER_ASSERT(r, *destPtr == expected);
1508*c8dee2aaSAndroid Build Coastguard Worker 
1509*c8dee2aaSAndroid Build Coastguard Worker                 ++destPtr;
1510*c8dee2aaSAndroid Build Coastguard Worker                 expected += 1;
1511*c8dee2aaSAndroid Build Coastguard Worker             }
1512*c8dee2aaSAndroid Build Coastguard Worker         }
1513*c8dee2aaSAndroid Build Coastguard Worker     }
1514*c8dee2aaSAndroid Build Coastguard Worker }
1515*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRasterPipeline_MatrixMultiply2x2,reporter)1516*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRasterPipeline_MatrixMultiply2x2, reporter) {
1517*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) float slots[12 * SkRasterPipeline_kMaxStride_highp];
1518*c8dee2aaSAndroid Build Coastguard Worker     const int N = SkOpts::raster_pipeline_highp_stride;
1519*c8dee2aaSAndroid Build Coastguard Worker 
1520*c8dee2aaSAndroid Build Coastguard Worker     // Populate the left- and right-matrix data. Slots 0-3 hold the result and are left as-is.
1521*c8dee2aaSAndroid Build Coastguard Worker     std::iota(&slots[4 * N], &slots[12 * N], 1.0f);
1522*c8dee2aaSAndroid Build Coastguard Worker 
1523*c8dee2aaSAndroid Build Coastguard Worker     // Perform a 2x2 matrix multiply.
1524*c8dee2aaSAndroid Build Coastguard Worker     SkArenaAlloc alloc(/*firstHeapAllocation=*/256);
1525*c8dee2aaSAndroid Build Coastguard Worker     SkRasterPipeline p(&alloc);
1526*c8dee2aaSAndroid Build Coastguard Worker     SkRasterPipeline_MatrixMultiplyCtx ctx;
1527*c8dee2aaSAndroid Build Coastguard Worker     ctx.dst = 0;
1528*c8dee2aaSAndroid Build Coastguard Worker     ctx.leftColumns = ctx.leftRows = ctx.rightColumns = ctx.rightRows = 2;
1529*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::set_base_pointer, &slots[0]);
1530*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::matrix_multiply_2, SkRPCtxUtils::Pack(ctx, &alloc));
1531*c8dee2aaSAndroid Build Coastguard Worker     p.run(0,0,1,1);
1532*c8dee2aaSAndroid Build Coastguard Worker 
1533*c8dee2aaSAndroid Build Coastguard Worker     // Verify that the result slots hold a 2x2 matrix multiply.
1534*c8dee2aaSAndroid Build Coastguard Worker     const float* const destPtr[2][2] = {
1535*c8dee2aaSAndroid Build Coastguard Worker             {&slots[0 * N], &slots[1 * N]},
1536*c8dee2aaSAndroid Build Coastguard Worker             {&slots[2 * N], &slots[3 * N]},
1537*c8dee2aaSAndroid Build Coastguard Worker     };
1538*c8dee2aaSAndroid Build Coastguard Worker     const float* const leftMtx[2][2] = {
1539*c8dee2aaSAndroid Build Coastguard Worker             {&slots[4 * N], &slots[5 * N]},
1540*c8dee2aaSAndroid Build Coastguard Worker             {&slots[6 * N], &slots[7 * N]},
1541*c8dee2aaSAndroid Build Coastguard Worker     };
1542*c8dee2aaSAndroid Build Coastguard Worker     const float* const rightMtx[2][2] = {
1543*c8dee2aaSAndroid Build Coastguard Worker             {&slots[8 * N],  &slots[9 * N]},
1544*c8dee2aaSAndroid Build Coastguard Worker             {&slots[10 * N], &slots[11 * N]},
1545*c8dee2aaSAndroid Build Coastguard Worker     };
1546*c8dee2aaSAndroid Build Coastguard Worker 
1547*c8dee2aaSAndroid Build Coastguard Worker     for (int c = 0; c < 2; ++c) {
1548*c8dee2aaSAndroid Build Coastguard Worker         for (int r = 0; r < 2; ++r) {
1549*c8dee2aaSAndroid Build Coastguard Worker             for (int lane = 0; lane < N; ++lane) {
1550*c8dee2aaSAndroid Build Coastguard Worker                 // Dot a vector from leftMtx[*][r] with rightMtx[c][*].
1551*c8dee2aaSAndroid Build Coastguard Worker                 float dot = 0;
1552*c8dee2aaSAndroid Build Coastguard Worker                 for (int n = 0; n < 2; ++n) {
1553*c8dee2aaSAndroid Build Coastguard Worker                     dot += leftMtx[n][r][lane] * rightMtx[c][n][lane];
1554*c8dee2aaSAndroid Build Coastguard Worker                 }
1555*c8dee2aaSAndroid Build Coastguard Worker                 REPORTER_ASSERT(reporter, destPtr[c][r][lane] == dot);
1556*c8dee2aaSAndroid Build Coastguard Worker             }
1557*c8dee2aaSAndroid Build Coastguard Worker         }
1558*c8dee2aaSAndroid Build Coastguard Worker     }
1559*c8dee2aaSAndroid Build Coastguard Worker }
1560*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRasterPipeline_MatrixMultiply3x3,reporter)1561*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRasterPipeline_MatrixMultiply3x3, reporter) {
1562*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) float slots[27 * SkRasterPipeline_kMaxStride_highp];
1563*c8dee2aaSAndroid Build Coastguard Worker     const int N = SkOpts::raster_pipeline_highp_stride;
1564*c8dee2aaSAndroid Build Coastguard Worker 
1565*c8dee2aaSAndroid Build Coastguard Worker     // Populate the left- and right-matrix data. Slots 0-8 hold the result and are left as-is.
1566*c8dee2aaSAndroid Build Coastguard Worker     // To keep results in full-precision float range, we only set values between 0 and 25.
1567*c8dee2aaSAndroid Build Coastguard Worker     float value = 0.0f;
1568*c8dee2aaSAndroid Build Coastguard Worker     for (int idx = 9 * N; idx < 27 * N; ++idx) {
1569*c8dee2aaSAndroid Build Coastguard Worker         slots[idx] = value;
1570*c8dee2aaSAndroid Build Coastguard Worker         value = fmodf(value + 1.0f, 25.0f);
1571*c8dee2aaSAndroid Build Coastguard Worker     }
1572*c8dee2aaSAndroid Build Coastguard Worker 
1573*c8dee2aaSAndroid Build Coastguard Worker     // Perform a 3x3 matrix multiply.
1574*c8dee2aaSAndroid Build Coastguard Worker     SkArenaAlloc alloc(/*firstHeapAllocation=*/256);
1575*c8dee2aaSAndroid Build Coastguard Worker     SkRasterPipeline p(&alloc);
1576*c8dee2aaSAndroid Build Coastguard Worker     SkRasterPipeline_MatrixMultiplyCtx ctx;
1577*c8dee2aaSAndroid Build Coastguard Worker     ctx.dst = 0;
1578*c8dee2aaSAndroid Build Coastguard Worker     ctx.leftColumns = ctx.leftRows = ctx.rightColumns = ctx.rightRows = 3;
1579*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::set_base_pointer, &slots[0]);
1580*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::matrix_multiply_3, SkRPCtxUtils::Pack(ctx, &alloc));
1581*c8dee2aaSAndroid Build Coastguard Worker     p.run(0,0,1,1);
1582*c8dee2aaSAndroid Build Coastguard Worker 
1583*c8dee2aaSAndroid Build Coastguard Worker     // Verify that the result slots hold a 3x3 matrix multiply.
1584*c8dee2aaSAndroid Build Coastguard Worker     const float* const destPtr[3][3] = {
1585*c8dee2aaSAndroid Build Coastguard Worker             {&slots[0 * N], &slots[1 * N], &slots[2 * N]},
1586*c8dee2aaSAndroid Build Coastguard Worker             {&slots[3 * N], &slots[4 * N], &slots[5 * N]},
1587*c8dee2aaSAndroid Build Coastguard Worker             {&slots[6 * N], &slots[7 * N], &slots[8 * N]},
1588*c8dee2aaSAndroid Build Coastguard Worker     };
1589*c8dee2aaSAndroid Build Coastguard Worker     const float* const leftMtx[3][3] = {
1590*c8dee2aaSAndroid Build Coastguard Worker             {&slots[9 * N],  &slots[10 * N], &slots[11 * N]},
1591*c8dee2aaSAndroid Build Coastguard Worker             {&slots[12 * N], &slots[13 * N], &slots[14 * N]},
1592*c8dee2aaSAndroid Build Coastguard Worker             {&slots[15 * N], &slots[16 * N], &slots[17 * N]},
1593*c8dee2aaSAndroid Build Coastguard Worker     };
1594*c8dee2aaSAndroid Build Coastguard Worker     const float* const rightMtx[3][3] = {
1595*c8dee2aaSAndroid Build Coastguard Worker             {&slots[18 * N], &slots[19 * N], &slots[20 * N]},
1596*c8dee2aaSAndroid Build Coastguard Worker             {&slots[21 * N], &slots[22 * N], &slots[23 * N]},
1597*c8dee2aaSAndroid Build Coastguard Worker             {&slots[24 * N], &slots[25 * N], &slots[26 * N]},
1598*c8dee2aaSAndroid Build Coastguard Worker     };
1599*c8dee2aaSAndroid Build Coastguard Worker 
1600*c8dee2aaSAndroid Build Coastguard Worker     for (int c = 0; c < 3; ++c) {
1601*c8dee2aaSAndroid Build Coastguard Worker         for (int r = 0; r < 3; ++r) {
1602*c8dee2aaSAndroid Build Coastguard Worker             for (int lane = 0; lane < N; ++lane) {
1603*c8dee2aaSAndroid Build Coastguard Worker                 // Dot a vector from leftMtx[*][r] with rightMtx[c][*].
1604*c8dee2aaSAndroid Build Coastguard Worker                 float dot = 0;
1605*c8dee2aaSAndroid Build Coastguard Worker                 for (int n = 0; n < 3; ++n) {
1606*c8dee2aaSAndroid Build Coastguard Worker                     dot += leftMtx[n][r][lane] * rightMtx[c][n][lane];
1607*c8dee2aaSAndroid Build Coastguard Worker                 }
1608*c8dee2aaSAndroid Build Coastguard Worker                 REPORTER_ASSERT(reporter, destPtr[c][r][lane] == dot);
1609*c8dee2aaSAndroid Build Coastguard Worker             }
1610*c8dee2aaSAndroid Build Coastguard Worker         }
1611*c8dee2aaSAndroid Build Coastguard Worker     }
1612*c8dee2aaSAndroid Build Coastguard Worker }
1613*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRasterPipeline_MatrixMultiply4x4,reporter)1614*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRasterPipeline_MatrixMultiply4x4, reporter) {
1615*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) float slots[48 * SkRasterPipeline_kMaxStride_highp];
1616*c8dee2aaSAndroid Build Coastguard Worker     const int N = SkOpts::raster_pipeline_highp_stride;
1617*c8dee2aaSAndroid Build Coastguard Worker 
1618*c8dee2aaSAndroid Build Coastguard Worker     // Populate the left- and right-matrix data. Slots 0-8 hold the result and are left as-is.
1619*c8dee2aaSAndroid Build Coastguard Worker     // To keep results in full-precision float range, we only set values between 0 and 25.
1620*c8dee2aaSAndroid Build Coastguard Worker     float value = 0.0f;
1621*c8dee2aaSAndroid Build Coastguard Worker     for (int idx = 16 * N; idx < 48 * N; ++idx) {
1622*c8dee2aaSAndroid Build Coastguard Worker         slots[idx] = value;
1623*c8dee2aaSAndroid Build Coastguard Worker         value = fmodf(value + 1.0f, 25.0f);
1624*c8dee2aaSAndroid Build Coastguard Worker     }
1625*c8dee2aaSAndroid Build Coastguard Worker 
1626*c8dee2aaSAndroid Build Coastguard Worker     // Perform a 4x4 matrix multiply.
1627*c8dee2aaSAndroid Build Coastguard Worker     SkArenaAlloc alloc(/*firstHeapAllocation=*/256);
1628*c8dee2aaSAndroid Build Coastguard Worker     SkRasterPipeline p(&alloc);
1629*c8dee2aaSAndroid Build Coastguard Worker     SkRasterPipeline_MatrixMultiplyCtx ctx;
1630*c8dee2aaSAndroid Build Coastguard Worker     ctx.dst = 0;
1631*c8dee2aaSAndroid Build Coastguard Worker     ctx.leftColumns = ctx.leftRows = ctx.rightColumns = ctx.rightRows = 4;
1632*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::set_base_pointer, &slots[0]);
1633*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::matrix_multiply_4, SkRPCtxUtils::Pack(ctx, &alloc));
1634*c8dee2aaSAndroid Build Coastguard Worker     p.run(0,0,1,1);
1635*c8dee2aaSAndroid Build Coastguard Worker 
1636*c8dee2aaSAndroid Build Coastguard Worker     // Verify that the result slots hold a 4x4 matrix multiply.
1637*c8dee2aaSAndroid Build Coastguard Worker     const float* const destPtr[4][4] = {
1638*c8dee2aaSAndroid Build Coastguard Worker             {&slots[0 * N],  &slots[1 * N],  &slots[2 * N],  &slots[3 * N]},
1639*c8dee2aaSAndroid Build Coastguard Worker             {&slots[4 * N],  &slots[5 * N],  &slots[6 * N],  &slots[7 * N]},
1640*c8dee2aaSAndroid Build Coastguard Worker             {&slots[8 * N],  &slots[9 * N],  &slots[10 * N], &slots[11 * N]},
1641*c8dee2aaSAndroid Build Coastguard Worker             {&slots[12 * N], &slots[13 * N], &slots[14 * N], &slots[15 * N]},
1642*c8dee2aaSAndroid Build Coastguard Worker     };
1643*c8dee2aaSAndroid Build Coastguard Worker     const float* const leftMtx[4][4] = {
1644*c8dee2aaSAndroid Build Coastguard Worker             {&slots[16 * N], &slots[17 * N], &slots[18 * N], &slots[19 * N]},
1645*c8dee2aaSAndroid Build Coastguard Worker             {&slots[20 * N], &slots[21 * N], &slots[22 * N], &slots[23 * N]},
1646*c8dee2aaSAndroid Build Coastguard Worker             {&slots[24 * N], &slots[25 * N], &slots[26 * N], &slots[27 * N]},
1647*c8dee2aaSAndroid Build Coastguard Worker             {&slots[28 * N], &slots[29 * N], &slots[30 * N], &slots[31 * N]},
1648*c8dee2aaSAndroid Build Coastguard Worker     };
1649*c8dee2aaSAndroid Build Coastguard Worker     const float* const rightMtx[4][4] = {
1650*c8dee2aaSAndroid Build Coastguard Worker             {&slots[32 * N], &slots[33 * N], &slots[34 * N], &slots[35 * N]},
1651*c8dee2aaSAndroid Build Coastguard Worker             {&slots[36 * N], &slots[37 * N], &slots[38 * N], &slots[39 * N]},
1652*c8dee2aaSAndroid Build Coastguard Worker             {&slots[40 * N], &slots[41 * N], &slots[42 * N], &slots[43 * N]},
1653*c8dee2aaSAndroid Build Coastguard Worker             {&slots[44 * N], &slots[45 * N], &slots[46 * N], &slots[47 * N]},
1654*c8dee2aaSAndroid Build Coastguard Worker     };
1655*c8dee2aaSAndroid Build Coastguard Worker 
1656*c8dee2aaSAndroid Build Coastguard Worker     for (int c = 0; c < 4; ++c) {
1657*c8dee2aaSAndroid Build Coastguard Worker         for (int r = 0; r < 4; ++r) {
1658*c8dee2aaSAndroid Build Coastguard Worker             for (int lane = 0; lane < N; ++lane) {
1659*c8dee2aaSAndroid Build Coastguard Worker                 // Dot a vector from leftMtx[*][r] with rightMtx[c][*].
1660*c8dee2aaSAndroid Build Coastguard Worker                 float dot = 0;
1661*c8dee2aaSAndroid Build Coastguard Worker                 for (int n = 0; n < 4; ++n) {
1662*c8dee2aaSAndroid Build Coastguard Worker                     dot += leftMtx[n][r][lane] * rightMtx[c][n][lane];
1663*c8dee2aaSAndroid Build Coastguard Worker                 }
1664*c8dee2aaSAndroid Build Coastguard Worker                 REPORTER_ASSERT(reporter, destPtr[c][r][lane] == dot);
1665*c8dee2aaSAndroid Build Coastguard Worker             }
1666*c8dee2aaSAndroid Build Coastguard Worker         }
1667*c8dee2aaSAndroid Build Coastguard Worker     }
1668*c8dee2aaSAndroid Build Coastguard Worker }
1669*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRasterPipeline_FloatArithmeticWithNSlots,r)1670*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRasterPipeline_FloatArithmeticWithNSlots, r) {
1671*c8dee2aaSAndroid Build Coastguard Worker     // Allocate space for 5 dest and 5 source slots.
1672*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) float slots[10 * SkRasterPipeline_kMaxStride_highp];
1673*c8dee2aaSAndroid Build Coastguard Worker     const int N = SkOpts::raster_pipeline_highp_stride;
1674*c8dee2aaSAndroid Build Coastguard Worker 
1675*c8dee2aaSAndroid Build Coastguard Worker     struct ArithmeticOp {
1676*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipelineOp stage;
1677*c8dee2aaSAndroid Build Coastguard Worker         std::function<float(float, float)> verify;
1678*c8dee2aaSAndroid Build Coastguard Worker     };
1679*c8dee2aaSAndroid Build Coastguard Worker 
1680*c8dee2aaSAndroid Build Coastguard Worker     static const ArithmeticOp kArithmeticOps[] = {
1681*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::add_n_floats, [](float a, float b) { return a + b; }},
1682*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::sub_n_floats, [](float a, float b) { return a - b; }},
1683*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::mul_n_floats, [](float a, float b) { return a * b; }},
1684*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::div_n_floats, [](float a, float b) { return a / b; }},
1685*c8dee2aaSAndroid Build Coastguard Worker     };
1686*c8dee2aaSAndroid Build Coastguard Worker 
1687*c8dee2aaSAndroid Build Coastguard Worker     for (const ArithmeticOp& op : kArithmeticOps) {
1688*c8dee2aaSAndroid Build Coastguard Worker         for (int numSlotsAffected = 1; numSlotsAffected <= 5; ++numSlotsAffected) {
1689*c8dee2aaSAndroid Build Coastguard Worker             // Initialize the slot values to 1,2,3...
1690*c8dee2aaSAndroid Build Coastguard Worker             std::iota(&slots[0], &slots[10 * N], 1.0f);
1691*c8dee2aaSAndroid Build Coastguard Worker 
1692*c8dee2aaSAndroid Build Coastguard Worker             // Run the arithmetic op over our data.
1693*c8dee2aaSAndroid Build Coastguard Worker             SkArenaAlloc alloc(/*firstHeapAllocation=*/256);
1694*c8dee2aaSAndroid Build Coastguard Worker             SkRasterPipeline p(&alloc);
1695*c8dee2aaSAndroid Build Coastguard Worker             SkRasterPipeline_BinaryOpCtx ctx;
1696*c8dee2aaSAndroid Build Coastguard Worker             ctx.dst = 0;
1697*c8dee2aaSAndroid Build Coastguard Worker             ctx.src = numSlotsAffected * N * sizeof(float);
1698*c8dee2aaSAndroid Build Coastguard Worker             p.append(SkRasterPipelineOp::set_base_pointer, &slots[0]);
1699*c8dee2aaSAndroid Build Coastguard Worker             p.append(op.stage, SkRPCtxUtils::Pack(ctx, &alloc));
1700*c8dee2aaSAndroid Build Coastguard Worker             p.run(0,0,1,1);
1701*c8dee2aaSAndroid Build Coastguard Worker 
1702*c8dee2aaSAndroid Build Coastguard Worker             // Verify that the affected slots now equal (1,2,3...) op (4,5,6...).
1703*c8dee2aaSAndroid Build Coastguard Worker             float leftValue = 1.0f;
1704*c8dee2aaSAndroid Build Coastguard Worker             float rightValue = float(numSlotsAffected * N) + 1.0f;
1705*c8dee2aaSAndroid Build Coastguard Worker             float* destPtr = &slots[0];
1706*c8dee2aaSAndroid Build Coastguard Worker             for (int checkSlot = 0; checkSlot < 10; ++checkSlot) {
1707*c8dee2aaSAndroid Build Coastguard Worker                 for (int checkLane = 0; checkLane < N; ++checkLane) {
1708*c8dee2aaSAndroid Build Coastguard Worker                     if (checkSlot < numSlotsAffected) {
1709*c8dee2aaSAndroid Build Coastguard Worker                         REPORTER_ASSERT(r, *destPtr == op.verify(leftValue, rightValue));
1710*c8dee2aaSAndroid Build Coastguard Worker                     } else {
1711*c8dee2aaSAndroid Build Coastguard Worker                         REPORTER_ASSERT(r, *destPtr == leftValue);
1712*c8dee2aaSAndroid Build Coastguard Worker                     }
1713*c8dee2aaSAndroid Build Coastguard Worker 
1714*c8dee2aaSAndroid Build Coastguard Worker                     ++destPtr;
1715*c8dee2aaSAndroid Build Coastguard Worker                     leftValue += 1.0f;
1716*c8dee2aaSAndroid Build Coastguard Worker                     rightValue += 1.0f;
1717*c8dee2aaSAndroid Build Coastguard Worker                 }
1718*c8dee2aaSAndroid Build Coastguard Worker             }
1719*c8dee2aaSAndroid Build Coastguard Worker         }
1720*c8dee2aaSAndroid Build Coastguard Worker     }
1721*c8dee2aaSAndroid Build Coastguard Worker }
1722*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRasterPipeline_FloatArithmeticWithHardcodedSlots,r)1723*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRasterPipeline_FloatArithmeticWithHardcodedSlots, r) {
1724*c8dee2aaSAndroid Build Coastguard Worker     // Allocate space for 5 dest and 5 source slots.
1725*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) float slots[10 * SkRasterPipeline_kMaxStride_highp];
1726*c8dee2aaSAndroid Build Coastguard Worker     const int N = SkOpts::raster_pipeline_highp_stride;
1727*c8dee2aaSAndroid Build Coastguard Worker 
1728*c8dee2aaSAndroid Build Coastguard Worker     struct ArithmeticOp {
1729*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipelineOp stage;
1730*c8dee2aaSAndroid Build Coastguard Worker         int numSlotsAffected;
1731*c8dee2aaSAndroid Build Coastguard Worker         std::function<float(float, float)> verify;
1732*c8dee2aaSAndroid Build Coastguard Worker     };
1733*c8dee2aaSAndroid Build Coastguard Worker 
1734*c8dee2aaSAndroid Build Coastguard Worker     static const ArithmeticOp kArithmeticOps[] = {
1735*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::add_float,    1, [](float a, float b) { return a + b; }},
1736*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::sub_float,    1, [](float a, float b) { return a - b; }},
1737*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::mul_float,    1, [](float a, float b) { return a * b; }},
1738*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::div_float,    1, [](float a, float b) { return a / b; }},
1739*c8dee2aaSAndroid Build Coastguard Worker 
1740*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::add_2_floats, 2, [](float a, float b) { return a + b; }},
1741*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::sub_2_floats, 2, [](float a, float b) { return a - b; }},
1742*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::mul_2_floats, 2, [](float a, float b) { return a * b; }},
1743*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::div_2_floats, 2, [](float a, float b) { return a / b; }},
1744*c8dee2aaSAndroid Build Coastguard Worker 
1745*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::add_3_floats, 3, [](float a, float b) { return a + b; }},
1746*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::sub_3_floats, 3, [](float a, float b) { return a - b; }},
1747*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::mul_3_floats, 3, [](float a, float b) { return a * b; }},
1748*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::div_3_floats, 3, [](float a, float b) { return a / b; }},
1749*c8dee2aaSAndroid Build Coastguard Worker 
1750*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::add_4_floats, 4, [](float a, float b) { return a + b; }},
1751*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::sub_4_floats, 4, [](float a, float b) { return a - b; }},
1752*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::mul_4_floats, 4, [](float a, float b) { return a * b; }},
1753*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::div_4_floats, 4, [](float a, float b) { return a / b; }},
1754*c8dee2aaSAndroid Build Coastguard Worker     };
1755*c8dee2aaSAndroid Build Coastguard Worker 
1756*c8dee2aaSAndroid Build Coastguard Worker     for (const ArithmeticOp& op : kArithmeticOps) {
1757*c8dee2aaSAndroid Build Coastguard Worker         // Initialize the slot values to 1,2,3...
1758*c8dee2aaSAndroid Build Coastguard Worker         std::iota(&slots[0], &slots[10 * N], 1.0f);
1759*c8dee2aaSAndroid Build Coastguard Worker 
1760*c8dee2aaSAndroid Build Coastguard Worker         // Run the arithmetic op over our data.
1761*c8dee2aaSAndroid Build Coastguard Worker         SkArenaAlloc alloc(/*firstHeapAllocation=*/256);
1762*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline p(&alloc);
1763*c8dee2aaSAndroid Build Coastguard Worker         p.append(op.stage, &slots[0]);
1764*c8dee2aaSAndroid Build Coastguard Worker         p.run(0,0,1,1);
1765*c8dee2aaSAndroid Build Coastguard Worker 
1766*c8dee2aaSAndroid Build Coastguard Worker         // Verify that the affected slots now equal (1,2,3...) op (4,5,6...).
1767*c8dee2aaSAndroid Build Coastguard Worker         float leftValue = 1.0f;
1768*c8dee2aaSAndroid Build Coastguard Worker         float rightValue = float(op.numSlotsAffected * N) + 1.0f;
1769*c8dee2aaSAndroid Build Coastguard Worker         float* destPtr = &slots[0];
1770*c8dee2aaSAndroid Build Coastguard Worker         for (int checkSlot = 0; checkSlot < 10; ++checkSlot) {
1771*c8dee2aaSAndroid Build Coastguard Worker             for (int checkLane = 0; checkLane < N; ++checkLane) {
1772*c8dee2aaSAndroid Build Coastguard Worker                 if (checkSlot < op.numSlotsAffected) {
1773*c8dee2aaSAndroid Build Coastguard Worker                     REPORTER_ASSERT(r, *destPtr == op.verify(leftValue, rightValue));
1774*c8dee2aaSAndroid Build Coastguard Worker                 } else {
1775*c8dee2aaSAndroid Build Coastguard Worker                     REPORTER_ASSERT(r, *destPtr == leftValue);
1776*c8dee2aaSAndroid Build Coastguard Worker                 }
1777*c8dee2aaSAndroid Build Coastguard Worker 
1778*c8dee2aaSAndroid Build Coastguard Worker                 ++destPtr;
1779*c8dee2aaSAndroid Build Coastguard Worker                 leftValue += 1.0f;
1780*c8dee2aaSAndroid Build Coastguard Worker                 rightValue += 1.0f;
1781*c8dee2aaSAndroid Build Coastguard Worker             }
1782*c8dee2aaSAndroid Build Coastguard Worker         }
1783*c8dee2aaSAndroid Build Coastguard Worker     }
1784*c8dee2aaSAndroid Build Coastguard Worker }
1785*c8dee2aaSAndroid Build Coastguard Worker 
divide_unsigned(int a,int b)1786*c8dee2aaSAndroid Build Coastguard Worker static int divide_unsigned(int a, int b) { return int(uint32_t(a) / uint32_t(b)); }
min_unsigned(int a,int b)1787*c8dee2aaSAndroid Build Coastguard Worker static int min_unsigned   (int a, int b) { return uint32_t(a) < uint32_t(b) ? a : b; }
max_unsigned(int a,int b)1788*c8dee2aaSAndroid Build Coastguard Worker static int max_unsigned   (int a, int b) { return uint32_t(a) > uint32_t(b) ? a : b; }
1789*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRasterPipeline_IntArithmeticWithNSlots,r)1790*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRasterPipeline_IntArithmeticWithNSlots, r) {
1791*c8dee2aaSAndroid Build Coastguard Worker     // Allocate space for 5 dest and 5 source slots.
1792*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) int slots[10 * SkRasterPipeline_kMaxStride_highp];
1793*c8dee2aaSAndroid Build Coastguard Worker     const int N = SkOpts::raster_pipeline_highp_stride;
1794*c8dee2aaSAndroid Build Coastguard Worker 
1795*c8dee2aaSAndroid Build Coastguard Worker     struct ArithmeticOp {
1796*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipelineOp stage;
1797*c8dee2aaSAndroid Build Coastguard Worker         std::function<int(int, int)> verify;
1798*c8dee2aaSAndroid Build Coastguard Worker     };
1799*c8dee2aaSAndroid Build Coastguard Worker 
1800*c8dee2aaSAndroid Build Coastguard Worker     static const ArithmeticOp kArithmeticOps[] = {
1801*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::add_n_ints,         [](int a, int b) { return a + b; }},
1802*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::sub_n_ints,         [](int a, int b) { return a - b; }},
1803*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::mul_n_ints,         [](int a, int b) { return a * b; }},
1804*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::div_n_ints,         [](int a, int b) { return a / b; }},
1805*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::div_n_uints,        divide_unsigned},
1806*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::bitwise_and_n_ints, [](int a, int b) { return a & b; }},
1807*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::bitwise_or_n_ints,  [](int a, int b) { return a | b; }},
1808*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::bitwise_xor_n_ints, [](int a, int b) { return a ^ b; }},
1809*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::min_n_ints,         [](int a, int b) { return a < b ? a : b; }},
1810*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::min_n_uints,        min_unsigned},
1811*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::max_n_ints,         [](int a, int b) { return a > b ? a : b; }},
1812*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::max_n_uints,        max_unsigned},
1813*c8dee2aaSAndroid Build Coastguard Worker     };
1814*c8dee2aaSAndroid Build Coastguard Worker 
1815*c8dee2aaSAndroid Build Coastguard Worker     for (const ArithmeticOp& op : kArithmeticOps) {
1816*c8dee2aaSAndroid Build Coastguard Worker         for (int numSlotsAffected = 1; numSlotsAffected <= 5; ++numSlotsAffected) {
1817*c8dee2aaSAndroid Build Coastguard Worker             // Initialize the slot values to 1,2,3...
1818*c8dee2aaSAndroid Build Coastguard Worker             std::iota(&slots[0], &slots[10 * N], 1);
1819*c8dee2aaSAndroid Build Coastguard Worker             int leftValue = slots[0];
1820*c8dee2aaSAndroid Build Coastguard Worker             int rightValue = slots[numSlotsAffected * N];
1821*c8dee2aaSAndroid Build Coastguard Worker 
1822*c8dee2aaSAndroid Build Coastguard Worker             // Run the op (e.g. `add_n_ints`) over our data.
1823*c8dee2aaSAndroid Build Coastguard Worker             SkArenaAlloc alloc(/*firstHeapAllocation=*/256);
1824*c8dee2aaSAndroid Build Coastguard Worker             SkRasterPipeline p(&alloc);
1825*c8dee2aaSAndroid Build Coastguard Worker             SkRasterPipeline_BinaryOpCtx ctx;
1826*c8dee2aaSAndroid Build Coastguard Worker             ctx.dst = 0;
1827*c8dee2aaSAndroid Build Coastguard Worker             ctx.src = numSlotsAffected * N * sizeof(float);
1828*c8dee2aaSAndroid Build Coastguard Worker             p.append(SkRasterPipelineOp::set_base_pointer, &slots[0]);
1829*c8dee2aaSAndroid Build Coastguard Worker             p.append(op.stage, SkRPCtxUtils::Pack(ctx, &alloc));
1830*c8dee2aaSAndroid Build Coastguard Worker             p.run(0,0,1,1);
1831*c8dee2aaSAndroid Build Coastguard Worker 
1832*c8dee2aaSAndroid Build Coastguard Worker             // Verify that the affected slots now equal (1,2,3...) op (4,5,6...).
1833*c8dee2aaSAndroid Build Coastguard Worker             int* destPtr = &slots[0];
1834*c8dee2aaSAndroid Build Coastguard Worker             for (int checkSlot = 0; checkSlot < 10; ++checkSlot) {
1835*c8dee2aaSAndroid Build Coastguard Worker                 for (int checkLane = 0; checkLane < N; ++checkLane) {
1836*c8dee2aaSAndroid Build Coastguard Worker                     if (checkSlot < numSlotsAffected) {
1837*c8dee2aaSAndroid Build Coastguard Worker                         REPORTER_ASSERT(r, *destPtr == op.verify(leftValue, rightValue));
1838*c8dee2aaSAndroid Build Coastguard Worker                     } else {
1839*c8dee2aaSAndroid Build Coastguard Worker                         REPORTER_ASSERT(r, *destPtr == leftValue);
1840*c8dee2aaSAndroid Build Coastguard Worker                     }
1841*c8dee2aaSAndroid Build Coastguard Worker 
1842*c8dee2aaSAndroid Build Coastguard Worker                     ++destPtr;
1843*c8dee2aaSAndroid Build Coastguard Worker                     leftValue += 1;
1844*c8dee2aaSAndroid Build Coastguard Worker                     rightValue += 1;
1845*c8dee2aaSAndroid Build Coastguard Worker                 }
1846*c8dee2aaSAndroid Build Coastguard Worker             }
1847*c8dee2aaSAndroid Build Coastguard Worker         }
1848*c8dee2aaSAndroid Build Coastguard Worker     }
1849*c8dee2aaSAndroid Build Coastguard Worker }
1850*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRasterPipeline_IntArithmeticWithHardcodedSlots,r)1851*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRasterPipeline_IntArithmeticWithHardcodedSlots, r) {
1852*c8dee2aaSAndroid Build Coastguard Worker     // Allocate space for 5 dest and 5 source slots.
1853*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) int slots[10 * SkRasterPipeline_kMaxStride_highp];
1854*c8dee2aaSAndroid Build Coastguard Worker     const int N = SkOpts::raster_pipeline_highp_stride;
1855*c8dee2aaSAndroid Build Coastguard Worker 
1856*c8dee2aaSAndroid Build Coastguard Worker     struct ArithmeticOp {
1857*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipelineOp stage;
1858*c8dee2aaSAndroid Build Coastguard Worker         int numSlotsAffected;
1859*c8dee2aaSAndroid Build Coastguard Worker         std::function<int(int, int)> verify;
1860*c8dee2aaSAndroid Build Coastguard Worker     };
1861*c8dee2aaSAndroid Build Coastguard Worker 
1862*c8dee2aaSAndroid Build Coastguard Worker     static const ArithmeticOp kArithmeticOps[] = {
1863*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::add_int,            1, [](int a, int b) { return a + b; }},
1864*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::sub_int,            1, [](int a, int b) { return a - b; }},
1865*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::mul_int,            1, [](int a, int b) { return a * b; }},
1866*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::div_int,            1, [](int a, int b) { return a / b; }},
1867*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::div_uint,           1, divide_unsigned},
1868*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::bitwise_and_int,    1, [](int a, int b) { return a & b; }},
1869*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::bitwise_or_int,     1, [](int a, int b) { return a | b; }},
1870*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::bitwise_xor_int,    1, [](int a, int b) { return a ^ b; }},
1871*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::min_int,            1, [](int a, int b) { return a < b ? a: b; }},
1872*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::min_uint,           1, min_unsigned},
1873*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::max_int,            1, [](int a, int b) { return a > b ? a: b; }},
1874*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::max_uint,           1, max_unsigned},
1875*c8dee2aaSAndroid Build Coastguard Worker 
1876*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::add_2_ints,         2, [](int a, int b) { return a + b; }},
1877*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::sub_2_ints,         2, [](int a, int b) { return a - b; }},
1878*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::mul_2_ints,         2, [](int a, int b) { return a * b; }},
1879*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::div_2_ints,         2, [](int a, int b) { return a / b; }},
1880*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::div_2_uints,        2, divide_unsigned},
1881*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::bitwise_and_2_ints, 2, [](int a, int b) { return a & b; }},
1882*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::bitwise_or_2_ints,  2, [](int a, int b) { return a | b; }},
1883*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::bitwise_xor_2_ints, 2, [](int a, int b) { return a ^ b; }},
1884*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::min_2_ints,         2, [](int a, int b) { return a < b ? a: b; }},
1885*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::min_2_uints,        2, min_unsigned},
1886*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::max_2_ints,         2, [](int a, int b) { return a > b ? a: b; }},
1887*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::max_2_uints,        2, max_unsigned},
1888*c8dee2aaSAndroid Build Coastguard Worker 
1889*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::add_3_ints,         3, [](int a, int b) { return a + b; }},
1890*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::sub_3_ints,         3, [](int a, int b) { return a - b; }},
1891*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::mul_3_ints,         3, [](int a, int b) { return a * b; }},
1892*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::div_3_ints,         3, [](int a, int b) { return a / b; }},
1893*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::div_3_uints,        3, divide_unsigned},
1894*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::bitwise_and_3_ints, 3, [](int a, int b) { return a & b; }},
1895*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::bitwise_or_3_ints,  3, [](int a, int b) { return a | b; }},
1896*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::bitwise_xor_3_ints, 3, [](int a, int b) { return a ^ b; }},
1897*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::min_3_ints,         3, [](int a, int b) { return a < b ? a: b; }},
1898*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::min_3_uints,        3, min_unsigned},
1899*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::max_3_ints,         3, [](int a, int b) { return a > b ? a: b; }},
1900*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::max_3_uints,        3, max_unsigned},
1901*c8dee2aaSAndroid Build Coastguard Worker 
1902*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::add_4_ints,         4, [](int a, int b) { return a + b; }},
1903*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::sub_4_ints,         4, [](int a, int b) { return a - b; }},
1904*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::mul_4_ints,         4, [](int a, int b) { return a * b; }},
1905*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::div_4_ints,         4, [](int a, int b) { return a / b; }},
1906*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::div_4_uints,        4, divide_unsigned},
1907*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::bitwise_and_4_ints, 4, [](int a, int b) { return a & b; }},
1908*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::bitwise_or_4_ints,  4, [](int a, int b) { return a | b; }},
1909*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::bitwise_xor_4_ints, 4, [](int a, int b) { return a ^ b; }},
1910*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::min_4_ints,         4, [](int a, int b) { return a < b ? a: b; }},
1911*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::min_4_uints,        4, min_unsigned},
1912*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::max_4_ints,         4, [](int a, int b) { return a > b ? a: b; }},
1913*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::max_4_uints,        4, max_unsigned},
1914*c8dee2aaSAndroid Build Coastguard Worker     };
1915*c8dee2aaSAndroid Build Coastguard Worker 
1916*c8dee2aaSAndroid Build Coastguard Worker     for (const ArithmeticOp& op : kArithmeticOps) {
1917*c8dee2aaSAndroid Build Coastguard Worker         // Initialize the slot values to 1,2,3...
1918*c8dee2aaSAndroid Build Coastguard Worker         std::iota(&slots[0], &slots[10 * N], 1);
1919*c8dee2aaSAndroid Build Coastguard Worker         int leftValue = slots[0];
1920*c8dee2aaSAndroid Build Coastguard Worker         int rightValue = slots[op.numSlotsAffected * N];
1921*c8dee2aaSAndroid Build Coastguard Worker 
1922*c8dee2aaSAndroid Build Coastguard Worker         // Run the op (e.g. `add_2_ints`) over our data.
1923*c8dee2aaSAndroid Build Coastguard Worker         SkArenaAlloc alloc(/*firstHeapAllocation=*/256);
1924*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline p(&alloc);
1925*c8dee2aaSAndroid Build Coastguard Worker         p.append(op.stage, &slots[0]);
1926*c8dee2aaSAndroid Build Coastguard Worker         p.run(0,0,1,1);
1927*c8dee2aaSAndroid Build Coastguard Worker 
1928*c8dee2aaSAndroid Build Coastguard Worker         // Verify that the affected slots now equal (1,2,3...) op (4,5,6...).
1929*c8dee2aaSAndroid Build Coastguard Worker         int* destPtr = &slots[0];
1930*c8dee2aaSAndroid Build Coastguard Worker         for (int checkSlot = 0; checkSlot < 10; ++checkSlot) {
1931*c8dee2aaSAndroid Build Coastguard Worker             for (int checkLane = 0; checkLane < N; ++checkLane) {
1932*c8dee2aaSAndroid Build Coastguard Worker                 if (checkSlot < op.numSlotsAffected) {
1933*c8dee2aaSAndroid Build Coastguard Worker                     REPORTER_ASSERT(r, *destPtr == op.verify(leftValue, rightValue));
1934*c8dee2aaSAndroid Build Coastguard Worker                 } else {
1935*c8dee2aaSAndroid Build Coastguard Worker                     REPORTER_ASSERT(r, *destPtr == leftValue);
1936*c8dee2aaSAndroid Build Coastguard Worker                 }
1937*c8dee2aaSAndroid Build Coastguard Worker 
1938*c8dee2aaSAndroid Build Coastguard Worker                 ++destPtr;
1939*c8dee2aaSAndroid Build Coastguard Worker                 leftValue += 1;
1940*c8dee2aaSAndroid Build Coastguard Worker                 rightValue += 1;
1941*c8dee2aaSAndroid Build Coastguard Worker             }
1942*c8dee2aaSAndroid Build Coastguard Worker         }
1943*c8dee2aaSAndroid Build Coastguard Worker     }
1944*c8dee2aaSAndroid Build Coastguard Worker }
1945*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRasterPipeline_CompareFloatsWithNSlots,r)1946*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRasterPipeline_CompareFloatsWithNSlots, r) {
1947*c8dee2aaSAndroid Build Coastguard Worker     // Allocate space for 5 dest and 5 source slots.
1948*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) float slots[10 * SkRasterPipeline_kMaxStride_highp];
1949*c8dee2aaSAndroid Build Coastguard Worker     const int N = SkOpts::raster_pipeline_highp_stride;
1950*c8dee2aaSAndroid Build Coastguard Worker 
1951*c8dee2aaSAndroid Build Coastguard Worker     struct CompareOp {
1952*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipelineOp stage;
1953*c8dee2aaSAndroid Build Coastguard Worker         std::function<bool(float, float)> verify;
1954*c8dee2aaSAndroid Build Coastguard Worker     };
1955*c8dee2aaSAndroid Build Coastguard Worker 
1956*c8dee2aaSAndroid Build Coastguard Worker     static const CompareOp kCompareOps[] = {
1957*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cmpeq_n_floats, [](float a, float b) { return a == b; }},
1958*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cmpne_n_floats, [](float a, float b) { return a != b; }},
1959*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cmplt_n_floats, [](float a, float b) { return a <  b; }},
1960*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cmple_n_floats, [](float a, float b) { return a <= b; }},
1961*c8dee2aaSAndroid Build Coastguard Worker     };
1962*c8dee2aaSAndroid Build Coastguard Worker 
1963*c8dee2aaSAndroid Build Coastguard Worker     for (const CompareOp& op : kCompareOps) {
1964*c8dee2aaSAndroid Build Coastguard Worker         for (int numSlotsAffected = 1; numSlotsAffected <= 5; ++numSlotsAffected) {
1965*c8dee2aaSAndroid Build Coastguard Worker             // Initialize the slot values to 0,1,2,0,1,2,0,1,2...
1966*c8dee2aaSAndroid Build Coastguard Worker             for (int index = 0; index < 10 * N; ++index) {
1967*c8dee2aaSAndroid Build Coastguard Worker                 slots[index] = std::fmod(index, 3.0f);
1968*c8dee2aaSAndroid Build Coastguard Worker             }
1969*c8dee2aaSAndroid Build Coastguard Worker 
1970*c8dee2aaSAndroid Build Coastguard Worker             float leftValue  = slots[0];
1971*c8dee2aaSAndroid Build Coastguard Worker             float rightValue = slots[numSlotsAffected * N];
1972*c8dee2aaSAndroid Build Coastguard Worker 
1973*c8dee2aaSAndroid Build Coastguard Worker             // Run the comparison op over our data.
1974*c8dee2aaSAndroid Build Coastguard Worker             SkArenaAlloc alloc(/*firstHeapAllocation=*/256);
1975*c8dee2aaSAndroid Build Coastguard Worker             SkRasterPipeline p(&alloc);
1976*c8dee2aaSAndroid Build Coastguard Worker             SkRasterPipeline_BinaryOpCtx ctx;
1977*c8dee2aaSAndroid Build Coastguard Worker             ctx.dst = 0;
1978*c8dee2aaSAndroid Build Coastguard Worker             ctx.src = numSlotsAffected * N * sizeof(float);
1979*c8dee2aaSAndroid Build Coastguard Worker             p.append(SkRasterPipelineOp::set_base_pointer, &slots[0]);
1980*c8dee2aaSAndroid Build Coastguard Worker             p.append(op.stage, SkRPCtxUtils::Pack(ctx, &alloc));
1981*c8dee2aaSAndroid Build Coastguard Worker             p.run(0, 0, 1, 1);
1982*c8dee2aaSAndroid Build Coastguard Worker 
1983*c8dee2aaSAndroid Build Coastguard Worker             // Verify that the affected slots now contain "(0,1,2,0...) op (1,2,0,1...)".
1984*c8dee2aaSAndroid Build Coastguard Worker             float* destPtr = &slots[0];
1985*c8dee2aaSAndroid Build Coastguard Worker             for (int checkSlot = 0; checkSlot < 10; ++checkSlot) {
1986*c8dee2aaSAndroid Build Coastguard Worker                 for (int checkLane = 0; checkLane < N; ++checkLane) {
1987*c8dee2aaSAndroid Build Coastguard Worker                     if (checkSlot < numSlotsAffected) {
1988*c8dee2aaSAndroid Build Coastguard Worker                         bool compareIsTrue = op.verify(leftValue, rightValue);
1989*c8dee2aaSAndroid Build Coastguard Worker                         REPORTER_ASSERT(r, *(int*)destPtr == (compareIsTrue ? ~0 : 0));
1990*c8dee2aaSAndroid Build Coastguard Worker                     } else {
1991*c8dee2aaSAndroid Build Coastguard Worker                         REPORTER_ASSERT(r, *destPtr == leftValue);
1992*c8dee2aaSAndroid Build Coastguard Worker                     }
1993*c8dee2aaSAndroid Build Coastguard Worker 
1994*c8dee2aaSAndroid Build Coastguard Worker                     ++destPtr;
1995*c8dee2aaSAndroid Build Coastguard Worker                     leftValue = std::fmod(leftValue + 1.0f, 3.0f);
1996*c8dee2aaSAndroid Build Coastguard Worker                     rightValue = std::fmod(rightValue + 1.0f, 3.0f);
1997*c8dee2aaSAndroid Build Coastguard Worker                 }
1998*c8dee2aaSAndroid Build Coastguard Worker             }
1999*c8dee2aaSAndroid Build Coastguard Worker         }
2000*c8dee2aaSAndroid Build Coastguard Worker     }
2001*c8dee2aaSAndroid Build Coastguard Worker }
2002*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRasterPipeline_CompareFloatsWithHardcodedSlots,r)2003*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRasterPipeline_CompareFloatsWithHardcodedSlots, r) {
2004*c8dee2aaSAndroid Build Coastguard Worker     // Allocate space for 5 dest and 5 source slots.
2005*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) float slots[10 * SkRasterPipeline_kMaxStride_highp];
2006*c8dee2aaSAndroid Build Coastguard Worker     const int N = SkOpts::raster_pipeline_highp_stride;
2007*c8dee2aaSAndroid Build Coastguard Worker 
2008*c8dee2aaSAndroid Build Coastguard Worker     struct CompareOp {
2009*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipelineOp stage;
2010*c8dee2aaSAndroid Build Coastguard Worker         int numSlotsAffected;
2011*c8dee2aaSAndroid Build Coastguard Worker         std::function<bool(float, float)> verify;
2012*c8dee2aaSAndroid Build Coastguard Worker     };
2013*c8dee2aaSAndroid Build Coastguard Worker 
2014*c8dee2aaSAndroid Build Coastguard Worker     static const CompareOp kCompareOps[] = {
2015*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cmpeq_float,    1, [](float a, float b) { return a == b; }},
2016*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cmpne_float,    1, [](float a, float b) { return a != b; }},
2017*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cmplt_float,    1, [](float a, float b) { return a <  b; }},
2018*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cmple_float,    1, [](float a, float b) { return a <= b; }},
2019*c8dee2aaSAndroid Build Coastguard Worker 
2020*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cmpeq_2_floats, 2, [](float a, float b) { return a == b; }},
2021*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cmpne_2_floats, 2, [](float a, float b) { return a != b; }},
2022*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cmplt_2_floats, 2, [](float a, float b) { return a <  b; }},
2023*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cmple_2_floats, 2, [](float a, float b) { return a <= b; }},
2024*c8dee2aaSAndroid Build Coastguard Worker 
2025*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cmpeq_3_floats, 3, [](float a, float b) { return a == b; }},
2026*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cmpne_3_floats, 3, [](float a, float b) { return a != b; }},
2027*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cmplt_3_floats, 3, [](float a, float b) { return a <  b; }},
2028*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cmple_3_floats, 3, [](float a, float b) { return a <= b; }},
2029*c8dee2aaSAndroid Build Coastguard Worker 
2030*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cmpeq_4_floats, 4, [](float a, float b) { return a == b; }},
2031*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cmpne_4_floats, 4, [](float a, float b) { return a != b; }},
2032*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cmplt_4_floats, 4, [](float a, float b) { return a <  b; }},
2033*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cmple_4_floats, 4, [](float a, float b) { return a <= b; }},
2034*c8dee2aaSAndroid Build Coastguard Worker     };
2035*c8dee2aaSAndroid Build Coastguard Worker 
2036*c8dee2aaSAndroid Build Coastguard Worker     for (const CompareOp& op : kCompareOps) {
2037*c8dee2aaSAndroid Build Coastguard Worker         // Initialize the slot values to 0,1,2,0,1,2,0,1,2...
2038*c8dee2aaSAndroid Build Coastguard Worker         for (int index = 0; index < 10 * N; ++index) {
2039*c8dee2aaSAndroid Build Coastguard Worker             slots[index] = std::fmod(index, 3.0f);
2040*c8dee2aaSAndroid Build Coastguard Worker         }
2041*c8dee2aaSAndroid Build Coastguard Worker 
2042*c8dee2aaSAndroid Build Coastguard Worker         float leftValue  = slots[0];
2043*c8dee2aaSAndroid Build Coastguard Worker         float rightValue = slots[op.numSlotsAffected * N];
2044*c8dee2aaSAndroid Build Coastguard Worker 
2045*c8dee2aaSAndroid Build Coastguard Worker         // Run the comparison op over our data.
2046*c8dee2aaSAndroid Build Coastguard Worker         SkArenaAlloc alloc(/*firstHeapAllocation=*/256);
2047*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline p(&alloc);
2048*c8dee2aaSAndroid Build Coastguard Worker         p.append(op.stage, &slots[0]);
2049*c8dee2aaSAndroid Build Coastguard Worker         p.run(0, 0, 1, 1);
2050*c8dee2aaSAndroid Build Coastguard Worker 
2051*c8dee2aaSAndroid Build Coastguard Worker         // Verify that the affected slots now contain "(0,1,2,0...) op (1,2,0,1...)".
2052*c8dee2aaSAndroid Build Coastguard Worker         float* destPtr = &slots[0];
2053*c8dee2aaSAndroid Build Coastguard Worker         for (int checkSlot = 0; checkSlot < 10; ++checkSlot) {
2054*c8dee2aaSAndroid Build Coastguard Worker             for (int checkLane = 0; checkLane < N; ++checkLane) {
2055*c8dee2aaSAndroid Build Coastguard Worker                 if (checkSlot < op.numSlotsAffected) {
2056*c8dee2aaSAndroid Build Coastguard Worker                     bool compareIsTrue = op.verify(leftValue, rightValue);
2057*c8dee2aaSAndroid Build Coastguard Worker                     REPORTER_ASSERT(r, *(int*)destPtr == (compareIsTrue ? ~0 : 0));
2058*c8dee2aaSAndroid Build Coastguard Worker                 } else {
2059*c8dee2aaSAndroid Build Coastguard Worker                     REPORTER_ASSERT(r, *destPtr == leftValue);
2060*c8dee2aaSAndroid Build Coastguard Worker                 }
2061*c8dee2aaSAndroid Build Coastguard Worker 
2062*c8dee2aaSAndroid Build Coastguard Worker                 ++destPtr;
2063*c8dee2aaSAndroid Build Coastguard Worker                 leftValue = std::fmod(leftValue + 1.0f, 3.0f);
2064*c8dee2aaSAndroid Build Coastguard Worker                 rightValue = std::fmod(rightValue + 1.0f, 3.0f);
2065*c8dee2aaSAndroid Build Coastguard Worker             }
2066*c8dee2aaSAndroid Build Coastguard Worker         }
2067*c8dee2aaSAndroid Build Coastguard Worker     }
2068*c8dee2aaSAndroid Build Coastguard Worker }
2069*c8dee2aaSAndroid Build Coastguard Worker 
compare_lt_uint(int a,int b)2070*c8dee2aaSAndroid Build Coastguard Worker static bool compare_lt_uint  (int a, int b) { return uint32_t(a) <  uint32_t(b); }
compare_lteq_uint(int a,int b)2071*c8dee2aaSAndroid Build Coastguard Worker static bool compare_lteq_uint(int a, int b) { return uint32_t(a) <= uint32_t(b); }
2072*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRasterPipeline_CompareIntsWithNSlots,r)2073*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRasterPipeline_CompareIntsWithNSlots, r) {
2074*c8dee2aaSAndroid Build Coastguard Worker     // Allocate space for 5 dest and 5 source slots.
2075*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) int slots[10 * SkRasterPipeline_kMaxStride_highp];
2076*c8dee2aaSAndroid Build Coastguard Worker     const int N = SkOpts::raster_pipeline_highp_stride;
2077*c8dee2aaSAndroid Build Coastguard Worker 
2078*c8dee2aaSAndroid Build Coastguard Worker     struct CompareOp {
2079*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipelineOp stage;
2080*c8dee2aaSAndroid Build Coastguard Worker         std::function<bool(int, int)> verify;
2081*c8dee2aaSAndroid Build Coastguard Worker     };
2082*c8dee2aaSAndroid Build Coastguard Worker 
2083*c8dee2aaSAndroid Build Coastguard Worker     static const CompareOp kCompareOps[] = {
2084*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cmpeq_n_ints,  [](int a, int b) { return a == b; }},
2085*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cmpne_n_ints,  [](int a, int b) { return a != b; }},
2086*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cmplt_n_ints,  [](int a, int b) { return a <  b; }},
2087*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cmple_n_ints,  [](int a, int b) { return a <= b; }},
2088*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cmplt_n_uints, compare_lt_uint},
2089*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cmple_n_uints, compare_lteq_uint},
2090*c8dee2aaSAndroid Build Coastguard Worker     };
2091*c8dee2aaSAndroid Build Coastguard Worker 
2092*c8dee2aaSAndroid Build Coastguard Worker     for (const CompareOp& op : kCompareOps) {
2093*c8dee2aaSAndroid Build Coastguard Worker         for (int numSlotsAffected = 1; numSlotsAffected <= 5; ++numSlotsAffected) {
2094*c8dee2aaSAndroid Build Coastguard Worker             // Initialize the slot values to -1,0,1,-1,0,1,-1,0,1,-1...
2095*c8dee2aaSAndroid Build Coastguard Worker             for (int index = 0; index < 10 * N; ++index) {
2096*c8dee2aaSAndroid Build Coastguard Worker                 slots[index] = (index % 3) - 1;
2097*c8dee2aaSAndroid Build Coastguard Worker             }
2098*c8dee2aaSAndroid Build Coastguard Worker 
2099*c8dee2aaSAndroid Build Coastguard Worker             int leftValue = slots[0];
2100*c8dee2aaSAndroid Build Coastguard Worker             int rightValue = slots[numSlotsAffected * N];
2101*c8dee2aaSAndroid Build Coastguard Worker 
2102*c8dee2aaSAndroid Build Coastguard Worker             // Run the comparison op over our data.
2103*c8dee2aaSAndroid Build Coastguard Worker             SkArenaAlloc alloc(/*firstHeapAllocation=*/256);
2104*c8dee2aaSAndroid Build Coastguard Worker             SkRasterPipeline p(&alloc);
2105*c8dee2aaSAndroid Build Coastguard Worker             SkRasterPipeline_BinaryOpCtx ctx;
2106*c8dee2aaSAndroid Build Coastguard Worker             ctx.dst = 0;
2107*c8dee2aaSAndroid Build Coastguard Worker             ctx.src = sizeof(float) * numSlotsAffected * N;
2108*c8dee2aaSAndroid Build Coastguard Worker             p.append(SkRasterPipelineOp::set_base_pointer, &slots[0]);
2109*c8dee2aaSAndroid Build Coastguard Worker             p.append(op.stage, SkRPCtxUtils::Pack(ctx, &alloc));
2110*c8dee2aaSAndroid Build Coastguard Worker             p.run(0, 0, 1, 1);
2111*c8dee2aaSAndroid Build Coastguard Worker 
2112*c8dee2aaSAndroid Build Coastguard Worker             // Verify that the affected slots now contain "(-1,0,1,-1...) op (0,1,-1,0...)".
2113*c8dee2aaSAndroid Build Coastguard Worker             int* destPtr = &slots[0];
2114*c8dee2aaSAndroid Build Coastguard Worker             for (int checkSlot = 0; checkSlot < 10; ++checkSlot) {
2115*c8dee2aaSAndroid Build Coastguard Worker                 for (int checkLane = 0; checkLane < N; ++checkLane) {
2116*c8dee2aaSAndroid Build Coastguard Worker                     if (checkSlot < numSlotsAffected) {
2117*c8dee2aaSAndroid Build Coastguard Worker                         bool compareIsTrue = op.verify(leftValue, rightValue);
2118*c8dee2aaSAndroid Build Coastguard Worker                         REPORTER_ASSERT(r, *destPtr == (compareIsTrue ? ~0 : 0));
2119*c8dee2aaSAndroid Build Coastguard Worker                     } else {
2120*c8dee2aaSAndroid Build Coastguard Worker                         REPORTER_ASSERT(r, *destPtr == leftValue);
2121*c8dee2aaSAndroid Build Coastguard Worker                     }
2122*c8dee2aaSAndroid Build Coastguard Worker 
2123*c8dee2aaSAndroid Build Coastguard Worker                     ++destPtr;
2124*c8dee2aaSAndroid Build Coastguard Worker                     if (++leftValue == 2) {
2125*c8dee2aaSAndroid Build Coastguard Worker                         leftValue = -1;
2126*c8dee2aaSAndroid Build Coastguard Worker                     }
2127*c8dee2aaSAndroid Build Coastguard Worker                     if (++rightValue == 2) {
2128*c8dee2aaSAndroid Build Coastguard Worker                         rightValue = -1;
2129*c8dee2aaSAndroid Build Coastguard Worker                     }
2130*c8dee2aaSAndroid Build Coastguard Worker                 }
2131*c8dee2aaSAndroid Build Coastguard Worker             }
2132*c8dee2aaSAndroid Build Coastguard Worker         }
2133*c8dee2aaSAndroid Build Coastguard Worker     }
2134*c8dee2aaSAndroid Build Coastguard Worker }
2135*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRasterPipeline_CompareIntsWithHardcodedSlots,r)2136*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRasterPipeline_CompareIntsWithHardcodedSlots, r) {
2137*c8dee2aaSAndroid Build Coastguard Worker     // Allocate space for 5 dest and 5 source slots.
2138*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) int slots[10 * SkRasterPipeline_kMaxStride_highp];
2139*c8dee2aaSAndroid Build Coastguard Worker     const int N = SkOpts::raster_pipeline_highp_stride;
2140*c8dee2aaSAndroid Build Coastguard Worker 
2141*c8dee2aaSAndroid Build Coastguard Worker     struct CompareOp {
2142*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipelineOp stage;
2143*c8dee2aaSAndroid Build Coastguard Worker         int numSlotsAffected;
2144*c8dee2aaSAndroid Build Coastguard Worker         std::function<bool(int, int)> verify;
2145*c8dee2aaSAndroid Build Coastguard Worker     };
2146*c8dee2aaSAndroid Build Coastguard Worker 
2147*c8dee2aaSAndroid Build Coastguard Worker     static const CompareOp kCompareOps[] = {
2148*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cmpeq_int,     1, [](int a, int b) { return a == b; }},
2149*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cmpne_int,     1, [](int a, int b) { return a != b; }},
2150*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cmplt_int,     1, [](int a, int b) { return a <  b; }},
2151*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cmple_int,     1, [](int a, int b) { return a <= b; }},
2152*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cmplt_uint,    1, compare_lt_uint},
2153*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cmple_uint,    1, compare_lteq_uint},
2154*c8dee2aaSAndroid Build Coastguard Worker 
2155*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cmpeq_2_ints,  2, [](int a, int b) { return a == b; }},
2156*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cmpne_2_ints,  2, [](int a, int b) { return a != b; }},
2157*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cmplt_2_ints,  2, [](int a, int b) { return a <  b; }},
2158*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cmple_2_ints,  2, [](int a, int b) { return a <= b; }},
2159*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cmplt_2_uints, 2, compare_lt_uint},
2160*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cmple_2_uints, 2, compare_lteq_uint},
2161*c8dee2aaSAndroid Build Coastguard Worker 
2162*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cmpeq_3_ints,  3, [](int a, int b) { return a == b; }},
2163*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cmpne_3_ints,  3, [](int a, int b) { return a != b; }},
2164*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cmplt_3_ints,  3, [](int a, int b) { return a <  b; }},
2165*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cmple_3_ints,  3, [](int a, int b) { return a <= b; }},
2166*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cmplt_3_uints, 3, compare_lt_uint},
2167*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cmple_3_uints, 3, compare_lteq_uint},
2168*c8dee2aaSAndroid Build Coastguard Worker 
2169*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cmpeq_4_ints,  4, [](int a, int b) { return a == b; }},
2170*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cmpne_4_ints,  4, [](int a, int b) { return a != b; }},
2171*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cmplt_4_ints,  4, [](int a, int b) { return a <  b; }},
2172*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cmple_4_ints,  4, [](int a, int b) { return a <= b; }},
2173*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cmplt_4_uints, 4, compare_lt_uint},
2174*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cmple_4_uints, 4, compare_lteq_uint},
2175*c8dee2aaSAndroid Build Coastguard Worker     };
2176*c8dee2aaSAndroid Build Coastguard Worker 
2177*c8dee2aaSAndroid Build Coastguard Worker     for (const CompareOp& op : kCompareOps) {
2178*c8dee2aaSAndroid Build Coastguard Worker         // Initialize the slot values to -1,0,1,-1,0,1,-1,0,1,-1...
2179*c8dee2aaSAndroid Build Coastguard Worker         for (int index = 0; index < 10 * N; ++index) {
2180*c8dee2aaSAndroid Build Coastguard Worker             slots[index] = (index % 3) - 1;
2181*c8dee2aaSAndroid Build Coastguard Worker         }
2182*c8dee2aaSAndroid Build Coastguard Worker 
2183*c8dee2aaSAndroid Build Coastguard Worker         int leftValue = slots[0];
2184*c8dee2aaSAndroid Build Coastguard Worker         int rightValue = slots[op.numSlotsAffected * N];
2185*c8dee2aaSAndroid Build Coastguard Worker 
2186*c8dee2aaSAndroid Build Coastguard Worker         // Run the comparison op over our data.
2187*c8dee2aaSAndroid Build Coastguard Worker         SkArenaAlloc alloc(/*firstHeapAllocation=*/256);
2188*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline p(&alloc);
2189*c8dee2aaSAndroid Build Coastguard Worker         p.append(op.stage, &slots[0]);
2190*c8dee2aaSAndroid Build Coastguard Worker         p.run(0, 0, 1, 1);
2191*c8dee2aaSAndroid Build Coastguard Worker 
2192*c8dee2aaSAndroid Build Coastguard Worker         // Verify that the affected slots now contain "(0,1,2,0...) op (1,2,0,1...)".
2193*c8dee2aaSAndroid Build Coastguard Worker         int* destPtr = &slots[0];
2194*c8dee2aaSAndroid Build Coastguard Worker         for (int checkSlot = 0; checkSlot < 10; ++checkSlot) {
2195*c8dee2aaSAndroid Build Coastguard Worker             for (int checkLane = 0; checkLane < N; ++checkLane) {
2196*c8dee2aaSAndroid Build Coastguard Worker                 if (checkSlot < op.numSlotsAffected) {
2197*c8dee2aaSAndroid Build Coastguard Worker                     bool compareIsTrue = op.verify(leftValue, rightValue);
2198*c8dee2aaSAndroid Build Coastguard Worker                     REPORTER_ASSERT(r, *destPtr == (compareIsTrue ? ~0 : 0));
2199*c8dee2aaSAndroid Build Coastguard Worker                 } else {
2200*c8dee2aaSAndroid Build Coastguard Worker                     REPORTER_ASSERT(r, *destPtr == leftValue);
2201*c8dee2aaSAndroid Build Coastguard Worker                 }
2202*c8dee2aaSAndroid Build Coastguard Worker 
2203*c8dee2aaSAndroid Build Coastguard Worker                 ++destPtr;
2204*c8dee2aaSAndroid Build Coastguard Worker                 if (++leftValue == 2) {
2205*c8dee2aaSAndroid Build Coastguard Worker                     leftValue = -1;
2206*c8dee2aaSAndroid Build Coastguard Worker                 }
2207*c8dee2aaSAndroid Build Coastguard Worker                 if (++rightValue == 2) {
2208*c8dee2aaSAndroid Build Coastguard Worker                     rightValue = -1;
2209*c8dee2aaSAndroid Build Coastguard Worker                 }
2210*c8dee2aaSAndroid Build Coastguard Worker             }
2211*c8dee2aaSAndroid Build Coastguard Worker         }
2212*c8dee2aaSAndroid Build Coastguard Worker     }
2213*c8dee2aaSAndroid Build Coastguard Worker }
2214*c8dee2aaSAndroid Build Coastguard Worker 
to_float(int a)2215*c8dee2aaSAndroid Build Coastguard Worker static int to_float(int a) { return sk_bit_cast<int>((float)a); }
2216*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRasterPipeline_UnaryIntOps,r)2217*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRasterPipeline_UnaryIntOps, r) {
2218*c8dee2aaSAndroid Build Coastguard Worker     // Allocate space for 5 slots.
2219*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) int slots[5 * SkRasterPipeline_kMaxStride_highp];
2220*c8dee2aaSAndroid Build Coastguard Worker     const int N = SkOpts::raster_pipeline_highp_stride;
2221*c8dee2aaSAndroid Build Coastguard Worker 
2222*c8dee2aaSAndroid Build Coastguard Worker     struct UnaryOp {
2223*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipelineOp stage;
2224*c8dee2aaSAndroid Build Coastguard Worker         int numSlotsAffected;
2225*c8dee2aaSAndroid Build Coastguard Worker         std::function<int(int)> verify;
2226*c8dee2aaSAndroid Build Coastguard Worker     };
2227*c8dee2aaSAndroid Build Coastguard Worker 
2228*c8dee2aaSAndroid Build Coastguard Worker     static const UnaryOp kUnaryOps[] = {
2229*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cast_to_float_from_int,    1, to_float},
2230*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cast_to_float_from_2_ints, 2, to_float},
2231*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cast_to_float_from_3_ints, 3, to_float},
2232*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cast_to_float_from_4_ints, 4, to_float},
2233*c8dee2aaSAndroid Build Coastguard Worker 
2234*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::abs_int,    1, [](int a) { return a < 0 ? -a : a; }},
2235*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::abs_2_ints, 2, [](int a) { return a < 0 ? -a : a; }},
2236*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::abs_3_ints, 3, [](int a) { return a < 0 ? -a : a; }},
2237*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::abs_4_ints, 4, [](int a) { return a < 0 ? -a : a; }},
2238*c8dee2aaSAndroid Build Coastguard Worker     };
2239*c8dee2aaSAndroid Build Coastguard Worker 
2240*c8dee2aaSAndroid Build Coastguard Worker     for (const UnaryOp& op : kUnaryOps) {
2241*c8dee2aaSAndroid Build Coastguard Worker         // Initialize the slot values to -10,-9,-8...
2242*c8dee2aaSAndroid Build Coastguard Worker         std::iota(&slots[0], &slots[5 * N], -10);
2243*c8dee2aaSAndroid Build Coastguard Worker         int inputValue = slots[0];
2244*c8dee2aaSAndroid Build Coastguard Worker 
2245*c8dee2aaSAndroid Build Coastguard Worker         // Run the unary op over our data.
2246*c8dee2aaSAndroid Build Coastguard Worker         SkArenaAlloc alloc(/*firstHeapAllocation=*/256);
2247*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline p(&alloc);
2248*c8dee2aaSAndroid Build Coastguard Worker         p.append(op.stage, &slots[0]);
2249*c8dee2aaSAndroid Build Coastguard Worker         p.run(0, 0, 1, 1);
2250*c8dee2aaSAndroid Build Coastguard Worker 
2251*c8dee2aaSAndroid Build Coastguard Worker         // Verify that the destination slots have been updated.
2252*c8dee2aaSAndroid Build Coastguard Worker         int* destPtr = &slots[0];
2253*c8dee2aaSAndroid Build Coastguard Worker         for (int checkSlot = 0; checkSlot < 5; ++checkSlot) {
2254*c8dee2aaSAndroid Build Coastguard Worker             for (int checkLane = 0; checkLane < N; ++checkLane) {
2255*c8dee2aaSAndroid Build Coastguard Worker                 if (checkSlot < op.numSlotsAffected) {
2256*c8dee2aaSAndroid Build Coastguard Worker                     int expected = op.verify(inputValue);
2257*c8dee2aaSAndroid Build Coastguard Worker                     REPORTER_ASSERT(r, *destPtr == expected);
2258*c8dee2aaSAndroid Build Coastguard Worker                 } else {
2259*c8dee2aaSAndroid Build Coastguard Worker                     REPORTER_ASSERT(r, *destPtr == inputValue);
2260*c8dee2aaSAndroid Build Coastguard Worker                 }
2261*c8dee2aaSAndroid Build Coastguard Worker 
2262*c8dee2aaSAndroid Build Coastguard Worker                 ++destPtr;
2263*c8dee2aaSAndroid Build Coastguard Worker                 ++inputValue;
2264*c8dee2aaSAndroid Build Coastguard Worker             }
2265*c8dee2aaSAndroid Build Coastguard Worker         }
2266*c8dee2aaSAndroid Build Coastguard Worker     }
2267*c8dee2aaSAndroid Build Coastguard Worker }
2268*c8dee2aaSAndroid Build Coastguard Worker 
to_int(float a)2269*c8dee2aaSAndroid Build Coastguard Worker static float to_int(float a)  { return sk_bit_cast<float>((int)a); }
to_uint(float a)2270*c8dee2aaSAndroid Build Coastguard Worker static float to_uint(float a) { return sk_bit_cast<float>((unsigned int)a); }
2271*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRasterPipeline_UnaryFloatOps,r)2272*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRasterPipeline_UnaryFloatOps, r) {
2273*c8dee2aaSAndroid Build Coastguard Worker     // Allocate space for 5 slots.
2274*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) float slots[5 * SkRasterPipeline_kMaxStride_highp];
2275*c8dee2aaSAndroid Build Coastguard Worker     const int N = SkOpts::raster_pipeline_highp_stride;
2276*c8dee2aaSAndroid Build Coastguard Worker 
2277*c8dee2aaSAndroid Build Coastguard Worker     struct UnaryOp {
2278*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipelineOp stage;
2279*c8dee2aaSAndroid Build Coastguard Worker         int numSlotsAffected;
2280*c8dee2aaSAndroid Build Coastguard Worker         std::function<float(float)> verify;
2281*c8dee2aaSAndroid Build Coastguard Worker     };
2282*c8dee2aaSAndroid Build Coastguard Worker 
2283*c8dee2aaSAndroid Build Coastguard Worker     static const UnaryOp kUnaryOps[] = {
2284*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cast_to_int_from_float,    1, to_int},
2285*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cast_to_int_from_2_floats, 2, to_int},
2286*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cast_to_int_from_3_floats, 3, to_int},
2287*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cast_to_int_from_4_floats, 4, to_int},
2288*c8dee2aaSAndroid Build Coastguard Worker 
2289*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cast_to_uint_from_float,    1, to_uint},
2290*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cast_to_uint_from_2_floats, 2, to_uint},
2291*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cast_to_uint_from_3_floats, 3, to_uint},
2292*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::cast_to_uint_from_4_floats, 4, to_uint},
2293*c8dee2aaSAndroid Build Coastguard Worker 
2294*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::floor_float,    1, [](float a) { return floorf(a); }},
2295*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::floor_2_floats, 2, [](float a) { return floorf(a); }},
2296*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::floor_3_floats, 3, [](float a) { return floorf(a); }},
2297*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::floor_4_floats, 4, [](float a) { return floorf(a); }},
2298*c8dee2aaSAndroid Build Coastguard Worker 
2299*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::ceil_float,    1, [](float a) { return ceilf(a); }},
2300*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::ceil_2_floats, 2, [](float a) { return ceilf(a); }},
2301*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::ceil_3_floats, 3, [](float a) { return ceilf(a); }},
2302*c8dee2aaSAndroid Build Coastguard Worker         {SkRasterPipelineOp::ceil_4_floats, 4, [](float a) { return ceilf(a); }},
2303*c8dee2aaSAndroid Build Coastguard Worker     };
2304*c8dee2aaSAndroid Build Coastguard Worker 
2305*c8dee2aaSAndroid Build Coastguard Worker     for (const UnaryOp& op : kUnaryOps) {
2306*c8dee2aaSAndroid Build Coastguard Worker         // The result of some ops are undefined with negative inputs, so only test positive values.
2307*c8dee2aaSAndroid Build Coastguard Worker         bool positiveOnly = (op.stage == SkRasterPipelineOp::cast_to_uint_from_float ||
2308*c8dee2aaSAndroid Build Coastguard Worker                              op.stage == SkRasterPipelineOp::cast_to_uint_from_2_floats ||
2309*c8dee2aaSAndroid Build Coastguard Worker                              op.stage == SkRasterPipelineOp::cast_to_uint_from_3_floats ||
2310*c8dee2aaSAndroid Build Coastguard Worker                              op.stage == SkRasterPipelineOp::cast_to_uint_from_4_floats);
2311*c8dee2aaSAndroid Build Coastguard Worker 
2312*c8dee2aaSAndroid Build Coastguard Worker         float iotaStart = positiveOnly ? 1.0f : -9.75f;
2313*c8dee2aaSAndroid Build Coastguard Worker         std::iota(&slots[0], &slots[5 * N], iotaStart);
2314*c8dee2aaSAndroid Build Coastguard Worker         float inputValue = slots[0];
2315*c8dee2aaSAndroid Build Coastguard Worker 
2316*c8dee2aaSAndroid Build Coastguard Worker         // Run the unary op over our data.
2317*c8dee2aaSAndroid Build Coastguard Worker         SkArenaAlloc alloc(/*firstHeapAllocation=*/256);
2318*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline p(&alloc);
2319*c8dee2aaSAndroid Build Coastguard Worker         p.append(op.stage, &slots[0]);
2320*c8dee2aaSAndroid Build Coastguard Worker         p.run(0, 0, 1, 1);
2321*c8dee2aaSAndroid Build Coastguard Worker 
2322*c8dee2aaSAndroid Build Coastguard Worker         // Verify that the destination slots have been updated.
2323*c8dee2aaSAndroid Build Coastguard Worker         float* destPtr = &slots[0];
2324*c8dee2aaSAndroid Build Coastguard Worker         for (int checkSlot = 0; checkSlot < 5; ++checkSlot) {
2325*c8dee2aaSAndroid Build Coastguard Worker             for (int checkLane = 0; checkLane < N; ++checkLane) {
2326*c8dee2aaSAndroid Build Coastguard Worker                 if (checkSlot < op.numSlotsAffected) {
2327*c8dee2aaSAndroid Build Coastguard Worker                     float expected = op.verify(inputValue);
2328*c8dee2aaSAndroid Build Coastguard Worker                     // The casting tests can generate NaN, depending on the input value, so a value
2329*c8dee2aaSAndroid Build Coastguard Worker                     // match (via ==) might not succeed.
2330*c8dee2aaSAndroid Build Coastguard Worker                     // The ceil tests can generate negative zeros _sometimes_, depending on the
2331*c8dee2aaSAndroid Build Coastguard Worker                     // exact implementation of ceil(), so a bitwise match might not succeed.
2332*c8dee2aaSAndroid Build Coastguard Worker                     // Because of this, we allow either a value match or a bitwise match.
2333*c8dee2aaSAndroid Build Coastguard Worker                     bool bitwiseMatch = (0 == memcmp(destPtr, &expected, sizeof(float)));
2334*c8dee2aaSAndroid Build Coastguard Worker                     bool valueMatch   = (*destPtr == expected);
2335*c8dee2aaSAndroid Build Coastguard Worker                     REPORTER_ASSERT(r, valueMatch || bitwiseMatch);
2336*c8dee2aaSAndroid Build Coastguard Worker                 } else {
2337*c8dee2aaSAndroid Build Coastguard Worker                     REPORTER_ASSERT(r, *destPtr == inputValue);
2338*c8dee2aaSAndroid Build Coastguard Worker                 }
2339*c8dee2aaSAndroid Build Coastguard Worker 
2340*c8dee2aaSAndroid Build Coastguard Worker                 ++destPtr;
2341*c8dee2aaSAndroid Build Coastguard Worker                 ++inputValue;
2342*c8dee2aaSAndroid Build Coastguard Worker             }
2343*c8dee2aaSAndroid Build Coastguard Worker         }
2344*c8dee2aaSAndroid Build Coastguard Worker     }
2345*c8dee2aaSAndroid Build Coastguard Worker }
2346*c8dee2aaSAndroid Build Coastguard Worker 
to_mix_weight(float value)2347*c8dee2aaSAndroid Build Coastguard Worker static float to_mix_weight(float value) {
2348*c8dee2aaSAndroid Build Coastguard Worker     // Convert a positive value to a mix-weight (a number between 0 and 1).
2349*c8dee2aaSAndroid Build Coastguard Worker     value /= 16.0f;
2350*c8dee2aaSAndroid Build Coastguard Worker     return value - std::floor(value);
2351*c8dee2aaSAndroid Build Coastguard Worker }
2352*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRasterPipeline_MixTest,r)2353*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRasterPipeline_MixTest, r) {
2354*c8dee2aaSAndroid Build Coastguard Worker     // Allocate space for 5 dest and 10 source slots.
2355*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) float slots[15 * SkRasterPipeline_kMaxStride_highp];
2356*c8dee2aaSAndroid Build Coastguard Worker     const int N = SkOpts::raster_pipeline_highp_stride;
2357*c8dee2aaSAndroid Build Coastguard Worker 
2358*c8dee2aaSAndroid Build Coastguard Worker     struct MixOp {
2359*c8dee2aaSAndroid Build Coastguard Worker         int numSlotsAffected;
2360*c8dee2aaSAndroid Build Coastguard Worker         std::function<void(SkRasterPipeline*, SkArenaAlloc*)> append;
2361*c8dee2aaSAndroid Build Coastguard Worker     };
2362*c8dee2aaSAndroid Build Coastguard Worker 
2363*c8dee2aaSAndroid Build Coastguard Worker     static const MixOp kMixOps[] = {
2364*c8dee2aaSAndroid Build Coastguard Worker         {1, [&](SkRasterPipeline* p, SkArenaAlloc* alloc) {
2365*c8dee2aaSAndroid Build Coastguard Worker                 p->append(SkRasterPipelineOp::mix_float, slots);
2366*c8dee2aaSAndroid Build Coastguard Worker             }},
2367*c8dee2aaSAndroid Build Coastguard Worker         {2, [&](SkRasterPipeline* p, SkArenaAlloc* alloc) {
2368*c8dee2aaSAndroid Build Coastguard Worker                 p->append(SkRasterPipelineOp::mix_2_floats, slots);
2369*c8dee2aaSAndroid Build Coastguard Worker             }},
2370*c8dee2aaSAndroid Build Coastguard Worker         {3, [&](SkRasterPipeline* p, SkArenaAlloc* alloc) {
2371*c8dee2aaSAndroid Build Coastguard Worker                 p->append(SkRasterPipelineOp::mix_3_floats, slots);
2372*c8dee2aaSAndroid Build Coastguard Worker             }},
2373*c8dee2aaSAndroid Build Coastguard Worker         {4, [&](SkRasterPipeline* p, SkArenaAlloc* alloc) {
2374*c8dee2aaSAndroid Build Coastguard Worker                 p->append(SkRasterPipelineOp::mix_4_floats, slots);
2375*c8dee2aaSAndroid Build Coastguard Worker             }},
2376*c8dee2aaSAndroid Build Coastguard Worker         {5, [&](SkRasterPipeline* p, SkArenaAlloc* alloc) {
2377*c8dee2aaSAndroid Build Coastguard Worker                 SkRasterPipeline_TernaryOpCtx ctx;
2378*c8dee2aaSAndroid Build Coastguard Worker                 ctx.dst = 0;
2379*c8dee2aaSAndroid Build Coastguard Worker                 ctx.delta = 5 * N * sizeof(float);
2380*c8dee2aaSAndroid Build Coastguard Worker                 p->append(SkRasterPipelineOp::mix_n_floats, SkRPCtxUtils::Pack(ctx, alloc));
2381*c8dee2aaSAndroid Build Coastguard Worker             }},
2382*c8dee2aaSAndroid Build Coastguard Worker     };
2383*c8dee2aaSAndroid Build Coastguard Worker 
2384*c8dee2aaSAndroid Build Coastguard Worker     for (const MixOp& op : kMixOps) {
2385*c8dee2aaSAndroid Build Coastguard Worker         // Initialize the values to 1,2,3...
2386*c8dee2aaSAndroid Build Coastguard Worker         std::iota(&slots[0], &slots[15 * N], 1.0f);
2387*c8dee2aaSAndroid Build Coastguard Worker 
2388*c8dee2aaSAndroid Build Coastguard Worker         float weightValue = slots[0];
2389*c8dee2aaSAndroid Build Coastguard Worker         float fromValue   = slots[1 * op.numSlotsAffected * N];
2390*c8dee2aaSAndroid Build Coastguard Worker         float toValue     = slots[2 * op.numSlotsAffected * N];
2391*c8dee2aaSAndroid Build Coastguard Worker 
2392*c8dee2aaSAndroid Build Coastguard Worker         // The first group of values (the weights) must be between zero and one.
2393*c8dee2aaSAndroid Build Coastguard Worker         for (int idx = 0; idx < 1 * op.numSlotsAffected * N; ++idx) {
2394*c8dee2aaSAndroid Build Coastguard Worker             slots[idx] = to_mix_weight(slots[idx]);
2395*c8dee2aaSAndroid Build Coastguard Worker         }
2396*c8dee2aaSAndroid Build Coastguard Worker 
2397*c8dee2aaSAndroid Build Coastguard Worker         // Run the mix op over our data.
2398*c8dee2aaSAndroid Build Coastguard Worker         SkArenaAlloc alloc(/*firstHeapAllocation=*/256);
2399*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline p(&alloc);
2400*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::set_base_pointer, &slots[0]);
2401*c8dee2aaSAndroid Build Coastguard Worker         op.append(&p, &alloc);
2402*c8dee2aaSAndroid Build Coastguard Worker         p.run(0,0,1,1);
2403*c8dee2aaSAndroid Build Coastguard Worker 
2404*c8dee2aaSAndroid Build Coastguard Worker         // Verify that the affected slots now equal mix({0.25, 0.3125...}, {3,4...}, {5,6...}, ).
2405*c8dee2aaSAndroid Build Coastguard Worker         float* destPtr = &slots[0];
2406*c8dee2aaSAndroid Build Coastguard Worker         for (int checkSlot = 0; checkSlot < op.numSlotsAffected; ++checkSlot) {
2407*c8dee2aaSAndroid Build Coastguard Worker             for (int checkLane = 0; checkLane < N; ++checkLane) {
2408*c8dee2aaSAndroid Build Coastguard Worker                 float checkValue = (toValue - fromValue) * to_mix_weight(weightValue) + fromValue;
2409*c8dee2aaSAndroid Build Coastguard Worker                 REPORTER_ASSERT(r, *destPtr == checkValue);
2410*c8dee2aaSAndroid Build Coastguard Worker 
2411*c8dee2aaSAndroid Build Coastguard Worker                 ++destPtr;
2412*c8dee2aaSAndroid Build Coastguard Worker                 fromValue += 1.0f;
2413*c8dee2aaSAndroid Build Coastguard Worker                 toValue += 1.0f;
2414*c8dee2aaSAndroid Build Coastguard Worker                 weightValue += 1.0f;
2415*c8dee2aaSAndroid Build Coastguard Worker             }
2416*c8dee2aaSAndroid Build Coastguard Worker         }
2417*c8dee2aaSAndroid Build Coastguard Worker     }
2418*c8dee2aaSAndroid Build Coastguard Worker }
2419*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRasterPipeline_MixIntTest,r)2420*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRasterPipeline_MixIntTest, r) {
2421*c8dee2aaSAndroid Build Coastguard Worker     // Allocate space for 5 dest and 10 source slots.
2422*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) int slots[15 * SkRasterPipeline_kMaxStride_highp];
2423*c8dee2aaSAndroid Build Coastguard Worker     const int N = SkOpts::raster_pipeline_highp_stride;
2424*c8dee2aaSAndroid Build Coastguard Worker 
2425*c8dee2aaSAndroid Build Coastguard Worker     struct MixOp {
2426*c8dee2aaSAndroid Build Coastguard Worker         int numSlotsAffected;
2427*c8dee2aaSAndroid Build Coastguard Worker         std::function<void(SkRasterPipeline*, SkArenaAlloc*)> append;
2428*c8dee2aaSAndroid Build Coastguard Worker     };
2429*c8dee2aaSAndroid Build Coastguard Worker 
2430*c8dee2aaSAndroid Build Coastguard Worker     static const MixOp kMixOps[] = {
2431*c8dee2aaSAndroid Build Coastguard Worker         {1, [&](SkRasterPipeline* p, SkArenaAlloc* alloc) {
2432*c8dee2aaSAndroid Build Coastguard Worker                 p->append(SkRasterPipelineOp::mix_int, slots);
2433*c8dee2aaSAndroid Build Coastguard Worker             }},
2434*c8dee2aaSAndroid Build Coastguard Worker         {2, [&](SkRasterPipeline* p, SkArenaAlloc* alloc) {
2435*c8dee2aaSAndroid Build Coastguard Worker                 p->append(SkRasterPipelineOp::mix_2_ints, slots);
2436*c8dee2aaSAndroid Build Coastguard Worker             }},
2437*c8dee2aaSAndroid Build Coastguard Worker         {3, [&](SkRasterPipeline* p, SkArenaAlloc* alloc) {
2438*c8dee2aaSAndroid Build Coastguard Worker                 p->append(SkRasterPipelineOp::mix_3_ints, slots);
2439*c8dee2aaSAndroid Build Coastguard Worker             }},
2440*c8dee2aaSAndroid Build Coastguard Worker         {4, [&](SkRasterPipeline* p, SkArenaAlloc* alloc) {
2441*c8dee2aaSAndroid Build Coastguard Worker                 p->append(SkRasterPipelineOp::mix_4_ints, slots);
2442*c8dee2aaSAndroid Build Coastguard Worker             }},
2443*c8dee2aaSAndroid Build Coastguard Worker         {5, [&](SkRasterPipeline* p, SkArenaAlloc* alloc) {
2444*c8dee2aaSAndroid Build Coastguard Worker                 SkRasterPipeline_TernaryOpCtx ctx;
2445*c8dee2aaSAndroid Build Coastguard Worker                 ctx.dst = 0;
2446*c8dee2aaSAndroid Build Coastguard Worker                 ctx.delta = 5 * N * sizeof(int);
2447*c8dee2aaSAndroid Build Coastguard Worker                 p->append(SkRasterPipelineOp::mix_n_ints, SkRPCtxUtils::Pack(ctx, alloc));
2448*c8dee2aaSAndroid Build Coastguard Worker             }},
2449*c8dee2aaSAndroid Build Coastguard Worker     };
2450*c8dee2aaSAndroid Build Coastguard Worker 
2451*c8dee2aaSAndroid Build Coastguard Worker     for (const MixOp& op : kMixOps) {
2452*c8dee2aaSAndroid Build Coastguard Worker         // Initialize the selector ("weight") values to alternating masks
2453*c8dee2aaSAndroid Build Coastguard Worker         for (int idx = 0; idx < 1 * op.numSlotsAffected * N; ++idx) {
2454*c8dee2aaSAndroid Build Coastguard Worker             slots[idx] = (idx & 1) ? ~0 : 0;
2455*c8dee2aaSAndroid Build Coastguard Worker         }
2456*c8dee2aaSAndroid Build Coastguard Worker 
2457*c8dee2aaSAndroid Build Coastguard Worker         // Initialize the other values to various NaNs
2458*c8dee2aaSAndroid Build Coastguard Worker         std::iota(&slots[1 * op.numSlotsAffected * N], &slots[15 * N], kLastSignalingNaN);
2459*c8dee2aaSAndroid Build Coastguard Worker 
2460*c8dee2aaSAndroid Build Coastguard Worker         int weightValue = slots[0];
2461*c8dee2aaSAndroid Build Coastguard Worker         int fromValue   = slots[1 * op.numSlotsAffected * N];
2462*c8dee2aaSAndroid Build Coastguard Worker         int toValue     = slots[2 * op.numSlotsAffected * N];
2463*c8dee2aaSAndroid Build Coastguard Worker 
2464*c8dee2aaSAndroid Build Coastguard Worker         // Run the mix op over our data.
2465*c8dee2aaSAndroid Build Coastguard Worker         SkArenaAlloc alloc(/*firstHeapAllocation=*/256);
2466*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline p(&alloc);
2467*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::set_base_pointer, &slots[0]);
2468*c8dee2aaSAndroid Build Coastguard Worker         op.append(&p, &alloc);
2469*c8dee2aaSAndroid Build Coastguard Worker         p.run(0,0,1,1);
2470*c8dee2aaSAndroid Build Coastguard Worker 
2471*c8dee2aaSAndroid Build Coastguard Worker         // Verify that the affected slots now equal either fromValue or toValue, correctly
2472*c8dee2aaSAndroid Build Coastguard Worker         int* destPtr = &slots[0];
2473*c8dee2aaSAndroid Build Coastguard Worker         for (int checkSlot = 0; checkSlot < op.numSlotsAffected; ++checkSlot) {
2474*c8dee2aaSAndroid Build Coastguard Worker             for (int checkLane = 0; checkLane < N; ++checkLane) {
2475*c8dee2aaSAndroid Build Coastguard Worker                 int checkValue = weightValue ? toValue : fromValue;
2476*c8dee2aaSAndroid Build Coastguard Worker                 REPORTER_ASSERT(r, *destPtr == checkValue);
2477*c8dee2aaSAndroid Build Coastguard Worker 
2478*c8dee2aaSAndroid Build Coastguard Worker                 ++destPtr;
2479*c8dee2aaSAndroid Build Coastguard Worker                 fromValue += 1;
2480*c8dee2aaSAndroid Build Coastguard Worker                 toValue += 1;
2481*c8dee2aaSAndroid Build Coastguard Worker                 weightValue = ~weightValue;
2482*c8dee2aaSAndroid Build Coastguard Worker             }
2483*c8dee2aaSAndroid Build Coastguard Worker         }
2484*c8dee2aaSAndroid Build Coastguard Worker     }
2485*c8dee2aaSAndroid Build Coastguard Worker }
2486*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRasterPipeline_Jump,r)2487*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRasterPipeline_Jump, r) {
2488*c8dee2aaSAndroid Build Coastguard Worker     // Allocate space for 4 slots.
2489*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) float slots[4 * SkRasterPipeline_kMaxStride_highp] = {};
2490*c8dee2aaSAndroid Build Coastguard Worker     const int N = SkOpts::raster_pipeline_highp_stride;
2491*c8dee2aaSAndroid Build Coastguard Worker 
2492*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) static constexpr float kColorDarkRed[4] = {0.5f, 0.0f, 0.0f, 0.75f};
2493*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) static constexpr float kColorGreen[4]   = {0.0f, 1.0f, 0.0f, 1.0f};
2494*c8dee2aaSAndroid Build Coastguard Worker     const int offset = 2;
2495*c8dee2aaSAndroid Build Coastguard Worker 
2496*c8dee2aaSAndroid Build Coastguard Worker     // Make a program which jumps over an appendConstantColor op.
2497*c8dee2aaSAndroid Build Coastguard Worker     SkArenaAlloc alloc(/*firstHeapAllocation=*/256);
2498*c8dee2aaSAndroid Build Coastguard Worker     SkRasterPipeline p(&alloc);
2499*c8dee2aaSAndroid Build Coastguard Worker     p.appendConstantColor(&alloc, kColorGreen);        // assign green
2500*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::jump, &offset);       // jump over the dark-red color assignment
2501*c8dee2aaSAndroid Build Coastguard Worker     p.appendConstantColor(&alloc, kColorDarkRed);      // (not executed)
2502*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::store_src, slots);    // store the result so we can check it
2503*c8dee2aaSAndroid Build Coastguard Worker     p.run(0,0,1,1);
2504*c8dee2aaSAndroid Build Coastguard Worker 
2505*c8dee2aaSAndroid Build Coastguard Worker     // Verify that the slots contain green.
2506*c8dee2aaSAndroid Build Coastguard Worker     float* destPtr = &slots[0];
2507*c8dee2aaSAndroid Build Coastguard Worker     for (int checkSlot = 0; checkSlot < 4; ++checkSlot) {
2508*c8dee2aaSAndroid Build Coastguard Worker         for (int checkLane = 0; checkLane < N; ++checkLane) {
2509*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(r, *destPtr == kColorGreen[checkSlot]);
2510*c8dee2aaSAndroid Build Coastguard Worker             ++destPtr;
2511*c8dee2aaSAndroid Build Coastguard Worker         }
2512*c8dee2aaSAndroid Build Coastguard Worker     }
2513*c8dee2aaSAndroid Build Coastguard Worker }
2514*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRasterPipeline_ExchangeSrc,r)2515*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRasterPipeline_ExchangeSrc, r) {
2516*c8dee2aaSAndroid Build Coastguard Worker     const int N = SkOpts::raster_pipeline_highp_stride;
2517*c8dee2aaSAndroid Build Coastguard Worker 
2518*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) int registerValue[4 * SkRasterPipeline_kMaxStride_highp] = {};
2519*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) int exchangeValue[4 * SkRasterPipeline_kMaxStride_highp] = {};
2520*c8dee2aaSAndroid Build Coastguard Worker 
2521*c8dee2aaSAndroid Build Coastguard Worker     std::iota(&registerValue[0], &registerValue[4 * N], kLastSignalingNaN);
2522*c8dee2aaSAndroid Build Coastguard Worker     std::iota(&exchangeValue[0], &exchangeValue[4 * N], kLastSignalingNegNaN);
2523*c8dee2aaSAndroid Build Coastguard Worker 
2524*c8dee2aaSAndroid Build Coastguard Worker     // This program should swap the contents of `registerValue` and `exchangeValue`.
2525*c8dee2aaSAndroid Build Coastguard Worker     SkArenaAlloc alloc(/*firstHeapAllocation=*/256);
2526*c8dee2aaSAndroid Build Coastguard Worker     SkRasterPipeline p(&alloc);
2527*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::load_src,     registerValue);
2528*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::exchange_src, exchangeValue);
2529*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::store_src,    registerValue);
2530*c8dee2aaSAndroid Build Coastguard Worker     p.run(0,0,N,1);
2531*c8dee2aaSAndroid Build Coastguard Worker 
2532*c8dee2aaSAndroid Build Coastguard Worker     int* registerPtr = &registerValue[0];
2533*c8dee2aaSAndroid Build Coastguard Worker     int* exchangePtr = &exchangeValue[0];
2534*c8dee2aaSAndroid Build Coastguard Worker     int expectedRegister = kLastSignalingNegNaN, expectedExchange = kLastSignalingNaN;
2535*c8dee2aaSAndroid Build Coastguard Worker     for (int checkSlot = 0; checkSlot < 4; ++checkSlot) {
2536*c8dee2aaSAndroid Build Coastguard Worker         for (int checkLane = 0; checkLane < N; ++checkLane) {
2537*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(r, *registerPtr++ == expectedRegister);
2538*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(r, *exchangePtr++ == expectedExchange);
2539*c8dee2aaSAndroid Build Coastguard Worker             expectedRegister += 1;
2540*c8dee2aaSAndroid Build Coastguard Worker             expectedExchange += 1;
2541*c8dee2aaSAndroid Build Coastguard Worker         }
2542*c8dee2aaSAndroid Build Coastguard Worker     }
2543*c8dee2aaSAndroid Build Coastguard Worker }
2544*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRasterPipeline_BranchIfAllLanesActive,r)2545*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRasterPipeline_BranchIfAllLanesActive, r) {
2546*c8dee2aaSAndroid Build Coastguard Worker     const int N = SkOpts::raster_pipeline_highp_stride;
2547*c8dee2aaSAndroid Build Coastguard Worker 
2548*c8dee2aaSAndroid Build Coastguard Worker     SkRasterPipeline_BranchIfAllLanesActiveCtx ctx;
2549*c8dee2aaSAndroid Build Coastguard Worker     ctx.offset = 2;
2550*c8dee2aaSAndroid Build Coastguard Worker 
2551*c8dee2aaSAndroid Build Coastguard Worker     // The branch should be taken when lane masks are all-on.
2552*c8dee2aaSAndroid Build Coastguard Worker     {
2553*c8dee2aaSAndroid Build Coastguard Worker         alignas(64) int32_t first [SkRasterPipeline_kMaxStride_highp];
2554*c8dee2aaSAndroid Build Coastguard Worker         alignas(64) int32_t second[SkRasterPipeline_kMaxStride_highp];
2555*c8dee2aaSAndroid Build Coastguard Worker         std::fill(&first [0], &first [N], 0x12345678);
2556*c8dee2aaSAndroid Build Coastguard Worker         std::fill(&second[0], &second[N], 0x12345678);
2557*c8dee2aaSAndroid Build Coastguard Worker 
2558*c8dee2aaSAndroid Build Coastguard Worker         SkArenaAlloc alloc(/*firstHeapAllocation=*/256);
2559*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline p(&alloc);
2560*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline_InitLaneMasksCtx initLaneMasksCtx;
2561*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::init_lane_masks, &initLaneMasksCtx);
2562*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::branch_if_all_lanes_active, &ctx);
2563*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::store_src_a, first);
2564*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::store_src_a, second);
2565*c8dee2aaSAndroid Build Coastguard Worker         p.run(0,0,N,1);
2566*c8dee2aaSAndroid Build Coastguard Worker 
2567*c8dee2aaSAndroid Build Coastguard Worker         int32_t* firstPtr = first;
2568*c8dee2aaSAndroid Build Coastguard Worker         int32_t* secondPtr = second;
2569*c8dee2aaSAndroid Build Coastguard Worker         for (int checkLane = 0; checkLane < N; ++checkLane) {
2570*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(r, *firstPtr++  == 0x12345678);
2571*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(r, *secondPtr++ != 0x12345678);
2572*c8dee2aaSAndroid Build Coastguard Worker         }
2573*c8dee2aaSAndroid Build Coastguard Worker     }
2574*c8dee2aaSAndroid Build Coastguard Worker     // The branch should not be taken when lane masks are all-off.
2575*c8dee2aaSAndroid Build Coastguard Worker     {
2576*c8dee2aaSAndroid Build Coastguard Worker         alignas(64) int32_t first [SkRasterPipeline_kMaxStride_highp];
2577*c8dee2aaSAndroid Build Coastguard Worker         alignas(64) int32_t second[SkRasterPipeline_kMaxStride_highp];
2578*c8dee2aaSAndroid Build Coastguard Worker         std::fill(&first [0], &first [N], 0x12345678);
2579*c8dee2aaSAndroid Build Coastguard Worker         std::fill(&second[0], &second[N], 0x12345678);
2580*c8dee2aaSAndroid Build Coastguard Worker 
2581*c8dee2aaSAndroid Build Coastguard Worker         alignas(64) constexpr int32_t kNoLanesActive[4 * SkRasterPipeline_kMaxStride_highp] = {};
2582*c8dee2aaSAndroid Build Coastguard Worker 
2583*c8dee2aaSAndroid Build Coastguard Worker         SkArenaAlloc alloc(/*firstHeapAllocation=*/256);
2584*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline p(&alloc);
2585*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::load_src, kNoLanesActive);
2586*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::branch_if_all_lanes_active, &ctx);
2587*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::store_src_a, first);
2588*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::store_src_a, second);
2589*c8dee2aaSAndroid Build Coastguard Worker         p.run(0,0,N,1);
2590*c8dee2aaSAndroid Build Coastguard Worker 
2591*c8dee2aaSAndroid Build Coastguard Worker         int32_t* firstPtr = first;
2592*c8dee2aaSAndroid Build Coastguard Worker         int32_t* secondPtr = second;
2593*c8dee2aaSAndroid Build Coastguard Worker         for (int checkLane = 0; checkLane < N; ++checkLane) {
2594*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(r, *firstPtr++  != 0x12345678);
2595*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(r, *secondPtr++ != 0x12345678);
2596*c8dee2aaSAndroid Build Coastguard Worker         }
2597*c8dee2aaSAndroid Build Coastguard Worker     }
2598*c8dee2aaSAndroid Build Coastguard Worker     // The branch should not be taken when lane masks are partially-on.
2599*c8dee2aaSAndroid Build Coastguard Worker     if (N > 1) {
2600*c8dee2aaSAndroid Build Coastguard Worker         alignas(64) int32_t first [SkRasterPipeline_kMaxStride_highp];
2601*c8dee2aaSAndroid Build Coastguard Worker         alignas(64) int32_t second[SkRasterPipeline_kMaxStride_highp];
2602*c8dee2aaSAndroid Build Coastguard Worker         std::fill(&first [0], &first [N], 0x12345678);
2603*c8dee2aaSAndroid Build Coastguard Worker         std::fill(&second[0], &second[N], 0x12345678);
2604*c8dee2aaSAndroid Build Coastguard Worker 
2605*c8dee2aaSAndroid Build Coastguard Worker         // An array of ~0s, except for a single zero in the last A slot.
2606*c8dee2aaSAndroid Build Coastguard Worker         alignas(64) int32_t oneLaneInactive[4 * SkRasterPipeline_kMaxStride_highp] = {};
2607*c8dee2aaSAndroid Build Coastguard Worker         std::fill(oneLaneInactive, &oneLaneInactive[4*N], ~0);
2608*c8dee2aaSAndroid Build Coastguard Worker         oneLaneInactive[4*N - 1] = 0;
2609*c8dee2aaSAndroid Build Coastguard Worker 
2610*c8dee2aaSAndroid Build Coastguard Worker         SkArenaAlloc alloc(/*firstHeapAllocation=*/256);
2611*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline p(&alloc);
2612*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::load_src, oneLaneInactive);
2613*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::branch_if_all_lanes_active, &ctx);
2614*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::store_src_a, first);
2615*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::store_src_a, second);
2616*c8dee2aaSAndroid Build Coastguard Worker         p.run(0,0,N,1);
2617*c8dee2aaSAndroid Build Coastguard Worker 
2618*c8dee2aaSAndroid Build Coastguard Worker         int32_t* firstPtr = first;
2619*c8dee2aaSAndroid Build Coastguard Worker         int32_t* secondPtr = second;
2620*c8dee2aaSAndroid Build Coastguard Worker         for (int checkLane = 0; checkLane < N; ++checkLane) {
2621*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(r, *firstPtr++  != 0x12345678);
2622*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(r, *secondPtr++ != 0x12345678);
2623*c8dee2aaSAndroid Build Coastguard Worker         }
2624*c8dee2aaSAndroid Build Coastguard Worker     }
2625*c8dee2aaSAndroid Build Coastguard Worker }
2626*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRasterPipeline_BranchIfAnyLanesActive,r)2627*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRasterPipeline_BranchIfAnyLanesActive, r) {
2628*c8dee2aaSAndroid Build Coastguard Worker     const int N = SkOpts::raster_pipeline_highp_stride;
2629*c8dee2aaSAndroid Build Coastguard Worker 
2630*c8dee2aaSAndroid Build Coastguard Worker     SkRasterPipeline_BranchCtx ctx;
2631*c8dee2aaSAndroid Build Coastguard Worker     ctx.offset = 2;
2632*c8dee2aaSAndroid Build Coastguard Worker 
2633*c8dee2aaSAndroid Build Coastguard Worker     // The branch should be taken when lane masks are all-on.
2634*c8dee2aaSAndroid Build Coastguard Worker     {
2635*c8dee2aaSAndroid Build Coastguard Worker         alignas(64) int32_t first [SkRasterPipeline_kMaxStride_highp];
2636*c8dee2aaSAndroid Build Coastguard Worker         alignas(64) int32_t second[SkRasterPipeline_kMaxStride_highp];
2637*c8dee2aaSAndroid Build Coastguard Worker         std::fill(&first [0], &first [N], 0x12345678);
2638*c8dee2aaSAndroid Build Coastguard Worker         std::fill(&second[0], &second[N], 0x12345678);
2639*c8dee2aaSAndroid Build Coastguard Worker 
2640*c8dee2aaSAndroid Build Coastguard Worker         SkArenaAlloc alloc(/*firstHeapAllocation=*/256);
2641*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline p(&alloc);
2642*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline_InitLaneMasksCtx initLaneMasksCtx;
2643*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::init_lane_masks, &initLaneMasksCtx);
2644*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::branch_if_any_lanes_active, &ctx);
2645*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::store_src_a, first);
2646*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::store_src_a, second);
2647*c8dee2aaSAndroid Build Coastguard Worker         p.run(0,0,N,1);
2648*c8dee2aaSAndroid Build Coastguard Worker 
2649*c8dee2aaSAndroid Build Coastguard Worker         int32_t* firstPtr = first;
2650*c8dee2aaSAndroid Build Coastguard Worker         int32_t* secondPtr = second;
2651*c8dee2aaSAndroid Build Coastguard Worker         for (int checkLane = 0; checkLane < N; ++checkLane) {
2652*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(r, *firstPtr++  == 0x12345678);
2653*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(r, *secondPtr++ != 0x12345678);
2654*c8dee2aaSAndroid Build Coastguard Worker         }
2655*c8dee2aaSAndroid Build Coastguard Worker     }
2656*c8dee2aaSAndroid Build Coastguard Worker     // The branch should not be taken when lane masks are all-off.
2657*c8dee2aaSAndroid Build Coastguard Worker     {
2658*c8dee2aaSAndroid Build Coastguard Worker         alignas(64) int32_t first [SkRasterPipeline_kMaxStride_highp];
2659*c8dee2aaSAndroid Build Coastguard Worker         alignas(64) int32_t second[SkRasterPipeline_kMaxStride_highp];
2660*c8dee2aaSAndroid Build Coastguard Worker         std::fill(&first [0], &first [N], 0x12345678);
2661*c8dee2aaSAndroid Build Coastguard Worker         std::fill(&second[0], &second[N], 0x12345678);
2662*c8dee2aaSAndroid Build Coastguard Worker 
2663*c8dee2aaSAndroid Build Coastguard Worker         alignas(64) constexpr int32_t kNoLanesActive[4 * SkRasterPipeline_kMaxStride_highp] = {};
2664*c8dee2aaSAndroid Build Coastguard Worker 
2665*c8dee2aaSAndroid Build Coastguard Worker         SkArenaAlloc alloc(/*firstHeapAllocation=*/256);
2666*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline p(&alloc);
2667*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::load_src, kNoLanesActive);
2668*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::branch_if_any_lanes_active, &ctx);
2669*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::store_src_a, first);
2670*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::store_src_a, second);
2671*c8dee2aaSAndroid Build Coastguard Worker         p.run(0,0,N,1);
2672*c8dee2aaSAndroid Build Coastguard Worker 
2673*c8dee2aaSAndroid Build Coastguard Worker         int32_t* firstPtr = first;
2674*c8dee2aaSAndroid Build Coastguard Worker         int32_t* secondPtr = second;
2675*c8dee2aaSAndroid Build Coastguard Worker         for (int checkLane = 0; checkLane < N; ++checkLane) {
2676*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(r, *firstPtr++  != 0x12345678);
2677*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(r, *secondPtr++ != 0x12345678);
2678*c8dee2aaSAndroid Build Coastguard Worker         }
2679*c8dee2aaSAndroid Build Coastguard Worker     }
2680*c8dee2aaSAndroid Build Coastguard Worker     // The branch should be taken when lane masks are partially-on.
2681*c8dee2aaSAndroid Build Coastguard Worker     if (N > 1) {
2682*c8dee2aaSAndroid Build Coastguard Worker         alignas(64) int32_t first [SkRasterPipeline_kMaxStride_highp];
2683*c8dee2aaSAndroid Build Coastguard Worker         alignas(64) int32_t second[SkRasterPipeline_kMaxStride_highp];
2684*c8dee2aaSAndroid Build Coastguard Worker         std::fill(&first [0], &first [N], 0x12345678);
2685*c8dee2aaSAndroid Build Coastguard Worker         std::fill(&second[0], &second[N], 0x12345678);
2686*c8dee2aaSAndroid Build Coastguard Worker 
2687*c8dee2aaSAndroid Build Coastguard Worker         // An array of all zeros, except for a single ~0 in the last A slot.
2688*c8dee2aaSAndroid Build Coastguard Worker         alignas(64) int32_t oneLaneActive[4 * SkRasterPipeline_kMaxStride_highp] = {};
2689*c8dee2aaSAndroid Build Coastguard Worker         oneLaneActive[4*N - 1] = ~0;
2690*c8dee2aaSAndroid Build Coastguard Worker 
2691*c8dee2aaSAndroid Build Coastguard Worker         SkArenaAlloc alloc(/*firstHeapAllocation=*/256);
2692*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline p(&alloc);
2693*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::load_src, oneLaneActive);
2694*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::branch_if_any_lanes_active, &ctx);
2695*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::store_src_a, first);
2696*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::store_src_a, second);
2697*c8dee2aaSAndroid Build Coastguard Worker         p.run(0,0,N,1);
2698*c8dee2aaSAndroid Build Coastguard Worker 
2699*c8dee2aaSAndroid Build Coastguard Worker         int32_t* firstPtr = first;
2700*c8dee2aaSAndroid Build Coastguard Worker         int32_t* secondPtr = second;
2701*c8dee2aaSAndroid Build Coastguard Worker         for (int checkLane = 0; checkLane < N; ++checkLane) {
2702*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(r, *firstPtr++  == 0x12345678);
2703*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(r, *secondPtr++ != 0x12345678);
2704*c8dee2aaSAndroid Build Coastguard Worker         }
2705*c8dee2aaSAndroid Build Coastguard Worker     }
2706*c8dee2aaSAndroid Build Coastguard Worker }
2707*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRasterPipeline_BranchIfNoLanesActive,r)2708*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRasterPipeline_BranchIfNoLanesActive, r) {
2709*c8dee2aaSAndroid Build Coastguard Worker     const int N = SkOpts::raster_pipeline_highp_stride;
2710*c8dee2aaSAndroid Build Coastguard Worker 
2711*c8dee2aaSAndroid Build Coastguard Worker     SkRasterPipeline_BranchCtx ctx;
2712*c8dee2aaSAndroid Build Coastguard Worker     ctx.offset = 2;
2713*c8dee2aaSAndroid Build Coastguard Worker 
2714*c8dee2aaSAndroid Build Coastguard Worker     // The branch should not be taken when lane masks are all-on.
2715*c8dee2aaSAndroid Build Coastguard Worker     {
2716*c8dee2aaSAndroid Build Coastguard Worker         alignas(64) int32_t first [SkRasterPipeline_kMaxStride_highp];
2717*c8dee2aaSAndroid Build Coastguard Worker         alignas(64) int32_t second[SkRasterPipeline_kMaxStride_highp];
2718*c8dee2aaSAndroid Build Coastguard Worker         std::fill(&first [0], &first [N], 0x12345678);
2719*c8dee2aaSAndroid Build Coastguard Worker         std::fill(&second[0], &second[N], 0x12345678);
2720*c8dee2aaSAndroid Build Coastguard Worker 
2721*c8dee2aaSAndroid Build Coastguard Worker         SkArenaAlloc alloc(/*firstHeapAllocation=*/256);
2722*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline p(&alloc);
2723*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline_InitLaneMasksCtx initLaneMasksCtx;
2724*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::init_lane_masks, &initLaneMasksCtx);
2725*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::branch_if_no_lanes_active, &ctx);
2726*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::store_src_a, first);
2727*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::store_src_a, second);
2728*c8dee2aaSAndroid Build Coastguard Worker         p.run(0,0,N,1);
2729*c8dee2aaSAndroid Build Coastguard Worker 
2730*c8dee2aaSAndroid Build Coastguard Worker         int32_t* firstPtr = first;
2731*c8dee2aaSAndroid Build Coastguard Worker         int32_t* secondPtr = second;
2732*c8dee2aaSAndroid Build Coastguard Worker         for (int checkLane = 0; checkLane < N; ++checkLane) {
2733*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(r, *firstPtr++  != 0x12345678);
2734*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(r, *secondPtr++ != 0x12345678);
2735*c8dee2aaSAndroid Build Coastguard Worker         }
2736*c8dee2aaSAndroid Build Coastguard Worker     }
2737*c8dee2aaSAndroid Build Coastguard Worker     // The branch should be taken when lane masks are all-off.
2738*c8dee2aaSAndroid Build Coastguard Worker     {
2739*c8dee2aaSAndroid Build Coastguard Worker         alignas(64) int32_t first [SkRasterPipeline_kMaxStride_highp];
2740*c8dee2aaSAndroid Build Coastguard Worker         alignas(64) int32_t second[SkRasterPipeline_kMaxStride_highp];
2741*c8dee2aaSAndroid Build Coastguard Worker         std::fill(&first [0], &first [N], 0x12345678);
2742*c8dee2aaSAndroid Build Coastguard Worker         std::fill(&second[0], &second[N], 0x12345678);
2743*c8dee2aaSAndroid Build Coastguard Worker 
2744*c8dee2aaSAndroid Build Coastguard Worker         alignas(64) constexpr int32_t kNoLanesActive[4 * SkRasterPipeline_kMaxStride_highp] = {};
2745*c8dee2aaSAndroid Build Coastguard Worker 
2746*c8dee2aaSAndroid Build Coastguard Worker         SkArenaAlloc alloc(/*firstHeapAllocation=*/256);
2747*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline p(&alloc);
2748*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::load_src, kNoLanesActive);
2749*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::branch_if_no_lanes_active, &ctx);
2750*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::store_src_a, first);
2751*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::store_src_a, second);
2752*c8dee2aaSAndroid Build Coastguard Worker         p.run(0,0,N,1);
2753*c8dee2aaSAndroid Build Coastguard Worker 
2754*c8dee2aaSAndroid Build Coastguard Worker         int32_t* firstPtr = first;
2755*c8dee2aaSAndroid Build Coastguard Worker         int32_t* secondPtr = second;
2756*c8dee2aaSAndroid Build Coastguard Worker         for (int checkLane = 0; checkLane < N; ++checkLane) {
2757*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(r, *firstPtr++  == 0x12345678);
2758*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(r, *secondPtr++ != 0x12345678);
2759*c8dee2aaSAndroid Build Coastguard Worker         }
2760*c8dee2aaSAndroid Build Coastguard Worker     }
2761*c8dee2aaSAndroid Build Coastguard Worker     // The branch should not be taken when lane masks are partially-on.
2762*c8dee2aaSAndroid Build Coastguard Worker     if (N > 1) {
2763*c8dee2aaSAndroid Build Coastguard Worker         alignas(64) int32_t first [SkRasterPipeline_kMaxStride_highp];
2764*c8dee2aaSAndroid Build Coastguard Worker         alignas(64) int32_t second[SkRasterPipeline_kMaxStride_highp];
2765*c8dee2aaSAndroid Build Coastguard Worker         std::fill(&first [0], &first [N], 0x12345678);
2766*c8dee2aaSAndroid Build Coastguard Worker         std::fill(&second[0], &second[N], 0x12345678);
2767*c8dee2aaSAndroid Build Coastguard Worker 
2768*c8dee2aaSAndroid Build Coastguard Worker         // An array of all zeros, except for a single ~0 in the last A slot.
2769*c8dee2aaSAndroid Build Coastguard Worker         alignas(64) int32_t oneLaneActive[4 * SkRasterPipeline_kMaxStride_highp] = {};
2770*c8dee2aaSAndroid Build Coastguard Worker         oneLaneActive[4*N - 1] = ~0;
2771*c8dee2aaSAndroid Build Coastguard Worker 
2772*c8dee2aaSAndroid Build Coastguard Worker         SkArenaAlloc alloc(/*firstHeapAllocation=*/256);
2773*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline p(&alloc);
2774*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::load_src, oneLaneActive);
2775*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::branch_if_no_lanes_active, &ctx);
2776*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::store_src_a, first);
2777*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::store_src_a, second);
2778*c8dee2aaSAndroid Build Coastguard Worker         p.run(0,0,N,1);
2779*c8dee2aaSAndroid Build Coastguard Worker 
2780*c8dee2aaSAndroid Build Coastguard Worker         int32_t* firstPtr = first;
2781*c8dee2aaSAndroid Build Coastguard Worker         int32_t* secondPtr = second;
2782*c8dee2aaSAndroid Build Coastguard Worker         for (int checkLane = 0; checkLane < N; ++checkLane) {
2783*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(r, *firstPtr++  != 0x12345678);
2784*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(r, *secondPtr++ != 0x12345678);
2785*c8dee2aaSAndroid Build Coastguard Worker         }
2786*c8dee2aaSAndroid Build Coastguard Worker     }
2787*c8dee2aaSAndroid Build Coastguard Worker }
2788*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRasterPipeline_BranchIfActiveLanesEqual,r)2789*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRasterPipeline_BranchIfActiveLanesEqual, r) {
2790*c8dee2aaSAndroid Build Coastguard Worker     // Allocate space for 4 slots.
2791*c8dee2aaSAndroid Build Coastguard Worker     const int N = SkOpts::raster_pipeline_highp_stride;
2792*c8dee2aaSAndroid Build Coastguard Worker 
2793*c8dee2aaSAndroid Build Coastguard Worker     // An array of all 6s.
2794*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) int allSixes[SkRasterPipeline_kMaxStride_highp] = {};
2795*c8dee2aaSAndroid Build Coastguard Worker     std::fill(std::begin(allSixes), std::end(allSixes), 6);
2796*c8dee2aaSAndroid Build Coastguard Worker 
2797*c8dee2aaSAndroid Build Coastguard Worker     // An array of all 6s, except for a single 5 in one lane.
2798*c8dee2aaSAndroid Build Coastguard Worker     alignas(64) int mostlySixesWithOneFive[SkRasterPipeline_kMaxStride_highp] = {};
2799*c8dee2aaSAndroid Build Coastguard Worker     std::fill(std::begin(mostlySixesWithOneFive), std::end(mostlySixesWithOneFive), 6);
2800*c8dee2aaSAndroid Build Coastguard Worker     mostlySixesWithOneFive[N - 1] = 5;
2801*c8dee2aaSAndroid Build Coastguard Worker 
2802*c8dee2aaSAndroid Build Coastguard Worker     SkRasterPipeline_BranchIfEqualCtx matching; // comparing all-six vs five will match
2803*c8dee2aaSAndroid Build Coastguard Worker     matching.offset = 2;
2804*c8dee2aaSAndroid Build Coastguard Worker     matching.value = 5;
2805*c8dee2aaSAndroid Build Coastguard Worker     matching.ptr = allSixes;
2806*c8dee2aaSAndroid Build Coastguard Worker 
2807*c8dee2aaSAndroid Build Coastguard Worker     SkRasterPipeline_BranchIfEqualCtx nonmatching;  // comparing mostly-six vs five won't match
2808*c8dee2aaSAndroid Build Coastguard Worker     nonmatching.offset = 2;
2809*c8dee2aaSAndroid Build Coastguard Worker     nonmatching.value = 5;
2810*c8dee2aaSAndroid Build Coastguard Worker     nonmatching.ptr = mostlySixesWithOneFive;
2811*c8dee2aaSAndroid Build Coastguard Worker 
2812*c8dee2aaSAndroid Build Coastguard Worker     // The branch should be taken when lane masks are all-on and we're checking 6 ≠ 5.
2813*c8dee2aaSAndroid Build Coastguard Worker     {
2814*c8dee2aaSAndroid Build Coastguard Worker         alignas(64) int32_t first [SkRasterPipeline_kMaxStride_highp];
2815*c8dee2aaSAndroid Build Coastguard Worker         alignas(64) int32_t second[SkRasterPipeline_kMaxStride_highp];
2816*c8dee2aaSAndroid Build Coastguard Worker         std::fill(&first [0], &first [N], 0x12345678);
2817*c8dee2aaSAndroid Build Coastguard Worker         std::fill(&second[0], &second[N], 0x12345678);
2818*c8dee2aaSAndroid Build Coastguard Worker 
2819*c8dee2aaSAndroid Build Coastguard Worker         SkArenaAlloc alloc(/*firstHeapAllocation=*/256);
2820*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline p(&alloc);
2821*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline_InitLaneMasksCtx initLaneMasksCtx;
2822*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::init_lane_masks, &initLaneMasksCtx);
2823*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::branch_if_no_active_lanes_eq, &matching);
2824*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::store_src_a, first);
2825*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::store_src_a, second);
2826*c8dee2aaSAndroid Build Coastguard Worker         p.run(0,0,N,1);
2827*c8dee2aaSAndroid Build Coastguard Worker 
2828*c8dee2aaSAndroid Build Coastguard Worker         int32_t* firstPtr = first;
2829*c8dee2aaSAndroid Build Coastguard Worker         int32_t* secondPtr = second;
2830*c8dee2aaSAndroid Build Coastguard Worker         for (int checkLane = 0; checkLane < N; ++checkLane) {
2831*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(r, *firstPtr++  == 0x12345678);
2832*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(r, *secondPtr++ != 0x12345678);
2833*c8dee2aaSAndroid Build Coastguard Worker         }
2834*c8dee2aaSAndroid Build Coastguard Worker     }
2835*c8dee2aaSAndroid Build Coastguard Worker     // The branch should not be taken when lane masks are all-on and we're checking 5 ≠ 5
2836*c8dee2aaSAndroid Build Coastguard Worker     {
2837*c8dee2aaSAndroid Build Coastguard Worker         alignas(64) int32_t first [SkRasterPipeline_kMaxStride_highp];
2838*c8dee2aaSAndroid Build Coastguard Worker         alignas(64) int32_t second[SkRasterPipeline_kMaxStride_highp];
2839*c8dee2aaSAndroid Build Coastguard Worker         std::fill(&first [0], &first [N], 0x12345678);
2840*c8dee2aaSAndroid Build Coastguard Worker         std::fill(&second[0], &second[N], 0x12345678);
2841*c8dee2aaSAndroid Build Coastguard Worker 
2842*c8dee2aaSAndroid Build Coastguard Worker         SkArenaAlloc alloc(/*firstHeapAllocation=*/256);
2843*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline p(&alloc);
2844*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline_InitLaneMasksCtx initLaneMasksCtx;
2845*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::init_lane_masks, &initLaneMasksCtx);
2846*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::branch_if_no_active_lanes_eq, &nonmatching);
2847*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::store_src_a, first);
2848*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::store_src_a, second);
2849*c8dee2aaSAndroid Build Coastguard Worker         p.run(0,0,N,1);
2850*c8dee2aaSAndroid Build Coastguard Worker 
2851*c8dee2aaSAndroid Build Coastguard Worker         int32_t* firstPtr = first;
2852*c8dee2aaSAndroid Build Coastguard Worker         int32_t* secondPtr = second;
2853*c8dee2aaSAndroid Build Coastguard Worker         for (int checkLane = 0; checkLane < N; ++checkLane) {
2854*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(r, *firstPtr++  != 0x12345678);
2855*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(r, *secondPtr++ != 0x12345678);
2856*c8dee2aaSAndroid Build Coastguard Worker         }
2857*c8dee2aaSAndroid Build Coastguard Worker     }
2858*c8dee2aaSAndroid Build Coastguard Worker     // The branch should be taken when the 5 = 5 lane is dead.
2859*c8dee2aaSAndroid Build Coastguard Worker     if (N > 1) {
2860*c8dee2aaSAndroid Build Coastguard Worker         alignas(64) int32_t first [SkRasterPipeline_kMaxStride_highp];
2861*c8dee2aaSAndroid Build Coastguard Worker         alignas(64) int32_t second[SkRasterPipeline_kMaxStride_highp];
2862*c8dee2aaSAndroid Build Coastguard Worker         std::fill(&first [0], &first [N], 0x12345678);
2863*c8dee2aaSAndroid Build Coastguard Worker         std::fill(&second[0], &second[N], 0x12345678);
2864*c8dee2aaSAndroid Build Coastguard Worker 
2865*c8dee2aaSAndroid Build Coastguard Worker         // An execution mask with all lanes on except for the five-lane.
2866*c8dee2aaSAndroid Build Coastguard Worker         alignas(64) int mask[4 * SkRasterPipeline_kMaxStride_highp] = {};
2867*c8dee2aaSAndroid Build Coastguard Worker         std::fill(std::begin(mask), std::end(mask), ~0);
2868*c8dee2aaSAndroid Build Coastguard Worker         mask[4*N - 1] = 0;
2869*c8dee2aaSAndroid Build Coastguard Worker 
2870*c8dee2aaSAndroid Build Coastguard Worker         SkArenaAlloc alloc(/*firstHeapAllocation=*/256);
2871*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline p(&alloc);
2872*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::load_src, mask);
2873*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::branch_if_no_active_lanes_eq, &nonmatching);
2874*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::store_src_a, first);
2875*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::store_src_a, second);
2876*c8dee2aaSAndroid Build Coastguard Worker         p.run(0,0,N,1);
2877*c8dee2aaSAndroid Build Coastguard Worker 
2878*c8dee2aaSAndroid Build Coastguard Worker         int32_t* firstPtr = first;
2879*c8dee2aaSAndroid Build Coastguard Worker         int32_t* secondPtr = second;
2880*c8dee2aaSAndroid Build Coastguard Worker         for (int checkLane = 0; checkLane < N; ++checkLane) {
2881*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(r, *firstPtr++  == 0x12345678);
2882*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(r, *secondPtr++ != 0x12345678);
2883*c8dee2aaSAndroid Build Coastguard Worker         }
2884*c8dee2aaSAndroid Build Coastguard Worker     }
2885*c8dee2aaSAndroid Build Coastguard Worker }
2886*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRasterPipeline_empty,r)2887*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRasterPipeline_empty, r) {
2888*c8dee2aaSAndroid Build Coastguard Worker     // No asserts... just a test that this is safe to run.
2889*c8dee2aaSAndroid Build Coastguard Worker     SkRasterPipeline_<256> p;
2890*c8dee2aaSAndroid Build Coastguard Worker     p.run(0,0,20,1);
2891*c8dee2aaSAndroid Build Coastguard Worker }
2892*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRasterPipeline_nonsense,r)2893*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRasterPipeline_nonsense, r) {
2894*c8dee2aaSAndroid Build Coastguard Worker     // No asserts... just a test that this is safe to run and terminates.
2895*c8dee2aaSAndroid Build Coastguard Worker     // srcover() calls st->next(); this makes sure we've always got something there to call.
2896*c8dee2aaSAndroid Build Coastguard Worker     SkRasterPipeline_<256> p;
2897*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::srcover);
2898*c8dee2aaSAndroid Build Coastguard Worker     p.run(0,0,20,1);
2899*c8dee2aaSAndroid Build Coastguard Worker }
2900*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRasterPipeline_JIT,r)2901*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRasterPipeline_JIT, r) {
2902*c8dee2aaSAndroid Build Coastguard Worker     // This tests a couple odd corners that a JIT backend can stumble over.
2903*c8dee2aaSAndroid Build Coastguard Worker 
2904*c8dee2aaSAndroid Build Coastguard Worker     uint32_t buf[72] = {
2905*c8dee2aaSAndroid Build Coastguard Worker          0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
2906*c8dee2aaSAndroid Build Coastguard Worker          1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12,
2907*c8dee2aaSAndroid Build Coastguard Worker         13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
2908*c8dee2aaSAndroid Build Coastguard Worker          0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
2909*c8dee2aaSAndroid Build Coastguard Worker          0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
2910*c8dee2aaSAndroid Build Coastguard Worker          0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
2911*c8dee2aaSAndroid Build Coastguard Worker     };
2912*c8dee2aaSAndroid Build Coastguard Worker 
2913*c8dee2aaSAndroid Build Coastguard Worker     SkRasterPipeline_MemoryCtx src = { buf +  0, 0 },
2914*c8dee2aaSAndroid Build Coastguard Worker                                dst = { buf + 36, 0 };
2915*c8dee2aaSAndroid Build Coastguard Worker 
2916*c8dee2aaSAndroid Build Coastguard Worker     // Copy buf[x] to buf[x+36] for x in [15,35).
2917*c8dee2aaSAndroid Build Coastguard Worker     SkRasterPipeline_<256> p;
2918*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::load_8888,  &src);
2919*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::store_8888, &dst);
2920*c8dee2aaSAndroid Build Coastguard Worker     p.run(15,0, 20,1);
2921*c8dee2aaSAndroid Build Coastguard Worker 
2922*c8dee2aaSAndroid Build Coastguard Worker     for (int i = 0; i < 36; i++) {
2923*c8dee2aaSAndroid Build Coastguard Worker         if (i < 15 || i == 35) {
2924*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(r, buf[i+36] == 0);
2925*c8dee2aaSAndroid Build Coastguard Worker         } else {
2926*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(r, buf[i+36] == (uint32_t)(i - 11));
2927*c8dee2aaSAndroid Build Coastguard Worker         }
2928*c8dee2aaSAndroid Build Coastguard Worker     }
2929*c8dee2aaSAndroid Build Coastguard Worker }
2930*c8dee2aaSAndroid Build Coastguard Worker 
h(float f)2931*c8dee2aaSAndroid Build Coastguard Worker static uint16_t h(float f) {
2932*c8dee2aaSAndroid Build Coastguard Worker     // Remember, a float is 1-8-23 (sign-exponent-mantissa) with 127 exponent bias.
2933*c8dee2aaSAndroid Build Coastguard Worker     uint32_t sem;
2934*c8dee2aaSAndroid Build Coastguard Worker     memcpy(&sem, &f, sizeof(sem));
2935*c8dee2aaSAndroid Build Coastguard Worker     uint32_t s  = sem & 0x80000000,
2936*c8dee2aaSAndroid Build Coastguard Worker              em = sem ^ s;
2937*c8dee2aaSAndroid Build Coastguard Worker 
2938*c8dee2aaSAndroid Build Coastguard Worker     // Convert to 1-5-10 half with 15 bias, flushing denorm halfs (including zero) to zero.
2939*c8dee2aaSAndroid Build Coastguard Worker     auto denorm = (int32_t)em < 0x38800000;  // I32 comparison is often quicker, and always safe
2940*c8dee2aaSAndroid Build Coastguard Worker     // here.
2941*c8dee2aaSAndroid Build Coastguard Worker     return denorm ? SkTo<uint16_t>(0)
2942*c8dee2aaSAndroid Build Coastguard Worker                   : SkTo<uint16_t>((s>>16) + (em>>13) - ((127-15)<<10));
2943*c8dee2aaSAndroid Build Coastguard Worker }
2944*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRasterPipeline_tail,r)2945*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRasterPipeline_tail, r) {
2946*c8dee2aaSAndroid Build Coastguard Worker     {
2947*c8dee2aaSAndroid Build Coastguard Worker         float data[][4] = {
2948*c8dee2aaSAndroid Build Coastguard Worker             {00, 01, 02, 03},
2949*c8dee2aaSAndroid Build Coastguard Worker             {10, 11, 12, 13},
2950*c8dee2aaSAndroid Build Coastguard Worker             {20, 21, 22, 23},
2951*c8dee2aaSAndroid Build Coastguard Worker             {30, 31, 32, 33},
2952*c8dee2aaSAndroid Build Coastguard Worker         };
2953*c8dee2aaSAndroid Build Coastguard Worker 
2954*c8dee2aaSAndroid Build Coastguard Worker         float buffer[4][4];
2955*c8dee2aaSAndroid Build Coastguard Worker 
2956*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline_MemoryCtx src = { &data[0][0], 0 },
2957*c8dee2aaSAndroid Build Coastguard Worker                            dst = { &buffer[0][0], 0 };
2958*c8dee2aaSAndroid Build Coastguard Worker 
2959*c8dee2aaSAndroid Build Coastguard Worker         for (unsigned i = 1; i <= 4; i++) {
2960*c8dee2aaSAndroid Build Coastguard Worker             memset(buffer, 0xff, sizeof(buffer));
2961*c8dee2aaSAndroid Build Coastguard Worker             SkRasterPipeline_<256> p;
2962*c8dee2aaSAndroid Build Coastguard Worker             p.append(SkRasterPipelineOp::load_f32, &src);
2963*c8dee2aaSAndroid Build Coastguard Worker             p.append(SkRasterPipelineOp::store_f32, &dst);
2964*c8dee2aaSAndroid Build Coastguard Worker             p.run(0,0, i,1);
2965*c8dee2aaSAndroid Build Coastguard Worker             for (unsigned j = 0; j < i; j++) {
2966*c8dee2aaSAndroid Build Coastguard Worker                 for (unsigned k = 0; k < 4; k++) {
2967*c8dee2aaSAndroid Build Coastguard Worker                     if (buffer[j][k] != data[j][k]) {
2968*c8dee2aaSAndroid Build Coastguard Worker                         ERRORF(r, "(%u, %u) - a: %g r: %g\n", j, k, data[j][k], buffer[j][k]);
2969*c8dee2aaSAndroid Build Coastguard Worker                     }
2970*c8dee2aaSAndroid Build Coastguard Worker                 }
2971*c8dee2aaSAndroid Build Coastguard Worker             }
2972*c8dee2aaSAndroid Build Coastguard Worker             for (int j = i; j < 4; j++) {
2973*c8dee2aaSAndroid Build Coastguard Worker                 for (auto f : buffer[j]) {
2974*c8dee2aaSAndroid Build Coastguard Worker                     REPORTER_ASSERT(r, SkIsNaN(f));
2975*c8dee2aaSAndroid Build Coastguard Worker                 }
2976*c8dee2aaSAndroid Build Coastguard Worker             }
2977*c8dee2aaSAndroid Build Coastguard Worker         }
2978*c8dee2aaSAndroid Build Coastguard Worker     }
2979*c8dee2aaSAndroid Build Coastguard Worker 
2980*c8dee2aaSAndroid Build Coastguard Worker     {
2981*c8dee2aaSAndroid Build Coastguard Worker         alignas(8) uint16_t data[][4] = {
2982*c8dee2aaSAndroid Build Coastguard Worker             {h(00), h(01), h(02), h(03)},
2983*c8dee2aaSAndroid Build Coastguard Worker             {h(10), h(11), h(12), h(13)},
2984*c8dee2aaSAndroid Build Coastguard Worker             {h(20), h(21), h(22), h(23)},
2985*c8dee2aaSAndroid Build Coastguard Worker             {h(30), h(31), h(32), h(33)},
2986*c8dee2aaSAndroid Build Coastguard Worker         };
2987*c8dee2aaSAndroid Build Coastguard Worker         alignas(8) uint16_t buffer[4][4];
2988*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline_MemoryCtx src = { &data[0][0], 0 },
2989*c8dee2aaSAndroid Build Coastguard Worker                            dst = { &buffer[0][0], 0 };
2990*c8dee2aaSAndroid Build Coastguard Worker 
2991*c8dee2aaSAndroid Build Coastguard Worker         for (unsigned i = 1; i <= 4; i++) {
2992*c8dee2aaSAndroid Build Coastguard Worker             memset(buffer, 0xff, sizeof(buffer));
2993*c8dee2aaSAndroid Build Coastguard Worker             SkRasterPipeline_<256> p;
2994*c8dee2aaSAndroid Build Coastguard Worker             p.append(SkRasterPipelineOp::load_f16, &src);
2995*c8dee2aaSAndroid Build Coastguard Worker             p.append(SkRasterPipelineOp::store_f16, &dst);
2996*c8dee2aaSAndroid Build Coastguard Worker             p.run(0,0, i,1);
2997*c8dee2aaSAndroid Build Coastguard Worker             for (unsigned j = 0; j < i; j++) {
2998*c8dee2aaSAndroid Build Coastguard Worker                 for (int k = 0; k < 4; k++) {
2999*c8dee2aaSAndroid Build Coastguard Worker                     REPORTER_ASSERT(r, buffer[j][k] == data[j][k]);
3000*c8dee2aaSAndroid Build Coastguard Worker                 }
3001*c8dee2aaSAndroid Build Coastguard Worker             }
3002*c8dee2aaSAndroid Build Coastguard Worker             for (int j = i; j < 4; j++) {
3003*c8dee2aaSAndroid Build Coastguard Worker                 for (auto f : buffer[j]) {
3004*c8dee2aaSAndroid Build Coastguard Worker                     REPORTER_ASSERT(r, f == 0xffff);
3005*c8dee2aaSAndroid Build Coastguard Worker                 }
3006*c8dee2aaSAndroid Build Coastguard Worker             }
3007*c8dee2aaSAndroid Build Coastguard Worker         }
3008*c8dee2aaSAndroid Build Coastguard Worker     }
3009*c8dee2aaSAndroid Build Coastguard Worker 
3010*c8dee2aaSAndroid Build Coastguard Worker     {
3011*c8dee2aaSAndroid Build Coastguard Worker         alignas(8) uint16_t data[]= {
3012*c8dee2aaSAndroid Build Coastguard Worker             h(00),
3013*c8dee2aaSAndroid Build Coastguard Worker             h(10),
3014*c8dee2aaSAndroid Build Coastguard Worker             h(20),
3015*c8dee2aaSAndroid Build Coastguard Worker             h(30),
3016*c8dee2aaSAndroid Build Coastguard Worker         };
3017*c8dee2aaSAndroid Build Coastguard Worker         alignas(8) uint16_t buffer[4][4];
3018*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline_MemoryCtx src = { &data[0], 0 },
3019*c8dee2aaSAndroid Build Coastguard Worker                 dst = { &buffer[0][0], 0 };
3020*c8dee2aaSAndroid Build Coastguard Worker 
3021*c8dee2aaSAndroid Build Coastguard Worker         for (unsigned i = 1; i <= 4; i++) {
3022*c8dee2aaSAndroid Build Coastguard Worker             memset(buffer, 0xff, sizeof(buffer));
3023*c8dee2aaSAndroid Build Coastguard Worker             SkRasterPipeline_<256> p;
3024*c8dee2aaSAndroid Build Coastguard Worker             p.append(SkRasterPipelineOp::load_af16, &src);
3025*c8dee2aaSAndroid Build Coastguard Worker             p.append(SkRasterPipelineOp::store_f16, &dst);
3026*c8dee2aaSAndroid Build Coastguard Worker             p.run(0,0, i,1);
3027*c8dee2aaSAndroid Build Coastguard Worker             for (unsigned j = 0; j < i; j++) {
3028*c8dee2aaSAndroid Build Coastguard Worker                 uint16_t expected[] = {0, 0, 0, data[j]};
3029*c8dee2aaSAndroid Build Coastguard Worker                 REPORTER_ASSERT(r, !memcmp(expected, &buffer[j][0], sizeof(buffer[j])));
3030*c8dee2aaSAndroid Build Coastguard Worker             }
3031*c8dee2aaSAndroid Build Coastguard Worker             for (int j = i; j < 4; j++) {
3032*c8dee2aaSAndroid Build Coastguard Worker                 for (auto f : buffer[j]) {
3033*c8dee2aaSAndroid Build Coastguard Worker                     REPORTER_ASSERT(r, f == 0xffff);
3034*c8dee2aaSAndroid Build Coastguard Worker                 }
3035*c8dee2aaSAndroid Build Coastguard Worker             }
3036*c8dee2aaSAndroid Build Coastguard Worker         }
3037*c8dee2aaSAndroid Build Coastguard Worker     }
3038*c8dee2aaSAndroid Build Coastguard Worker 
3039*c8dee2aaSAndroid Build Coastguard Worker     {
3040*c8dee2aaSAndroid Build Coastguard Worker         alignas(8) uint16_t data[][4] = {
3041*c8dee2aaSAndroid Build Coastguard Worker             {h(00), h(01), h(02), h(03)},
3042*c8dee2aaSAndroid Build Coastguard Worker             {h(10), h(11), h(12), h(13)},
3043*c8dee2aaSAndroid Build Coastguard Worker             {h(20), h(21), h(22), h(23)},
3044*c8dee2aaSAndroid Build Coastguard Worker             {h(30), h(31), h(32), h(33)},
3045*c8dee2aaSAndroid Build Coastguard Worker         };
3046*c8dee2aaSAndroid Build Coastguard Worker         alignas(8) uint16_t buffer[4];
3047*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline_MemoryCtx src = { &data[0][0], 0 },
3048*c8dee2aaSAndroid Build Coastguard Worker                 dst = { &buffer[0], 0 };
3049*c8dee2aaSAndroid Build Coastguard Worker 
3050*c8dee2aaSAndroid Build Coastguard Worker         for (unsigned i = 1; i <= 4; i++) {
3051*c8dee2aaSAndroid Build Coastguard Worker             memset(buffer, 0xff, sizeof(buffer));
3052*c8dee2aaSAndroid Build Coastguard Worker             SkRasterPipeline_<256> p;
3053*c8dee2aaSAndroid Build Coastguard Worker             p.append(SkRasterPipelineOp::load_f16, &src);
3054*c8dee2aaSAndroid Build Coastguard Worker             p.append(SkRasterPipelineOp::store_af16, &dst);
3055*c8dee2aaSAndroid Build Coastguard Worker             p.run(0,0, i,1);
3056*c8dee2aaSAndroid Build Coastguard Worker             for (unsigned j = 0; j < i; j++) {
3057*c8dee2aaSAndroid Build Coastguard Worker                 REPORTER_ASSERT(r, !memcmp(&data[j][3], &buffer[j], sizeof(buffer[j])));
3058*c8dee2aaSAndroid Build Coastguard Worker             }
3059*c8dee2aaSAndroid Build Coastguard Worker             for (int j = i; j < 4; j++) {
3060*c8dee2aaSAndroid Build Coastguard Worker                 REPORTER_ASSERT(r, buffer[j] == 0xffff);
3061*c8dee2aaSAndroid Build Coastguard Worker             }
3062*c8dee2aaSAndroid Build Coastguard Worker         }
3063*c8dee2aaSAndroid Build Coastguard Worker     }
3064*c8dee2aaSAndroid Build Coastguard Worker 
3065*c8dee2aaSAndroid Build Coastguard Worker     {
3066*c8dee2aaSAndroid Build Coastguard Worker         alignas(8) uint16_t data[][4] = {
3067*c8dee2aaSAndroid Build Coastguard Worker             {h(00), h(01), h(02), h(03)},
3068*c8dee2aaSAndroid Build Coastguard Worker             {h(10), h(11), h(12), h(13)},
3069*c8dee2aaSAndroid Build Coastguard Worker             {h(20), h(21), h(22), h(23)},
3070*c8dee2aaSAndroid Build Coastguard Worker             {h(30), h(31), h(32), h(33)},
3071*c8dee2aaSAndroid Build Coastguard Worker         };
3072*c8dee2aaSAndroid Build Coastguard Worker         alignas(8) uint16_t buffer[4][2];
3073*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline_MemoryCtx src = { &data[0][0], 0 },
3074*c8dee2aaSAndroid Build Coastguard Worker                 dst = { &buffer[0][0], 0 };
3075*c8dee2aaSAndroid Build Coastguard Worker 
3076*c8dee2aaSAndroid Build Coastguard Worker         for (unsigned i = 1; i <= 4; i++) {
3077*c8dee2aaSAndroid Build Coastguard Worker             memset(buffer, 0xff, sizeof(buffer));
3078*c8dee2aaSAndroid Build Coastguard Worker             SkRasterPipeline_<256> p;
3079*c8dee2aaSAndroid Build Coastguard Worker             p.append(SkRasterPipelineOp::load_f16, &src);
3080*c8dee2aaSAndroid Build Coastguard Worker             p.append(SkRasterPipelineOp::store_rgf16, &dst);
3081*c8dee2aaSAndroid Build Coastguard Worker             p.run(0,0, i,1);
3082*c8dee2aaSAndroid Build Coastguard Worker             for (unsigned j = 0; j < i; j++) {
3083*c8dee2aaSAndroid Build Coastguard Worker                 REPORTER_ASSERT(r, !memcmp(&buffer[j], &data[j], 2 * sizeof(uint16_t)));
3084*c8dee2aaSAndroid Build Coastguard Worker             }
3085*c8dee2aaSAndroid Build Coastguard Worker             for (int j = i; j < 4; j++) {
3086*c8dee2aaSAndroid Build Coastguard Worker                 for (auto h : buffer[j]) {
3087*c8dee2aaSAndroid Build Coastguard Worker                     REPORTER_ASSERT(r, h == 0xffff);
3088*c8dee2aaSAndroid Build Coastguard Worker                 }
3089*c8dee2aaSAndroid Build Coastguard Worker             }
3090*c8dee2aaSAndroid Build Coastguard Worker         }
3091*c8dee2aaSAndroid Build Coastguard Worker     }
3092*c8dee2aaSAndroid Build Coastguard Worker 
3093*c8dee2aaSAndroid Build Coastguard Worker     {
3094*c8dee2aaSAndroid Build Coastguard Worker         alignas(8) uint16_t data[][2] = {
3095*c8dee2aaSAndroid Build Coastguard Worker             {h(00), h(01)},
3096*c8dee2aaSAndroid Build Coastguard Worker             {h(10), h(11)},
3097*c8dee2aaSAndroid Build Coastguard Worker             {h(20), h(21)},
3098*c8dee2aaSAndroid Build Coastguard Worker             {h(30), h(31)},
3099*c8dee2aaSAndroid Build Coastguard Worker         };
3100*c8dee2aaSAndroid Build Coastguard Worker         alignas(8) uint16_t buffer[4][4];
3101*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline_MemoryCtx src = { &data[0][0], 0 },
3102*c8dee2aaSAndroid Build Coastguard Worker                 dst = { &buffer[0][0], 0 };
3103*c8dee2aaSAndroid Build Coastguard Worker 
3104*c8dee2aaSAndroid Build Coastguard Worker         for (unsigned i = 1; i <= 4; i++) {
3105*c8dee2aaSAndroid Build Coastguard Worker             memset(buffer, 0xff, sizeof(buffer));
3106*c8dee2aaSAndroid Build Coastguard Worker             SkRasterPipeline_<256> p;
3107*c8dee2aaSAndroid Build Coastguard Worker             p.append(SkRasterPipelineOp::load_rgf16, &src);
3108*c8dee2aaSAndroid Build Coastguard Worker             p.append(SkRasterPipelineOp::store_f16, &dst);
3109*c8dee2aaSAndroid Build Coastguard Worker             p.run(0,0, i,1);
3110*c8dee2aaSAndroid Build Coastguard Worker             for (unsigned j = 0; j < i; j++) {
3111*c8dee2aaSAndroid Build Coastguard Worker                 uint16_t expected[] = {data[j][0], data[j][1], h(0), h(1)};
3112*c8dee2aaSAndroid Build Coastguard Worker                 REPORTER_ASSERT(r, !memcmp(&buffer[j], expected, sizeof(expected)));
3113*c8dee2aaSAndroid Build Coastguard Worker             }
3114*c8dee2aaSAndroid Build Coastguard Worker             for (int j = i; j < 4; j++) {
3115*c8dee2aaSAndroid Build Coastguard Worker                 for (auto h : buffer[j]) {
3116*c8dee2aaSAndroid Build Coastguard Worker                     REPORTER_ASSERT(r, h == 0xffff);
3117*c8dee2aaSAndroid Build Coastguard Worker                 }
3118*c8dee2aaSAndroid Build Coastguard Worker             }
3119*c8dee2aaSAndroid Build Coastguard Worker         }
3120*c8dee2aaSAndroid Build Coastguard Worker     }
3121*c8dee2aaSAndroid Build Coastguard Worker }
3122*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRasterPipeline_u16,r)3123*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRasterPipeline_u16, r) {
3124*c8dee2aaSAndroid Build Coastguard Worker     {
3125*c8dee2aaSAndroid Build Coastguard Worker         alignas(8) uint16_t data[][2] = {
3126*c8dee2aaSAndroid Build Coastguard Worker             {0x0000, 0x0111},
3127*c8dee2aaSAndroid Build Coastguard Worker             {0x1010, 0x1111},
3128*c8dee2aaSAndroid Build Coastguard Worker             {0x2020, 0x2121},
3129*c8dee2aaSAndroid Build Coastguard Worker             {0x3030, 0x3131},
3130*c8dee2aaSAndroid Build Coastguard Worker         };
3131*c8dee2aaSAndroid Build Coastguard Worker         uint8_t buffer[4][4];
3132*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline_MemoryCtx src = { &data[0][0], 0 },
3133*c8dee2aaSAndroid Build Coastguard Worker                 dst = { &buffer[0][0], 0 };
3134*c8dee2aaSAndroid Build Coastguard Worker 
3135*c8dee2aaSAndroid Build Coastguard Worker         for (unsigned i = 1; i <= 4; i++) {
3136*c8dee2aaSAndroid Build Coastguard Worker             memset(buffer, 0xab, sizeof(buffer));
3137*c8dee2aaSAndroid Build Coastguard Worker             SkRasterPipeline_<256> p;
3138*c8dee2aaSAndroid Build Coastguard Worker             p.append(SkRasterPipelineOp::load_rg1616, &src);
3139*c8dee2aaSAndroid Build Coastguard Worker             p.append(SkRasterPipelineOp::store_8888, &dst);
3140*c8dee2aaSAndroid Build Coastguard Worker             p.run(0,0, i,1);
3141*c8dee2aaSAndroid Build Coastguard Worker             for (unsigned j = 0; j < i; j++) {
3142*c8dee2aaSAndroid Build Coastguard Worker                 uint8_t expected[] = {
3143*c8dee2aaSAndroid Build Coastguard Worker                     SkToU8(data[j][0] >> 8),
3144*c8dee2aaSAndroid Build Coastguard Worker                     SkToU8(data[j][1] >> 8),
3145*c8dee2aaSAndroid Build Coastguard Worker                     000,
3146*c8dee2aaSAndroid Build Coastguard Worker                     0xff
3147*c8dee2aaSAndroid Build Coastguard Worker                 };
3148*c8dee2aaSAndroid Build Coastguard Worker                 REPORTER_ASSERT(r, !memcmp(&buffer[j], expected, sizeof(expected)));
3149*c8dee2aaSAndroid Build Coastguard Worker             }
3150*c8dee2aaSAndroid Build Coastguard Worker             for (int j = i; j < 4; j++) {
3151*c8dee2aaSAndroid Build Coastguard Worker                 for (auto b : buffer[j]) {
3152*c8dee2aaSAndroid Build Coastguard Worker                     REPORTER_ASSERT(r, b == 0xab);
3153*c8dee2aaSAndroid Build Coastguard Worker                 }
3154*c8dee2aaSAndroid Build Coastguard Worker             }
3155*c8dee2aaSAndroid Build Coastguard Worker         }
3156*c8dee2aaSAndroid Build Coastguard Worker     }
3157*c8dee2aaSAndroid Build Coastguard Worker 
3158*c8dee2aaSAndroid Build Coastguard Worker     {
3159*c8dee2aaSAndroid Build Coastguard Worker         alignas(8) uint16_t data[] = {
3160*c8dee2aaSAndroid Build Coastguard Worker                 0x0000,
3161*c8dee2aaSAndroid Build Coastguard Worker                 0x1010,
3162*c8dee2aaSAndroid Build Coastguard Worker                 0x2020,
3163*c8dee2aaSAndroid Build Coastguard Worker                 0x3030,
3164*c8dee2aaSAndroid Build Coastguard Worker         };
3165*c8dee2aaSAndroid Build Coastguard Worker         uint8_t buffer[4][4];
3166*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline_MemoryCtx src = { &data[0], 0 },
3167*c8dee2aaSAndroid Build Coastguard Worker                 dst = { &buffer[0][0], 0 };
3168*c8dee2aaSAndroid Build Coastguard Worker 
3169*c8dee2aaSAndroid Build Coastguard Worker         for (unsigned i = 1; i <= 4; i++) {
3170*c8dee2aaSAndroid Build Coastguard Worker             memset(buffer, 0xff, sizeof(buffer));
3171*c8dee2aaSAndroid Build Coastguard Worker             SkRasterPipeline_<256> p;
3172*c8dee2aaSAndroid Build Coastguard Worker             p.append(SkRasterPipelineOp::load_a16, &src);
3173*c8dee2aaSAndroid Build Coastguard Worker             p.append(SkRasterPipelineOp::store_8888, &dst);
3174*c8dee2aaSAndroid Build Coastguard Worker             p.run(0,0, i,1);
3175*c8dee2aaSAndroid Build Coastguard Worker             for (unsigned j = 0; j < i; j++) {
3176*c8dee2aaSAndroid Build Coastguard Worker                 uint8_t expected[] = {0x00, 0x00, 0x00, SkToU8(data[j] >> 8)};
3177*c8dee2aaSAndroid Build Coastguard Worker                 REPORTER_ASSERT(r, !memcmp(&buffer[j], expected, sizeof(expected)));
3178*c8dee2aaSAndroid Build Coastguard Worker             }
3179*c8dee2aaSAndroid Build Coastguard Worker             for (int j = i; j < 4; j++) {
3180*c8dee2aaSAndroid Build Coastguard Worker                 for (auto b : buffer[j]) {
3181*c8dee2aaSAndroid Build Coastguard Worker                     REPORTER_ASSERT(r, b == 0xff);
3182*c8dee2aaSAndroid Build Coastguard Worker                 }
3183*c8dee2aaSAndroid Build Coastguard Worker             }
3184*c8dee2aaSAndroid Build Coastguard Worker         }
3185*c8dee2aaSAndroid Build Coastguard Worker     }
3186*c8dee2aaSAndroid Build Coastguard Worker 
3187*c8dee2aaSAndroid Build Coastguard Worker     {
3188*c8dee2aaSAndroid Build Coastguard Worker         uint8_t data[][4] = {
3189*c8dee2aaSAndroid Build Coastguard Worker             {0x00, 0x01, 0x02, 0x03},
3190*c8dee2aaSAndroid Build Coastguard Worker             {0x10, 0x11, 0x12, 0x13},
3191*c8dee2aaSAndroid Build Coastguard Worker             {0x20, 0x21, 0x22, 0x23},
3192*c8dee2aaSAndroid Build Coastguard Worker             {0x30, 0x31, 0x32, 0x33},
3193*c8dee2aaSAndroid Build Coastguard Worker         };
3194*c8dee2aaSAndroid Build Coastguard Worker         alignas(8) uint16_t buffer[4];
3195*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline_MemoryCtx src = { &data[0][0], 0 },
3196*c8dee2aaSAndroid Build Coastguard Worker                 dst = { &buffer[0], 0 };
3197*c8dee2aaSAndroid Build Coastguard Worker 
3198*c8dee2aaSAndroid Build Coastguard Worker         for (unsigned i = 1; i <= 4; i++) {
3199*c8dee2aaSAndroid Build Coastguard Worker             memset(buffer, 0xff, sizeof(buffer));
3200*c8dee2aaSAndroid Build Coastguard Worker             SkRasterPipeline_<256> p;
3201*c8dee2aaSAndroid Build Coastguard Worker             p.append(SkRasterPipelineOp::load_8888, &src);
3202*c8dee2aaSAndroid Build Coastguard Worker             p.append(SkRasterPipelineOp::store_a16, &dst);
3203*c8dee2aaSAndroid Build Coastguard Worker             p.run(0,0, i,1);
3204*c8dee2aaSAndroid Build Coastguard Worker             for (unsigned j = 0; j < i; j++) {
3205*c8dee2aaSAndroid Build Coastguard Worker                 uint16_t expected = (data[j][3] << 8) | data[j][3];
3206*c8dee2aaSAndroid Build Coastguard Worker                 REPORTER_ASSERT(r, buffer[j] == expected);
3207*c8dee2aaSAndroid Build Coastguard Worker             }
3208*c8dee2aaSAndroid Build Coastguard Worker             for (int j = i; j < 4; j++) {
3209*c8dee2aaSAndroid Build Coastguard Worker                 REPORTER_ASSERT(r, buffer[j] == 0xffff);
3210*c8dee2aaSAndroid Build Coastguard Worker             }
3211*c8dee2aaSAndroid Build Coastguard Worker         }
3212*c8dee2aaSAndroid Build Coastguard Worker     }
3213*c8dee2aaSAndroid Build Coastguard Worker 
3214*c8dee2aaSAndroid Build Coastguard Worker     {
3215*c8dee2aaSAndroid Build Coastguard Worker         alignas(8) uint16_t data[][4] = {
3216*c8dee2aaSAndroid Build Coastguard Worker             {0x0000, 0x1000, 0x2000, 0x3000},
3217*c8dee2aaSAndroid Build Coastguard Worker             {0x0001, 0x1001, 0x2001, 0x3001},
3218*c8dee2aaSAndroid Build Coastguard Worker             {0x0002, 0x1002, 0x2002, 0x3002},
3219*c8dee2aaSAndroid Build Coastguard Worker             {0x0003, 0x1003, 0x2003, 0x3003},
3220*c8dee2aaSAndroid Build Coastguard Worker         };
3221*c8dee2aaSAndroid Build Coastguard Worker         alignas(8) uint16_t buffer[4][4];
3222*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline_MemoryCtx src = { &data[0][0], 0 },
3223*c8dee2aaSAndroid Build Coastguard Worker                 dst = { &buffer[0], 0 };
3224*c8dee2aaSAndroid Build Coastguard Worker 
3225*c8dee2aaSAndroid Build Coastguard Worker         for (unsigned i = 1; i <= 4; i++) {
3226*c8dee2aaSAndroid Build Coastguard Worker             memset(buffer, 0xff, sizeof(buffer));
3227*c8dee2aaSAndroid Build Coastguard Worker             SkRasterPipeline_<256> p;
3228*c8dee2aaSAndroid Build Coastguard Worker             p.append(SkRasterPipelineOp::load_16161616, &src);
3229*c8dee2aaSAndroid Build Coastguard Worker             p.append(SkRasterPipelineOp::swap_rb);
3230*c8dee2aaSAndroid Build Coastguard Worker             p.append(SkRasterPipelineOp::store_16161616, &dst);
3231*c8dee2aaSAndroid Build Coastguard Worker             p.run(0,0, i,1);
3232*c8dee2aaSAndroid Build Coastguard Worker             for (unsigned j = 0; j < i; j++) {
3233*c8dee2aaSAndroid Build Coastguard Worker                 uint16_t expected[4] = {data[j][2], data[j][1], data[j][0], data[j][3]};
3234*c8dee2aaSAndroid Build Coastguard Worker                 REPORTER_ASSERT(r, !memcmp(&expected[0], &buffer[j], sizeof(expected)));
3235*c8dee2aaSAndroid Build Coastguard Worker             }
3236*c8dee2aaSAndroid Build Coastguard Worker             for (int j = i; j < 4; j++) {
3237*c8dee2aaSAndroid Build Coastguard Worker                 for (uint16_t u16 : buffer[j])
3238*c8dee2aaSAndroid Build Coastguard Worker                 REPORTER_ASSERT(r, u16 == 0xffff);
3239*c8dee2aaSAndroid Build Coastguard Worker             }
3240*c8dee2aaSAndroid Build Coastguard Worker         }
3241*c8dee2aaSAndroid Build Coastguard Worker     }
3242*c8dee2aaSAndroid Build Coastguard Worker }
3243*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRasterPipeline_lowp,r)3244*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRasterPipeline_lowp, r) {
3245*c8dee2aaSAndroid Build Coastguard Worker     uint32_t rgba[64];
3246*c8dee2aaSAndroid Build Coastguard Worker     for (int i = 0; i < 64; i++) {
3247*c8dee2aaSAndroid Build Coastguard Worker         rgba[i] = (4*i+0) << 0
3248*c8dee2aaSAndroid Build Coastguard Worker                 | (4*i+1) << 8
3249*c8dee2aaSAndroid Build Coastguard Worker                 | (4*i+2) << 16
3250*c8dee2aaSAndroid Build Coastguard Worker                 | (4*i+3) << 24;
3251*c8dee2aaSAndroid Build Coastguard Worker     }
3252*c8dee2aaSAndroid Build Coastguard Worker 
3253*c8dee2aaSAndroid Build Coastguard Worker     SkRasterPipeline_MemoryCtx ptr = { rgba, 0 };
3254*c8dee2aaSAndroid Build Coastguard Worker 
3255*c8dee2aaSAndroid Build Coastguard Worker     SkRasterPipeline_<256> p;
3256*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::load_8888,  &ptr);
3257*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::swap_rb);
3258*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::store_8888, &ptr);
3259*c8dee2aaSAndroid Build Coastguard Worker     p.run(0,0,64,1);
3260*c8dee2aaSAndroid Build Coastguard Worker 
3261*c8dee2aaSAndroid Build Coastguard Worker     for (int i = 0; i < 64; i++) {
3262*c8dee2aaSAndroid Build Coastguard Worker         uint32_t want = (4*i+0) << 16
3263*c8dee2aaSAndroid Build Coastguard Worker                       | (4*i+1) << 8
3264*c8dee2aaSAndroid Build Coastguard Worker                       | (4*i+2) << 0
3265*c8dee2aaSAndroid Build Coastguard Worker                       | (4*i+3) << 24;
3266*c8dee2aaSAndroid Build Coastguard Worker         if (rgba[i] != want) {
3267*c8dee2aaSAndroid Build Coastguard Worker             ERRORF(r, "got %08x, want %08x\n", rgba[i], want);
3268*c8dee2aaSAndroid Build Coastguard Worker         }
3269*c8dee2aaSAndroid Build Coastguard Worker     }
3270*c8dee2aaSAndroid Build Coastguard Worker }
3271*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRasterPipeline_swizzle,r)3272*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRasterPipeline_swizzle, r) {
3273*c8dee2aaSAndroid Build Coastguard Worker     // This takes the lowp code path
3274*c8dee2aaSAndroid Build Coastguard Worker     {
3275*c8dee2aaSAndroid Build Coastguard Worker         uint16_t rg[64];
3276*c8dee2aaSAndroid Build Coastguard Worker         for (int i = 0; i < 64; i++) {
3277*c8dee2aaSAndroid Build Coastguard Worker             rg[i] = (4*i+0) << 0
3278*c8dee2aaSAndroid Build Coastguard Worker                   | (4*i+1) << 8;
3279*c8dee2aaSAndroid Build Coastguard Worker         }
3280*c8dee2aaSAndroid Build Coastguard Worker 
3281*c8dee2aaSAndroid Build Coastguard Worker         skgpu::Swizzle swizzle("g1b1");
3282*c8dee2aaSAndroid Build Coastguard Worker 
3283*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline_MemoryCtx ptr = { rg, 0 };
3284*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline_<256> p;
3285*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::load_rg88,  &ptr);
3286*c8dee2aaSAndroid Build Coastguard Worker         swizzle.apply(&p);
3287*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::store_rg88, &ptr);
3288*c8dee2aaSAndroid Build Coastguard Worker         p.run(0,0,64,1);
3289*c8dee2aaSAndroid Build Coastguard Worker 
3290*c8dee2aaSAndroid Build Coastguard Worker         for (int i = 0; i < 64; i++) {
3291*c8dee2aaSAndroid Build Coastguard Worker             uint32_t want = 0xff    << 8
3292*c8dee2aaSAndroid Build Coastguard Worker                           | (4*i+1) << 0;
3293*c8dee2aaSAndroid Build Coastguard Worker             if (rg[i] != want) {
3294*c8dee2aaSAndroid Build Coastguard Worker                 ERRORF(r, "got %08x, want %08x\n", rg[i], want);
3295*c8dee2aaSAndroid Build Coastguard Worker             }
3296*c8dee2aaSAndroid Build Coastguard Worker         }
3297*c8dee2aaSAndroid Build Coastguard Worker     }
3298*c8dee2aaSAndroid Build Coastguard Worker     // This takes the highp code path
3299*c8dee2aaSAndroid Build Coastguard Worker     {
3300*c8dee2aaSAndroid Build Coastguard Worker         float rg[64][4];
3301*c8dee2aaSAndroid Build Coastguard Worker         for (int i = 0; i < 64; i++) {
3302*c8dee2aaSAndroid Build Coastguard Worker             rg[i][0] = i + 1;
3303*c8dee2aaSAndroid Build Coastguard Worker             rg[i][1] = 2 * i + 1;
3304*c8dee2aaSAndroid Build Coastguard Worker             rg[i][2] = 0;
3305*c8dee2aaSAndroid Build Coastguard Worker             rg[i][3] = 1;
3306*c8dee2aaSAndroid Build Coastguard Worker         }
3307*c8dee2aaSAndroid Build Coastguard Worker 
3308*c8dee2aaSAndroid Build Coastguard Worker         skgpu::Swizzle swizzle("0gra");
3309*c8dee2aaSAndroid Build Coastguard Worker 
3310*c8dee2aaSAndroid Build Coastguard Worker         uint16_t buffer[64][4];
3311*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline_MemoryCtx src = { rg,     0 },
3312*c8dee2aaSAndroid Build Coastguard Worker                                    dst = { buffer, 0};
3313*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline_<256> p;
3314*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::load_f32,  &src);
3315*c8dee2aaSAndroid Build Coastguard Worker         swizzle.apply(&p);
3316*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::store_f16, &dst);
3317*c8dee2aaSAndroid Build Coastguard Worker         p.run(0,0,64,1);
3318*c8dee2aaSAndroid Build Coastguard Worker 
3319*c8dee2aaSAndroid Build Coastguard Worker         for (int i = 0; i < 64; i++) {
3320*c8dee2aaSAndroid Build Coastguard Worker             uint16_t want[4] {
3321*c8dee2aaSAndroid Build Coastguard Worker                 h(0),
3322*c8dee2aaSAndroid Build Coastguard Worker                 h(2 * i + 1),
3323*c8dee2aaSAndroid Build Coastguard Worker                 h(i + 1),
3324*c8dee2aaSAndroid Build Coastguard Worker                 h(1),
3325*c8dee2aaSAndroid Build Coastguard Worker             };
3326*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(r, !memcmp(want, buffer[i], sizeof(buffer[i])));
3327*c8dee2aaSAndroid Build Coastguard Worker         }
3328*c8dee2aaSAndroid Build Coastguard Worker     }
3329*c8dee2aaSAndroid Build Coastguard Worker }
3330*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRasterPipeline_lowp_clamp01,r)3331*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRasterPipeline_lowp_clamp01, r) {
3332*c8dee2aaSAndroid Build Coastguard Worker     // This may seem like a funny pipeline to create,
3333*c8dee2aaSAndroid Build Coastguard Worker     // but it certainly shouldn't crash when you run it.
3334*c8dee2aaSAndroid Build Coastguard Worker 
3335*c8dee2aaSAndroid Build Coastguard Worker     uint32_t rgba = 0xff00ff00;
3336*c8dee2aaSAndroid Build Coastguard Worker 
3337*c8dee2aaSAndroid Build Coastguard Worker     SkRasterPipeline_MemoryCtx ptr = { &rgba, 0 };
3338*c8dee2aaSAndroid Build Coastguard Worker 
3339*c8dee2aaSAndroid Build Coastguard Worker     SkRasterPipeline_<256> p;
3340*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::load_8888,  &ptr);
3341*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::swap_rb);
3342*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::clamp_01);
3343*c8dee2aaSAndroid Build Coastguard Worker     p.append(SkRasterPipelineOp::store_8888, &ptr);
3344*c8dee2aaSAndroid Build Coastguard Worker     p.run(0,0,1,1);
3345*c8dee2aaSAndroid Build Coastguard Worker }
3346*c8dee2aaSAndroid Build Coastguard Worker 
3347*c8dee2aaSAndroid Build Coastguard Worker // Helper struct that can be used to scrape stack addresses at different points in a pipeline
3348*c8dee2aaSAndroid Build Coastguard Worker class StackCheckerCtx : SkRasterPipeline_CallbackCtx {
3349*c8dee2aaSAndroid Build Coastguard Worker public:
StackCheckerCtx()3350*c8dee2aaSAndroid Build Coastguard Worker     StackCheckerCtx() {
3351*c8dee2aaSAndroid Build Coastguard Worker         this->fn = [](SkRasterPipeline_CallbackCtx* self, int active_pixels) {
3352*c8dee2aaSAndroid Build Coastguard Worker             auto ctx = (StackCheckerCtx*)self;
3353*c8dee2aaSAndroid Build Coastguard Worker             ctx->fStackAddrs.push_back(&active_pixels);
3354*c8dee2aaSAndroid Build Coastguard Worker         };
3355*c8dee2aaSAndroid Build Coastguard Worker     }
3356*c8dee2aaSAndroid Build Coastguard Worker 
3357*c8dee2aaSAndroid Build Coastguard Worker     enum class Behavior {
3358*c8dee2aaSAndroid Build Coastguard Worker         kGrowth,
3359*c8dee2aaSAndroid Build Coastguard Worker         kBaseline,
3360*c8dee2aaSAndroid Build Coastguard Worker         kUnknown,
3361*c8dee2aaSAndroid Build Coastguard Worker     };
3362*c8dee2aaSAndroid Build Coastguard Worker 
GrowthBehavior()3363*c8dee2aaSAndroid Build Coastguard Worker     static Behavior GrowthBehavior() {
3364*c8dee2aaSAndroid Build Coastguard Worker         // Only some stages use the musttail attribute, so we have no way of knowing what's going to
3365*c8dee2aaSAndroid Build Coastguard Worker         // happen. In release builds, it's likely that the compiler will apply tail-call
3366*c8dee2aaSAndroid Build Coastguard Worker         // optimization. Even in some debug builds (on Windows), we don't see stack growth.
3367*c8dee2aaSAndroid Build Coastguard Worker         return Behavior::kUnknown;
3368*c8dee2aaSAndroid Build Coastguard Worker     }
3369*c8dee2aaSAndroid Build Coastguard Worker 
3370*c8dee2aaSAndroid Build Coastguard Worker     // Call one of these two each time the checker callback is added:
expectGrowth()3371*c8dee2aaSAndroid Build Coastguard Worker     StackCheckerCtx* expectGrowth() {
3372*c8dee2aaSAndroid Build Coastguard Worker         fExpectedBehavior.push_back(GrowthBehavior());
3373*c8dee2aaSAndroid Build Coastguard Worker         return this;
3374*c8dee2aaSAndroid Build Coastguard Worker     }
3375*c8dee2aaSAndroid Build Coastguard Worker 
expectBaseline()3376*c8dee2aaSAndroid Build Coastguard Worker     StackCheckerCtx* expectBaseline() {
3377*c8dee2aaSAndroid Build Coastguard Worker         fExpectedBehavior.push_back(Behavior::kBaseline);
3378*c8dee2aaSAndroid Build Coastguard Worker         return this;
3379*c8dee2aaSAndroid Build Coastguard Worker     }
3380*c8dee2aaSAndroid Build Coastguard Worker 
validate(skiatest::Reporter * r)3381*c8dee2aaSAndroid Build Coastguard Worker     void validate(skiatest::Reporter* r) {
3382*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, fStackAddrs.size() == fExpectedBehavior.size());
3383*c8dee2aaSAndroid Build Coastguard Worker 
3384*c8dee2aaSAndroid Build Coastguard Worker         // This test is storing and comparing stack pointers (to dead stack frames) as a way of
3385*c8dee2aaSAndroid Build Coastguard Worker         // measuring stack usage. Unsurprisingly, ASAN doesn't like that. HWASAN actually inserts
3386*c8dee2aaSAndroid Build Coastguard Worker         // tag bytes in the pointers, causing them not to match. Newer versions of vanilla ASAN
3387*c8dee2aaSAndroid Build Coastguard Worker         // also appear to salt the stack slightly, causing repeated calls to scrape different
3388*c8dee2aaSAndroid Build Coastguard Worker         // addresses, even though $rsp is identical on each invocation of the lambda.
3389*c8dee2aaSAndroid Build Coastguard Worker #if !defined(SK_SANITIZE_ADDRESS)
3390*c8dee2aaSAndroid Build Coastguard Worker         void* baseline = fStackAddrs[0];
3391*c8dee2aaSAndroid Build Coastguard Worker         for (size_t i = 1; i < fStackAddrs.size(); i++) {
3392*c8dee2aaSAndroid Build Coastguard Worker             if (fExpectedBehavior[i] == Behavior::kGrowth) {
3393*c8dee2aaSAndroid Build Coastguard Worker                 REPORTER_ASSERT(r, fStackAddrs[i] != baseline);
3394*c8dee2aaSAndroid Build Coastguard Worker             } else if (fExpectedBehavior[i] == Behavior::kBaseline) {
3395*c8dee2aaSAndroid Build Coastguard Worker                 REPORTER_ASSERT(r, fStackAddrs[i] == baseline);
3396*c8dee2aaSAndroid Build Coastguard Worker             } else {
3397*c8dee2aaSAndroid Build Coastguard Worker                 // Unknown behavior, nothing we can assert here
3398*c8dee2aaSAndroid Build Coastguard Worker             }
3399*c8dee2aaSAndroid Build Coastguard Worker         }
3400*c8dee2aaSAndroid Build Coastguard Worker #endif
3401*c8dee2aaSAndroid Build Coastguard Worker     }
3402*c8dee2aaSAndroid Build Coastguard Worker 
3403*c8dee2aaSAndroid Build Coastguard Worker private:
3404*c8dee2aaSAndroid Build Coastguard Worker     std::vector<void*>    fStackAddrs;
3405*c8dee2aaSAndroid Build Coastguard Worker     std::vector<Behavior> fExpectedBehavior;
3406*c8dee2aaSAndroid Build Coastguard Worker };
3407*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRasterPipeline_stack_rewind,r)3408*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRasterPipeline_stack_rewind, r) {
3409*c8dee2aaSAndroid Build Coastguard Worker     // This test verifies that we can control stack usage with stack_rewind
3410*c8dee2aaSAndroid Build Coastguard Worker 
3411*c8dee2aaSAndroid Build Coastguard Worker     // Without stack_rewind, we should (maybe) see stack growth
3412*c8dee2aaSAndroid Build Coastguard Worker     {
3413*c8dee2aaSAndroid Build Coastguard Worker         StackCheckerCtx stack;
3414*c8dee2aaSAndroid Build Coastguard Worker         uint32_t rgba = 0xff0000ff;
3415*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline_MemoryCtx ptr = { &rgba, 0 };
3416*c8dee2aaSAndroid Build Coastguard Worker 
3417*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline_<256> p;
3418*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::callback, stack.expectBaseline());
3419*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::load_8888,  &ptr);
3420*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::callback, stack.expectGrowth());
3421*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::swap_rb);
3422*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::callback, stack.expectGrowth());
3423*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::store_8888, &ptr);
3424*c8dee2aaSAndroid Build Coastguard Worker         p.run(0,0,1,1);
3425*c8dee2aaSAndroid Build Coastguard Worker 
3426*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, rgba == 0xffff0000); // Ensure the pipeline worked
3427*c8dee2aaSAndroid Build Coastguard Worker         stack.validate(r);
3428*c8dee2aaSAndroid Build Coastguard Worker     }
3429*c8dee2aaSAndroid Build Coastguard Worker 
3430*c8dee2aaSAndroid Build Coastguard Worker     // With stack_rewind, we should (always) be able to get back to baseline
3431*c8dee2aaSAndroid Build Coastguard Worker     {
3432*c8dee2aaSAndroid Build Coastguard Worker         StackCheckerCtx stack;
3433*c8dee2aaSAndroid Build Coastguard Worker         uint32_t rgba = 0xff0000ff;
3434*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline_MemoryCtx ptr = { &rgba, 0 };
3435*c8dee2aaSAndroid Build Coastguard Worker 
3436*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline_<256> p;
3437*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::callback, stack.expectBaseline());
3438*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::load_8888,  &ptr);
3439*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::callback, stack.expectGrowth());
3440*c8dee2aaSAndroid Build Coastguard Worker         p.appendStackRewind();
3441*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::callback, stack.expectBaseline());
3442*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::swap_rb);
3443*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::callback, stack.expectGrowth());
3444*c8dee2aaSAndroid Build Coastguard Worker         p.appendStackRewind();
3445*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::callback, stack.expectBaseline());
3446*c8dee2aaSAndroid Build Coastguard Worker         p.append(SkRasterPipelineOp::store_8888, &ptr);
3447*c8dee2aaSAndroid Build Coastguard Worker         p.run(0,0,1,1);
3448*c8dee2aaSAndroid Build Coastguard Worker 
3449*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, rgba == 0xffff0000); // Ensure the pipeline worked
3450*c8dee2aaSAndroid Build Coastguard Worker         stack.validate(r);
3451*c8dee2aaSAndroid Build Coastguard Worker     }
3452*c8dee2aaSAndroid Build Coastguard Worker }
3453