Improve speed of canvas painting
Kevinpgalligan opened this issue · 3 comments
While trying to replicate Coding Train's Mandelbrot set sketch, I found that canvas painting is very slow.
Here's my code to create a canvas and draw a picture of the Mandelbrot set on it (requires alexandria
for the utility function):
(defun make-mandelbrot-image (width height min-val max-val max-iters bound)
(let ((canvas (make-canvas width height)))
(dotimes (x width)
(dotimes (y height)
(let ((c (complex (remap x 0 width min-val max-val)
(remap y 0 height min-val max-val)))
(z 0)
(n 0))
(loop while (and (< n max-iters) (< (abs z) bound))
do (setf z (+ (* z z) c))
do (incf n))
(canvas-paint canvas
(gray-255 (remap n 0 max-iters 0 255))
x
y))))
(canvas-lock canvas)
canvas))
(defun remap (x la ha lb hb)
"Takes x from the interval [la, ha] and remaps it to the
interval [lb, hb]. If x is outside the expected interval, then
it gets clamped."
(setf x (alexandria:clamp x la ha))
(+ lb (* (- hb lb) (/ (- x la) (- ha la)))))
Running (time (make-mandelbrot-image 100 100 -1 +1 10 16))
, this takes over 9 seconds on my machine, even for this relatively small canvas size.
Extracting the logic to compute the value at each coordinate, it takes only 0.015 seconds. This indicates that the slowness comes from painting the canvas or generating the resulting image.
(time
(let ((width 100) (height 100) (max-val 1) (min-val 1) (max-iters 10) (bound 16))
(dotimes (x width)
(dotimes (y height)
(let ((c (complex (remap x 0 width min-val max-val)
(remap y 0 height min-val max-val)))
(z 0)
(n 0))
(loop while (and (< n max-iters) (< (abs z) bound))
do (setf z (+ (* z z) c))
do (incf n)))))))
Finally, commenting out the (canvas-lock canvas)
line in the original code, which (as far as I understand) is responsible for generating an image, it takes just as much time as before (9+ seconds). This leads me to believe that (canvas-paint)
is the bottleneck. I am happy to dig into this and work on optimising it. Ideally, I think that pixel poking should be just as fast as in p5.js or Processing.
Hm, commenting out canvas-paint
doesn't reduce time either.
Maybe the time difference comes from the fact that in the second snippet of code you have (min-val 1)
while in the call to make-mandelbrot-image
you pass -1
?
Oh, woops, you are completely right! I guess that my crappy code is the problem. Was struggling to understand why it was taking much longer to render than in Coding Train's videos. Happy to close this, then.
@Kevinpgalligan remap
exists in sketch as normalize
. I really don't know why I decided to make the API use kwd args called "out-low" and "out-high". They probably should've been optionals.