Adventure Game Studio | Forums

AGS Support => Advanced Technical Forum => Topic started by: Dualnames on 10 Feb 2019, 14:26

Title: Slow Drawing, how can I improve it
Post by: Dualnames on 10 Feb 2019, 14:26
This basically creates a circle and saves each frame on a Dynamic Sprite array (size of 20).
I've noticed that this takes 2-4 seconds to execute, and the whole issue stands (i've commented parts of the code etc) inside the two while loops, is there a way I can make this faster/better?



Code: Adventure Game Studio
  1.  
  2. //CODE IS EXECUTED TILL 20 Dynamic Sprite FRAMES ARE CREATED.
  3.  
  4.   waveFrames[waveFrameC]=DynamicSprite.CreateFromExistingSprite(waveFrames[0].Graphic, true);//waveFrameC
  5.   DrawingSurface*area = waveFrames[waveFrameC].GetDrawingSurface();
  6.   DrawingSurface*refarea = waveFrames[0].GetDrawingSurface();//waveFrameC-1
  7.  
  8.       float Size=6.0;//2.0;
  9.       float IncX=2.6;
  10.       int CircleSize=((wvc+1)-waveFrameC);
  11.       int incBy=waveFrameC*FloatToInt(Size, eRoundNearest);//*6;
  12.      
  13.       int incByx=FloatToInt(IntToFloat(incBy)*IncX, eRoundNearest);
  14.      
  15.      
  16.       int CentralX=cEgo.x;
  17.       int CentralY=cEgo.y-150;
  18.      
  19.       int x=CentralX+(incByx);
  20.       int x2=CentralX-(incByx);
  21.      
  22.       int y=CentralY+(incBy);
  23.       int y2=CentralY-(incBy);//180-(incBy);
  24.      
  25.       int d=x2;
  26.       int d1=0;
  27.       int f=y2;
  28.       int f1=0;
  29.      
  30.      
  31.       float difX;
  32.       float difY;
  33.       float sum;
  34.       float distance;        
  35.       int checkDist;
  36.      
  37.       float gx=IntToFloat(waveFrameC)*Size*IncX;
  38.       int getXv=FloatToInt(gx, eRoundNearest);
  39.       int getX=CentralX-getXv;
  40.       int getY=CentralY-(waveFrameC*6);
  41.      
  42.       while (d < x && CircleSize>0)//while
  43.       {
  44.         f=y2;
  45.         f1=0;
  46.         while (f < y)
  47.         {
  48.           //CALCULATE DISTANCE BETWEEN TWO POINTS
  49.          
  50.           difX = IntToFloat(CentralX) - IntToFloat(d);
  51.           difY = IntToFloat(CentralY) - IntToFloat(f);
  52.           difX = Maths.RaiseToPower(difX, 2.0);
  53.           difY = Maths.RaiseToPower(difY, 2.0);
  54.           sum = difX+difY;
  55.           distance = Maths.Sqrt(sum);        
  56.           checkDist = FloatToInt(distance, eRoundNearest);
  57.          
  58.           if (checkDist<= incBy && checkDist>incBy-CircleSize)
  59.           {            
  60.             area.DrawingColor=refarea.GetPixel(getX+d1, getY + (f1-1));            
  61.             area.DrawPixel(d, f);
  62.           }
  63.          
  64.           f++;
  65.           f1++;
  66.         }
  67.         d1++;
  68.         d++;
  69.       }    
  70.  
  71.  
  72.   area.Release();
  73.   refarea.Release();
  74.   waveFrameC++;
  75.  
  76.  
  77. }
Title: Re: Slow Drawing, how can I improve it
Post by: Snarky on 10 Feb 2019, 15:04
A common trick is to skip the square root and instead compare the squared distance to the square of the threshold(s).

Also, you don't need to do the difX/Y calculations as floats, do you?

Another approach might be to just use DrawingSurface.DrawCircle() and DynamicSprite.CopyTransparencyMask() to handle the masking, instead of drawing it manually. I'm not sure whether it's quicker, but it might be worth a try.
Title: Re: Slow Drawing, how can I improve it
Post by: Dualnames on 10 Feb 2019, 15:06
The issue doesn't stand on the calculations, albeit those can be improved, the issue lies on the two nested while loops

Code: Adventure Game Studio
  1. while (d < x && CircleSize>0)
  2. {
  3.  f=y2;
  4.         f1=0;
  5.         while (f < y)
  6.         {        
  7.           f++;
  8.           f1++;
  9.         }
  10.         d1++;
  11.         d++;
  12.       }    
  13.  

Even like this, no drawing whatsoever, the frame rate drops to 10 for a solid 2 seconds.
Title: Re: Slow Drawing, how can I improve it
Post by: Snarky on 10 Feb 2019, 15:16
Well, have you calculated or checked how many loops that works out to be? Trying to follow your code, it appears to depend on the player x/y position.
Title: Re: Slow Drawing, how can I improve it
Post by: Dualnames on 10 Feb 2019, 15:42
It takes about 3 seconds to finish executing the code, around 120-150 loops. I did your adjustments, but same problem, I'm afraid.
Title: Re: Slow Drawing, how can I improve it
Post by: Snarky on 10 Feb 2019, 16:37
Even like this, no drawing whatsoever, the frame rate drops to 10 for a solid 2 seconds.

Well, you're a very good coder (better than I am, for sure), so you don't need me to tell you that the AGS framerate isn't gonna drop to 10 fps just because you're counting to 150 each game loop.

Which means that something is off in your analysis. Either you're counting wrong and it's way more loops than that (150 inside each loop, maybe?), or the main source of the slowdown is elsewhere.
Title: Re: Slow Drawing, how can I improve it
Post by: Dualnames on 10 Feb 2019, 16:38
I've noticed i was drawing a smaller circle than a rectangle, so now frames are at 29. 11 fps droprate.
Title: Re: Slow Drawing, how can I improve it
Post by: Jack on 10 Feb 2019, 21:23
Unless it's changed recently, pixel operations are too slow in AGS to be used outside of AGI resolutions.
Title: Re: Slow Drawing, how can I improve it
Post by: Crimson Wizard on 10 Feb 2019, 21:25
Unless it's changed recently, pixel operations are too slow in AGS to be used outside of AGI resolutions.

I'd say it's definitely not something you'd like to do every game tick.



The issue doesn't stand on the calculations, albeit those can be improved, the issue lies on the two nested while loops
<...>
Even like this, no drawing whatsoever, the frame rate drops to 10 for a solid 2 seconds.

In the end was this confirmed or something else is causing slowdowns?
Could you post the newer code after changes made by Snarky's advice?


Also, why are you writing your own circle drawing instead of using DrawCircle? It seems like you need to combine certain images?. TBH I am too tired right now to understand what the code is doing without comments...
Title: Re: Slow Drawing, how can I improve it
Post by: Snarky on 10 Feb 2019, 21:47
If I understand the code correctly, it draws a sprite that's a copy of some given area, except that in a ring between an outer circle and an inner circle, the coordinates are offset somehow (by a fixed amount, it seems). The size of the circles depends on a formula that uses the position of the player as well as the index of the sprite in an array, but working out the details without seeing it in action is beyond me. The excerpt we're seeing draws a single frame, it presumably exists within a bigger loop.

My interpretation is that it's meant to draw some sort of animated bubbles (or similar).
Title: Re: Slow Drawing, how can I improve it
Post by: Dualnames on 10 Feb 2019, 22:50
This is what it's supposed to be doing


I altered the code to make it run to 40 fps or so and made the effect a bit more subtle.

&feature=youtu.be

Ignore the 2 characters, there are still WIP parts of this.
But you can see the frame drop is non existent.
Title: Re: Slow Drawing, how can I improve it
Post by: Dualnames on 10 Feb 2019, 22:55
Code: Adventure Game Studio
  1.  
  2. int i=0;
  3. while (i <20)
  4. {
  5. while (d < x && CircleSize>0)
  6. {
  7.  f=y2;
  8.         f1=0;
  9.         while (f < y)
  10.         {        
  11.           f++;
  12.           f1++;
  13.         }
  14.         d1++;
  15.         d++;
  16.       }    
  17. i++;
  18. }
  19.  
  20.  

My main thing, is that the issues within AGS's inability to perform complex drawing stuff in real-time, isn't on the drawing functions. A big while like the one above, seems to be the problem in most cases. Like for example doing

Code: Adventure Game Studio
  1.  
  2. int x=0;
  3. while (x < width)//Width being 640 or 320 or whatever
  4. {
  5. int y=0;
  6. while (y < height)//same as above
  7. {
  8. y++;
  9. }
  10. x++;
  11. }
  12.  
  13.  

This loop without anything inside the while conditions, just as is, will cause framerate drop. I'm not sure if that's ameniable/fixable in any way, but I think it would solve a lot of issues. I wanted to use LUA for AGS but it's very risky given the state of that plugin. Regardless, I appreciate your work and I love you super hardcore <3.
Title: Re: Slow Drawing, how can I improve it
Post by: Jack on 10 Feb 2019, 22:56
Very cool.
Title: Re: Slow Drawing, how can I improve it
Post by: Gurok on 12 Feb 2019, 00:41
Jack suggested lowering the resolution on IRC. Another way to control the number of iterations would be to draw the radius of the circle at various angles like a bunch of tiny sectors instead of checking all possible x/y values for the ones that lie in the circle.
Title: Re: Slow Drawing, how can I improve it
Post by: eri0o on 15 Feb 2019, 00:49
Just wanted to add that this is a very cool effect!

One way I can see it be emulated is creating a circle with the desired alpha for transitions and stuff in a bigger size, then resizing it, creating a new sprite in a square area from the background, copying the prepared circle alpha on this square area you just got from the background, and do another resize at a slightly bigger size. It's a bit tricky but would avoid the while and could possibly run with no frame drops - need to test though.
Title: Re: Slow Drawing, how can I improve it
Post by: Kweepa on 16 Feb 2019, 00:51
It looks like you are modifying a ring about 150-200 pixels in diameter, so writing maybe 14000 pixels.
I seem to remember AGS was capable of updating 160x100=16000 pixels at about 20 FPS back in the day, so it should be able to manage 40 FPS now.
To ensure you are looping through the minimum number of iterations, I would work per scan line, and calculate the left and right extent of the circle, then decide if you are in a section with an inner unmodified part, and skip over the unmodified pixels, something like this:

// for a ring with small radius r and large radius R
for (y = (centerY - R) to (centerY + R))
   h = abs(y - centerY)
   halfOuterWidth = sqrt(R*R - h * h)
   if (h < r)
      halfInnerWidth = sqrt(r * r - h * h)
      // modify pixels from centerX-halfOuterWidth to centerX-halfInnerWidth and from centerX+halfInnerWidth to centerX+halfOuterWidth
   else
      // modify pixels from centerX-halfOuterWidth to centerX+halfOuterWidth
Title: Re: Slow Drawing, how can I improve it
Post by: Snarky on 16 Feb 2019, 06:52
I altered the code to make it run to 40 fps or so

I'd be interested to hear what you did to make that happen.

// for a ring with small radius r and large radius R
for (y = (centerY - R) to (centerY + R))

You don't need to run through the whole way from the top to bottom of the circle, because of symmetry; only the top half, then do the same operations except with the signs changed.
Title: Re: Slow Drawing, how can I improve it
Post by: Kweepa on 16 Feb 2019, 17:55
You don't need to run through the whole way from the top to bottom of the circle, because of symmetry; only the top half, then do the same operations except with the signs changed.
Yeah, but the slow bit is the bit inside the inner loop, so it just complicates things.

This runs at 40 fps on my laptop.
I just move the ring in and out with a sin() for simplicity.

http://www.kweepa.org/step/ags/tech/Screech.zip (http://www.kweepa.org/step/ags/tech/Screech.zip)

Code: Adventure Game Studio
  1. // room script file
  2.  
  3. DynamicSprite *unchangedBg;
  4.  
  5. float time = 0.0;
  6.  // when I made the ring 20 pixels thick it slowed down
  7. float ringThickness = 15.0;
  8.  
  9. function room_RepExec()
  10. {
  11.   time += 0.1;
  12.   DynamicSprite *newBg = DynamicSprite.CreateFromExistingSprite(unchangedBg.Graphic);
  13.   float bigR = 60.0 + 39.0 * Maths.Sin(time);
  14.   float weeR = bigR - ringThickness;
  15.   float x;
  16.   float y;
  17.   DrawingSurface *oldSurf = unchangedBg.GetDrawingSurface();
  18.   DrawingSurface *newSurf = newBg.GetDrawingSurface();
  19.   for (y = -bigR; y < bigR; y += 1.0)
  20.   {
  21.     float p = bigR*bigR - y*y;
  22.     if (p > 1.0)
  23.     {
  24.       float how = Maths.Sqrt(p);
  25.       float q = weeR*weeR - y*y;
  26.       if (q > 1.0)
  27.       {
  28.         float hiw = Maths.Sqrt(q);
  29.         for (x = -how; x < -hiw; x+= 1.0)
  30.         {
  31.           float d = 1.0 - 3.0 / Maths.Sqrt(x*x + y*y);
  32.           newSurf.DrawingColor = oldSurf.GetPixel(100 + FloatToInt(x*d), 100 + FloatToInt(y*d));
  33.           newSurf.DrawPixel(100 + FloatToInt(x), 100 + FloatToInt(y));
  34.         }
  35.         for (x = hiw; x < how; x+= 1.0)
  36.         {
  37.           float d = 1.0 - 3.0 / Maths.Sqrt(x*x + y*y);
  38.           newSurf.DrawingColor = oldSurf.GetPixel(100 + FloatToInt(x*d), 100 + FloatToInt(y*d));
  39.           newSurf.DrawPixel(100 + FloatToInt(x), 100 + FloatToInt(y));
  40.         }
  41.       }
  42.       else
  43.       {
  44.         for (x = -how; x < how; x+= 1.0)
  45.         {
  46.           float d = 1.0 - 3.0 / Maths.Sqrt(x*x + y*y);
  47.           newSurf.DrawingColor = oldSurf.GetPixel(100 + FloatToInt(x*d), 100 + FloatToInt(y*d));
  48.           newSurf.DrawPixel(100 + FloatToInt(x), 100 + FloatToInt(y));
  49.         }
  50.       }
  51.     }
  52.   }
  53.  
  54.   DrawingSurface *ds = Room.GetDrawingSurfaceForBackground();
  55.   ds.DrawImage(100, 50, newBg.Graphic);
  56.   newBg.Delete();
  57. }
  58.  
  59. function room_Load()
  60. {
  61.   Debug(4, 1);
  62.   unchangedBg = DynamicSprite.CreateFromBackground(0, 100, 50, 200, 200);
  63. }
  64.  
Title: Re: Slow Drawing, how can I improve it
Post by: Dualnames on 16 Feb 2019, 20:46
what a bastard, that's amazing, really nice idea on solving the thing with the circle from your previous post!
Title: Re: Slow Drawing, how can I improve it
Post by: Jack on 16 Feb 2019, 21:33
I was thinking, it might be the nested loops slowing it down. Maybe it will go better if you do one big loop for all pixels and use div and modulus to get the column and row indexes.
Title: Re: Slow Drawing, how can I improve it
Post by: Dualnames on 16 Feb 2019, 22:03
It is the iterations, Steve is doing way less of those.
Title: Re: Slow Drawing, how can I improve it
Post by: Scavenger on 17 Feb 2019, 01:33
Wait a minute, I think I've done a similar effect in Terror of the Vampire. You don't even need to use PutPixel, because DrawCircle and CopyTransparencyMask are much, much faster. It ends up looking like this:
(It's at 2:25)

Basically, you draw two circles over each other (one to draw the ring, the other to cut out the middle), take a screenshot of the current screen, and cut it out with the mask. Then you blit that translucently to the screen with maybe just 1 pixel of random jitter to throw everything off, and the next frame, before you even remove the first bit, you take another screenshot and do the same thing. It's crude, but it's FAST. Also I'm really regretting not commenting all my code, but this was for a MAGS game so I didn't have much time to document it.
Code: Adventure Game Studio
  1.  
  2. // new module script
  3. DynamicSprite *echosprite;
  4. DynamicSprite *echosprite2;
  5. DynamicSprite *echomask;
  6. int angle = 1;
  7. int echoX;
  8. int echoY;
  9. int echotimer=0;
  10. bool doEcho = false;
  11. int size;
  12.  
  13. static function VFX::EchoAt (int x, int y,int radius)
  14. {
  15.   echoX = x;
  16.   echoY = y;
  17.   echotimer = 0;
  18.   doEcho = true;
  19.   size = radius*2;
  20.   echosprite = DynamicSprite.Create (radius*2, radius*2);
  21.   echosprite2 = DynamicSprite.CreateFromExistingSprite (echosprite.Graphic);
  22.   sldEcho.Min = 0;
  23.   sldEcho.Max = radius;
  24.   sldEcho.Value = 0;
  25.   sldEcho.TweenValue (1.0, radius, eEaseLinearTween, eNoBlockTween);
  26.   sldEcho2.Min = 0;
  27.   sldEcho2.Max = radius;
  28.   sldEcho2.Value = 0;
  29.   sldEcho2.TweenValue (1.2, radius, eEaseInOutSineTween, eNoBlockTween);
  30. }
  31.  
  32. function MakeEcho ()
  33. {
  34.   if (echotimer < size && doEcho)
  35.   {
  36.   echomask = DynamicSprite.Create (size, size);
  37.   DrawingSurface *surf = echomask.GetDrawingSurface ();
  38.   surf.DrawingColor = 15;
  39.   surf.DrawCircle (size/2, size/2, sldEcho.Value);
  40.   surf.DrawingColor = 0;
  41.   surf.DrawCircle (size/2, size/2, sldEcho2.Value);
  42.   surf.Release ();
  43.   echosprite2 = DynamicSprite.CreateFromExistingSprite (echosprite.Graphic);
  44.   echosprite = DynamicSprite.CreateFromScreenShot (320, 200);
  45.   int newX = echoX-(size/2);
  46.   int w=newX+size;
  47.   int underflowX;
  48.   int overflowX;
  49.   int newY = echoY-(size/2);
  50.   int h = newY+size;
  51.   int underflowY;
  52.   int overflowY;
  53.   if (newX < 0)
  54.   {
  55.     underflowX =  newX * (-1);
  56.     newX = 0;
  57.   }
  58.   if (newX+size > 320)
  59.   {
  60.     overflowX = (newX+size-320);
  61.     w = 320-overflowX;
  62.   }
  63.   {
  64.    
  65.   }
  66.    if (newY < 0)
  67.   {
  68.     underflowY =  newY * (-1);
  69.     newY = 0;
  70.   }
  71.   if (newY+size > 200)
  72.   {
  73.     overflowY = (newY+size-200);
  74.     h = 200-overflowY;
  75.   }
  76.   if (w+newX > echosprite.Width)
  77.   {
  78.     newX = 0;
  79.     w = echosprite.Width;
  80.   }
  81.   if (h+newY > echosprite.Height)
  82.   {
  83.     newY = 0;
  84.     h = echosprite.Height;
  85.   }
  86.   echosprite.Crop (newX, newY, w, h);
  87.   echosprite.ChangeCanvasSize (size, size, overflowX-underflowX, overflowY-underflowY);
  88.   echosprite.CopyTransparencyMask (echomask.Graphic);
  89.   Translucence.CreateOverlay (80, echosprite.Graphic, 128, 2, echoX-(size/2)-1-Random(1), echoY-(size/2)+1-Random(1)); //You can replace this with drawing at 50% transparency.
  90.   //Translucence.CreateOverlay (81, echomask.Graphic, 128-echotimer, 2, echoX-(size/2), echoY-size/2);
  91.   echotimer = sldEcho.Value;
  92.   }
  93.   else
  94.   {
  95.     echotimer = 0;
  96.     echoX = 0;
  97.     echoY = 0;
  98.     doEcho = false;
  99.     Translucence.DeleteOverlay (80);
  100.     Translucence.DeleteOverlay (81);
  101.   }
  102. }
  103.  
Title: Re: Slow Drawing, how can I improve it
Post by: Kweepa on 17 Feb 2019, 02:30
what a bastard
I love you too, Dual!
Smart idea with the mask, Snarky & Scavenger! Kind of hard to tell with the video compression but it sounds like it would work well. (And lovely art all round!)
Title: Re: Slow Drawing, how can I improve it
Post by: Dualnames on 17 Feb 2019, 05:55
Well, my issues with my code is i wanted to do a rectangle then i thought why not a circle, then proceeded to circle the rectangle instead of drawing a circle lol, also wow that is almost the exact same effect!