Jump to content
Sign in to follow this  
Mason Wheeler

Why does this draw so slowly?

Recommended Posts

I've set up a class to draw an imperfect circle. It's still in progress, but the basic idea is working.

 

Unfortunately, it's also horribly slow, for something that should be really simple. Is there any way to speed it up?

 

To run this, create a new Canvas Game Project, add this unit, and add a TImperfectCercle to the Application. The ApplicationStarting method should include this line:

 

 
  FCircle := TImperfectCircle.Create(200, 200, 100); 

 

and PaintView should include this line:

 

 
  FCircle.Draw(canvas); 

 

When I run this, I can barely get above 10 FPS.

 

Code:

 
unit Unit1; 

interface 

uses  
  w3system, w3Graphics; 

type 
  TValues = record 
     x, y, radius, width: float; 
  end; 

  TImperfectCircle = class 
  private 
     FPoints: array of TValues; 
  public 
     constructor Create (x, y, radius: float); 
     procedure Draw(canvas: TW3Canvas); 
  end; 

implementation 

{ TImperfectCircle } 

function perturb(lw: float): float; 
var 
   direction: float; 
begin 
  direction := (Random() * 2) - 1; 
  result := lw + (direction / 3); 
end; 

constructor TImperfectCircle.Create(x, y, radius: float); 
var 
  i: integer; 
  lw: float; 
  item: TValues; 
begin 
  lw := 5; 
  for i := 0 to 179 do 
  begin 
     item.x := x; 
     item.y := y; 
     item.radius := radius; 
     item.width := lw; 
     FPoints.add(item); 
     lw := perturb(lw); 
     radius := perturb(radius); 
     x := perturb(x); 
     y := perturb(y); 
  end; 
end; 

procedure TImperfectCircle.Draw(canvas: TW3Canvas); 
begin 
  canvas.BeginPath; 
  canvas.StrokeStyle := 'White'; 
  for var i := 0 to 179 do 
  begin 
    var segment := FPoints[ i ]; 
    canvas.LineWidth := segment.width; 
    Canvas.ArcF(segment.x, segment.y, segment.radius, DegToRad(i * 2), degToRad((i + 1) * 2), false); 
    canvas.stroke; 
  end; 

  //draw a perfect circle nearby, for comparison 
  canvas.BeginPath; 
  canvas.ArcF(FPoints[0].x + 4 * FPoints[0].Radius, FPoints[0].y, FPoints[0].Radius, 0, 2 * pi, false); 
  canvas.LineWidth := 5; 
  canvas.stroke; 
end; 

end. 

Share this post


Link to post
Share on other sites

For starters, you could move canvas.stroke out of the 'for' loop. That gives me 33 fps.

 

 
  for var i := 0 to 179 do 
  begin 
    var segment := FPoints[ i ]; 
    canvas.LineWidth := segment.width; 
    Canvas.ArcF(segment.x, segment.y, segment.radius, DegToRad(i * 2), degToRad((i + 1) * 2), false); 
  end; 
  canvas.stroke; 

Share this post


Link to post
Share on other sites

Btw, if you perturb coordinates and radius in each loop, you get an interesting effect.

 

 
  for var i := 0 to 179 do 
  begin 
    var segment := FPoints[ i ]; 
    canvas.LineWidth := segment.width; 
    Canvas.ArcF(segment.x, segment.y, segment.radius, DegToRad(i * 2), degToRad((i + 1) * 2), false); 
    FPoints[ i ].radius := perturb(segment.radius); 
    FPoints[ i ].x := perturb(segment.x); 
    FPoints[ i ].y := perturb(segment.y); 
  end; 
  canvas.stroke; 

Share this post


Link to post
Share on other sites

Partial Arcs are actually rendered as fairly complex high-resolution polygon with caps (http://www.html5canvastutorials.com/tutorials/html5-canvas-arcs/), so they're going to be fairly expensive (some browser waive the caps for thin lines, but the polygon is still high-resolution).

Full circles (arc 0.. 2PI) can benefit from some rendering optimisations, but partial arcs don't.

 

If you're going to render by iterating on angles, you might as well subdivide a bit further and use several lines instead of arcs, and render them with one stroke, that should allow you to drop resolution a bit and go up in the FPS (you can then also use a pre-computed sin/cos table, as it's going to matter).

 

If you want to go faster still, and your imperfect circle is going to be constant after creation, a better and simpler solution is to just render it through a TBufferedImage (cf. http://delphitools.info/2012/05/24/buffered-image-for-smartms/). If you do that, you can go high-resolution and be flexible, maintain high-framerate, etc. The buffered image can also be drawn scaled and rotated at high performance.

Share this post


Link to post
Share on other sites

Gabr: Unfortunately, if I move the call to Canvas.Stroke out of the FOR loop, the line width changes don't take effect.

 

Eric: AHA! I knew I'd seen a render target implementation for Smart somewhere, but I couldn't find it. Thanks! I'll see if that works.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

×