From a1e39f812a0687467f671c364f22f99eb6388c92 Mon Sep 17 00:00:00 2001 From: treeform Date: Thu, 24 Sep 2026 16:25:31 -0700 Subject: [PATCH] replace hasSimd macro with explicit when dispatch Co-Authored-By: Claude Opus 5.5 --- src/pixie/images.nim | 348 ++++++++++++++++++++++++---------------- src/pixie/internal.nim | 66 +++++--- src/pixie/paths.nim | 95 ++++++++--- src/pixie/simd.nim | 63 ++++---- src/pixie/simd/avx.nim | 4 +- src/pixie/simd/avx2.nim | 32 ++-- src/pixie/simd/neon.nim | 36 ++--- src/pixie/simd/sse2.nim | 36 ++--- 8 files changed, 416 insertions(+), 264 deletions(-) diff --git a/src/pixie/images.nim b/src/pixie/images.nim index 63bcc96e..30d2ec30 100644 --- a/src/pixie/images.nim +++ b/src/pixie/images.nim @@ -56,20 +56,36 @@ proc fill*(image: Image, color: SomeColor) {.inline, raises: [].} = ## Fills the image with the color. fillUnsafe(image.data, color, 0, image.data.len) -proc isOneColor*(image: Image): bool {.hasSimd, raises: [].} = +proc isOneColor*(image: Image): bool {.raises: [].} = ## Checks if the entire image is the same color. - result = true - let color = cast[uint32](image.data[0]) - for i in 0 ..< image.data.len: - if cast[uint32](image.data[i]) != color: - return false + when allowAvx: + if cpuHasAvx2: + return isOneColorAvx2(image) + when allowSse2: + return isOneColorSse2(image) + elif allowNeon: + return isOneColorNeon(image) + else: + result = true + let color = cast[uint32](image.data[0]) + for i in 0 ..< image.data.len: + if cast[uint32](image.data[i]) != color: + return false -proc isTransparent*(image: Image): bool {.hasSimd, raises: [].} = +proc isTransparent*(image: Image): bool {.raises: [].} = ## Checks if this image is fully transparent or not. - result = true - for i in 0 ..< image.data.len: - if image.data[i].a != 0: - return false + when allowAvx: + if cpuHasAvx2: + return isTransparentAvx2(image) + when allowSse2: + return isTransparentSse2(image) + elif allowNeon: + return isTransparentNeon(image) + else: + result = true + for i in 0 ..< image.data.len: + if image.data[i].a != 0: + return false proc isOpaque*(image: Image): bool {.raises: [].} = ## Checks if the entire image is opaque (alpha values are all 255). @@ -167,139 +183,179 @@ proc diff*(master, image: Image): (float32, Image) {.raises: [PixieError].} = proc minifyBy2*( image: Image, power = 1 -): Image {.hasSimd, raises: [PixieError].} = +): Image {.raises: [PixieError].} = ## Scales the image down by an integer scale. - if power < 0: - raise newException(PixieError, "Cannot minifyBy2 with negative power") - if power == 0: - return image.copy() - - var src = image - for _ in 1 .. power: - # When minifying an image of odd size, round the result image size up - # so a 99 x 99 src image returns a 50 x 50 image. - let - srcWidthIsOdd = (src.width mod 2) != 0 - srcHeightIsOdd = (src.height mod 2) != 0 - resultEvenWidth = src.width div 2 - resultEvenHeight = src.height div 2 - result = newImage( - if srcWidthIsOdd: resultEvenWidth + 1 else: resultEvenWidth, - if srcHeightIsOdd: resultEvenHeight + 1 else: resultEvenHeight - ) - for y in 0 ..< resultEvenHeight: + when allowAvx: + if cpuHasAvx2: + return minifyBy2Avx2(image, power) + when allowSse2: + return minifyBy2Sse2(image, power) + elif allowNeon: + return minifyBy2Neon(image, power) + else: + if power < 0: + raise newException(PixieError, "Cannot minifyBy2 with negative power") + if power == 0: + return image.copy() + + var src = image + for _ in 1 .. power: + # When minifying an image of odd size, round the result image size up + # so a 99 x 99 src image returns a 50 x 50 image. let - topRowStart = src.dataIndex(0, y * 2) - bottomRowStart = src.dataIndex(0, y * 2 + 1) - for x in 0 ..< resultEvenWidth: + srcWidthIsOdd = (src.width mod 2) != 0 + srcHeightIsOdd = (src.height mod 2) != 0 + resultEvenWidth = src.width div 2 + resultEvenHeight = src.height div 2 + result = newImage( + if srcWidthIsOdd: resultEvenWidth + 1 else: resultEvenWidth, + if srcHeightIsOdd: resultEvenHeight + 1 else: resultEvenHeight + ) + for y in 0 ..< resultEvenHeight: let - a = src.data[topRowStart + x * 2] - b = src.data[topRowStart + x * 2 + 1] - c = src.data[bottomRowStart + x * 2 + 1] - d = src.data[bottomRowStart + x * 2] - mixed = rgbx( - ((a.r.uint32 + b.r + c.r + d.r + 2) div 4).uint8, - ((a.g.uint32 + b.g + c.g + d.g + 2) div 4).uint8, - ((a.b.uint32 + b.b + c.b + d.b + 2) div 4).uint8, - ((a.a.uint32 + b.a + c.a + d.a + 2) div 4).uint8 - ) - result.data[result.dataIndex(x, y)] = mixed - - if srcWidthIsOdd: - let rgbx = mix( - src.data[src.dataIndex(src.width - 1, y * 2 + 0)], - src.data[src.dataIndex(src.width - 1, y * 2 + 1)], - 0.5 - ) * 0.5 - result.data[result.dataIndex(result.width - 1, y)] = rgbx - - if srcHeightIsOdd: - for x in 0 ..< resultEvenWidth: - let rgbx = mix( - src.data[src.dataIndex(x * 2 + 0, src.height - 1)], - src.data[src.dataIndex(x * 2 + 1, src.height - 1)], - 0.5 - ) * 0.5 - result.data[result.dataIndex(x, result.height - 1)] = rgbx - - if srcWidthIsOdd: - result.data[result.dataIndex(result.width - 1, result.height - 1)] = - src.data[src.dataIndex(src.width - 1, src.height - 1)] * 0.25 - - # Set src as this result for if we do another power - src = result + topRowStart = src.dataIndex(0, y * 2) + bottomRowStart = src.dataIndex(0, y * 2 + 1) + for x in 0 ..< resultEvenWidth: + let + a = src.data[topRowStart + x * 2] + b = src.data[topRowStart + x * 2 + 1] + c = src.data[bottomRowStart + x * 2 + 1] + d = src.data[bottomRowStart + x * 2] + mixed = rgbx( + ((a.r.uint32 + b.r + c.r + d.r + 2) div 4).uint8, + ((a.g.uint32 + b.g + c.g + d.g + 2) div 4).uint8, + ((a.b.uint32 + b.b + c.b + d.b + 2) div 4).uint8, + ((a.a.uint32 + b.a + c.a + d.a + 2) div 4).uint8 + ) + result.data[result.dataIndex(x, y)] = mixed + + if srcWidthIsOdd: + let rgbx = mix( + src.data[src.dataIndex(src.width - 1, y * 2 + 0)], + src.data[src.dataIndex(src.width - 1, y * 2 + 1)], + 0.5 + ) * 0.5 + result.data[result.dataIndex(result.width - 1, y)] = rgbx + + if srcHeightIsOdd: + for x in 0 ..< resultEvenWidth: + let rgbx = mix( + src.data[src.dataIndex(x * 2 + 0, src.height - 1)], + src.data[src.dataIndex(x * 2 + 1, src.height - 1)], + 0.5 + ) * 0.5 + result.data[result.dataIndex(x, result.height - 1)] = rgbx + + if srcWidthIsOdd: + result.data[result.dataIndex(result.width - 1, result.height - 1)] = + src.data[src.dataIndex(src.width - 1, src.height - 1)] * 0.25 + + # Set src as this result for if we do another power + src = result proc magnifyBy2*( image: Image, power = 1 -): Image {.hasSimd, raises: [PixieError].} = +): Image {.raises: [PixieError].} = ## Scales image up by 2 ^ power. - if power < 0: - raise newException(PixieError, "Cannot magnifyBy2 with negative power") + when allowSse2: + return magnifyBy2Sse2(image, power) + elif allowNeon: + return magnifyBy2Neon(image, power) + else: + if power < 0: + raise newException(PixieError, "Cannot magnifyBy2 with negative power") - let scale = 2 ^ power - result = newImage(image.width * scale, image.height * scale) + let scale = 2 ^ power + result = newImage(image.width * scale, image.height * scale) - for y in 0 ..< image.height: - # Write one row of pixels duplicated by scale - let - sourceRowStart = image.dataIndex(0, y) - resultRowStart = result.dataIndex(0, y * scale) - for x in 0 ..< image.width: + for y in 0 ..< image.height: + # Write one row of pixels duplicated by scale let - rgbx = image.data[sourceRowStart + x] - resultIdx = resultRowStart + x * scale - for i in 0 ..< scale: - result.data[resultIdx + i] = rgbx - # Copy that row of pixels into (scale - 1) more rows - for i in 1 ..< scale: - copyMem( - result.data[resultRowStart + result.width * i].addr, - result.data[resultRowStart].addr, - result.width * 4 - ) + sourceRowStart = image.dataIndex(0, y) + resultRowStart = result.dataIndex(0, y * scale) + for x in 0 ..< image.width: + let + rgbx = image.data[sourceRowStart + x] + resultIdx = resultRowStart + x * scale + for i in 0 ..< scale: + result.data[resultIdx + i] = rgbx + # Copy that row of pixels into (scale - 1) more rows + for i in 1 ..< scale: + copyMem( + result.data[resultRowStart + result.width * i].addr, + result.data[resultRowStart].addr, + result.width * 4 + ) -proc applyOpacity*(image: Image, opacity: float32) {.hasSimd, raises: [].} = +proc applyOpacity*(image: Image, opacity: float32) {.raises: [].} = ## Multiplies alpha of the image by opacity. - let opacity = round(255 * opacity).uint16 - if opacity == 255: - return - - if opacity == 0: - image.fill(rgbx(0, 0, 0, 0)) - return - - for i in 0 ..< image.data.len: - var rgbx = image.data[i] - rgbx.r = ((rgbx.r * opacity) div 255).uint8 - rgbx.g = ((rgbx.g * opacity) div 255).uint8 - rgbx.b = ((rgbx.b * opacity) div 255).uint8 - rgbx.a = ((rgbx.a * opacity) div 255).uint8 - image.data[i] = rgbx - -proc invert*(image: Image) {.hasSimd, raises: [].} = + when allowAvx: + if cpuHasAvx2: + applyOpacityAvx2(image, opacity) + return + when allowSse2: + applyOpacitySse2(image, opacity) + elif allowNeon: + applyOpacityNeon(image, opacity) + else: + let opacity = round(255 * opacity).uint16 + if opacity == 255: + return + + if opacity == 0: + image.fill(rgbx(0, 0, 0, 0)) + return + + for i in 0 ..< image.data.len: + var rgbx = image.data[i] + rgbx.r = ((rgbx.r * opacity) div 255).uint8 + rgbx.g = ((rgbx.g * opacity) div 255).uint8 + rgbx.b = ((rgbx.b * opacity) div 255).uint8 + rgbx.a = ((rgbx.a * opacity) div 255).uint8 + image.data[i] = rgbx + +proc invert*(image: Image) {.raises: [].} = ## Inverts all of the colors and alpha. - for i in 0 ..< image.data.len: - var rgbx = image.data[i] - rgbx.r = 255 - rgbx.r - rgbx.g = 255 - rgbx.g - rgbx.b = 255 - rgbx.b - rgbx.a = 255 - rgbx.a - image.data[i] = rgbx - - # Inverting rgbx(50, 100, 150, 200) becomes rgbx(205, 155, 105, 55). This - # is not a valid premultiplied alpha color. - # We need to convert back to premultiplied alpha after inverting. - image.data.toPremultipliedAlpha() - -proc ceil*(image: Image) {.hasSimd, raises: [].} = + when allowAvx: + if cpuHasAvx2: + invertAvx2(image) + return + when allowSse2: + invertSse2(image) + elif allowNeon: + invertNeon(image) + else: + for i in 0 ..< image.data.len: + var rgbx = image.data[i] + rgbx.r = 255 - rgbx.r + rgbx.g = 255 - rgbx.g + rgbx.b = 255 - rgbx.b + rgbx.a = 255 - rgbx.a + image.data[i] = rgbx + + # Inverting rgbx(50, 100, 150, 200) becomes rgbx(205, 155, 105, 55). This + # is not a valid premultiplied alpha color. + # We need to convert back to premultiplied alpha after inverting. + image.data.toPremultipliedAlpha() + +proc ceil*(image: Image) {.raises: [].} = ## A value of 0 stays 0. Anything else turns into 255. - for i in 0 ..< image.data.len: - var rgbx = image.data[i] - rgbx.r = if rgbx.r == 0: 0 else: 255 - rgbx.g = if rgbx.g == 0: 0 else: 255 - rgbx.b = if rgbx.b == 0: 0 else: 255 - rgbx.a = if rgbx.a == 0: 0 else: 255 - image.data[i] = rgbx + when allowAvx: + if cpuHasAvx2: + ceilAvx2(image) + return + when allowSse2: + ceilSse2(image) + elif allowNeon: + ceilNeon(image) + else: + for i in 0 ..< image.data.len: + var rgbx = image.data[i] + rgbx.r = if rgbx.r == 0: 0 else: 255 + rgbx.g = if rgbx.g == 0: 0 else: 255 + rgbx.b = if rgbx.b == 0: 0 else: 255 + rgbx.a = if rgbx.a == 0: 0 else: 255 + image.data[i] = rgbx proc blur*( image: Image, radius: float32, outOfBounds: SomeColor = color(0, 0, 0, 0) @@ -473,13 +529,31 @@ proc blendLineOverwrite( proc blendLineNormal( a, b: ptr UncheckedArray[ColorRGBX], len: int -) {.hasSimd.} = - for i in 0 ..< len: - a[i] = blendNormal(a[i], b[i]) - -proc blendLineMask(a, b: ptr UncheckedArray[ColorRGBX], len: int) {.hasSimd.} = - for i in 0 ..< len: - a[i] = blendMask(a[i], b[i]) +) = + when allowAvx: + if cpuHasAvx2: + blendLineNormalAvx2(a, b, len) + return + when allowSse2: + blendLineNormalSse2(a, b, len) + elif allowNeon: + blendLineNormalNeon(a, b, len) + else: + for i in 0 ..< len: + a[i] = blendNormal(a[i], b[i]) + +proc blendLineMask(a, b: ptr UncheckedArray[ColorRGBX], len: int) = + when allowAvx: + if cpuHasAvx2: + blendLineMaskAvx2(a, b, len) + return + when allowSse2: + blendLineMaskSse2(a, b, len) + elif allowNeon: + blendLineMaskNeon(a, b, len) + else: + for i in 0 ..< len: + a[i] = blendMask(a[i], b[i]) proc blendRect(a, b: Image, pos: Ivec2, blendMode: BlendMode) = let diff --git a/src/pixie/internal.nim b/src/pixie/internal.nim index a4e9938b..c63b59f9 100644 --- a/src/pixie/internal.nim +++ b/src/pixie/internal.nim @@ -54,16 +54,25 @@ template getUncheckedArray*( proc fillUnsafe*( data: var seq[ColorRGBX], color: SomeColor, start, len: int -) {.hasSimd, raises: [].} = +) {.raises: [].} = ## Fills the image data with the color starting at index start and ## continuing for len indices. - let rgbx = color.asRgbx() - # Use memset when every byte has the same value - if rgbx.r == rgbx.g and rgbx.r == rgbx.b and rgbx.r == rgbx.a: - nimSetMem(data[start].addr, rgbx.r.cint, len * 4) + when allowAvx: + if cpuHasAvx: + fillUnsafeAvx(data, color, start, len) + return + when allowSse2: + fillUnsafeSse2(data, color, start, len) + elif allowNeon: + fillUnsafeNeon(data, color, start, len) else: - for i in start ..< start + len: - data[i] = rgbx + let rgbx = color.asRgbx() + # Use memset when every byte has the same value + if rgbx.r == rgbx.g and rgbx.r == rgbx.b and rgbx.r == rgbx.a: + nimSetMem(data[start].addr, rgbx.r.cint, len * 4) + else: + for i in start ..< start + len: + data[i] = rgbx const straightAlphaTable = block: var table: array[256, array[256, uint8]] @@ -85,21 +94,38 @@ proc toStraightAlpha*(data: var seq[ColorRGBA | ColorRGBX]) {.raises: [].} = proc toPremultipliedAlpha*( data: var seq[ColorRGBA | ColorRGBX] -) {.hasSimd, raises: [].} = +) {.raises: [].} = ## Converts an image to premultiplied alpha from straight alpha. - for i in 0 ..< data.len: - var c = data[i] - if c.a != 255: - c.r = ((c.r.uint32 * c.a + 127) div 255).uint8 - c.g = ((c.g.uint32 * c.a + 127) div 255).uint8 - c.b = ((c.b.uint32 * c.a + 127) div 255).uint8 - data[i] = c + when allowAvx: + if cpuHasAvx2: + toPremultipliedAlphaAvx2(data) + return + when allowSse2: + toPremultipliedAlphaSse2(data) + elif allowNeon: + toPremultipliedAlphaNeon(data) + else: + for i in 0 ..< data.len: + var c = data[i] + if c.a != 255: + c.r = ((c.r.uint32 * c.a + 127) div 255).uint8 + c.g = ((c.g.uint32 * c.a + 127) div 255).uint8 + c.b = ((c.b.uint32 * c.a + 127) div 255).uint8 + data[i] = c -proc isOpaque*(data: var seq[ColorRGBX], start, len: int): bool {.hasSimd.} = - result = true - for i in start ..< start + len: - if data[i].a != 255: - return false +proc isOpaque*(data: var seq[ColorRGBX], start, len: int): bool = + when allowAvx: + if cpuHasAvx2: + return isOpaqueAvx2(data, start, len) + when allowSse2: + return isOpaqueSse2(data, start, len) + elif allowNeon: + return isOpaqueNeon(data, start, len) + else: + result = true + for i in start ..< start + len: + if data[i].a != 255: + return false when defined(release): {.pop.} diff --git a/src/pixie/paths.nim b/src/pixie/paths.nim index b201a919..181efd77 100644 --- a/src/pixie/paths.nim +++ b/src/pixie/paths.nim @@ -1444,37 +1444,64 @@ proc blendLineCoverageOverwrite( coverages: ptr UncheckedArray[uint8], rgbx: ColorRGBX, len: int - ) {.hasSimd.} = - for i in 0 ..< len: - let coverage = coverages[i] - if coverage != 0: - line[i] = rgbx * coverage +) = + when allowAvx: + if cpuHasAvx2: + blendLineCoverageOverwriteAvx2(line, coverages, rgbx, len) + return + when allowSse2: + blendLineCoverageOverwriteSse2(line, coverages, rgbx, len) + elif allowNeon: + blendLineCoverageOverwriteNeon(line, coverages, rgbx, len) + else: + for i in 0 ..< len: + let coverage = coverages[i] + if coverage != 0: + line[i] = rgbx * coverage proc blendLineCoverageNormal( line: ptr UncheckedArray[ColorRGBX], coverages: ptr UncheckedArray[uint8], rgbx: ColorRGBX, len: int -) {.hasSimd.} = - for i in 0 ..< len: - let coverage = coverages[i] - if coverage == 0: - discard - else: - line[i] = blendNormal(line[i], rgbx * coverage) +) = + when allowAvx: + if cpuHasAvx2: + blendLineCoverageNormalAvx2(line, coverages, rgbx, len) + return + when allowSse2: + blendLineCoverageNormalSse2(line, coverages, rgbx, len) + elif allowNeon: + blendLineCoverageNormalNeon(line, coverages, rgbx, len) + else: + for i in 0 ..< len: + let coverage = coverages[i] + if coverage == 0: + discard + else: + line[i] = blendNormal(line[i], rgbx * coverage) proc blendLineCoverageMask( line: ptr UncheckedArray[ColorRGBX], coverages: ptr UncheckedArray[uint8], rgbx: ColorRGBX, len: int -) {.hasSimd.} = - for i in 0 ..< len: - let coverage = coverages[i] - if coverage == 255: - discard - else: - line[i] = blendMask(line[i], rgbx * coverage) +) = + when allowAvx: + if cpuHasAvx2: + blendLineCoverageMaskAvx2(line, coverages, rgbx, len) + return + when allowSse2: + blendLineCoverageMaskSse2(line, coverages, rgbx, len) + elif allowNeon: + blendLineCoverageMaskNeon(line, coverages, rgbx, len) + else: + for i in 0 ..< len: + let coverage = coverages[i] + if coverage == 255: + discard + else: + line[i] = blendMask(line[i], rgbx * coverage) proc fillCoverage( image: Image, @@ -1527,15 +1554,33 @@ proc fillCoverage( proc blendLineNormal( line: ptr UncheckedArray[ColorRGBX], rgbx: ColorRGBX, len: int -) {.hasSimd.} = - for i in 0 ..< len: - line[i] = blendNormal(line[i], rgbx) +) = + when allowAvx: + if cpuHasAvx2: + blendLineNormalAvx2(line, rgbx, len) + return + when allowSse2: + blendLineNormalSse2(line, rgbx, len) + elif allowNeon: + blendLineNormalNeon(line, rgbx, len) + else: + for i in 0 ..< len: + line[i] = blendNormal(line[i], rgbx) proc blendLineMask( line: ptr UncheckedArray[ColorRGBX], rgbx: ColorRGBX, len: int -) {.hasSimd.} = - for i in 0 ..< len: - line[i] = blendMask(line[i], rgbx) +) = + when allowAvx: + if cpuHasAvx2: + blendLineMaskAvx2(line, rgbx, len) + return + when allowSse2: + blendLineMaskSse2(line, rgbx, len) + elif allowNeon: + blendLineMaskNeon(line, rgbx, len) + else: + for i in 0 ..< len: + line[i] = blendMask(line[i], rgbx) proc fillHits( image: Image, diff --git a/src/pixie/simd.nim b/src/pixie/simd.nim index bc2cbb6e..98e5d123 100644 --- a/src/pixie/simd.nim +++ b/src/pixie/simd.nim @@ -1,28 +1,35 @@ -import nimsimd/hassimd - -export hassimd - -const allowSimd* = not defined(pixieNoSimd) and not defined(tcc) - -when allowSimd: - when defined(amd64): - import simd/sse2 - export sse2 - - when not defined(pixieNoAvx): - import nimsimd/runtimecheck, simd/avx, simd/avx2 - export avx, avx2 - - let - cpuHasAvx* = checkInstructionSets({AVX}) - cpuHasAvx2* = checkInstructionSets({AVX, AVX2}) - - import nimsimd/sse2 as nimsimdsse2 - export nimsimdsse2 - - elif defined(arm64): - import simd/neon - export neon - - import nimsimd/neon as nimsimdneon - export nimsimdneon +const + allowSimd* = not defined(pixieNoSimd) and not defined(tcc) + allowSse2* = allowSimd and defined(amd64) + allowAvx* = allowSse2 and not defined(pixieNoAvx) + allowNeon* = allowSimd and defined(arm64) + +when allowSse2: + import simd/sse2 + export sse2 + + import nimsimd/sse2 as nimsimdsse2 + export nimsimdsse2 + +when allowAvx: + import simd/avx, simd/avx2 + export avx, avx2 + + when defined(pixieAvx2): + # The target CPU is known to have AVX2, skip the runtime check. + const + cpuHasAvx* = true + cpuHasAvx2* = true + else: + import nimsimd/runtimecheck + + let + cpuHasAvx* = checkInstructionSets({AVX}) + cpuHasAvx2* = checkInstructionSets({AVX, AVX2}) + +when allowNeon: + import simd/neon + export neon + + import nimsimd/neon as nimsimdneon + export nimsimdneon diff --git a/src/pixie/simd/avx.nim b/src/pixie/simd/avx.nim index 6d30b95c..a2284783 100644 --- a/src/pixie/simd/avx.nim +++ b/src/pixie/simd/avx.nim @@ -1,4 +1,4 @@ -import chroma, nimsimd/hassimd, nimsimd/avx +import chroma, nimsimd/avx when defined(gcc) or defined(clang): {.localPassC: "-mavx".} @@ -10,7 +10,7 @@ proc fillUnsafeAvx*( data: var seq[ColorRGBX], color: SomeColor, start, len: int -) {.simd.} = +) = let rgbx = color.asRgbx() var diff --git a/src/pixie/simd/avx2.nim b/src/pixie/simd/avx2.nim index b6c93bba..e4a4bdd8 100644 --- a/src/pixie/simd/avx2.nim +++ b/src/pixie/simd/avx2.nim @@ -1,4 +1,4 @@ -import avx, chroma, nimsimd/hassimd, nimsimd/avx2, ../blends, ../common, vmath +import avx, chroma, nimsimd/avx2, ../blends, ../common, vmath when defined(gcc) or defined(clang): {.localPassC: "-mavx2".} @@ -41,7 +41,7 @@ template blendMaskSimd(backdrop, source: M256i): M256i = mm256_or_si256(backdropEven, mm256_slli_epi16(backdropOdd, 8)) -proc isOneColorAvx2*(image: Image): bool {.simd.} = +proc isOneColorAvx2*(image: Image): bool = result = true let color = image.data[0] @@ -71,7 +71,7 @@ proc isOneColorAvx2*(image: Image): bool {.simd.} = if image.data[i] != color: return false -proc isTransparentAvx2*(image: Image): bool {.simd.} = +proc isTransparentAvx2*(image: Image): bool = result = true var i: int @@ -98,7 +98,7 @@ proc isTransparentAvx2*(image: Image): bool {.simd.} = if image.data[i].a != 0: return false -proc isOpaqueAvx2*(data: var seq[ColorRGBX], start, len: int): bool {.simd.} = +proc isOpaqueAvx2*(data: var seq[ColorRGBX], start, len: int): bool = result = true var i = start @@ -125,7 +125,7 @@ proc isOpaqueAvx2*(data: var seq[ColorRGBX], start, len: int): bool {.simd.} = if data[i].a != 255: return false -proc toPremultipliedAlphaAvx2*(data: var seq[ColorRGBA | ColorRGBX]) {.simd.} = +proc toPremultipliedAlphaAvx2*(data: var seq[ColorRGBA | ColorRGBX]) = var i: int p = cast[uint](data[0].addr) @@ -185,7 +185,7 @@ proc toPremultipliedAlphaAvx2*(data: var seq[ColorRGBA | ColorRGBX]) {.simd.} = rgbx.b = ((rgbx.b.uint32 * rgbx.a + 127) div 255).uint8 data[i] = rgbx -proc invertAvx2*(image: Image) {.simd.} = +proc invertAvx2*(image: Image) = var i: int p = cast[uint](image.data[0].addr) @@ -222,7 +222,7 @@ proc invertAvx2*(image: Image) {.simd.} = toPremultipliedAlphaAvx2(image.data) -proc applyOpacityAvx2*(image: Image, opacity: float32) {.simd.} = +proc applyOpacityAvx2*(image: Image, opacity: float32) = let opacity = round(255 * opacity).uint16 if opacity == 255: return @@ -278,7 +278,7 @@ proc applyOpacityAvx2*(image: Image, opacity: float32) {.simd.} = rgbx.a = ((rgbx.a * opacity) div 255).uint8 image.data[i] = rgbx -proc ceilAvx2*(image: Image) {.simd.} = +proc ceilAvx2*(image: Image) = var i: int p = cast[uint](image.data[0].addr) @@ -313,7 +313,7 @@ proc ceilAvx2*(image: Image) {.simd.} = rgbx.a = if rgbx.a == 0: 0 else: 255 image.data[i] = rgbx -proc minifyBy2Avx2*(image: Image, power = 1): Image {.simd.} = +proc minifyBy2Avx2*(image: Image, power = 1): Image = ## Scales the image down by an integer scale. if power < 0: raise newException(PixieError, "Cannot minifyBy2 with negative power") @@ -438,7 +438,7 @@ proc blendLineCoverageOverwriteAvx2*( coverages: ptr UncheckedArray[uint8], rgbx: ColorRGBX, len: int -) {.simd.} = +) = var i: int while i < len and (cast[uint](line[i].addr) and 31) != 0: let coverage = coverages[i] @@ -499,7 +499,7 @@ proc blendLineCoverageOverwriteAvx2*( proc blendLineNormalAvx2*( line: ptr UncheckedArray[ColorRGBX], rgbx: ColorRGBX, len: int -) {.simd.} = +) = var i: int while i < len and (cast[uint](line[i].addr) and 31) != 0: line[i] = blendNormal(line[i], rgbx) @@ -525,7 +525,7 @@ proc blendLineNormalAvx2*( proc blendLineNormalAvx2*( a, b: ptr UncheckedArray[ColorRGBX], len: int -) {.simd.} = +) = var i: int while i < len and (cast[uint](a[i].addr) and 31) != 0: a[i] = blendNormal(a[i], b[i]) @@ -560,7 +560,7 @@ proc blendLineCoverageNormalAvx2*( coverages: ptr UncheckedArray[uint8], rgbx: ColorRGBX, len: int -) {.simd.} = +) = var i: int while i < len and (cast[uint](line[i].addr) and 31) != 0: let coverage = coverages[i] @@ -622,7 +622,7 @@ proc blendLineCoverageNormalAvx2*( proc blendLineMaskAvx2*( line: ptr UncheckedArray[ColorRGBX], rgbx: ColorRGBX, len: int -) {.simd.} = +) = var i: int while i < len and (cast[uint](line[i].addr) and 31) != 0: line[i] = blendMask(line[i], rgbx) @@ -647,7 +647,7 @@ proc blendLineMaskAvx2*( proc blendLineMaskAvx2*( a, b: ptr UncheckedArray[ColorRGBX], len: int -) {.simd.} = +) = var i: int while i < len and (cast[uint](a[i].addr) and 31) != 0: a[i] = blendMask(a[i], b[i]) @@ -681,7 +681,7 @@ proc blendLineCoverageMaskAvx2*( coverages: ptr UncheckedArray[uint8], rgbx: ColorRGBX, len: int -) {.simd.} = +) = var i: int while i < len and (cast[uint](line[i].addr) and 31) != 0: let coverage = coverages[i] diff --git a/src/pixie/simd/neon.nim b/src/pixie/simd/neon.nim index fe5def30..0d93e28e 100644 --- a/src/pixie/simd/neon.nim +++ b/src/pixie/simd/neon.nim @@ -1,4 +1,4 @@ -import chroma, nimsimd/hassimd, nimsimd/neon, ../blends, ../common, vmath +import chroma, nimsimd/neon, ../blends, ../common, vmath when defined(release): {.push checks: off.} @@ -31,7 +31,7 @@ proc fillUnsafeNeon*( data: var seq[ColorRGBX], color: SomeColor, start, len: int -) {.simd.} = +) = let rgbx = color.asRgbx() var @@ -54,7 +54,7 @@ proc fillUnsafeNeon*( for i in i ..< start + len: data[i] = rgbx -proc isOneColorNeon*(image: Image): bool {.simd.} = +proc isOneColorNeon*(image: Image): bool = result = true let color = image.data[0] @@ -93,7 +93,7 @@ proc isOneColorNeon*(image: Image): bool {.simd.} = if image.data[i] != color: return false -proc isTransparentNeon*(image: Image): bool {.simd.} = +proc isTransparentNeon*(image: Image): bool = var i: int p = cast[uint](image.data[0].addr) @@ -124,7 +124,7 @@ proc isTransparentNeon*(image: Image): bool {.simd.} = if image.data[i].a != 0: return false -proc isOpaqueNeon*(data: var seq[ColorRGBX], start, len: int): bool {.simd.} = +proc isOpaqueNeon*(data: var seq[ColorRGBX], start, len: int): bool = result = true var @@ -155,7 +155,7 @@ proc isOpaqueNeon*(data: var seq[ColorRGBX], start, len: int): bool {.simd.} = if data[i].a != 255: return false -proc toPremultipliedAlphaNeon*(data: var seq[ColorRGBA | ColorRGBX]) {.simd.} = +proc toPremultipliedAlphaNeon*(data: var seq[ColorRGBA | ColorRGBX]) = var i: int p = cast[uint](data[0].addr) @@ -188,7 +188,7 @@ proc toPremultipliedAlphaNeon*(data: var seq[ColorRGBA | ColorRGBX]) {.simd.} = c.b = ((c.b.uint32 * c.a + 127) div 255).uint8 data[i] = c -proc invertNeon*(image: Image) {.simd.} = +proc invertNeon*(image: Image) = var i: int p = cast[uint](image.data[0].addr) @@ -226,7 +226,7 @@ proc invertNeon*(image: Image) {.simd.} = toPremultipliedAlphaNeon(image.data) -proc applyOpacityNeon*(image: Image, opacity: float32) {.simd.} = +proc applyOpacityNeon*(image: Image, opacity: float32) = let opacity = round(255 * opacity).uint8 if opacity == 255: return @@ -260,7 +260,7 @@ proc applyOpacityNeon*(image: Image, opacity: float32) {.simd.} = rgbx.a = ((rgbx.a * opacity) div 255).uint8 image.data[i] = rgbx -proc ceilNeon*(image: Image) {.simd.} = +proc ceilNeon*(image: Image) = var i: int p = cast[uint](image.data[0].addr) @@ -285,7 +285,7 @@ proc ceilNeon*(image: Image) {.simd.} = rgbx.a = if rgbx.a == 0: 0 else: 255 image.data[i] = rgbx -proc minifyBy2Neon*(image: Image, power = 1): Image {.simd.} = +proc minifyBy2Neon*(image: Image, power = 1): Image = ## Scales the image down by an integer scale. if power < 0: raise newException(PixieError, "Cannot minifyBy2 with negative power") @@ -383,7 +383,7 @@ proc minifyBy2Neon*(image: Image, power = 1): Image {.simd.} = # Set src as this result for if we do another power src = result -proc magnifyBy2Neon*(image: Image, power = 1): Image {.simd.} = +proc magnifyBy2Neon*(image: Image, power = 1): Image = ## Scales image up by 2 ^ power. if power < 0: raise newException(PixieError, "Cannot magnifyBy2 with negative power") @@ -429,7 +429,7 @@ proc blendLineCoverageOverwriteNeon*( coverages: ptr UncheckedArray[uint8], rgbx: ColorRGBX, len: int -) {.simd.} = +) = var i: int while i < len and (cast[uint](line[i].addr) and 15) != 0: let coverage = coverages[i] @@ -487,7 +487,7 @@ proc blendLineCoverageOverwriteNeon*( proc blendLineNormalNeon*( line: ptr UncheckedArray[ColorRGBX], rgbx: ColorRGBX, len: int -) {.simd.} = +) = var i: int while i < len and (cast[uint](line[i].addr) and 15) != 0: line[i] = blendNormal(line[i], rgbx) @@ -510,7 +510,7 @@ proc blendLineNormalNeon*( proc blendLineNormalNeon*( a, b: ptr UncheckedArray[ColorRGBX], len: int -) {.simd.} = +) = var i: int while i < len and (cast[uint](a[i].addr) and 15) != 0: a[i] = blendNormal(a[i], b[i]) @@ -540,7 +540,7 @@ proc blendLineCoverageNormalNeon*( coverages: ptr UncheckedArray[uint8], rgbx: ColorRGBX, len: int -) {.simd.} = +) = var i: int while i < len and (cast[uint](line[i].addr) and 15) != 0: let coverage = coverages[i] @@ -595,7 +595,7 @@ proc blendLineCoverageNormalNeon*( proc blendLineMaskNeon*( line: ptr UncheckedArray[ColorRGBX], rgbx: ColorRGBX, len: int -) {.simd.} = +) = var i: int while i < len and (cast[uint](line[i].addr) and 15) != 0: line[i] = blendMask(line[i], rgbx) @@ -617,7 +617,7 @@ proc blendLineMaskNeon*( proc blendLineMaskNeon*( a, b: ptr UncheckedArray[ColorRGBX], len: int -) {.simd.} = +) = var i: int while i < len and (cast[uint](a[i].addr) and 15) != 0: a[i] = blendMask(a[i], b[i]) @@ -652,7 +652,7 @@ proc blendLineCoverageMaskNeon*( coverages: ptr UncheckedArray[uint8], rgbx: ColorRGBX, len: int -) {.simd.} = +) = var i: int while i < len and (cast[uint](line[i].addr) and 15) != 0: let coverage = coverages[i] diff --git a/src/pixie/simd/sse2.nim b/src/pixie/simd/sse2.nim index 111cc827..77e5e0aa 100644 --- a/src/pixie/simd/sse2.nim +++ b/src/pixie/simd/sse2.nim @@ -1,4 +1,4 @@ -import chroma, nimsimd/hassimd, nimsimd/sse2, ../blends, ../common, vmath +import chroma, nimsimd/sse2, ../blends, ../common, vmath when defined(release): {.push checks: off.} @@ -49,7 +49,7 @@ proc fillUnsafeSse2*( data: var seq[ColorRGBX], color: SomeColor, start, len: int -) {.simd.} = +) = let rgbx = color.asRgbx() var @@ -73,7 +73,7 @@ proc fillUnsafeSse2*( for i in i ..< start + len: data[i] = rgbx -proc isOneColorSse2*(image: Image): bool {.simd.} = +proc isOneColorSse2*(image: Image): bool = result = true let color = image.data[0] @@ -111,7 +111,7 @@ proc isOneColorSse2*(image: Image): bool {.simd.} = if image.data[i] != color: return false -proc isTransparentSse2*(image: Image): bool {.simd.} = +proc isTransparentSse2*(image: Image): bool = var i: int p = cast[uint](image.data[0].addr) @@ -145,7 +145,7 @@ proc isTransparentSse2*(image: Image): bool {.simd.} = if image.data[i].a != 0: return false -proc isOpaqueSse2*(data: var seq[ColorRGBX], start, len: int): bool {.simd.} = +proc isOpaqueSse2*(data: var seq[ColorRGBX], start, len: int): bool = result = true var @@ -180,7 +180,7 @@ proc isOpaqueSse2*(data: var seq[ColorRGBX], start, len: int): bool {.simd.} = if data[i].a != 255: return false -proc toPremultipliedAlphaSse2*(data: var seq[ColorRGBA | ColorRGBX]) {.simd.} = +proc toPremultipliedAlphaSse2*(data: var seq[ColorRGBA | ColorRGBX]) = var i: int # Not worth aligning @@ -225,7 +225,7 @@ proc toPremultipliedAlphaSse2*(data: var seq[ColorRGBA | ColorRGBX]) {.simd.} = rgbx.b = ((rgbx.b.uint32 * rgbx.a + 127) div 255).uint8 data[i] = rgbx -proc invertSse2*(image: Image) {.simd.} = +proc invertSse2*(image: Image) = var i: int p = cast[uint](image.data[0].addr) @@ -266,7 +266,7 @@ proc invertSse2*(image: Image) {.simd.} = toPremultipliedAlphaSse2(image.data) -proc applyOpacitySse2*(image: Image, opacity: float32) {.simd.} = +proc applyOpacitySse2*(image: Image, opacity: float32) = let opacity = round(255 * opacity).uint16 if opacity == 255: return @@ -320,7 +320,7 @@ proc applyOpacitySse2*(image: Image, opacity: float32) {.simd.} = rgbx.a = ((rgbx.a * opacity) div 255).uint8 image.data[i] = rgbx -proc ceilSse2*(image: Image) {.simd.} = +proc ceilSse2*(image: Image) = var i: int p = cast[uint](image.data[0].addr) @@ -360,7 +360,7 @@ proc ceilSse2*(image: Image) {.simd.} = rgbx.a = if rgbx.a == 0: 0 else: 255 image.data[i] = rgbx -proc minifyBy2Sse2*(image: Image, power = 1): Image {.simd.} = +proc minifyBy2Sse2*(image: Image, power = 1): Image = ## Scales the image down by an integer scale. if power < 0: raise newException(PixieError, "Cannot minifyBy2 with negative power") @@ -467,7 +467,7 @@ proc minifyBy2Sse2*(image: Image, power = 1): Image {.simd.} = # Set src as this result for if we do another power src = result -proc magnifyBy2Sse2*(image: Image, power = 1): Image {.simd.} = +proc magnifyBy2Sse2*(image: Image, power = 1): Image = ## Scales image up by 2 ^ power. if power < 0: raise newException(PixieError, "Cannot magnifyBy2 with negative power") @@ -528,7 +528,7 @@ proc blendLineCoverageOverwriteSse2*( coverages: ptr UncheckedArray[uint8], rgbx: ColorRGBX, len: int -) {.simd.} = +) = var i: int while i < len and (cast[uint](line[i].addr) and 15) != 0: let coverage = coverages[i] @@ -581,7 +581,7 @@ proc blendLineCoverageOverwriteSse2*( proc blendLineNormalSse2*( line: ptr UncheckedArray[ColorRGBX], rgbx: ColorRGBX, len: int -) {.simd.} = +) = var i: int while i < len and (cast[uint](line[i].addr) and 15) != 0: line[i] = blendNormal(line[i], rgbx) @@ -603,7 +603,7 @@ proc blendLineNormalSse2*( proc blendLineNormalSse2*( a, b: ptr UncheckedArray[ColorRGBX], len: int -) {.simd.} = +) = var i: int while i < len and (cast[uint](a[i].addr) and 15) != 0: a[i] = blendNormal(a[i], b[i]) @@ -634,7 +634,7 @@ proc blendLineCoverageNormalSse2*( coverages: ptr UncheckedArray[uint8], rgbx: ColorRGBX, len: int -) {.simd.} = +) = var i: int while i < len and (cast[uint](line[i].addr) and 15) != 0: let coverage = coverages[i] @@ -682,7 +682,7 @@ proc blendLineCoverageNormalSse2*( proc blendLineMaskSse2*( line: ptr UncheckedArray[ColorRGBX], rgbx: ColorRGBX, len: int -) {.simd.} = +) = var i: int while i < len and (cast[uint](line[i].addr) and 15) != 0: line[i] = blendMask(line[i], rgbx) @@ -703,7 +703,7 @@ proc blendLineMaskSse2*( proc blendLineMaskSse2*( a, b: ptr UncheckedArray[ColorRGBX], len: int -) {.simd.} = +) = var i: int while i < len and (cast[uint](a[i].addr) and 15) != 0: a[i] = blendMask(a[i], b[i]) @@ -733,7 +733,7 @@ proc blendLineCoverageMaskSse2*( coverages: ptr UncheckedArray[uint8], rgbx: ColorRGBX, len: int -) {.simd.} = +) = var i: int while i < len and (cast[uint](line[i].addr) and 15) != 0: let coverage = coverages[i]