Adventure Game Studio | Forums

AGS Support => Advanced Technical Forum => Topic started by: Dualnames on Sun 10/02/2019 14:26:02

Title: Slow Drawing, how can I improve it
Post by: Dualnames on Sun 10/02/2019 14:26:02
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 (ags) Select


//CODE IS EXECUTED TILL 20 Dynamic Sprite FRAMES ARE CREATED.

  waveFrames[waveFrameC]=DynamicSprite.CreateFromExistingSprite(waveFrames[0].Graphic, true);//waveFrameC
  DrawingSurface*area = waveFrames[waveFrameC].GetDrawingSurface();
  DrawingSurface*refarea = waveFrames[0].GetDrawingSurface();//waveFrameC-1
 
      float Size=6.0;//2.0;
      float IncX=2.6;
      int CircleSize=((wvc+1)-waveFrameC);
      int incBy=waveFrameC*FloatToInt(Size, eRoundNearest);//*6;
     
      int incByx=FloatToInt(IntToFloat(incBy)*IncX, eRoundNearest);
     
     
      int CentralX=cEgo.x;
      int CentralY=cEgo.y-150;
     
      int x=CentralX+(incByx);
      int x2=CentralX-(incByx);
     
      int y=CentralY+(incBy);
      int y2=CentralY-(incBy);//180-(incBy);
     
      int d=x2;
      int d1=0;
      int f=y2;
      int f1=0;
     
     
      float difX;
      float difY;
      float sum;
      float distance;       
      int checkDist;
     
      float gx=IntToFloat(waveFrameC)*Size*IncX;
      int getXv=FloatToInt(gx, eRoundNearest);
      int getX=CentralX-getXv;
      int getY=CentralY-(waveFrameC*6);
     
      while (d < x && CircleSize>0)//while
      {
        f=y2;
        f1=0;
        while (f < y)
        {
          //CALCULATE DISTANCE BETWEEN TWO POINTS
         
          difX = IntToFloat(CentralX) - IntToFloat(d);
          difY = IntToFloat(CentralY) - IntToFloat(f);
          difX = Maths.RaiseToPower(difX, 2.0);
          difY = Maths.RaiseToPower(difY, 2.0);
          sum = difX+difY;
          distance = Maths.Sqrt(sum);       
          checkDist = FloatToInt(distance, eRoundNearest);
         
          if (checkDist<= incBy && checkDist>incBy-CircleSize)
          {           
            area.DrawingColor=refarea.GetPixel(getX+d1, getY + (f1-1));           
            area.DrawPixel(d, f);
          }
         
          f++;
          f1++;
        }
        d1++;
        d++;
      }   
 
 
  area.Release();
  refarea.Release();
  waveFrameC++;
 
 
}
Title: Re: Slow Drawing, how can I improve it
Post by: Snarky on Sun 10/02/2019 15:04:48
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 Sun 10/02/2019 15:06:51
The issue doesn't stand on the calculations, albeit those can be improved, the issue lies on the two nested while loops

Code (ags) Select

while (d < x && CircleSize>0)
{
f=y2;
        f1=0;
        while (f < y)
        {         
          f++;
          f1++;
        }
        d1++;
        d++;
      }   


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 Sun 10/02/2019 15:16:23
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 Sun 10/02/2019 15:42:20
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 Sun 10/02/2019 16:37:17
Quote from: Dualnames on Sun 10/02/2019 15:06:51
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 Sun 10/02/2019 16:38:51
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 Sun 10/02/2019 21:23:10
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 Sun 10/02/2019 21:25:55
Quote from: Jack on Sun 10/02/2019 21:23:10
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.



Quote from: Dualnames on Sun 10/02/2019 15:06:51
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 Sun 10/02/2019 21:47:30
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 Sun 10/02/2019 22:50:45
This is what it's supposed to be doing https://www.youtube.com/watch?v=HUxcmeT3LKs

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

https://www.youtube.com/watch?v=vXQCf3zYez4&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 Sun 10/02/2019 22:55:53
Code (ags) Select


int i=0;
while (i <20)
{
while (d < x && CircleSize>0)
{
f=y2;
        f1=0;
        while (f < y)
        {         
          f++;
          f1++;
        }
        d1++;
        d++;
      }   
i++;
}



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 (ags) Select


int x=0;
while (x < width)//Width being 640 or 320 or whatever
{
int y=0;
while (y < height)//same as above
{
y++;
}
x++;
}



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 Sun 10/02/2019 22:56:31
Very cool.
Title: Re: Slow Drawing, how can I improve it
Post by: Gurok on Tue 12/02/2019 00:41:11
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 Fri 15/02/2019 00:49:58
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 Sat 16/02/2019 00:51:01
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 Sat 16/02/2019 06:52:01
Quote from: Dualnames on Sun 10/02/2019 22:50:45
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.

Quote from: Kweepa on Sat 16/02/2019 00:51:01
// 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 Sat 16/02/2019 17:55:39
Quote from: Snarky on Sat 16/02/2019 06:52:01
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 (ags) Select

// room script file

DynamicSprite *unchangedBg;

float time = 0.0;
// when I made the ring 20 pixels thick it slowed down
float ringThickness = 15.0;

function room_RepExec()
{
  time += 0.1;
  DynamicSprite *newBg = DynamicSprite.CreateFromExistingSprite(unchangedBg.Graphic);
  float bigR = 60.0 + 39.0 * Maths.Sin(time);
  float weeR = bigR - ringThickness;
  float x;
  float y;
  DrawingSurface *oldSurf = unchangedBg.GetDrawingSurface();
  DrawingSurface *newSurf = newBg.GetDrawingSurface();
  for (y = -bigR; y < bigR; y += 1.0)
  {
    float p = bigR*bigR - y*y;
    if (p > 1.0)
    {
      float how = Maths.Sqrt(p);
      float q = weeR*weeR - y*y;
      if (q > 1.0)
      {
        float hiw = Maths.Sqrt(q);
        for (x = -how; x < -hiw; x+= 1.0)
        {
          float d = 1.0 - 3.0 / Maths.Sqrt(x*x + y*y);
          newSurf.DrawingColor = oldSurf.GetPixel(100 + FloatToInt(x*d), 100 + FloatToInt(y*d));
          newSurf.DrawPixel(100 + FloatToInt(x), 100 + FloatToInt(y));
        }
        for (x = hiw; x < how; x+= 1.0)
        {
          float d = 1.0 - 3.0 / Maths.Sqrt(x*x + y*y);
          newSurf.DrawingColor = oldSurf.GetPixel(100 + FloatToInt(x*d), 100 + FloatToInt(y*d));
          newSurf.DrawPixel(100 + FloatToInt(x), 100 + FloatToInt(y));
        }
      }
      else
      {
        for (x = -how; x < how; x+= 1.0)
        {
          float d = 1.0 - 3.0 / Maths.Sqrt(x*x + y*y);
          newSurf.DrawingColor = oldSurf.GetPixel(100 + FloatToInt(x*d), 100 + FloatToInt(y*d));
          newSurf.DrawPixel(100 + FloatToInt(x), 100 + FloatToInt(y));
        }
      }
    }
  }
 
  DrawingSurface *ds = Room.GetDrawingSurfaceForBackground();
  ds.DrawImage(100, 50, newBg.Graphic);
  newBg.Delete();
}

function room_Load()
{
  Debug(4, 1);
  unchangedBg = DynamicSprite.CreateFromBackground(0, 100, 50, 200, 200);
}
Title: Re: Slow Drawing, how can I improve it
Post by: Dualnames on Sat 16/02/2019 20:46:06
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 Sat 16/02/2019 21:33:36
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 Sat 16/02/2019 22:03:17
It is the iterations, Steve is doing way less of those.
Title: Re: Slow Drawing, how can I improve it
Post by: Scavenger on Sun 17/02/2019 01:33:18
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 (AGS) Select


// new module script
DynamicSprite *echosprite;
DynamicSprite *echosprite2;
DynamicSprite *echomask;
int angle = 1;
int echoX;
int echoY;
int echotimer=0;
bool doEcho = false;
int size;

static function VFX::EchoAt (int x, int y,int radius)
{
  echoX = x;
  echoY = y;
  echotimer = 0;
  doEcho = true;
  size = radius*2;
  echosprite = DynamicSprite.Create (radius*2, radius*2);
  echosprite2 = DynamicSprite.CreateFromExistingSprite (echosprite.Graphic);
  sldEcho.Min = 0;
  sldEcho.Max = radius;
  sldEcho.Value = 0;
  sldEcho.TweenValue (1.0, radius, eEaseLinearTween, eNoBlockTween);
  sldEcho2.Min = 0;
  sldEcho2.Max = radius;
  sldEcho2.Value = 0;
  sldEcho2.TweenValue (1.2, radius, eEaseInOutSineTween, eNoBlockTween);
}

function MakeEcho ()
{
  if (echotimer < size && doEcho)
  {
  echomask = DynamicSprite.Create (size, size);
  DrawingSurface *surf = echomask.GetDrawingSurface ();
  surf.DrawingColor = 15;
  surf.DrawCircle (size/2, size/2, sldEcho.Value);
  surf.DrawingColor = 0;
  surf.DrawCircle (size/2, size/2, sldEcho2.Value);
  surf.Release ();
  echosprite2 = DynamicSprite.CreateFromExistingSprite (echosprite.Graphic);
  echosprite = DynamicSprite.CreateFromScreenShot (320, 200);
  int newX = echoX-(size/2);
  int w=newX+size;
  int underflowX;
  int overflowX;
  int newY = echoY-(size/2);
  int h = newY+size;
  int underflowY;
  int overflowY;
  if (newX < 0)
  {
    underflowX =  newX * (-1);
    newX = 0;
  }
  if (newX+size > 320)
  {
    overflowX = (newX+size-320);
    w = 320-overflowX;
  }
  {
   
  }
   if (newY < 0)
  {
    underflowY =  newY * (-1);
    newY = 0;
  }
  if (newY+size > 200)
  {
    overflowY = (newY+size-200);
    h = 200-overflowY;
  }
  if (w+newX > echosprite.Width)
  {
    newX = 0;
    w = echosprite.Width;
  }
  if (h+newY > echosprite.Height)
  {
    newY = 0;
    h = echosprite.Height;
  }
  echosprite.Crop (newX, newY, w, h);
  echosprite.ChangeCanvasSize (size, size, overflowX-underflowX, overflowY-underflowY);
  echosprite.CopyTransparencyMask (echomask.Graphic);
  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.
  //Translucence.CreateOverlay (81, echomask.Graphic, 128-echotimer, 2, echoX-(size/2), echoY-size/2);
  echotimer = sldEcho.Value;
  }
  else
  {
    echotimer = 0;
    echoX = 0;
    echoY = 0;
    doEcho = false;
    Translucence.DeleteOverlay (80);
    Translucence.DeleteOverlay (81);
  }
}
Title: Re: Slow Drawing, how can I improve it
Post by: Kweepa on Sun 17/02/2019 02:30:53
Quote from: Dualnames on Sat 16/02/2019 20:46:06
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 Sun 17/02/2019 05:55:53
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!