Draw only portions of the circle visible to the user

Started by
16 comments, last by Khatharr 8 years, 3 months ago
Its hard to give concrete advice because problems like this could be different parts of the implementation and it may not be obvious what the slowdown is caused by.

You may want to experiment and do your best to try profiling it and see where the actual bottleneck is. Just drawing a large circle shouldn't be some massive slowdown since in theory it should be clipped in the rendering at some point. Is it just large circles or is it the collection of drawing all your circles that is bad?
Advertisement

I am kind of curious about how many circles you are drawing per frame? Like do you draw multiple circles on top of each other? How bout some hacky approaches such like you draw large circle on a texture then splat that texture around the screen with different scale-values? It just seems odd that drawing circles would be too slow to accomplish with modern computers, unless the arc-drawing algorithm is terrible written. I would also try to see if there is something like draw circle-function, cos the arc-algorithm could be far less optimized than drawing circle.

For drawing circles there is at least Bresenham's algorithm which at least back in the old days was one of those tricks to draw circles faster, but that involved drawing one pixel / line at a time, and this algorithm might be irrelevant nowadays.

Did you try just not drawing circles bigger than some X? While the image is obviously broken then, it should give you a big boost in performance if you are correct.

That is, you can do simple experiments to check your own conclusions, before going in and properly fix it.

So i decided to skip drawing circles above a certain size to test it, like you suggested, here is the results.

With the removal of large circles above x size:

HAqBRiW.png

Without removal of large circles:

hynGZ7M.png

Notice stroke() is painfully bad. Suggesting i need to skip drawing portions of circles not visible on screen to reduce the expensive process of stroke function.

This also tells me that if the circle is not visible on the screen, the browser still does a lot of processing for it, so it will probably be better to find a way to skip the function call entirely when the circle is not visible to the viewer's output with a simple math formula, assuming its simple and not going to be any more expensive.

But a 600ms time for a single function is definitely too high.

I also added a basic JavaScript circle example to my original post.

At least, something is clearly being done in "stroke" due to large circles.

Maybe "arc" just spams "stroke" with a long list of short lines, which are all discarded then?

A better bounding box may be the answer. I don't know what the engine does, but likely, it just puts a bounding box around the entire figure, and that fails for skipping large circles.

Maybe it helps if you give the engine a chance to improve its bounding box calculation. What about drawing a circle in 4 parts, but symmetrical around the axes?

-45 to +45 degrees, +45 to +135 degrees, +135 to +225 degrees, and +225 to +315 degrees (which is also -45 degrees, my numbers seem to match up).

This should give the engine the opportunity to make a bounding box for each part that does not include the center, thus improving its ability to detect off-screenness of some parts.

If that fails, I guess the next step is to run that bounding box check yourself, which is fairly simple to compute.

Further improvements would be to switch to 1/8 of a circle by cutting the above at the axes as well. This only works if in general you only see the part above or below (or left/right) the axis, rather than see a small part at both sides of an axis. With 1/8 you can improve the bounding box, by cutting away the inner half, and make it a triangle instead:


x1,y1 = r*cos(0), r*sin(0)
x2,y2 = r*cos(45), r*sin(45)
Box (x1,y1), (x2, y2), (x2, y1)

Computationally, it's more complicated though.

On Google Chrome the code that you posted on first post gave me 1 - 5 ms Stroke time and 1 - 5ms time for Arc-function, and I didn't notice much difference in performance depending if the radius was 50 or 200. If you got the results you're showing with the code you've added on the first post, maybe your hardware acceleration is disabled in your browser? I think I had some issues with hardware acceleration on Linux in Google Chrome, but that might not be your case.

I tried drawing multiple circles by modifying the draw function to draw multiple cirlces as following:


         function draw()
{
   ctx.clearRect(0,0,canvas.width,canvas.height);
   ctx.beginPath();
   for( y = 0; y < 20; ++y )
   {
      for( x = 0; x < 20; ++ x )
      {
        ctx.arc(x + pos.x, y + pos.y, (x + 1) * (y + 1),0,2*Math.PI);	
      }
   }

   ctx.stroke();

   requestAnimationFrame(draw);
}

And got these results from the F12 debugger on Google Chrome with the modified code that draws 400 different sized circles:

sJiGP4f.png

If your circles dont change significantly over time, and/or if you have many circles that look the same, draw the circle to an image and blit that to the screen, which should perform significantly better (you can update the image, even every frame if theres many circles sharing the same image and thus saving calculations).

o3o

If your circles dont change significantly over time, and/or if you have many circles that look the same, draw the circle to an image and blit that to the screen, which should perform significantly better (you can update the image, even every frame if theres many circles sharing the same image and thus saving calculations).

I'm not sure if this is beneficial, given when zooming in and out because i would be basically drawing a huge circle to an image then drawing the image, i don't see how this will reduce processing time, as i am adding a step and still drawing the circle either way? Also, simply resizing the image will pixelate or make the perimeter line extremely thin when zoomed out far.

If your circles dont change significantly over time, and/or if you have many circles that look the same, draw the circle to an image and blit that to the screen, which should perform significantly better (you can update the image, even every frame if theres many circles sharing the same image and thus saving calculations).

I'm not sure if this is beneficial, given when zooming in and out because i would be basically drawing a huge circle to an image then drawing the image, i don't see how this will reduce processing time, as i am adding a step and still drawing the circle either way? Also, simply resizing the image will pixelate or make the perimeter line extremely thin when zoomed out far.

He means to use the image as a cache so that you're not drawing the circle every frame.

void hurrrrrrrr() {__asm sub [ebp+4],5;}

There are ten kinds of people in this world: those who understand binary and those who don't.

This topic is closed to new replies.

Advertisement