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(®isterValue[0], ®isterValue[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 = ®isterValue[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