From 0f3b0712b5494f3a679516506ff8705ba804bf76 Mon Sep 17 00:00:00 2001 From: Ryan Oldenburg Date: Thu, 25 Nov 2021 23:30:05 -0600 Subject: [PATCH] better cairo benchmark --- experiments/benchmark_cairo.nim | 145 +++++++++++++++++++++----------- src/pixie/paths.nim | 33 +++----- 2 files changed, 109 insertions(+), 69 deletions(-) diff --git a/experiments/benchmark_cairo.nim b/experiments/benchmark_cairo.nim index 754f661..ba0da0f 100644 --- a/experiments/benchmark_cairo.nim +++ b/experiments/benchmark_cairo.nim @@ -1,96 +1,141 @@ -import benchy, cairo, chroma, math, pixie +import benchy, cairo, chroma, math, pixie, pixie/paths {.all.}, strformat + +proc doDiff(a, b: Image, name: string) = + let (diffScore, diffImage) = diff(a, b) + echo &"{name} score: {diffScore}" + diffImage.writeFile(&"{name}_diff.png") block: + let path = newPath() + path.moveTo(0, 0) + path.lineTo(1920, 0) + path.lineTo(1920, 1080) + path.lineTo(0, 1080) + path.closePath() + + let shapes = path.commandsToShapes(true, 1) + let surface = imageSurfaceCreate(FORMAT_ARGB32, 1920, 1080) ctx = surface.create() - ctx.setSourceRgba(0, 0, 1, 1) timeIt "cairo1": ctx.newPath() - ctx.moveTo(0, 0) - ctx.lineTo(1920, 0) - ctx.lineTo(1920, 1080) - ctx.lineTo(0, 1080) - ctx.closePath() + ctx.moveTo(shapes[0][0].x, shapes[0][0].y) + for shape in shapes: + for v in shape: + ctx.lineTo(v.x, v.y) ctx.fill() surface.flush() # discard surface.writeToPng("cairo1.png") let a = newImage(1920, 1080) - a.fill(rgba(255, 255, 255, 255)) timeIt "pixie1": let p = newPath() - p.moveTo(0, 0) - p.lineTo(1920, 0) - p.lineTo(1920, 1080) - p.lineTo(0, 1080) - p.closePath() + p.moveTo(shapes[0][0]) + for shape in shapes: + for v in shape: + p.lineTo(v) a.fillPath(p, rgba(0, 0, 255, 255)) # a.writeFile("pixie1.png") block: + let path = newPath() + path.moveTo(500, 240) + path.lineTo(1500, 240) + path.lineTo(1920, 600) + path.lineTo(0, 600) + path.closePath() + + let shapes = path.commandsToShapes(true, 1) + let surface = imageSurfaceCreate(FORMAT_ARGB32, 1920, 1080) ctx = surface.create() - ctx.setSourceRgba(0, 0, 1, 1) - timeIt "cairo2": + ctx.setSourceRgba(1, 1, 1, 1) + let operator = ctx.getOperator() + ctx.setOperator(OperatorSource) + ctx.paint() + ctx.setOperator(operator) + + ctx.setSourceRgba(0, 0, 1, 1) + ctx.newPath() - ctx.moveTo(500, 240) - ctx.lineTo(1500, 240) - ctx.lineTo(1920, 600) - ctx.lineTo(0, 600) - ctx.closePath() + ctx.moveTo(shapes[0][0].x, shapes[0][0].y) + for shape in shapes: + for v in shape: + ctx.lineTo(v.x, v.y) ctx.fill() - surface.flush() + surface.flush() # discard surface.writeToPng("cairo2.png") let a = newImage(1920, 1080) - a.fill(rgba(255, 255, 255, 255)) timeIt "pixie2": + a.fill(rgba(255, 255, 255, 255)) + let p = newPath() - p.moveTo(500, 240) - p.lineTo(1500, 240) - p.lineTo(1920, 600) - p.lineTo(0, 600) - p.closePath() + p.moveTo(shapes[0][0]) + for shape in shapes: + for v in shape: + p.lineTo(v) a.fillPath(p, rgba(0, 0, 255, 255)) # a.writeFile("pixie2.png") -# block: -# let -# a = imageSurfaceCreate(FORMAT_ARGB32, 1000, 1000) -# b = imageSurfaceCreate(FORMAT_ARGB32, 500, 500) -# ac = a.create() -# bc = b.create() +block: + let path = parsePath(""" + M 100,300 + A 200,200 0,0,1 500,300 + A 200,200 0,0,1 900,300 + Q 900,600 500,900 + Q 100,600 100,300 z + """) -# ac.setSourceRgba(1, 0, 0, 1) -# ac.newPath() -# ac.rectangle(0, 0, 1000, 1000) -# ac.fill() + let shapes = path.commandsToShapes(true, 1) -# bc.setSourceRgba(0, 1, 0, 1) -# bc.newPath() -# bc.rectangle(0, 0, 500, 500) -# bc.fill() + let + surface = imageSurfaceCreate(FORMAT_ARGB32, 1000, 1000) + ctx = surface.create() -# let pattern = patternCreateForSurface(b) + timeIt "cairo3": + ctx.setSourceRgba(1, 1, 1, 1) + let operator = ctx.getOperator() + ctx.setOperator(OperatorSource) + ctx.paint() + ctx.setOperator(operator) -# timeIt "a": -# ac.setSource(pattern) -# ac.save() -# ac.translate(25.2, 25.2) -# ac.rectangle(0, 0, 500, 500) -# ac.fill() -# ac.restore() + ctx.setSourceRgba(1, 0, 0, 1) -# discard a.writeToPng("a.png") + ctx.newPath() + ctx.moveTo(shapes[0][0].x, shapes[0][0].y) + for shape in shapes: + for v in shape: + ctx.lineTo(v.x, v.y) + ctx.fill() + surface.flush() + + # discard surface.writeToPng("cairo3.png") + + let a = newImage(1000, 1000) + + timeIt "pixie3": + a.fill(rgba(255, 255, 255, 255)) + + let p = newPath() + p.moveTo(shapes[0][0]) + for shape in shapes: + for v in shape: + p.lineTo(v) + a.fillPath(p, rgba(255, 0, 0, 255)) + + # a.writeFile("pixie3.png") + + # doDiff(readImage("cairo3.png"), a, "cairo3") diff --git a/src/pixie/paths.nim b/src/pixie/paths.nim index 8cb4a11..5d22dfe 100644 --- a/src/pixie/paths.nim +++ b/src/pixie/paths.nim @@ -1136,23 +1136,18 @@ proc maxEntryCount(partitioning: Partitioning): int = for i in 0 ..< partitioning.partitions.len: result = max(result, partitioning.partitions[i].len) -proc insertionSort( - hits: var seq[(float32, int16)], lo, hi: int -) {.inline.} = - for i in lo + 1 .. hi: - var - j = i - 1 - k = i - while j >= 0 and hits[j][0] > hits[k][0]: - swap(hits[j + 1], hits[j]) - dec j - dec k - -proc sort(hits: var seq[(float32, int16)], inl, inr: int) = +proc sortHits(hits: var seq[(float32, int16)], inl, inr: int) = ## Quicksort + insertion sort, in-place and faster than standard lib sort. let n = inr - inl + 1 - if n < 32: - insertionSort(hits, inl, inr) + if n < 32: # Use insertion sort for the rest + for i in inl + 1 .. inr: + var + j = i - 1 + k = i + while j >= 0 and hits[j][0] > hits[k][0]: + swap(hits[j + 1], hits[j]) + dec j + dec k return var l = inl @@ -1167,8 +1162,8 @@ proc sort(hits: var seq[(float32, int16)], inl, inr: int) = swap(hits[l], hits[r]) inc l dec r - sort(hits, inl, r) - sort(hits, l, inr) + sortHits(hits, inl, r) + sortHits(hits, l, inr) proc shouldFill( windingRule: WindingRule, count: int @@ -1258,7 +1253,7 @@ proc computeCoverage( inc numHits if numHits > 0: - sort(hits, 0, numHits - 1) + sortHits(hits, 0, numHits - 1) if aa: for (prevAt, at, count) in hits.walk(numHits, windingRule, y, width): @@ -1991,7 +1986,7 @@ proc overlaps( if segment.to != at: hits.add((at.x, winding)) - sort(hits, 0, hits.high) + sortHits(hits, 0, hits.high) var count: int for (at, winding) in hits: