blob: 5e0ba420c216ccdbdfd8b8e4b9b6b57f50e7e50d [file] [log] [blame]
Mike Klein68c50d02019-05-29 12:57:54 -05001/*
2 * Copyright 2019 Google LLC
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "include/core/SkColorPriv.h"
9#include "include/private/SkColorData.h"
10#include "src/core/SkVM.h"
11#include "tests/Test.h"
Mike Klein267f5072019-06-03 16:27:46 -050012#include "tools/Resources.h"
Mike Klein7b7077c2019-06-03 17:10:59 -050013#include "tools/SkVMBuilders.h"
Mike Klein68c50d02019-05-29 12:57:54 -050014
Mike Klein7b7077c2019-06-03 17:10:59 -050015using Fmt = SrcoverBuilder_F32::Fmt;
Mike Klein68c50d02019-05-29 12:57:54 -050016const char* fmt_name(Fmt fmt) {
17 switch (fmt) {
Mike Klein7b7077c2019-06-03 17:10:59 -050018 case Fmt::A8: return "A8";
19 case Fmt::G8: return "G8";
20 case Fmt::RGBA_8888: return "RGBA_8888";
Mike Klein68c50d02019-05-29 12:57:54 -050021 }
22 return "";
23}
24
Mike Klein68c50d02019-05-29 12:57:54 -050025DEF_TEST(SkVM, r) {
Mike Klein267f5072019-06-03 16:27:46 -050026 SkDynamicMemoryWStream buf;
Mike Klein7b7077c2019-06-03 17:10:59 -050027
28 // Write all combinations of SrcoverBuilder_F32
Mike Klein68c50d02019-05-29 12:57:54 -050029 for (int s = 0; s < 3; s++)
30 for (int d = 0; d < 3; d++) {
31 auto srcFmt = (Fmt)s,
32 dstFmt = (Fmt)d;
Mike Klein22ea7e92019-06-10 12:05:48 -050033 SrcoverBuilder_F32 builder{srcFmt, dstFmt};
34 skvm::Program program = builder.done();
Mike Klein68c50d02019-05-29 12:57:54 -050035
Mike Klein267f5072019-06-03 16:27:46 -050036 buf.writeText(fmt_name(srcFmt));
37 buf.writeText(" over ");
38 buf.writeText(fmt_name(dstFmt));
39 buf.writeText("\n");
Mike Klein22ea7e92019-06-10 12:05:48 -050040 builder.dump(&buf);
41 buf.writeText("\n");
Mike Klein68c50d02019-05-29 12:57:54 -050042 program.dump(&buf);
Mike Klein267f5072019-06-03 16:27:46 -050043 buf.writeText("\n");
44 }
Mike Klein68c50d02019-05-29 12:57:54 -050045
Mike Klein7b7077c2019-06-03 17:10:59 -050046 // Write the I32 Srcovers also.
47 {
Mike Klein397fc882019-06-20 11:37:10 -050048 skvm::Program program = SrcoverBuilder_I32_Naive{}.done();
49 buf.writeText("I32 (Naive) 8888 over 8888\n");
50 program.dump(&buf);
51 buf.writeText("\n");
52 }
53 {
Mike Klein7b7077c2019-06-03 17:10:59 -050054 skvm::Program program = SrcoverBuilder_I32{}.done();
55 buf.writeText("I32 8888 over 8888\n");
56 program.dump(&buf);
57 buf.writeText("\n");
58 }
59 {
60 skvm::Program program = SrcoverBuilder_I32_SWAR{}.done();
61 buf.writeText("I32 (SWAR) 8888 over 8888\n");
62 program.dump(&buf);
63 buf.writeText("\n");
64 }
65
Mike Klein267f5072019-06-03 16:27:46 -050066 sk_sp<SkData> blob = buf.detachAsData();
67 {
68
69 sk_sp<SkData> expected = GetResourceAsData("SkVMTest.expected");
Mike Klein77163312019-06-04 13:35:32 -050070 REPORTER_ASSERT(r, expected, "Couldn't load SkVMTest.expected.");
71 if (expected) {
72 if (blob->size() != expected->size()
73 || 0 != memcmp(blob->data(), expected->data(), blob->size())) {
Mike Klein267f5072019-06-03 16:27:46 -050074
Mike Klein77163312019-06-04 13:35:32 -050075 ERRORF(r, "SkVMTest expected\n%.*s\nbut got\n%.*s\n",
76 expected->size(), expected->data(),
77 blob->size(), blob->data());
78 }
79
80 SkFILEWStream out(GetResourcePath("SkVMTest.expected").c_str());
81 if (out.isValid()) {
82 out.write(blob->data(), blob->size());
83 }
Mike Klein68c50d02019-05-29 12:57:54 -050084 }
85 }
86
Mike Klein3f593792019-06-12 12:54:52 -050087 auto test_8888 = [&](const skvm::Program& program) {
88 uint32_t src[9];
89 uint32_t dst[SK_ARRAY_COUNT(src)];
Mike Klein68c50d02019-05-29 12:57:54 -050090
Mike Klein3f593792019-06-12 12:54:52 -050091 for (int i = 0; i < (int)SK_ARRAY_COUNT(src); i++) {
92 src[i] = 0xbb007733;
93 dst[i] = 0xffaaccee;
94 }
Mike Klein68c50d02019-05-29 12:57:54 -050095
Mike Klein3f593792019-06-12 12:54:52 -050096 SkPMColor expected = SkPMSrcOver(src[0], dst[0]); // 0xff2dad73
97
98 program.eval((int)SK_ARRAY_COUNT(src), src, dst);
Mike Klein68c50d02019-05-29 12:57:54 -050099
100 // dst is probably 0xff2dad72.
Mike Klein3f593792019-06-12 12:54:52 -0500101 for (auto got : dst) {
102 auto want = expected;
103 for (int i = 0; i < 4; i++) {
104 uint8_t d = got & 0xff,
105 w = want & 0xff;
106 REPORTER_ASSERT(r, abs(d-w) < 2);
107 got >>= 8;
108 want >>= 8;
109 }
Mike Klein68c50d02019-05-29 12:57:54 -0500110 }
Mike Klein3f593792019-06-12 12:54:52 -0500111 };
Mike Klein68c50d02019-05-29 12:57:54 -0500112
Mike Klein3f593792019-06-12 12:54:52 -0500113 test_8888(SrcoverBuilder_F32{Fmt::RGBA_8888, Fmt::RGBA_8888}.done());
Mike Klein397fc882019-06-20 11:37:10 -0500114 test_8888(SrcoverBuilder_I32_Naive{}.done());
Mike Klein56403972019-06-12 14:01:10 -0500115 test_8888(SrcoverBuilder_I32{}.done());
Mike Klein3f593792019-06-12 12:54:52 -0500116 test_8888(SrcoverBuilder_I32_SWAR{}.done());
Mike Klein7b7077c2019-06-03 17:10:59 -0500117
118 {
119 skvm::Program program = SrcoverBuilder_F32{Fmt::RGBA_8888, Fmt::G8}.done();
Mike Klein68c50d02019-05-29 12:57:54 -0500120
Mike Klein3f593792019-06-12 12:54:52 -0500121 uint32_t src[9];
122 uint8_t dst[SK_ARRAY_COUNT(src)];
123
124 for (int i = 0; i < (int)SK_ARRAY_COUNT(src); i++) {
125 src[i] = 0xbb007733;
126 dst[i] = 0x42;
127 }
128
129 SkPMColor over = SkPMSrcOver(SkPackARGB32(0xbb, 0x33, 0x77, 0x00),
130 0xff424242);
Mike Klein68c50d02019-05-29 12:57:54 -0500131
132 uint8_t want = SkComputeLuminance(SkGetPackedR32(over),
133 SkGetPackedG32(over),
134 SkGetPackedB32(over));
Mike Klein3f593792019-06-12 12:54:52 -0500135 program.eval((int)SK_ARRAY_COUNT(src), src, dst);
Mike Klein68c50d02019-05-29 12:57:54 -0500136
Mike Klein3f593792019-06-12 12:54:52 -0500137 for (auto got : dst) {
138 REPORTER_ASSERT(r, abs(got-want) < 3);
139 }
Mike Klein68c50d02019-05-29 12:57:54 -0500140 }
141
142 {
Mike Klein7b7077c2019-06-03 17:10:59 -0500143 skvm::Program program = SrcoverBuilder_F32{Fmt::A8, Fmt::A8}.done();
Mike Klein68c50d02019-05-29 12:57:54 -0500144
145 uint8_t src[256],
146 dst[256];
147 for (int i = 0; i < 256; i++) {
148 src[i] = 255 - i;
149 dst[i] = i;
150 }
151
152 program.eval(256, src, dst);
153
154 for (int i = 0; i < 256; i++) {
155 uint8_t want = SkGetPackedA32(SkPMSrcOver(SkPackARGB32(src[i], 0,0,0),
156 SkPackARGB32( i, 0,0,0)));
157 REPORTER_ASSERT(r, abs(dst[i]-want) < 2);
158 }
159 }
160}
Mike Klein81756e42019-06-12 11:36:28 -0500161
162DEF_TEST(SkVM_LoopCounts, r) {
163 // Make sure we cover all the exact N we want.
164
165 int buf[64];
166 for (int N = 0; N <= (int)SK_ARRAY_COUNT(buf); N++) {
167 for (int i = 0; i < (int)SK_ARRAY_COUNT(buf); i++) {
168 buf[i] = i;
169 }
170
171 // buf[i] += 1
172 skvm::Builder b;
173 b.store32(b.arg(0),
174 b.add(b.splat(1),
175 b.load32(b.arg(0))));
176
177 skvm::Program program = b.done();
178 program.eval(N, buf);
179
180 for (int i = 0; i < N; i++) {
181 REPORTER_ASSERT(r, buf[i] == i+1);
182 }
183 for (int i = N; i < (int)SK_ARRAY_COUNT(buf); i++) {
184 REPORTER_ASSERT(r, buf[i] == i);
185 }
186 }
187}
Mike Klein05642042019-06-18 12:16:06 -0500188
189
190#if defined(SKVM_JIT)
191
192template <typename Fn>
193static void test_asm(skiatest::Reporter* r, Fn&& fn, std::initializer_list<uint8_t> expected) {
194 skvm::Assembler a;
195 fn(a);
196
197 REPORTER_ASSERT(r, a.size() == expected.size());
198
199 auto got = (const uint8_t*)a.code(),
200 want = expected.begin();
201 for (int i = 0; i < (int)std::min(a.size(), expected.size()); i++) {
Mike Klein61703a62019-06-18 15:01:12 -0500202 REPORTER_ASSERT(r, got[i] == want[i],
203 "byte %d was %02x, want %02x", i, got[i], want[i]);
Mike Klein05642042019-06-18 12:16:06 -0500204 }
205}
206
207DEF_TEST(SkVM_Assembler, r) {
Mike Klein397fc882019-06-20 11:37:10 -0500208 // Easiest way to generate test cases is
209 //
210 // echo '...some asm...' | llvm-mc -show-encoding -x86-asm-syntax=intel
211 //
212 // The -x86-asm-syntax=intel bit is optional, controlling the
213 // input syntax only; the output will always be AT&T op x,y,dst style.
214 // Our APIs read more like Intel op dst,x,y as op(dst,x,y), so I find
215 // that a bit easier to use here, despite maybe favoring AT&T overall.
216
217 using A = skvm::Assembler;
Mike Klein05642042019-06-18 12:16:06 -0500218 // Our exit strategy from AVX code.
Mike Klein397fc882019-06-20 11:37:10 -0500219 test_asm(r, [&](A& a) {
Mike Klein05642042019-06-18 12:16:06 -0500220 a.vzeroupper();
221 a.ret();
222 },{
223 0xc5, 0xf8, 0x77,
224 0xc3,
225 });
226
227 // Align should pad with nop().
Mike Klein397fc882019-06-20 11:37:10 -0500228 test_asm(r, [&](A& a) {
Mike Klein05642042019-06-18 12:16:06 -0500229 a.ret();
230 a.align(4);
231 },{
232 0xc3,
233 0x90, 0x90, 0x90,
234 });
Mike Klein61703a62019-06-18 15:01:12 -0500235
Mike Klein397fc882019-06-20 11:37:10 -0500236 test_asm(r, [&](A& a) {
237 a.add(A::rax, 8); // Always good to test rax.
238 a.sub(A::rax, 32);
Mike Kleind3e75a72019-06-18 15:26:08 -0500239
Mike Klein397fc882019-06-20 11:37:10 -0500240 a.add(A::rdi, 12); // Last 0x48 REX
241 a.sub(A::rdi, 8);
Mike Kleind3e75a72019-06-18 15:26:08 -0500242
Mike Klein397fc882019-06-20 11:37:10 -0500243 a.add(A::r8 , 7); // First 0x4c REX
244 a.sub(A::r8 , 4);
Mike Kleind3e75a72019-06-18 15:26:08 -0500245
Mike Klein397fc882019-06-20 11:37:10 -0500246 a.add(A::rsi, 128); // Requires 4 byte immediate.
247 a.sub(A::r8 , 1000000);
Mike Klein61703a62019-06-18 15:01:12 -0500248 },{
Mike Kleind3e75a72019-06-18 15:26:08 -0500249 0x48, 0x83, 0b11'000'000, 0x08,
Mike Klein61703a62019-06-18 15:01:12 -0500250 0x48, 0x83, 0b11'101'000, 0x20,
Mike Kleind3e75a72019-06-18 15:26:08 -0500251
252 0x48, 0x83, 0b11'000'111, 0x0c,
Mike Klein61703a62019-06-18 15:01:12 -0500253 0x48, 0x83, 0b11'101'111, 0x08,
Mike Kleind3e75a72019-06-18 15:26:08 -0500254
255 0x4c, 0x83, 0b11'000'000, 0x07,
Mike Klein61703a62019-06-18 15:01:12 -0500256 0x4c, 0x83, 0b11'101'000, 0x04,
Mike Kleind3e75a72019-06-18 15:26:08 -0500257
258 0x48, 0x81, 0b11'000'110, 0x80, 0x00, 0x00, 0x00,
Mike Klein61703a62019-06-18 15:01:12 -0500259 0x4c, 0x81, 0b11'101'000, 0x40, 0x42, 0x0f, 0x00,
260 });
Mike Klein397fc882019-06-20 11:37:10 -0500261
262
263 test_asm(r, [&](A& a) {
264 a.vpaddd (A::ymm0, A::ymm1, A::ymm2); // Low registers and 0x0f map -> 2-byte VEX.
265 a.vpaddd (A::ymm8, A::ymm1, A::ymm2); // A high dst register is ok -> 2-byte VEX.
266 a.vpaddd (A::ymm0, A::ymm8, A::ymm2); // A high first argument register -> 2-byte VEX.
267 a.vpaddd (A::ymm0, A::ymm1, A::ymm8); // A high second argument -> 3-byte VEX.
268 a.vpmulld(A::ymm0, A::ymm1, A::ymm2); // Using non-0x0f map instruction -> 3-byte VEX.
269 a.vpsubd (A::ymm0, A::ymm1, A::ymm2); // Test vpsubd to ensure argument order is right.
270 },{
271 /* VEX */ /*op*/ /*modRM*/
272 0xc5, 0xf5, 0xfe, 0xc2,
273 0xc5, 0x75, 0xfe, 0xc2,
274 0xc5, 0xbd, 0xfe, 0xc2,
275 0xc4, 0xc1, 0x75, 0xfe, 0xc0,
276 0xc4, 0xe2, 0x75, 0x40, 0xc2,
277 0xc5, 0xf5, 0xfa, 0xc2,
278 });
Mike Kleinff0ae812019-06-20 15:03:44 -0500279
280 test_asm(r, [&](A& a) {
281 a.vpsrld(A::ymm15, A::ymm2, 8);
282 a.vpsrld(A::ymm0 , A::ymm8, 5);
283 },{
284 0xc5, 0x85, 0x72,0xd2, 0x08,
285 0xc4,0xc1,0x7d, 0x72,0xd0, 0x05,
286 });
287
288 test_asm(r, [&](A& a) {
289 a.vpermq(A::ymm1, A::ymm2, 5);
290 },{
291 0xc4,0xe3,0xfd, 0x00,0xca, 0x05,
292 });
Mike Klein05642042019-06-18 12:16:06 -0500293}
294
295#endif