Mike Klein | 68c50d0 | 2019-05-29 12:57:54 -0500 | [diff] [blame] | 1 | /* |
| 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 Klein | 267f507 | 2019-06-03 16:27:46 -0500 | [diff] [blame] | 12 | #include "tools/Resources.h" |
Mike Klein | 7b7077c | 2019-06-03 17:10:59 -0500 | [diff] [blame] | 13 | #include "tools/SkVMBuilders.h" |
Mike Klein | 68c50d0 | 2019-05-29 12:57:54 -0500 | [diff] [blame] | 14 | |
Mike Klein | 7b7077c | 2019-06-03 17:10:59 -0500 | [diff] [blame] | 15 | using Fmt = SrcoverBuilder_F32::Fmt; |
Mike Klein | 68c50d0 | 2019-05-29 12:57:54 -0500 | [diff] [blame] | 16 | const char* fmt_name(Fmt fmt) { |
| 17 | switch (fmt) { |
Mike Klein | 7b7077c | 2019-06-03 17:10:59 -0500 | [diff] [blame] | 18 | case Fmt::A8: return "A8"; |
| 19 | case Fmt::G8: return "G8"; |
| 20 | case Fmt::RGBA_8888: return "RGBA_8888"; |
Mike Klein | 68c50d0 | 2019-05-29 12:57:54 -0500 | [diff] [blame] | 21 | } |
| 22 | return ""; |
| 23 | } |
| 24 | |
Mike Klein | 68c50d0 | 2019-05-29 12:57:54 -0500 | [diff] [blame] | 25 | DEF_TEST(SkVM, r) { |
Mike Klein | 267f507 | 2019-06-03 16:27:46 -0500 | [diff] [blame] | 26 | SkDynamicMemoryWStream buf; |
Mike Klein | 7b7077c | 2019-06-03 17:10:59 -0500 | [diff] [blame] | 27 | |
| 28 | // Write all combinations of SrcoverBuilder_F32 |
Mike Klein | 68c50d0 | 2019-05-29 12:57:54 -0500 | [diff] [blame] | 29 | 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 Klein | 22ea7e9 | 2019-06-10 12:05:48 -0500 | [diff] [blame] | 33 | SrcoverBuilder_F32 builder{srcFmt, dstFmt}; |
| 34 | skvm::Program program = builder.done(); |
Mike Klein | 68c50d0 | 2019-05-29 12:57:54 -0500 | [diff] [blame] | 35 | |
Mike Klein | 267f507 | 2019-06-03 16:27:46 -0500 | [diff] [blame] | 36 | buf.writeText(fmt_name(srcFmt)); |
| 37 | buf.writeText(" over "); |
| 38 | buf.writeText(fmt_name(dstFmt)); |
| 39 | buf.writeText("\n"); |
Mike Klein | 22ea7e9 | 2019-06-10 12:05:48 -0500 | [diff] [blame] | 40 | builder.dump(&buf); |
| 41 | buf.writeText("\n"); |
Mike Klein | 68c50d0 | 2019-05-29 12:57:54 -0500 | [diff] [blame] | 42 | program.dump(&buf); |
Mike Klein | 267f507 | 2019-06-03 16:27:46 -0500 | [diff] [blame] | 43 | buf.writeText("\n"); |
| 44 | } |
Mike Klein | 68c50d0 | 2019-05-29 12:57:54 -0500 | [diff] [blame] | 45 | |
Mike Klein | 7b7077c | 2019-06-03 17:10:59 -0500 | [diff] [blame] | 46 | // Write the I32 Srcovers also. |
| 47 | { |
Mike Klein | 397fc88 | 2019-06-20 11:37:10 -0500 | [diff] [blame] | 48 | 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 Klein | 7b7077c | 2019-06-03 17:10:59 -0500 | [diff] [blame] | 54 | 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 Klein | 267f507 | 2019-06-03 16:27:46 -0500 | [diff] [blame] | 66 | sk_sp<SkData> blob = buf.detachAsData(); |
| 67 | { |
| 68 | |
| 69 | sk_sp<SkData> expected = GetResourceAsData("SkVMTest.expected"); |
Mike Klein | 7716331 | 2019-06-04 13:35:32 -0500 | [diff] [blame] | 70 | 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 Klein | 267f507 | 2019-06-03 16:27:46 -0500 | [diff] [blame] | 74 | |
Mike Klein | 7716331 | 2019-06-04 13:35:32 -0500 | [diff] [blame] | 75 | 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 Klein | 68c50d0 | 2019-05-29 12:57:54 -0500 | [diff] [blame] | 84 | } |
| 85 | } |
| 86 | |
Mike Klein | 3f59379 | 2019-06-12 12:54:52 -0500 | [diff] [blame] | 87 | auto test_8888 = [&](const skvm::Program& program) { |
| 88 | uint32_t src[9]; |
| 89 | uint32_t dst[SK_ARRAY_COUNT(src)]; |
Mike Klein | 68c50d0 | 2019-05-29 12:57:54 -0500 | [diff] [blame] | 90 | |
Mike Klein | 3f59379 | 2019-06-12 12:54:52 -0500 | [diff] [blame] | 91 | for (int i = 0; i < (int)SK_ARRAY_COUNT(src); i++) { |
| 92 | src[i] = 0xbb007733; |
| 93 | dst[i] = 0xffaaccee; |
| 94 | } |
Mike Klein | 68c50d0 | 2019-05-29 12:57:54 -0500 | [diff] [blame] | 95 | |
Mike Klein | 3f59379 | 2019-06-12 12:54:52 -0500 | [diff] [blame] | 96 | SkPMColor expected = SkPMSrcOver(src[0], dst[0]); // 0xff2dad73 |
| 97 | |
| 98 | program.eval((int)SK_ARRAY_COUNT(src), src, dst); |
Mike Klein | 68c50d0 | 2019-05-29 12:57:54 -0500 | [diff] [blame] | 99 | |
| 100 | // dst is probably 0xff2dad72. |
Mike Klein | 3f59379 | 2019-06-12 12:54:52 -0500 | [diff] [blame] | 101 | 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 Klein | 68c50d0 | 2019-05-29 12:57:54 -0500 | [diff] [blame] | 110 | } |
Mike Klein | 3f59379 | 2019-06-12 12:54:52 -0500 | [diff] [blame] | 111 | }; |
Mike Klein | 68c50d0 | 2019-05-29 12:57:54 -0500 | [diff] [blame] | 112 | |
Mike Klein | 3f59379 | 2019-06-12 12:54:52 -0500 | [diff] [blame] | 113 | test_8888(SrcoverBuilder_F32{Fmt::RGBA_8888, Fmt::RGBA_8888}.done()); |
Mike Klein | 397fc88 | 2019-06-20 11:37:10 -0500 | [diff] [blame] | 114 | test_8888(SrcoverBuilder_I32_Naive{}.done()); |
Mike Klein | 5640397 | 2019-06-12 14:01:10 -0500 | [diff] [blame] | 115 | test_8888(SrcoverBuilder_I32{}.done()); |
Mike Klein | 3f59379 | 2019-06-12 12:54:52 -0500 | [diff] [blame] | 116 | test_8888(SrcoverBuilder_I32_SWAR{}.done()); |
Mike Klein | 7b7077c | 2019-06-03 17:10:59 -0500 | [diff] [blame] | 117 | |
| 118 | { |
| 119 | skvm::Program program = SrcoverBuilder_F32{Fmt::RGBA_8888, Fmt::G8}.done(); |
Mike Klein | 68c50d0 | 2019-05-29 12:57:54 -0500 | [diff] [blame] | 120 | |
Mike Klein | 3f59379 | 2019-06-12 12:54:52 -0500 | [diff] [blame] | 121 | 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 Klein | 68c50d0 | 2019-05-29 12:57:54 -0500 | [diff] [blame] | 131 | |
| 132 | uint8_t want = SkComputeLuminance(SkGetPackedR32(over), |
| 133 | SkGetPackedG32(over), |
| 134 | SkGetPackedB32(over)); |
Mike Klein | 3f59379 | 2019-06-12 12:54:52 -0500 | [diff] [blame] | 135 | program.eval((int)SK_ARRAY_COUNT(src), src, dst); |
Mike Klein | 68c50d0 | 2019-05-29 12:57:54 -0500 | [diff] [blame] | 136 | |
Mike Klein | 3f59379 | 2019-06-12 12:54:52 -0500 | [diff] [blame] | 137 | for (auto got : dst) { |
| 138 | REPORTER_ASSERT(r, abs(got-want) < 3); |
| 139 | } |
Mike Klein | 68c50d0 | 2019-05-29 12:57:54 -0500 | [diff] [blame] | 140 | } |
| 141 | |
| 142 | { |
Mike Klein | 7b7077c | 2019-06-03 17:10:59 -0500 | [diff] [blame] | 143 | skvm::Program program = SrcoverBuilder_F32{Fmt::A8, Fmt::A8}.done(); |
Mike Klein | 68c50d0 | 2019-05-29 12:57:54 -0500 | [diff] [blame] | 144 | |
| 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 Klein | 81756e4 | 2019-06-12 11:36:28 -0500 | [diff] [blame] | 161 | |
| 162 | DEF_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 Klein | 0564204 | 2019-06-18 12:16:06 -0500 | [diff] [blame] | 188 | |
| 189 | |
| 190 | #if defined(SKVM_JIT) |
| 191 | |
| 192 | template <typename Fn> |
| 193 | static 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 Klein | 61703a6 | 2019-06-18 15:01:12 -0500 | [diff] [blame] | 202 | REPORTER_ASSERT(r, got[i] == want[i], |
| 203 | "byte %d was %02x, want %02x", i, got[i], want[i]); |
Mike Klein | 0564204 | 2019-06-18 12:16:06 -0500 | [diff] [blame] | 204 | } |
| 205 | } |
| 206 | |
| 207 | DEF_TEST(SkVM_Assembler, r) { |
Mike Klein | 397fc88 | 2019-06-20 11:37:10 -0500 | [diff] [blame] | 208 | // 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 Klein | 0564204 | 2019-06-18 12:16:06 -0500 | [diff] [blame] | 218 | // Our exit strategy from AVX code. |
Mike Klein | 397fc88 | 2019-06-20 11:37:10 -0500 | [diff] [blame] | 219 | test_asm(r, [&](A& a) { |
Mike Klein | 0564204 | 2019-06-18 12:16:06 -0500 | [diff] [blame] | 220 | a.vzeroupper(); |
| 221 | a.ret(); |
| 222 | },{ |
| 223 | 0xc5, 0xf8, 0x77, |
| 224 | 0xc3, |
| 225 | }); |
| 226 | |
| 227 | // Align should pad with nop(). |
Mike Klein | 397fc88 | 2019-06-20 11:37:10 -0500 | [diff] [blame] | 228 | test_asm(r, [&](A& a) { |
Mike Klein | 0564204 | 2019-06-18 12:16:06 -0500 | [diff] [blame] | 229 | a.ret(); |
| 230 | a.align(4); |
| 231 | },{ |
| 232 | 0xc3, |
| 233 | 0x90, 0x90, 0x90, |
| 234 | }); |
Mike Klein | 61703a6 | 2019-06-18 15:01:12 -0500 | [diff] [blame] | 235 | |
Mike Klein | 397fc88 | 2019-06-20 11:37:10 -0500 | [diff] [blame] | 236 | test_asm(r, [&](A& a) { |
| 237 | a.add(A::rax, 8); // Always good to test rax. |
| 238 | a.sub(A::rax, 32); |
Mike Klein | d3e75a7 | 2019-06-18 15:26:08 -0500 | [diff] [blame] | 239 | |
Mike Klein | 397fc88 | 2019-06-20 11:37:10 -0500 | [diff] [blame] | 240 | a.add(A::rdi, 12); // Last 0x48 REX |
| 241 | a.sub(A::rdi, 8); |
Mike Klein | d3e75a7 | 2019-06-18 15:26:08 -0500 | [diff] [blame] | 242 | |
Mike Klein | 397fc88 | 2019-06-20 11:37:10 -0500 | [diff] [blame] | 243 | a.add(A::r8 , 7); // First 0x4c REX |
| 244 | a.sub(A::r8 , 4); |
Mike Klein | d3e75a7 | 2019-06-18 15:26:08 -0500 | [diff] [blame] | 245 | |
Mike Klein | 397fc88 | 2019-06-20 11:37:10 -0500 | [diff] [blame] | 246 | a.add(A::rsi, 128); // Requires 4 byte immediate. |
| 247 | a.sub(A::r8 , 1000000); |
Mike Klein | 61703a6 | 2019-06-18 15:01:12 -0500 | [diff] [blame] | 248 | },{ |
Mike Klein | d3e75a7 | 2019-06-18 15:26:08 -0500 | [diff] [blame] | 249 | 0x48, 0x83, 0b11'000'000, 0x08, |
Mike Klein | 61703a6 | 2019-06-18 15:01:12 -0500 | [diff] [blame] | 250 | 0x48, 0x83, 0b11'101'000, 0x20, |
Mike Klein | d3e75a7 | 2019-06-18 15:26:08 -0500 | [diff] [blame] | 251 | |
| 252 | 0x48, 0x83, 0b11'000'111, 0x0c, |
Mike Klein | 61703a6 | 2019-06-18 15:01:12 -0500 | [diff] [blame] | 253 | 0x48, 0x83, 0b11'101'111, 0x08, |
Mike Klein | d3e75a7 | 2019-06-18 15:26:08 -0500 | [diff] [blame] | 254 | |
| 255 | 0x4c, 0x83, 0b11'000'000, 0x07, |
Mike Klein | 61703a6 | 2019-06-18 15:01:12 -0500 | [diff] [blame] | 256 | 0x4c, 0x83, 0b11'101'000, 0x04, |
Mike Klein | d3e75a7 | 2019-06-18 15:26:08 -0500 | [diff] [blame] | 257 | |
| 258 | 0x48, 0x81, 0b11'000'110, 0x80, 0x00, 0x00, 0x00, |
Mike Klein | 61703a6 | 2019-06-18 15:01:12 -0500 | [diff] [blame] | 259 | 0x4c, 0x81, 0b11'101'000, 0x40, 0x42, 0x0f, 0x00, |
| 260 | }); |
Mike Klein | 397fc88 | 2019-06-20 11:37:10 -0500 | [diff] [blame] | 261 | |
| 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 Klein | ff0ae81 | 2019-06-20 15:03:44 -0500 | [diff] [blame] | 279 | |
| 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 Klein | 0564204 | 2019-06-18 12:16:06 -0500 | [diff] [blame] | 293 | } |
| 294 | |
| 295 | #endif |