git clone https://git.lucas.co/go_mono.git
vector/acc_test.go (26.9K)
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
5 package vector
6
7 import (
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
27 func 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
42 func TestFixedAccumulateSIMDUnaligned(t *testing.T) {
43 if !haveAccumulateSIMD {
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
56 func TestFloatingAccumulateSIMDUnaligned(t *testing.T) {
57 if !haveAccumulateSIMD {
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
73 func TestFixedAccumulateSIMDShortDst(t *testing.T) {
74 if !haveAccumulateSIMD {
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
97 func TestFloatingAccumulateSIMDShortDst(t *testing.T) {
98 if !haveAccumulateSIMD {
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
121 func TestFixedAccumulateOpOverShort(t *testing.T) { testAcc(t, fxInShort, fxMaskShort, "over") }
122 func TestFixedAccumulateOpSrcShort(t *testing.T) { testAcc(t, fxInShort, fxMaskShort, "src") }
123 func TestFixedAccumulateMaskShort(t *testing.T) { testAcc(t, fxInShort, fxMaskShort, "mask") }
124 func TestFloatingAccumulateOpOverShort(t *testing.T) { testAcc(t, flInShort, flMaskShort, "over") }
125 func TestFloatingAccumulateOpSrcShort(t *testing.T) { testAcc(t, flInShort, flMaskShort, "src") }
126 func TestFloatingAccumulateMaskShort(t *testing.T) { testAcc(t, flInShort, flMaskShort, "mask") }
127
128 func TestFixedAccumulateOpOver16(t *testing.T) { testAcc(t, fxIn16, fxMask16, "over") }
129 func TestFixedAccumulateOpSrc16(t *testing.T) { testAcc(t, fxIn16, fxMask16, "src") }
130 func TestFixedAccumulateMask16(t *testing.T) { testAcc(t, fxIn16, fxMask16, "mask") }
131 func TestFloatingAccumulateOpOver16(t *testing.T) { testAcc(t, flIn16, flMask16, "over") }
132 func TestFloatingAccumulateOpSrc16(t *testing.T) { testAcc(t, flIn16, flMask16, "src") }
133 func TestFloatingAccumulateMask16(t *testing.T) { testAcc(t, flIn16, flMask16, "mask") }
134
135 func 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 && !haveAccumulateSIMD {
141 continue
142 }
143 maxN = len(in)
144 case []float32:
145 if simd && !haveAccumulateSIMD {
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.
272 func 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.
280 var 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 //
292 // 0.0142602495543672
293 // 0.014260249212384224
294 //
295 // On GOARCH=wasm, printing x and y gives:
296 //
297 // 0.0142602495543672
298 // 0.0142602495543672
299 //
300 // The infinitely precise (mathematical) answer is:
301 //
302 // 0.014260249554367201426024955436720142602495543672recurring...
303 //
304 // See https://play.golang.org/p/RxzKSdD_suE
305 //
306 // This test establishes a lower bound on approxEquals' tolerance constant.
307 // Passing this one test (on all of the various supported GOARCH's) is a
308 // necessary but not a sufficient condition on that value. Other tests in this
309 // package that call uint32sMatch or float32sMatch (such as TestMakeFxInXxx,
310 // TestMakeFlInXxx or anything calling testAcc) also require a sufficiently
311 // large tolerance. But those tests are more complicated, and if there is a
312 // problem with the tolerance constant, debugging this test can be simpler.
313 func TestFloat32ArithmeticWithinTolerance(t *testing.T) {
314 x := float64(sixteen) / 1122 // Always use 64-bit division.
315 y := float64(sixteen / 1122) // Use 32- or 64-bit division (GOARCH dependent).
316 if !approxEquals(x, y) {
317 t.Errorf("x and y were not approximately equal:\nx = %v\ny = %v", x, y)
318 }
319 }
320
321 func uint32sMatch(xs, ys []uint32) bool {
322 if len(xs) != len(ys) {
323 return false
324 }
325 if runtime.GOARCH == "wasm" {
326 for i := range xs {
327 if !approxEquals(float64(xs[i]), float64(ys[i])) {
328 return false
329 }
330 }
331 } else {
332 for i := range xs {
333 if xs[i] != ys[i] {
334 return false
335 }
336 }
337 }
338 return true
339 }
340
341 func float32sMatch(xs, ys []float32) bool {
342 if len(xs) != len(ys) {
343 return false
344 }
345 if runtime.GOARCH == "wasm" {
346 for i := range xs {
347 if !approxEquals(float64(xs[i]), float64(ys[i])) {
348 return false
349 }
350 }
351 } else {
352 for i := range xs {
353 if xs[i] != ys[i] {
354 return false
355 }
356 }
357 }
358 return true
359 }
360
361 func BenchmarkFixedAccumulateOpOver16(b *testing.B) { benchAcc(b, fxIn16, "over", false) }
362 func BenchmarkFixedAccumulateOpOverSIMD16(b *testing.B) { benchAcc(b, fxIn16, "over", true) }
363 func BenchmarkFixedAccumulateOpSrc16(b *testing.B) { benchAcc(b, fxIn16, "src", false) }
364 func BenchmarkFixedAccumulateOpSrcSIMD16(b *testing.B) { benchAcc(b, fxIn16, "src", true) }
365 func BenchmarkFixedAccumulateMask16(b *testing.B) { benchAcc(b, fxIn16, "mask", false) }
366 func BenchmarkFixedAccumulateMaskSIMD16(b *testing.B) { benchAcc(b, fxIn16, "mask", true) }
367 func BenchmarkFloatingAccumulateOpOver16(b *testing.B) { benchAcc(b, flIn16, "over", false) }
368 func BenchmarkFloatingAccumulateOpOverSIMD16(b *testing.B) { benchAcc(b, flIn16, "over", true) }
369 func BenchmarkFloatingAccumulateOpSrc16(b *testing.B) { benchAcc(b, flIn16, "src", false) }
370 func BenchmarkFloatingAccumulateOpSrcSIMD16(b *testing.B) { benchAcc(b, flIn16, "src", true) }
371 func BenchmarkFloatingAccumulateMask16(b *testing.B) { benchAcc(b, flIn16, "mask", false) }
372 func BenchmarkFloatingAccumulateMaskSIMD16(b *testing.B) { benchAcc(b, flIn16, "mask", true) }
373
374 func BenchmarkFixedAccumulateOpOver64(b *testing.B) { benchAcc(b, fxIn64, "over", false) }
375 func BenchmarkFixedAccumulateOpOverSIMD64(b *testing.B) { benchAcc(b, fxIn64, "over", true) }
376 func BenchmarkFixedAccumulateOpSrc64(b *testing.B) { benchAcc(b, fxIn64, "src", false) }
377 func BenchmarkFixedAccumulateOpSrcSIMD64(b *testing.B) { benchAcc(b, fxIn64, "src", true) }
378 func BenchmarkFixedAccumulateMask64(b *testing.B) { benchAcc(b, fxIn64, "mask", false) }
379 func BenchmarkFixedAccumulateMaskSIMD64(b *testing.B) { benchAcc(b, fxIn64, "mask", true) }
380 func BenchmarkFloatingAccumulateOpOver64(b *testing.B) { benchAcc(b, flIn64, "over", false) }
381 func BenchmarkFloatingAccumulateOpOverSIMD64(b *testing.B) { benchAcc(b, flIn64, "over", true) }
382 func BenchmarkFloatingAccumulateOpSrc64(b *testing.B) { benchAcc(b, flIn64, "src", false) }
383 func BenchmarkFloatingAccumulateOpSrcSIMD64(b *testing.B) { benchAcc(b, flIn64, "src", true) }
384 func BenchmarkFloatingAccumulateMask64(b *testing.B) { benchAcc(b, flIn64, "mask", false) }
385 func BenchmarkFloatingAccumulateMaskSIMD64(b *testing.B) { benchAcc(b, flIn64, "mask", true) }
386
387 func benchAcc(b *testing.B, in interface{}, op string, simd bool) {
388 var f func()
389
390 switch in := in.(type) {
391 case []uint32:
392 if simd && !haveAccumulateSIMD {
393 b.Skip("No SIMD implemention")
394 }
395
396 switch op {
397 case "over":
398 dst := make([]uint8, len(in))
399 if simd {
400 f = func() { fixedAccumulateOpOverSIMD(dst, in) }
401 } else {
402 f = func() { fixedAccumulateOpOver(dst, in) }
403 }
404 case "src":
405 dst := make([]uint8, len(in))
406 if simd {
407 f = func() { fixedAccumulateOpSrcSIMD(dst, in) }
408 } else {
409 f = func() { fixedAccumulateOpSrc(dst, in) }
410 }
411 case "mask":
412 buf := make([]uint32, len(in))
413 copy(buf, in)
414 if simd {
415 f = func() { fixedAccumulateMaskSIMD(buf) }
416 } else {
417 f = func() { fixedAccumulateMask(buf) }
418 }
419 }
420
421 case []float32:
422 if simd && !haveAccumulateSIMD {
423 b.Skip("No SIMD implemention")
424 }
425
426 switch op {
427 case "over":
428 dst := make([]uint8, len(in))
429 if simd {
430 f = func() { floatingAccumulateOpOverSIMD(dst, in) }
431 } else {
432 f = func() { floatingAccumulateOpOver(dst, in) }
433 }
434 case "src":
435 dst := make([]uint8, len(in))
436 if simd {
437 f = func() { floatingAccumulateOpSrcSIMD(dst, in) }
438 } else {
439 f = func() { floatingAccumulateOpSrc(dst, in) }
440 }
441 case "mask":
442 dst := make([]uint32, len(in))
443 if simd {
444 f = func() { floatingAccumulateMaskSIMD(dst, in) }
445 } else {
446 f = func() { floatingAccumulateMask(dst, in) }
447 }
448 }
449 }
450
451 b.ResetTimer()
452 for i := 0; i < b.N; i++ {
453 f()
454 }
455 }
456
457 // itou exists because "uint32(int2ϕ(-1))" doesn't compile: constant -1
458 // overflows uint32.
459 func itou(i int2ϕ) uint32 {
460 return uint32(i)
461 }
462
463 var fxInShort = []uint32{
464 itou(+0x08000), // +0.125, // Running sum: +0.125
465 itou(-0x20000), // -0.500, // Running sum: -0.375
466 itou(+0x10000), // +0.250, // Running sum: -0.125
467 itou(+0x18000), // +0.375, // Running sum: +0.250
468 itou(+0x08000), // +0.125, // Running sum: +0.375
469 itou(+0x00000), // +0.000, // Running sum: +0.375
470 itou(-0x40000), // -1.000, // Running sum: -0.625
471 itou(-0x20000), // -0.500, // Running sum: -1.125
472 itou(+0x10000), // +0.250, // Running sum: -0.875
473 itou(+0x38000), // +0.875, // Running sum: +0.000
474 itou(+0x10000), // +0.250, // Running sum: +0.250
475 itou(+0x30000), // +0.750, // Running sum: +1.000
476 }
477
478 var flInShort = []float32{
479 +0.125, // Running sum: +0.125
480 -0.500, // Running sum: -0.375
481 +0.250, // Running sum: -0.125
482 +0.375, // Running sum: +0.250
483 +0.125, // Running sum: +0.375
484 +0.000, // Running sum: +0.375
485 -1.000, // Running sum: -0.625
486 -0.500, // Running sum: -1.125
487 +0.250, // Running sum: -0.875
488 +0.875, // Running sum: +0.000
489 +0.250, // Running sum: +0.250
490 +0.750, // Running sum: +1.000
491 }
492
493 // It's OK for fxMaskShort and flMaskShort to have slightly different values.
494 // Both the fixed and floating point implementations already have (different)
495 // rounding errors in the xxxLineTo methods before we get to accumulation. It's
496 // OK for 50% coverage (in ideal math) to be approximated by either 0x7fff or
497 // 0x8000. Both slices do contain checks that 0% and 100% map to 0x0000 and
498 // 0xffff, as does checkCornersCenter in vector_test.go.
499 //
500 // It is important, though, for the SIMD and non-SIMD fixed point
501 // implementations to give the exact same output, and likewise for the floating
502 // point implementations.
503
504 var fxMaskShort = []uint32{
505 0x2000,
506 0x6000,
507 0x2000,
508 0x4000,
509 0x6000,
510 0x6000,
511 0xa000,
512 0xffff,
513 0xe000,
514 0x0000,
515 0x4000,
516 0xffff,
517 }
518
519 var flMaskShort = []uint32{
520 0x1fff,
521 0x5fff,
522 0x1fff,
523 0x3fff,
524 0x5fff,
525 0x5fff,
526 0x9fff,
527 0xffff,
528 0xdfff,
529 0x0000,
530 0x3fff,
531 0xffff,
532 }
533
534 func TestMakeFxInXxx(t *testing.T) {
535 dump := func(us []uint32) string {
536 var b bytes.Buffer
537 for i, u := range us {
538 if i%8 == 0 {
539 b.WriteByte('\n')
540 }
541 fmt.Fprintf(&b, "%#08x, ", u)
542 }
543 return b.String()
544 }
545
546 if !uint32sMatch(fxIn16, hardCodedFxIn16) {
547 t.Errorf("height 16: got:%v\nwant:%v", dump(fxIn16), dump(hardCodedFxIn16))
548 }
549 }
550
551 func TestMakeFlInXxx(t *testing.T) {
552 dump := func(fs []float32) string {
553 var b bytes.Buffer
554 for i, f := range fs {
555 if i%8 == 0 {
556 b.WriteByte('\n')
557 }
558 fmt.Fprintf(&b, "%v, ", f)
559 }
560 return b.String()
561 }
562
563 if !float32sMatch(flIn16, hardCodedFlIn16) {
564 t.Errorf("height 16: got:%v\nwant:%v", dump(flIn16), dump(hardCodedFlIn16))
565 }
566 }
567
568 func makeInXxx(height int, useFloatingPointMath bool) *Rasterizer {
569 width, data := scaledBenchmarkGlyphData(height)
570 z := NewRasterizer(width, height)
571 z.setUseFloatingPointMath(useFloatingPointMath)
572 for _, d := range data {
573 switch d.n {
574 case 0:
575 z.MoveTo(d.px, d.py)
576 case 1:
577 z.LineTo(d.px, d.py)
578 case 2:
579 z.QuadTo(d.px, d.py, d.qx, d.qy)
580 }
581 }
582 return z
583 }
584
585 func makeFxInXxx(height int) []uint32 {
586 z := makeInXxx(height, false)
587 return z.bufU32
588 }
589
590 func makeFlInXxx(height int) []float32 {
591 z := makeInXxx(height, true)
592 return z.bufF32
593 }
594
595 // fxInXxx and flInXxx are the z.bufU32 and z.bufF32 inputs to the accumulate
596 // functions when rasterizing benchmarkGlyphData at a height of Xxx pixels.
597 //
598 // fxMaskXxx and flMaskXxx are the corresponding golden outputs of those
599 // accumulateMask functions.
600 //
601 // The hardCodedEtc versions are a sanity check for unexpected changes in the
602 // rasterization implementations up to but not including accumulation.
603
604 var (
605 fxIn16 = makeFxInXxx(16)
606 fxIn64 = makeFxInXxx(64)
607 flIn16 = makeFlInXxx(16)
608 flIn64 = makeFlInXxx(64)
609 )
610
611 var hardCodedFxIn16 = []uint32{
612 0x00000000, 0x00000000, 0xffffe91d, 0xfffe7c4a, 0xfffeaa9f, 0xffff4e33, 0xffffc1c5, 0x00007782,
613 0x00009619, 0x0001a857, 0x000129e9, 0x00000028, 0x00000000, 0x00000000, 0xffff6e70, 0xfffd3199,
614 0xffff5ff8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00014b29,
615 0x0002acf3, 0x000007e2, 0xffffca5a, 0xfffcab73, 0xffff8a34, 0x00001b55, 0x0001b334, 0x0001449e,
616 0x0000434d, 0xffff62ec, 0xfffe1443, 0xffff325d, 0x00000000, 0x0002234a, 0x0001dcb6, 0xfffe2948,
617 0xfffdd6b8, 0x00000000, 0x00028cc0, 0x00017340, 0x00000000, 0x00000000, 0x00000000, 0xffffd2d6,
618 0xfffcadd0, 0xffff7f5c, 0x00007400, 0x00038c00, 0xfffe9260, 0xffff2da0, 0x0000023a, 0x0002259b,
619 0x0000182a, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xfffdc600, 0xfffe3a00, 0x00000059,
620 0x0003a44d, 0x00005b59, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
621 0x00000000, 0x00000000, 0xfffe33f3, 0xfffdcc0d, 0x00000000, 0x00033c02, 0x0000c3fe, 0x00000000,
622 0x00000000, 0xffffa13d, 0xfffeeec8, 0xffff8c02, 0xffff8c48, 0xffffc7b5, 0x00000000, 0xffff5b68,
623 0xffff3498, 0x00000000, 0x00033c00, 0x0000c400, 0xffff9bc4, 0xfffdf4a3, 0xfffe8df3, 0xffffe1a8,
624 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00033c00,
625 0x000092c7, 0xfffcf373, 0xffff3dc7, 0x00000fcc, 0x00011ae7, 0x000130c3, 0x0000680d, 0x00004a59,
626 0x00000a20, 0xfffe9dc4, 0xfffe4a3c, 0x00000000, 0x00033c00, 0xfffe87ef, 0xfffe3c11, 0x0000105e,
627 0x0002b9c4, 0x000135dc, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xfffe3600, 0xfffdca00,
628 0x00000000, 0x00033c00, 0xfffd9000, 0xffff3400, 0x0000e400, 0x00031c00, 0x00000000, 0x00000000,
629 0x00000000, 0x00000000, 0x00000000, 0xfffe3600, 0xfffdca00, 0x00000000, 0x00033c00, 0xfffcf9a5,
630 0xffffca5b, 0x000120e6, 0x0002df1a, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
631 0xfffdb195, 0xfffe4e6b, 0x00000000, 0x00033c00, 0xfffd9e00, 0xffff2600, 0x00002f0e, 0x00033ea3,
632 0x0000924d, 0x00000000, 0x00000000, 0x00000000, 0xfffe83b3, 0xfffd881d, 0xfffff431, 0x00000000,
633 0x00031f60, 0xffff297a, 0xfffdb726, 0x00000000, 0x000053a7, 0x0001b506, 0x0000a24b, 0xffffa32d,
634 0xfffead9b, 0xffff0479, 0xffffffc9, 0x00000000, 0x00000000, 0x0002d800, 0x0001249d, 0xfffd67bb,
635 0xfffe9baa, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0000ac03, 0x0001448b,
636 0xfffe0f70, 0x00000000, 0x000229ea, 0x0001d616, 0xffffff8c, 0xfffebf76, 0xfffe54d9, 0xffff5d9e,
637 0xffffd3eb, 0x0000c65e, 0x0000fc15, 0x0001d491, 0xffffb566, 0xfffd9433, 0x00000000, 0x0000e4ec,
638 }
639
640 var hardCodedFlIn16 = []float32{
641 0, 0, -0.022306755, -0.3782405, -0.33334962, -0.1741521, -0.0607556, 0.11660573,
642 0.14664596, 0.41462868, 0.2907673, 0.0001568835, 0, 0, -0.14239307, -0.7012868,
643 -0.15632017, 0, 0, 0, 0, 0, 0, 0.3230303,
644 0.6690931, 0.007876594, -0.05189419, -0.832786, -0.11531975, 0.026225802, 0.42518616, 0.3154636,
645 0.06598757, -0.15304244, -0.47969276, -0.20012794, 0, 0.5327272, 0.46727282, -0.45950258,
646 -0.5404974, 0, 0.63484025, 0.36515975, 0, 0, 0, -0.04351709,
647 -0.8293345, -0.12714837, 0.11087036, 0.88912964, -0.35792422, -0.2053554, 0.0022513224, 0.5374398,
648 0.023588525, 0, 0, 0, 0, -0.55346966, -0.44653034, 0.0002531938,
649 0.9088273, 0.090919495, 0, 0, 0, 0, 0, 0,
650 0, 0, -0.44745448, -0.5525455, 0, 0.80748945, 0.19251058, 0,
651 0, -0.092476256, -0.2661464, -0.11322958, -0.11298219, -0.055094406, 0, -0.16045958,
652 -0.1996116, 0, 0.80748653, 0.19251347, -0.09804727, -0.51129663, -0.3610403, -0.029615778,
653 0, 0, 0, 0, 0, 0, 0, 0.80748653,
654 0.14411622, -0.76251525, -0.1890875, 0.01527351, 0.27528667, 0.29730347, 0.101477206, 0.07259522,
655 0.009900213, -0.34395567, -0.42788061, 0, 0.80748653, -0.3648737, -0.44261283, 0.015778137,
656 0.6826565, 0.30156538, 0, 0, 0, 0, -0.44563293, -0.55436707,
657 0, 0.80748653, -0.60703933, -0.20044717, 0.22371745, 0.77628255, 0, 0,
658 0, 0, 0, -0.44563293, -0.55436707, 0, 0.80748653, -0.7550391,
659 -0.05244744, 0.2797074, 0.72029257, 0, 0, 0, 0, 0,
660 -0.57440215, -0.42559785, 0, 0.80748653, -0.59273535, -0.21475118, 0.04544862, 0.81148535,
661 0.14306602, 0, 0, 0, -0.369642, -0.61841226, -0.011945802, 0,
662 0.7791623, -0.20691396, -0.57224834, 0, 0.08218567, 0.42637306, 0.1586175, -0.089709565,
663 -0.32935485, -0.24788953, -0.00022224105, 0, 0, 0.7085409, 0.28821066, -0.64765793,
664 -0.34909368, 0, 0, 0, 0, 0, 0.16679136, 0.31914657,
665 -0.48593786, 0, 0.537915, 0.462085, -0.00041967133, -0.3120329, -0.41914812, -0.15886839,
666 -0.042683028, 0.19370951, 0.24624406, 0.45803425, -0.07049577, -0.6091341, 0, 0.22253075,
667 }
668
669 var fxMask16 = []uint32{
670 0x0000, 0x0000, 0x05b8, 0x66a6, 0xbbfe, 0xe871, 0xf800, 0xda20, 0xb499, 0x4a84, 0x0009, 0x0000, 0x0000,
671 0x0000, 0x2463, 0xd7fd, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xad35, 0x01f8, 0x0000,
672 0x0d69, 0xe28c, 0xffff, 0xf92a, 0x8c5d, 0x3b36, 0x2a62, 0x51a7, 0xcc97, 0xffff, 0xffff, 0x772d, 0x0000,
673 0x75ad, 0xffff, 0xffff, 0x5ccf, 0x0000, 0x0000, 0x0000, 0x0000, 0x0b4a, 0xdfd6, 0xffff, 0xe2ff, 0x0000,
674 0x5b67, 0x8fff, 0x8f70, 0x060a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8e7f, 0xffff, 0xffe9, 0x16d6,
675 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x7303, 0xffff, 0xffff, 0x30ff,
676 0x0000, 0x0000, 0x0000, 0x17b0, 0x5bfe, 0x78fe, 0x95ec, 0xa3fe, 0xa3fe, 0xcd24, 0xfffe, 0xfffe, 0x30fe,
677 0x0001, 0x190d, 0x9be5, 0xf868, 0xfffe, 0xfffe, 0xfffe, 0xfffe, 0xfffe, 0xfffe, 0xfffe, 0xfffe, 0x30fe,
678 0x0c4c, 0xcf6f, 0xfffe, 0xfc0b, 0xb551, 0x6920, 0x4f1d, 0x3c87, 0x39ff, 0x928e, 0xffff, 0xffff, 0x30ff,
679 0x8f03, 0xffff, 0xfbe7, 0x4d76, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x727f, 0xffff, 0xffff, 0x30ff,
680 0xccff, 0xffff, 0xc6ff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x727f, 0xffff, 0xffff, 0x30ff,
681 0xf296, 0xffff, 0xb7c6, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x939a, 0xffff, 0xffff, 0x30ff,
682 0xc97f, 0xffff, 0xf43c, 0x2493, 0x0000, 0x0000, 0x0000, 0x0000, 0x5f13, 0xfd0c, 0xffff, 0xffff, 0x3827,
683 0x6dc9, 0xffff, 0xffff, 0xeb16, 0x7dd4, 0x5541, 0x6c76, 0xc10f, 0xfff1, 0xffff, 0xffff, 0xffff, 0x49ff,
684 0x00d8, 0xa6e9, 0xfffe, 0xfffe, 0xfffe, 0xfffe, 0xfffe, 0xfffe, 0xd4fe, 0x83db, 0xffff, 0xffff, 0x7584,
685 0x0000, 0x001c, 0x503e, 0xbb08, 0xe3a1, 0xeea6, 0xbd0e, 0x7e09, 0x08e5, 0x1b8b, 0xb67f, 0xb67f, 0x7d44,
686 }
687
688 var flMask16 = []uint32{
689 0x0000, 0x0000, 0x05b5, 0x668a, 0xbbe0, 0xe875, 0xf803, 0xda29, 0xb49f, 0x4a7a, 0x000a, 0x0000, 0x0000,
690 0x0000, 0x2473, 0xd7fb, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xad4d, 0x0204, 0x0000,
691 0x0d48, 0xe27a, 0xffff, 0xf949, 0x8c70, 0x3bae, 0x2ac9, 0x51f7, 0xccc4, 0xffff, 0xffff, 0x779f, 0x0000,
692 0x75a1, 0xffff, 0xffff, 0x5d7b, 0x0000, 0x0000, 0x0000, 0x0000, 0x0b23, 0xdf73, 0xffff, 0xe39d, 0x0000,
693 0x5ba0, 0x9033, 0x8f9f, 0x0609, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8db0, 0xffff, 0xffef, 0x1746,
694 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x728c, 0xffff, 0xffff, 0x3148,
695 0x0000, 0x0000, 0x0000, 0x17ac, 0x5bce, 0x78cb, 0x95b7, 0xa3d2, 0xa3d2, 0xcce6, 0xffff, 0xffff, 0x3148,
696 0x0000, 0x1919, 0x9bfd, 0xf86b, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x3148,
697 0x0c63, 0xcf97, 0xffff, 0xfc17, 0xb59d, 0x6981, 0x4f87, 0x3cf1, 0x3a68, 0x9276, 0xffff, 0xffff, 0x3148,
698 0x8eb0, 0xffff, 0xfbf5, 0x4d33, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x7214, 0xffff, 0xffff, 0x3148,
699 0xccaf, 0xffff, 0xc6ba, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x7214, 0xffff, 0xffff, 0x3148,
700 0xf292, 0xffff, 0xb865, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x930c, 0xffff, 0xffff, 0x3148,
701 0xc906, 0xffff, 0xf45d, 0x249f, 0x0000, 0x0000, 0x0000, 0x0000, 0x5ea0, 0xfcf1, 0xffff, 0xffff, 0x3888,
702 0x6d81, 0xffff, 0xffff, 0xeaf5, 0x7dcf, 0x5533, 0x6c2b, 0xc07b, 0xfff1, 0xffff, 0xffff, 0xffff, 0x4a9d,
703 0x00d4, 0xa6a1, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xd54d, 0x8399, 0xffff, 0xffff, 0x764b,
704 0x0000, 0x001b, 0x4ffc, 0xbb4a, 0xe3f5, 0xeee3, 0xbd4c, 0x7e42, 0x0900, 0x1b0c, 0xb6fc, 0xb6fc, 0x7e04,
705 }
706
707 // TestFixedFloatingCloseness compares the closeness of the fixed point and
708 // floating point rasterizer.
709 func TestFixedFloatingCloseness(t *testing.T) {
710 if len(fxMask16) != len(flMask16) {
711 t.Fatalf("len(fxMask16) != len(flMask16)")
712 }
713
714 total := uint32(0)
715 for i := range fxMask16 {
716 a := fxMask16[i]
717 b := flMask16[i]
718 if a > b {
719 total += a - b
720 } else {
721 total += b - a
722 }
723 }
724 n := len(fxMask16)
725
726 // This log message is useful when changing the fixed point rasterizer
727 // implementation, such as by changing ϕ. Assuming that the floating point
728 // rasterizer is accurate, the average difference is a measure of how
729 // inaccurate the (faster) fixed point rasterizer is.
730 //
731 // Smaller is better.
732 percent := float64(total*100) / float64(n*65535)
733 t.Logf("Comparing closeness of the fixed point and floating point rasterizer.\n"+
734 "Specifically, the elements of fxMask16 and flMask16.\n"+
735 "Total diff = %d, n = %d, avg = %.5f out of 65535, or %.5f%%.\n",
736 total, n, float64(total)/float64(n), percent)
737
738 const thresholdPercent = 1.0
739 if percent > thresholdPercent {
740 t.Errorf("average difference: got %.5f%%, want <= %.5f%%", percent, thresholdPercent)
741 }
742 }