lengthdir functions faster alternative?

obscene

Member
I'm having a little slowdown when using a lot of lensflares partly because I use a lot of lengthdir_x and lengthdir_y calculations.

Basically the lensflares are created out of a dozen or so little sprites that are drawn various distances from the center of the screen out at an angle opposite from the light source. It looks like this...

Code:
// Draw flare
draw_set_blend_mode(bm_add)
draw_sprite_ext(spr_lensflarecrepuscular,-1,px,py,1-distance/1500,1-distance/1500,px*.01+py*.01,c_white,intensity*.5-distance/750)
draw_sprite_ext(spr_lensflare_prime_rays,-1,px,py,1-distance/500,1-distance/500,px*.01+py*.01,c_white,(intensity-distance/750)*.4)
draw_sprite_ext(spr_lensflare_prime_edge,-1,px+lengthdir_x(distance*1.1,angle),py+lengthdir_y(distance*1.1,angle),distance/700,distance/700,angle,color,intensity*(distance/1000)-.1)
draw_sprite_ext(spr_lensflare_prime_orb,-1,px+lengthdir_x(distance*2.5,angle),py+lengthdir_y(distance*2.5,angle),2-distance/1000,2-distance/1000,angle,color,intensity-distance/2500-.5)
draw_sprite_ext(spr_lensflare_prime_circle_blue,-1,px+lengthdir_x(distance*1.12,angle),py+lengthdir_y(distance*1.12,angle),.2+(1-distance/1000)*.1,.2+(1-distance/1000)*.1,angle,color,intensity-distance/4200-.2)
draw_sprite_ext(spr_lensflare_prime_circle_green,-1,px+lengthdir_x(distance*1.13,angle),py+lengthdir_y(distance*1.13,angle),.3+(1-distance/1000)*.2,.3+(1-distance/1000)*.2,angle,color,intensity-distance/3800-.2)
draw_sprite_ext(spr_lensflare_prime_circle_red,-1,px+lengthdir_x(distance*1.15,angle),py+lengthdir_y(distance*1.15,angle),.5+(1-distance/1000)*.3,.5+(1-distance/1000)*.3,angle,color,intensity-distance/3200-.21)   
draw_sprite_ext(spr_lensflare_prime_circle_blue,-1,px+lengthdir_x(distance*1.18,angle),py+lengthdir_y(distance*1.18,angle),.8+(1-distance/1000)*.5,.8+(1-distance/1000)*.5,angle,color,intensity-distance/2800-.2)
draw_sprite_ext(spr_lensflare_prime_circle_green,-1,px+lengthdir_x(distance*1.22,angle),py+lengthdir_y(distance*1.22,angle),1.2+(1-distance/1000)*.8,1.2+(1-distance/1000)*.8,angle,color,intensity-distance/2400-.2)
draw_sprite_ext(spr_lensflare_prime_circle_red,-1,px+lengthdir_x(distance*1.26,angle),py+lengthdir_y(distance*1.26,angle),1.6+(1-distance/1000)*1.2,1.6+(1-distance/1000)*1.2,angle,color,intensity-distance/2000-.2)
draw_sprite_ext(spr_lensflareline,-1,px,py,intensity*8,intensity*8*image_yscale,0,color,intensity*.2)
draw_sprite_ext(spr_lensflarehalfcircle,-1,px+lengthdir_x(distance*1.4,angle),py+lengthdir_y(distance*1.4,angle),.7,.7,angle,color,intensity*.1)
draw_sprite_ext(spr_lensflarehalfcircle,-1,px+lengthdir_x(distance*2.5,angle),py+lengthdir_y(distance*2.5,angle),1.7,1.7,angle,c_olive,intensity*.1)
draw_set_blend_mode(bm_normal)
My only thought is to just reduce the calculations to an alarm every other frame or something... but hoping someone may have a better solution. Also I know I need to change those divisions to multiplications and precalculte some of the recurring math... doing that now.
 
Last edited:

Nocturne

Friendly Tyrant
Forum Staff
Admin
Okay, a couple of things here... where do you calculate "distance"? Is it every step or just once? If it is just once, then you can create an array of values for things like "distance*1.4" etc... Also, you can remove a load of redundant code by creating some vars at the start of drawing for things like "distance / 1000" so that they are not calculated so often. Basically, if a calculation appears more than once in the code, make it a local variable...
 

obscene

Member
I've been doing exactly that after I posted that and took a better look at it. (I wrote this a long time ago it was pretty bad.) Removed a few graphics from it and calculations.

distance is calculated every step unfortunately as it's the distance between the camera and the light source. Pretty much all the variables are calculated every step unless the flare object is off screen.

I've actually realized I was looking at the debugger wrong and this isn't as intensive as I thought. The drawing is worse. :p

Code:
// Draw flare
draw_set_blend_mode(bm_add)   
//draw_sprite_ext(spr_lensflare,-1,px,py,intensity,intensity,intensity,color,intensity)
draw_sprite_ext(spr_lensflarecrepuscular,-1,px,py,1-distance/1500,1-distance/1500,px*.01+py*.01,c_white,intensity*.5-distance*.0013)
draw_sprite_ext(spr_lensflare_prime_rays,-1,px,py,1-distance*.002,1-distance*.002,px*.01+py*.01,c_white,(intensity-distance*.0013)*.4)   
draw_sprite_ext(spr_lensflare_prime_edge,-1,px+lengthdir_x(distance*1.1,angle),py+lengthdir_y(distance*1.1,angle),distance*.0014,distance*.0014,angle,color,intensity*(distance*.001)-.1)
draw_sprite_ext(spr_lensflare_prime_orb,-1,px+lengthdir_x(distance*2.5,angle),py+lengthdir_y(distance*2.5,angle),2-distance*.001,2-distance*.001,angle,color,intensity-distance*.0004-.5)
var d=1-distance*.001;
draw_sprite_ext(spr_lensflare_prime_circle_blue,-1,px+lengthdir_x(distance*1.12,angle),py+lengthdir_y(distance*1.12,angle),.2+d*.1,.2+d*.1,angle,color,intensity-distance/4200-.2)
draw_sprite_ext(spr_lensflare_prime_circle_green,-1,px+lengthdir_x(distance*1.13,angle),py+lengthdir_y(distance*1.13,angle),.3+d*.2,.3+d*.2,angle,color,intensity-distance/3800-.2)
draw_sprite_ext(spr_lensflare_prime_circle_red,-1,px+lengthdir_x(distance*1.15,angle),py+lengthdir_y(distance*1.15,angle),.5+d*.3,.5+d*.3,angle,color,intensity-distance/3200-.21) 
draw_sprite_ext(spr_lensflareline,-1,px,py,intensity*8,intensity*8*image_yscale,0,color,intensity*.2)
draw_sprite_ext(spr_lensflarehalfcircle,-1,px+lengthdir_x(distance*1.4,angle),py+lengthdir_y(distance*1.4,angle),.7,.7,angle,color,intensity*.1)
draw_sprite_ext(spr_lensflarehalfcircle,-1,px+lengthdir_x(distance*2.5,angle),py+lengthdir_y(distance*2.5,angle),1.7,1.7,angle,c_olive,intensity*.1)
draw_set_blend_mode(bm_normal)
 
J

Jaqueta

Guest
In this case, you could use lenghtdir once, as a multiplier for the other values
Code:
var ldx=lengthdir_x(1,angle), ldy=lengthdir_y(1,angle);
draw_sprite_ext(...,...,px+(ldx*distance*1.1),py+(ldy*distance*1.1),..................)
 
I

icuurd12b42

Guest
^ above to leverage the math

How much are you stretching the sprites BTW
 

Nocturne

Friendly Tyrant
Forum Staff
Admin
In this case, you could use lenghtdir once, as a multiplier for the other values
Code:
var ldx=lengthdir_x(1,angle), ldy=lengthdir_y(1,angle);
draw_sprite_ext(...,...,px+(ldx*distance*1.1),py+(ldy*distance*1.1),..................)
That's a clever way to do it... notes have been taken! :)
 

obscene

Member
In this case, you could use lenghtdir once, as a multiplier for the other values
Code:
var ldx=lengthdir_x(1,angle), ldy=lengthdir_y(1,angle);
draw_sprite_ext(...,...,px+(ldx*distance*1.1),py+(ldy*distance*1.1),..................)
Ok that's pretty smart. Will do it!

^ above to leverage the math

How much are you stretching the sprites BTW
They're all probably scaling with values from .5 to 1.5 based on where the light is on screen at any given time.
 
I

icuurd12b42

Guest
dcos and dsin instead of lengthdir with 1 btw.

The scaling does not seem that bad. how many of these are you drawing? Specifically how many bm_add to bm_normal switching are you causing in the draw
 

Nocturne

Friendly Tyrant
Forum Staff
Admin
The scaling does not seem that bad. how many of these are you drawing? Specifically how many bm_add to bm_normal switching are you causing in the draw
That is actually a really good question. Setting the blend mode is very slow... In SKein, I have a two scripts for drawing with additive blend modes, one for things drawn below the main game objects and one for drawing above, and the script simply calls a "with(OBJECT TO DRAW)" and the draw code, with the blend mode setting at the very start and the very end. This way, no matter what is happening on screen, I never set the blend mode more than twice in any step. EG:
Code:
draw_set_blend_mode(bm_add);
with(obj_explode)
{
draw_self();
}
with (obj_effects)
{
draw_sprite(spr_magic, image_index, x, y);
}
// etc...
draw_set_blend_mode(bm_normal);
 

obscene

Member
Good question. Normally no more than 3-4 flares are on the screen at once but I'm working on a special area with tons of lights and so I'm going overboard with sometimes as many as 8 and suddenly it's an issue. I hadn't even thought about the blend mode switching.... I've learned a lot since I did this initially so I think I can get this whole effect under one object with one draw call and one blend mode siwtch now that you mention it.

EDIT: Just as Nocturne suggested! :p
 

Fern

Member
This might sound obvious, but run the debugger profiler and see what code in specific is causing slow down here. If it says lengthdir_ is the slowest, well yo uwere right.
 
J

Jaqueta

Guest
I'm no Shader specialist but, wouldn't a Shader give better results? Like, look better, use less CPU, less GPU and even less Memory at the same time?
 

obscene

Member
Sure... you offering to write me a shader? :p

Seabass... yes it was obvious. I've done that and the lengthdir functions are heavy due to the sheer number of them. But the big drain is the swapping of blend modes back and forth as Iccurd pointed out which doesn't appear in the profiler. (EDIT actually it does but I don't trust the numbers it gives) Working on that today.
 
Last edited:

obscene

Member
I'm not touching shaders. :p Every time I do it ends in problems I don't want to deal with. I don't understand the language. Plus what I need is very specific. I need specific sprites drawn at specific places and all scaled and alpha depending on the light location and I've found nothing like that in a shader or know how to make it on my own.
 

obscene

Member
They are indeed kinda scary at first xD
This one seems to be pretty good for me, I'll try to implement this to GM later when I got the time., if I get any results, I'll let you know.
That looks pretty dang nice actually. I don't understand how it works at all. I don't see any drawing in that code. Are they sprites? It makes no sense to me.


Update on my own flares. With everyone's advice I was able to reduce the swaps to 2 (instead of 2 x the number of flares), reduce the lengthdir functions to 2 per flare and bury the whole thing way down in the profiler! :D Now if I could do the same for my crepuscular rays! :(
 
I

icuurd12b42

Guest
>Now if I could do the same for my crepuscular rays

Arent those drawn on a surface and post process at the end of the draw (trying to recall your code)
 
I

icuurd12b42

Guest
Well as I said the script batch system is way more easier to do this stuff with

draw
if(inview())
{
draw_self();
add to batch global.layer1_batch, sb_draw_sprite_ext,........
}

in you layer1 controller
set target surface layer 1
set blend mod to what I want
process batch global.layer1_batch
surface reset target...

draw surface, layer 1 surface...
 
Top