1// Copyright 2016 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package vector 6 7import ( 8 "bytes" 9 "fmt" 10 "math" 11 "math/rand" 12 "runtime" 13 "testing" 14) 15 16// TestDivideByFFFF tests that dividing by 0xffff is equivalent to multiplying 17// and then shifting by magic constants. The Go compiler itself issues this 18// multiply-and-shift for a division by the constant value 0xffff. This trick 19// is used in the asm code as the GOARCH=amd64 SIMD instructions have parallel 20// multiply but not parallel divide. 21// 22// There's undoubtedly a justification somewhere in Hacker's Delight chapter 10 23// "Integer Division by Constants", but I don't have a more specific link. 24// 25// http://www.hackersdelight.org/divcMore.pdf and 26// http://www.hackersdelight.org/magic.htm 27func TestDivideByFFFF(t *testing.T) { 28 const mul, shift = 0x80008001, 47 29 rng := rand.New(rand.NewSource(1)) 30 for i := 0; i < 20000; i++ { 31 u := rng.Uint32() 32 got := uint32((uint64(u) * mul) >> shift) 33 want := u / 0xffff 34 if got != want { 35 t.Fatalf("i=%d, u=%#08x: got %#08x, want %#08x", i, u, got, want) 36 } 37 } 38} 39 40// TestXxxSIMDUnaligned tests that unaligned SIMD loads/stores don't crash. 41 42func TestFixedAccumulateSIMDUnaligned(t *testing.T) { 43 if !haveFixedAccumulateSIMD { 44 t.Skip("No SIMD implemention") 45 } 46 47 dst := make([]uint8, 64) 48 src := make([]uint32, 64) 49 for d := 0; d < 16; d++ { 50 for s := 0; s < 16; s++ { 51 fixedAccumulateOpSrcSIMD(dst[d:d+32], src[s:s+32]) 52 } 53 } 54} 55 56func TestFloatingAccumulateSIMDUnaligned(t *testing.T) { 57 if !haveFloatingAccumulateSIMD { 58 t.Skip("No SIMD implemention") 59 } 60 61 dst := make([]uint8, 64) 62 src := make([]float32, 64) 63 for d := 0; d < 16; d++ { 64 for s := 0; s < 16; s++ { 65 floatingAccumulateOpSrcSIMD(dst[d:d+32], src[s:s+32]) 66 } 67 } 68} 69 70// TestXxxSIMDShortDst tests that the SIMD implementations don't write past the 71// end of the dst buffer. 72 73func TestFixedAccumulateSIMDShortDst(t *testing.T) { 74 if !haveFixedAccumulateSIMD { 75 t.Skip("No SIMD implemention") 76 } 77 78 const oneQuarter = uint32(int2ϕ(fxOne*fxOne)) / 4 79 src := []uint32{oneQuarter, oneQuarter, oneQuarter, oneQuarter} 80 for i := 0; i < 4; i++ { 81 dst := make([]uint8, 4) 82 fixedAccumulateOpSrcSIMD(dst[:i], src[:i]) 83 for j := range dst { 84 if j < i { 85 if got := dst[j]; got == 0 { 86 t.Errorf("i=%d, j=%d: got %#02x, want non-zero", i, j, got) 87 } 88 } else { 89 if got := dst[j]; got != 0 { 90 t.Errorf("i=%d, j=%d: got %#02x, want zero", i, j, got) 91 } 92 } 93 } 94 } 95} 96 97func TestFloatingAccumulateSIMDShortDst(t *testing.T) { 98 if !haveFloatingAccumulateSIMD { 99 t.Skip("No SIMD implemention") 100 } 101 102 const oneQuarter = 0.25 103 src := []float32{oneQuarter, oneQuarter, oneQuarter, oneQuarter} 104 for i := 0; i < 4; i++ { 105 dst := make([]uint8, 4) 106 floatingAccumulateOpSrcSIMD(dst[:i], src[:i]) 107 for j := range dst { 108 if j < i { 109 if got := dst[j]; got == 0 { 110 t.Errorf("i=%d, j=%d: got %#02x, want non-zero", i, j, got) 111 } 112 } else { 113 if got := dst[j]; got != 0 { 114 t.Errorf("i=%d, j=%d: got %#02x, want zero", i, j, got) 115 } 116 } 117 } 118 } 119} 120 121func TestFixedAccumulateOpOverShort(t *testing.T) { testAcc(t, fxInShort, fxMaskShort, "over") } 122func TestFixedAccumulateOpSrcShort(t *testing.T) { testAcc(t, fxInShort, fxMaskShort, "src") } 123func TestFixedAccumulateMaskShort(t *testing.T) { testAcc(t, fxInShort, fxMaskShort, "mask") } 124func TestFloatingAccumulateOpOverShort(t *testing.T) { testAcc(t, flInShort, flMaskShort, "over") } 125func TestFloatingAccumulateOpSrcShort(t *testing.T) { testAcc(t, flInShort, flMaskShort, "src") } 126func TestFloatingAccumulateMaskShort(t *testing.T) { testAcc(t, flInShort, flMaskShort, "mask") } 127 128func TestFixedAccumulateOpOver16(t *testing.T) { testAcc(t, fxIn16, fxMask16, "over") } 129func TestFixedAccumulateOpSrc16(t *testing.T) { testAcc(t, fxIn16, fxMask16, "src") } 130func TestFixedAccumulateMask16(t *testing.T) { testAcc(t, fxIn16, fxMask16, "mask") } 131func TestFloatingAccumulateOpOver16(t *testing.T) { testAcc(t, flIn16, flMask16, "over") } 132func TestFloatingAccumulateOpSrc16(t *testing.T) { testAcc(t, flIn16, flMask16, "src") } 133func TestFloatingAccumulateMask16(t *testing.T) { testAcc(t, flIn16, flMask16, "mask") } 134 135func testAcc(t *testing.T, in interface{}, mask []uint32, op string) { 136 for _, simd := range []bool{false, true} { 137 maxN := 0 138 switch in := in.(type) { 139 case []uint32: 140 if simd && !haveFixedAccumulateSIMD { 141 continue 142 } 143 maxN = len(in) 144 case []float32: 145 if simd && !haveFloatingAccumulateSIMD { 146 continue 147 } 148 maxN = len(in) 149 } 150 151 for _, n := range []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 152 33, 55, 79, 96, 120, 165, 256, maxN} { 153 154 if n > maxN { 155 continue 156 } 157 158 var ( 159 got8, want8 []uint8 160 got32, want32 []uint32 161 ) 162 switch op { 163 case "over": 164 const background = 0x40 165 got8 = make([]uint8, n) 166 for i := range got8 { 167 got8[i] = background 168 } 169 want8 = make([]uint8, n) 170 for i := range want8 { 171 dstA := uint32(background * 0x101) 172 maskA := mask[i] 173 outA := dstA*(0xffff-maskA)/0xffff + maskA 174 want8[i] = uint8(outA >> 8) 175 } 176 177 case "src": 178 got8 = make([]uint8, n) 179 want8 = make([]uint8, n) 180 for i := range want8 { 181 want8[i] = uint8(mask[i] >> 8) 182 } 183 184 case "mask": 185 got32 = make([]uint32, n) 186 want32 = mask[:n] 187 } 188 189 switch in := in.(type) { 190 case []uint32: 191 switch op { 192 case "over": 193 if simd { 194 fixedAccumulateOpOverSIMD(got8, in[:n]) 195 } else { 196 fixedAccumulateOpOver(got8, in[:n]) 197 } 198 case "src": 199 if simd { 200 fixedAccumulateOpSrcSIMD(got8, in[:n]) 201 } else { 202 fixedAccumulateOpSrc(got8, in[:n]) 203 } 204 case "mask": 205 copy(got32, in[:n]) 206 if simd { 207 fixedAccumulateMaskSIMD(got32) 208 } else { 209 fixedAccumulateMask(got32) 210 } 211 } 212 case []float32: 213 switch op { 214 case "over": 215 if simd { 216 floatingAccumulateOpOverSIMD(got8, in[:n]) 217 } else { 218 floatingAccumulateOpOver(got8, in[:n]) 219 } 220 case "src": 221 if simd { 222 floatingAccumulateOpSrcSIMD(got8, in[:n]) 223 } else { 224 floatingAccumulateOpSrc(got8, in[:n]) 225 } 226 case "mask": 227 if simd { 228 floatingAccumulateMaskSIMD(got32, in[:n]) 229 } else { 230 floatingAccumulateMask(got32, in[:n]) 231 } 232 } 233 } 234 235 if op != "mask" { 236 if !bytes.Equal(got8, want8) { 237 t.Errorf("simd=%t, n=%d:\ngot: % x\nwant: % x", simd, n, got8, want8) 238 } 239 } else { 240 if !uint32sMatch(got32, want32) { 241 t.Errorf("simd=%t, n=%d:\ngot: % x\nwant: % x", simd, n, got32, want32) 242 } 243 } 244 } 245 } 246} 247 248// This package contains multiple implementations of the same algorithm, e.g. 249// there are both SIMD and non-SIMD (vanilla) implementations on GOARCH=amd64. 250// In general, the tests in this file check that the output is *exactly* the 251// same, regardless of implementation. 252// 253// On GOARCH=wasm, float32 arithmetic is done with 64 bit precision. This is 254// allowed by the Go specification: only explicit conversions to float32 have 255// to round to 32 bit precision. However, the vanilla implementation therefore 256// produces different output for GOARCH=wasm than on other GOARCHes. 257// 258// We therefore treat GOARCH=wasm as a special case, where the tests check that 259// the output is only *approximately* the same (within a 0.1% tolerance). 260// 261// It's not that, on GOARCH=wasm, we produce the "wrong" answer. In fact, the 262// computation is more, not less, accurate on GOARCH=wasm. It's that the golden 263// output that the tests compare to were, for historical reasons, produced on 264// GOARCH=amd64 and so done with less accuracy (where float32 arithmetic is 265// performed entirely with 32 bits, not with 64 bits and then rounded back to 266// 32 bits). Furthermore, on amd64, we still want to test that SIMD and 267// non-SIMD produce exactly the same (albeit less accurate) output. The SIMD 268// implementation in particular is limited by what the underlying hardware 269// instructions provide, which often favors speed over accuracy. 270 271// approxEquals returns whether got is within 0.1% of want. 272func approxEquals(got, want float64) bool { 273 const tolerance = 0.001 274 return math.Abs(got-want) <= math.Abs(want)*tolerance 275} 276 277// sixteen is used by TestFloat32ArithmeticWithinTolerance, below. It needs to 278// be a package-level variable so that the compiler does not replace the 279// calculation with a single constant. 280var sixteen float32 = 16 281 282// TestFloat32ArithmeticWithinTolerance checks that approxEquals' tolerance is 283// sufficiently high so that the results of two separate ways of computing the 284// arbitrary fraction 16 / 1122 are deemed "approximately equal" even if they 285// aren't "exactly equal". 286// 287// We're not testing whether the computation on amd64 or wasm is "right" or 288// "wrong". We're testing that we cope with them being different. 289// 290// On GOARCH=amd64, printing x and y gives: 291// 0.0142602495543672 292// 0.014260249212384224 293// 294// On GOARCH=wasm, printing x and y gives: 295// 0.0142602495543672 296// 0.0142602495543672 297// 298// The infinitely precise (mathematical) answer is: 299// 0.014260249554367201426024955436720142602495543672recurring... 300// See https://play.golang.org/p/RxzKSdD_suE 301// 302// This test establishes a lower bound on approxEquals' tolerance constant. 303// Passing this one test (on all of the various supported GOARCH's) is a 304// necessary but not a sufficient condition on that value. Other tests in this 305// package that call uint32sMatch or float32sMatch (such as TestMakeFxInXxx, 306// TestMakeFlInXxx or anything calling testAcc) also require a sufficiently 307// large tolerance. But those tests are more complicated, and if there is a 308// problem with the tolerance constant, debugging this test can be simpler. 309func TestFloat32ArithmeticWithinTolerance(t *testing.T) { 310 x := float64(sixteen) / 1122 // Always use 64-bit division. 311 y := float64(sixteen / 1122) // Use 32- or 64-bit division (GOARCH dependent). 312 if !approxEquals(x, y) { 313 t.Errorf("x and y were not approximately equal:\nx = %v\ny = %v", x, y) 314 } 315} 316 317func uint32sMatch(xs, ys []uint32) bool { 318 if len(xs) != len(ys) { 319 return false 320 } 321 if runtime.GOARCH == "wasm" { 322 for i := range xs { 323 if !approxEquals(float64(xs[i]), float64(ys[i])) { 324 return false 325 } 326 } 327 } else { 328 for i := range xs { 329 if xs[i] != ys[i] { 330 return false 331 } 332 } 333 } 334 return true 335} 336 337func float32sMatch(xs, ys []float32) bool { 338 if len(xs) != len(ys) { 339 return false 340 } 341 if runtime.GOARCH == "wasm" { 342 for i := range xs { 343 if !approxEquals(float64(xs[i]), float64(ys[i])) { 344 return false 345 } 346 } 347 } else { 348 for i := range xs { 349 if xs[i] != ys[i] { 350 return false 351 } 352 } 353 } 354 return true 355} 356 357func BenchmarkFixedAccumulateOpOver16(b *testing.B) { benchAcc(b, fxIn16, "over", false) } 358func BenchmarkFixedAccumulateOpOverSIMD16(b *testing.B) { benchAcc(b, fxIn16, "over", true) } 359func BenchmarkFixedAccumulateOpSrc16(b *testing.B) { benchAcc(b, fxIn16, "src", false) } 360func BenchmarkFixedAccumulateOpSrcSIMD16(b *testing.B) { benchAcc(b, fxIn16, "src", true) } 361func BenchmarkFixedAccumulateMask16(b *testing.B) { benchAcc(b, fxIn16, "mask", false) } 362func BenchmarkFixedAccumulateMaskSIMD16(b *testing.B) { benchAcc(b, fxIn16, "mask", true) } 363func BenchmarkFloatingAccumulateOpOver16(b *testing.B) { benchAcc(b, flIn16, "over", false) } 364func BenchmarkFloatingAccumulateOpOverSIMD16(b *testing.B) { benchAcc(b, flIn16, "over", true) } 365func BenchmarkFloatingAccumulateOpSrc16(b *testing.B) { benchAcc(b, flIn16, "src", false) } 366func BenchmarkFloatingAccumulateOpSrcSIMD16(b *testing.B) { benchAcc(b, flIn16, "src", true) } 367func BenchmarkFloatingAccumulateMask16(b *testing.B) { benchAcc(b, flIn16, "mask", false) } 368func BenchmarkFloatingAccumulateMaskSIMD16(b *testing.B) { benchAcc(b, flIn16, "mask", true) } 369 370func BenchmarkFixedAccumulateOpOver64(b *testing.B) { benchAcc(b, fxIn64, "over", false) } 371func BenchmarkFixedAccumulateOpOverSIMD64(b *testing.B) { benchAcc(b, fxIn64, "over", true) } 372func BenchmarkFixedAccumulateOpSrc64(b *testing.B) { benchAcc(b, fxIn64, "src", false) } 373func BenchmarkFixedAccumulateOpSrcSIMD64(b *testing.B) { benchAcc(b, fxIn64, "src", true) } 374func BenchmarkFixedAccumulateMask64(b *testing.B) { benchAcc(b, fxIn64, "mask", false) } 375func BenchmarkFixedAccumulateMaskSIMD64(b *testing.B) { benchAcc(b, fxIn64, "mask", true) } 376func BenchmarkFloatingAccumulateOpOver64(b *testing.B) { benchAcc(b, flIn64, "over", false) } 377func BenchmarkFloatingAccumulateOpOverSIMD64(b *testing.B) { benchAcc(b, flIn64, "over", true) } 378func BenchmarkFloatingAccumulateOpSrc64(b *testing.B) { benchAcc(b, flIn64, "src", false) } 379func BenchmarkFloatingAccumulateOpSrcSIMD64(b *testing.B) { benchAcc(b, flIn64, "src", true) } 380func BenchmarkFloatingAccumulateMask64(b *testing.B) { benchAcc(b, flIn64, "mask", false) } 381func BenchmarkFloatingAccumulateMaskSIMD64(b *testing.B) { benchAcc(b, flIn64, "mask", true) } 382 383func benchAcc(b *testing.B, in interface{}, op string, simd bool) { 384 var f func() 385 386 switch in := in.(type) { 387 case []uint32: 388 if simd && !haveFixedAccumulateSIMD { 389 b.Skip("No SIMD implemention") 390 } 391 392 switch op { 393 case "over": 394 dst := make([]uint8, len(in)) 395 if simd { 396 f = func() { fixedAccumulateOpOverSIMD(dst, in) } 397 } else { 398 f = func() { fixedAccumulateOpOver(dst, in) } 399 } 400 case "src": 401 dst := make([]uint8, len(in)) 402 if simd { 403 f = func() { fixedAccumulateOpSrcSIMD(dst, in) } 404 } else { 405 f = func() { fixedAccumulateOpSrc(dst, in) } 406 } 407 case "mask": 408 buf := make([]uint32, len(in)) 409 copy(buf, in) 410 if simd { 411 f = func() { fixedAccumulateMaskSIMD(buf) } 412 } else { 413 f = func() { fixedAccumulateMask(buf) } 414 } 415 } 416 417 case []float32: 418 if simd && !haveFloatingAccumulateSIMD { 419 b.Skip("No SIMD implemention") 420 } 421 422 switch op { 423 case "over": 424 dst := make([]uint8, len(in)) 425 if simd { 426 f = func() { floatingAccumulateOpOverSIMD(dst, in) } 427 } else { 428 f = func() { floatingAccumulateOpOver(dst, in) } 429 } 430 case "src": 431 dst := make([]uint8, len(in)) 432 if simd { 433 f = func() { floatingAccumulateOpSrcSIMD(dst, in) } 434 } else { 435 f = func() { floatingAccumulateOpSrc(dst, in) } 436 } 437 case "mask": 438 dst := make([]uint32, len(in)) 439 if simd { 440 f = func() { floatingAccumulateMaskSIMD(dst, in) } 441 } else { 442 f = func() { floatingAccumulateMask(dst, in) } 443 } 444 } 445 } 446 447 b.ResetTimer() 448 for i := 0; i < b.N; i++ { 449 f() 450 } 451} 452 453// itou exists because "uint32(int2ϕ(-1))" doesn't compile: constant -1 454// overflows uint32. 455func itou(i int2ϕ) uint32 { 456 return uint32(i) 457} 458 459var fxInShort = []uint32{ 460 itou(+0x08000), // +0.125, // Running sum: +0.125 461 itou(-0x20000), // -0.500, // Running sum: -0.375 462 itou(+0x10000), // +0.250, // Running sum: -0.125 463 itou(+0x18000), // +0.375, // Running sum: +0.250 464 itou(+0x08000), // +0.125, // Running sum: +0.375 465 itou(+0x00000), // +0.000, // Running sum: +0.375 466 itou(-0x40000), // -1.000, // Running sum: -0.625 467 itou(-0x20000), // -0.500, // Running sum: -1.125 468 itou(+0x10000), // +0.250, // Running sum: -0.875 469 itou(+0x38000), // +0.875, // Running sum: +0.000 470 itou(+0x10000), // +0.250, // Running sum: +0.250 471 itou(+0x30000), // +0.750, // Running sum: +1.000 472} 473 474var flInShort = []float32{ 475 +0.125, // Running sum: +0.125 476 -0.500, // Running sum: -0.375 477 +0.250, // Running sum: -0.125 478 +0.375, // Running sum: +0.250 479 +0.125, // Running sum: +0.375 480 +0.000, // Running sum: +0.375 481 -1.000, // Running sum: -0.625 482 -0.500, // Running sum: -1.125 483 +0.250, // Running sum: -0.875 484 +0.875, // Running sum: +0.000 485 +0.250, // Running sum: +0.250 486 +0.750, // Running sum: +1.000 487} 488 489// It's OK for fxMaskShort and flMaskShort to have slightly different values. 490// Both the fixed and floating point implementations already have (different) 491// rounding errors in the xxxLineTo methods before we get to accumulation. It's 492// OK for 50% coverage (in ideal math) to be approximated by either 0x7fff or 493// 0x8000. Both slices do contain checks that 0% and 100% map to 0x0000 and 494// 0xffff, as does checkCornersCenter in vector_test.go. 495// 496// It is important, though, for the SIMD and non-SIMD fixed point 497// implementations to give the exact same output, and likewise for the floating 498// point implementations. 499 500var fxMaskShort = []uint32{ 501 0x2000, 502 0x6000, 503 0x2000, 504 0x4000, 505 0x6000, 506 0x6000, 507 0xa000, 508 0xffff, 509 0xe000, 510 0x0000, 511 0x4000, 512 0xffff, 513} 514 515var flMaskShort = []uint32{ 516 0x1fff, 517 0x5fff, 518 0x1fff, 519 0x3fff, 520 0x5fff, 521 0x5fff, 522 0x9fff, 523 0xffff, 524 0xdfff, 525 0x0000, 526 0x3fff, 527 0xffff, 528} 529 530func TestMakeFxInXxx(t *testing.T) { 531 dump := func(us []uint32) string { 532 var b bytes.Buffer 533 for i, u := range us { 534 if i%8 == 0 { 535 b.WriteByte('\n') 536 } 537 fmt.Fprintf(&b, "%#08x, ", u) 538 } 539 return b.String() 540 } 541 542 if !uint32sMatch(fxIn16, hardCodedFxIn16) { 543 t.Errorf("height 16: got:%v\nwant:%v", dump(fxIn16), dump(hardCodedFxIn16)) 544 } 545} 546 547func TestMakeFlInXxx(t *testing.T) { 548 dump := func(fs []float32) string { 549 var b bytes.Buffer 550 for i, f := range fs { 551 if i%8 == 0 { 552 b.WriteByte('\n') 553 } 554 fmt.Fprintf(&b, "%v, ", f) 555 } 556 return b.String() 557 } 558 559 if !float32sMatch(flIn16, hardCodedFlIn16) { 560 t.Errorf("height 16: got:%v\nwant:%v", dump(flIn16), dump(hardCodedFlIn16)) 561 } 562} 563 564func makeInXxx(height int, useFloatingPointMath bool) *Rasterizer { 565 width, data := scaledBenchmarkGlyphData(height) 566 z := NewRasterizer(width, height) 567 z.setUseFloatingPointMath(useFloatingPointMath) 568 for _, d := range data { 569 switch d.n { 570 case 0: 571 z.MoveTo(d.px, d.py) 572 case 1: 573 z.LineTo(d.px, d.py) 574 case 2: 575 z.QuadTo(d.px, d.py, d.qx, d.qy) 576 } 577 } 578 return z 579} 580 581func makeFxInXxx(height int) []uint32 { 582 z := makeInXxx(height, false) 583 return z.bufU32 584} 585 586func makeFlInXxx(height int) []float32 { 587 z := makeInXxx(height, true) 588 return z.bufF32 589} 590 591// fxInXxx and flInXxx are the z.bufU32 and z.bufF32 inputs to the accumulate 592// functions when rasterizing benchmarkGlyphData at a height of Xxx pixels. 593// 594// fxMaskXxx and flMaskXxx are the corresponding golden outputs of those 595// accumulateMask functions. 596// 597// The hardCodedEtc versions are a sanity check for unexpected changes in the 598// rasterization implementations up to but not including accumulation. 599 600var ( 601 fxIn16 = makeFxInXxx(16) 602 fxIn64 = makeFxInXxx(64) 603 flIn16 = makeFlInXxx(16) 604 flIn64 = makeFlInXxx(64) 605) 606 607var hardCodedFxIn16 = []uint32{ 608 0x00000000, 0x00000000, 0xffffe91d, 0xfffe7c4a, 0xfffeaa9f, 0xffff4e33, 0xffffc1c5, 0x00007782, 609 0x00009619, 0x0001a857, 0x000129e9, 0x00000028, 0x00000000, 0x00000000, 0xffff6e70, 0xfffd3199, 610 0xffff5ff8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00014b29, 611 0x0002acf3, 0x000007e2, 0xffffca5a, 0xfffcab73, 0xffff8a34, 0x00001b55, 0x0001b334, 0x0001449e, 612 0x0000434d, 0xffff62ec, 0xfffe1443, 0xffff325d, 0x00000000, 0x0002234a, 0x0001dcb6, 0xfffe2948, 613 0xfffdd6b8, 0x00000000, 0x00028cc0, 0x00017340, 0x00000000, 0x00000000, 0x00000000, 0xffffd2d6, 614 0xfffcadd0, 0xffff7f5c, 0x00007400, 0x00038c00, 0xfffe9260, 0xffff2da0, 0x0000023a, 0x0002259b, 615 0x0000182a, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xfffdc600, 0xfffe3a00, 0x00000059, 616 0x0003a44d, 0x00005b59, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 617 0x00000000, 0x00000000, 0xfffe33f3, 0xfffdcc0d, 0x00000000, 0x00033c02, 0x0000c3fe, 0x00000000, 618 0x00000000, 0xffffa13d, 0xfffeeec8, 0xffff8c02, 0xffff8c48, 0xffffc7b5, 0x00000000, 0xffff5b68, 619 0xffff3498, 0x00000000, 0x00033c00, 0x0000c400, 0xffff9bc4, 0xfffdf4a3, 0xfffe8df3, 0xffffe1a8, 620 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00033c00, 621 0x000092c7, 0xfffcf373, 0xffff3dc7, 0x00000fcc, 0x00011ae7, 0x000130c3, 0x0000680d, 0x00004a59, 622 0x00000a20, 0xfffe9dc4, 0xfffe4a3c, 0x00000000, 0x00033c00, 0xfffe87ef, 0xfffe3c11, 0x0000105e, 623 0x0002b9c4, 0x000135dc, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xfffe3600, 0xfffdca00, 624 0x00000000, 0x00033c00, 0xfffd9000, 0xffff3400, 0x0000e400, 0x00031c00, 0x00000000, 0x00000000, 625 0x00000000, 0x00000000, 0x00000000, 0xfffe3600, 0xfffdca00, 0x00000000, 0x00033c00, 0xfffcf9a5, 626 0xffffca5b, 0x000120e6, 0x0002df1a, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 627 0xfffdb195, 0xfffe4e6b, 0x00000000, 0x00033c00, 0xfffd9e00, 0xffff2600, 0x00002f0e, 0x00033ea3, 628 0x0000924d, 0x00000000, 0x00000000, 0x00000000, 0xfffe83b3, 0xfffd881d, 0xfffff431, 0x00000000, 629 0x00031f60, 0xffff297a, 0xfffdb726, 0x00000000, 0x000053a7, 0x0001b506, 0x0000a24b, 0xffffa32d, 630 0xfffead9b, 0xffff0479, 0xffffffc9, 0x00000000, 0x00000000, 0x0002d800, 0x0001249d, 0xfffd67bb, 631 0xfffe9baa, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0000ac03, 0x0001448b, 632 0xfffe0f70, 0x00000000, 0x000229ea, 0x0001d616, 0xffffff8c, 0xfffebf76, 0xfffe54d9, 0xffff5d9e, 633 0xffffd3eb, 0x0000c65e, 0x0000fc15, 0x0001d491, 0xffffb566, 0xfffd9433, 0x00000000, 0x0000e4ec, 634} 635 636var hardCodedFlIn16 = []float32{ 637 0, 0, -0.022306755, -0.3782405, -0.33334962, -0.1741521, -0.0607556, 0.11660573, 638 0.14664596, 0.41462868, 0.2907673, 0.0001568835, 0, 0, -0.14239307, -0.7012868, 639 -0.15632017, 0, 0, 0, 0, 0, 0, 0.3230303, 640 0.6690931, 0.007876594, -0.05189419, -0.832786, -0.11531975, 0.026225802, 0.42518616, 0.3154636, 641 0.06598757, -0.15304244, -0.47969276, -0.20012794, 0, 0.5327272, 0.46727282, -0.45950258, 642 -0.5404974, 0, 0.63484025, 0.36515975, 0, 0, 0, -0.04351709, 643 -0.8293345, -0.12714837, 0.11087036, 0.88912964, -0.35792422, -0.2053554, 0.0022513224, 0.5374398, 644 0.023588525, 0, 0, 0, 0, -0.55346966, -0.44653034, 0.0002531938, 645 0.9088273, 0.090919495, 0, 0, 0, 0, 0, 0, 646 0, 0, -0.44745448, -0.5525455, 0, 0.80748945, 0.19251058, 0, 647 0, -0.092476256, -0.2661464, -0.11322958, -0.11298219, -0.055094406, 0, -0.16045958, 648 -0.1996116, 0, 0.80748653, 0.19251347, -0.09804727, -0.51129663, -0.3610403, -0.029615778, 649 0, 0, 0, 0, 0, 0, 0, 0.80748653, 650 0.14411622, -0.76251525, -0.1890875, 0.01527351, 0.27528667, 0.29730347, 0.101477206, 0.07259522, 651 0.009900213, -0.34395567, -0.42788061, 0, 0.80748653, -0.3648737, -0.44261283, 0.015778137, 652 0.6826565, 0.30156538, 0, 0, 0, 0, -0.44563293, -0.55436707, 653 0, 0.80748653, -0.60703933, -0.20044717, 0.22371745, 0.77628255, 0, 0, 654 0, 0, 0, -0.44563293, -0.55436707, 0, 0.80748653, -0.7550391, 655 -0.05244744, 0.2797074, 0.72029257, 0, 0, 0, 0, 0, 656 -0.57440215, -0.42559785, 0, 0.80748653, -0.59273535, -0.21475118, 0.04544862, 0.81148535, 657 0.14306602, 0, 0, 0, -0.369642, -0.61841226, -0.011945802, 0, 658 0.7791623, -0.20691396, -0.57224834, 0, 0.08218567, 0.42637306, 0.1586175, -0.089709565, 659 -0.32935485, -0.24788953, -0.00022224105, 0, 0, 0.7085409, 0.28821066, -0.64765793, 660 -0.34909368, 0, 0, 0, 0, 0, 0.16679136, 0.31914657, 661 -0.48593786, 0, 0.537915, 0.462085, -0.00041967133, -0.3120329, -0.41914812, -0.15886839, 662 -0.042683028, 0.19370951, 0.24624406, 0.45803425, -0.07049577, -0.6091341, 0, 0.22253075, 663} 664 665var fxMask16 = []uint32{ 666 0x0000, 0x0000, 0x05b8, 0x66a6, 0xbbfe, 0xe871, 0xf800, 0xda20, 0xb499, 0x4a84, 0x0009, 0x0000, 0x0000, 667 0x0000, 0x2463, 0xd7fd, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xad35, 0x01f8, 0x0000, 668 0x0d69, 0xe28c, 0xffff, 0xf92a, 0x8c5d, 0x3b36, 0x2a62, 0x51a7, 0xcc97, 0xffff, 0xffff, 0x772d, 0x0000, 669 0x75ad, 0xffff, 0xffff, 0x5ccf, 0x0000, 0x0000, 0x0000, 0x0000, 0x0b4a, 0xdfd6, 0xffff, 0xe2ff, 0x0000, 670 0x5b67, 0x8fff, 0x8f70, 0x060a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8e7f, 0xffff, 0xffe9, 0x16d6, 671 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x7303, 0xffff, 0xffff, 0x30ff, 672 0x0000, 0x0000, 0x0000, 0x17b0, 0x5bfe, 0x78fe, 0x95ec, 0xa3fe, 0xa3fe, 0xcd24, 0xfffe, 0xfffe, 0x30fe, 673 0x0001, 0x190d, 0x9be5, 0xf868, 0xfffe, 0xfffe, 0xfffe, 0xfffe, 0xfffe, 0xfffe, 0xfffe, 0xfffe, 0x30fe, 674 0x0c4c, 0xcf6f, 0xfffe, 0xfc0b, 0xb551, 0x6920, 0x4f1d, 0x3c87, 0x39ff, 0x928e, 0xffff, 0xffff, 0x30ff, 675 0x8f03, 0xffff, 0xfbe7, 0x4d76, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x727f, 0xffff, 0xffff, 0x30ff, 676 0xccff, 0xffff, 0xc6ff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x727f, 0xffff, 0xffff, 0x30ff, 677 0xf296, 0xffff, 0xb7c6, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x939a, 0xffff, 0xffff, 0x30ff, 678 0xc97f, 0xffff, 0xf43c, 0x2493, 0x0000, 0x0000, 0x0000, 0x0000, 0x5f13, 0xfd0c, 0xffff, 0xffff, 0x3827, 679 0x6dc9, 0xffff, 0xffff, 0xeb16, 0x7dd4, 0x5541, 0x6c76, 0xc10f, 0xfff1, 0xffff, 0xffff, 0xffff, 0x49ff, 680 0x00d8, 0xa6e9, 0xfffe, 0xfffe, 0xfffe, 0xfffe, 0xfffe, 0xfffe, 0xd4fe, 0x83db, 0xffff, 0xffff, 0x7584, 681 0x0000, 0x001c, 0x503e, 0xbb08, 0xe3a1, 0xeea6, 0xbd0e, 0x7e09, 0x08e5, 0x1b8b, 0xb67f, 0xb67f, 0x7d44, 682} 683 684var flMask16 = []uint32{ 685 0x0000, 0x0000, 0x05b5, 0x668a, 0xbbe0, 0xe875, 0xf803, 0xda29, 0xb49f, 0x4a7a, 0x000a, 0x0000, 0x0000, 686 0x0000, 0x2473, 0xd7fb, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xad4d, 0x0204, 0x0000, 687 0x0d48, 0xe27a, 0xffff, 0xf949, 0x8c70, 0x3bae, 0x2ac9, 0x51f7, 0xccc4, 0xffff, 0xffff, 0x779f, 0x0000, 688 0x75a1, 0xffff, 0xffff, 0x5d7b, 0x0000, 0x0000, 0x0000, 0x0000, 0x0b23, 0xdf73, 0xffff, 0xe39d, 0x0000, 689 0x5ba0, 0x9033, 0x8f9f, 0x0609, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8db0, 0xffff, 0xffef, 0x1746, 690 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x728c, 0xffff, 0xffff, 0x3148, 691 0x0000, 0x0000, 0x0000, 0x17ac, 0x5bce, 0x78cb, 0x95b7, 0xa3d2, 0xa3d2, 0xcce6, 0xffff, 0xffff, 0x3148, 692 0x0000, 0x1919, 0x9bfd, 0xf86b, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x3148, 693 0x0c63, 0xcf97, 0xffff, 0xfc17, 0xb59d, 0x6981, 0x4f87, 0x3cf1, 0x3a68, 0x9276, 0xffff, 0xffff, 0x3148, 694 0x8eb0, 0xffff, 0xfbf5, 0x4d33, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x7214, 0xffff, 0xffff, 0x3148, 695 0xccaf, 0xffff, 0xc6ba, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x7214, 0xffff, 0xffff, 0x3148, 696 0xf292, 0xffff, 0xb865, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x930c, 0xffff, 0xffff, 0x3148, 697 0xc906, 0xffff, 0xf45d, 0x249f, 0x0000, 0x0000, 0x0000, 0x0000, 0x5ea0, 0xfcf1, 0xffff, 0xffff, 0x3888, 698 0x6d81, 0xffff, 0xffff, 0xeaf5, 0x7dcf, 0x5533, 0x6c2b, 0xc07b, 0xfff1, 0xffff, 0xffff, 0xffff, 0x4a9d, 699 0x00d4, 0xa6a1, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xd54d, 0x8399, 0xffff, 0xffff, 0x764b, 700 0x0000, 0x001b, 0x4ffc, 0xbb4a, 0xe3f5, 0xeee3, 0xbd4c, 0x7e42, 0x0900, 0x1b0c, 0xb6fc, 0xb6fc, 0x7e04, 701} 702 703// TestFixedFloatingCloseness compares the closeness of the fixed point and 704// floating point rasterizer. 705func TestFixedFloatingCloseness(t *testing.T) { 706 if len(fxMask16) != len(flMask16) { 707 t.Fatalf("len(fxMask16) != len(flMask16)") 708 } 709 710 total := uint32(0) 711 for i := range fxMask16 { 712 a := fxMask16[i] 713 b := flMask16[i] 714 if a > b { 715 total += a - b 716 } else { 717 total += b - a 718 } 719 } 720 n := len(fxMask16) 721 722 // This log message is useful when changing the fixed point rasterizer 723 // implementation, such as by changing ϕ. Assuming that the floating point 724 // rasterizer is accurate, the average difference is a measure of how 725 // inaccurate the (faster) fixed point rasterizer is. 726 // 727 // Smaller is better. 728 percent := float64(total*100) / float64(n*65535) 729 t.Logf("Comparing closeness of the fixed point and floating point rasterizer.\n"+ 730 "Specifically, the elements of fxMask16 and flMask16.\n"+ 731 "Total diff = %d, n = %d, avg = %.5f out of 65535, or %.5f%%.\n", 732 total, n, float64(total)/float64(n), percent) 733 734 const thresholdPercent = 1.0 735 if percent > thresholdPercent { 736 t.Errorf("average difference: got %.5f%%, want <= %.5f%%", percent, thresholdPercent) 737 } 738} 739