Jump to content


Photo

Why does this draw so slowly?


  • Please log in to reply
5 replies to this topic

#1 Mason Wheeler

Mason Wheeler
  • Members
  • 39 posts

Posted 22 May 2013 - 11:07 PM

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. 


#2 gabr42

gabr42

    Boss

  • Administrators
  • 192 posts

Posted 23 May 2013 - 12:35 AM

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; 


#3 gabr42

gabr42

    Boss

  • Administrators
  • 192 posts

Posted 23 May 2013 - 12:40 AM

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; 


#4 Eric

Eric
  • Moderators
  • 96 posts

Posted 23 May 2013 - 01:10 AM

Partial Arcs are actually rendered as fairly complex high-resolution polygon with caps (http://www.html5canv...l5-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.i...ge-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.

#5 Mason Wheeler

Mason Wheeler
  • Members
  • 39 posts

Posted 23 May 2013 - 01:34 AM

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.

#6 Mason Wheeler

Mason Wheeler
  • Members
  • 39 posts

Posted 23 May 2013 - 02:11 AM

OK, I managed to get it working at a much better FPS with TW3BufferedImage. Thanks, Eric!




0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users