From 2221cfc09d4d447137fefdd359dfbcb273fac646 Mon Sep 17 00:00:00 2001 From: Ryan McConnell Date: Thu, 30 Jul 2026 12:15:51 -0400 Subject: [PATCH 1/2] avx2-patch --- src/pixie/simd/avx2.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pixie/simd/avx2.nim b/src/pixie/simd/avx2.nim index 1800e583..b2405569 100644 --- a/src/pixie/simd/avx2.nim +++ b/src/pixie/simd/avx2.nim @@ -695,7 +695,7 @@ proc blendLineCoverageMaskAvx2*( 15, -1, 15, -1, 11, -1, 11, -1, 7, -1, 7, -1, 3, -1, 3, -1, 15, -1, 15, -1, 11, -1, 11, -1, 7, -1, 7, -1, 3, -1, 3, -1 ) - while i < len - 16: + while i < len - 32: let coverage = mm256_loadu_si256(coverages[i].addr) eqZero = mm256_cmpeq_epi8(coverage, vecZero) From 8f06f1b4a817d008584b71942fbdbd0a2ec7dd56 Mon Sep 17 00:00:00 2001 From: treeform Date: Sun, 13 Sep 2026 09:11:14 -0700 Subject: [PATCH 2/2] Keep full AVX2 mask blocks vectorized and add coverage tests --- src/pixie/simd/avx2.nim | 2 +- tests/test_simd.nim | 51 +++++++++++++++++++++++++++++++++++++++++ tests/tests.nim | 1 + 3 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 tests/test_simd.nim diff --git a/src/pixie/simd/avx2.nim b/src/pixie/simd/avx2.nim index b2405569..d467c32c 100644 --- a/src/pixie/simd/avx2.nim +++ b/src/pixie/simd/avx2.nim @@ -695,7 +695,7 @@ proc blendLineCoverageMaskAvx2*( 15, -1, 15, -1, 11, -1, 11, -1, 7, -1, 7, -1, 3, -1, 3, -1, 15, -1, 15, -1, 11, -1, 11, -1, 7, -1, 7, -1, 3, -1, 3, -1 ) - while i < len - 32: + while i <= len - 32: let coverage = mm256_loadu_si256(coverages[i].addr) eqZero = mm256_cmpeq_epi8(coverage, vecZero) diff --git a/tests/test_simd.nim b/tests/test_simd.nim new file mode 100644 index 00000000..5b4535a4 --- /dev/null +++ b/tests/test_simd.nim @@ -0,0 +1,51 @@ +import pixie/simd + +when allowSimd and defined(amd64) and not defined(pixieNoAvx): + import chroma, pixie/blends, pixie/common + + if cpuHasAvx2: + let + original = rgbx(120, 80, 40, 255) + mask = rgbx(255, 255, 255, 255) + var cases: int + for length in 0 .. 160: + for shift in 0 .. 7: + for pattern in 0 .. 3: + var pixels = newSeq[ColorRGBX](length + 96) + for pixel in pixels.mitems: + pixel = original + + # Exercise every pixel alignment, with sentinels on both sides. + var start: int + while (cast[uint](pixels[start].addr) and 31) != 0: + inc start + start += 16 + shift + + # Padding makes the old overread safe while its extra stores still + # corrupt the sentinels, independently of the allocator's layout. + var coverages = newSeq[uint8](length + 32) + for i in 0 ..< length: + coverages[i] = case pattern + of 0: 0'u8 + of 1: 255'u8 + of 2: 128'u8 + else: ((i * 73 + 19) mod 256).uint8 + + blendLineCoverageMaskAvx2( + cast[ptr UncheckedArray[ColorRGBX]](pixels[start].addr), + cast[ptr UncheckedArray[uint8]](coverages[0].addr), + mask, + length + ) + + for i, pixel in pixels: + let expected = + if i >= start and i < start + length: + blendMask(original, mask * coverages[i - start]) + else: + original + doAssert pixel == expected, + "length=" & $length & " shift=" & $shift & + " pattern=" & $pattern & " pixel=" & $(i - start) + inc cases + echo "AVX2 coverage mask: ", cases, " cases passed" diff --git a/tests/tests.nim b/tests/tests.nim index 002bfeeb..ef6bc496 100644 --- a/tests/tests.nim +++ b/tests/tests.nim @@ -12,6 +12,7 @@ import test_png, test_ppm, test_qoi, + test_simd, test_webp, test_svg, xrays