Creating sprites causes hiccups
by Vern Jensen · in Torque Game Builder · 06/08/2009 (7:55 pm) · 58 replies
I've found that creating a sprite and adding it to the sceneGraph is really slow. Enough to cause the creation of even 10 or so to generate a noticeable hiccup on slightly older systems. (i.e. 1 GHz processors) I did some tests with this method:
I added key bindings to call this any time a key was pressed. In one version, I left it as-is, where only *one* sprite is created, but then the code above is called on it 500 times. This was a test to see if executing these lines of script 500 times was what was creating the noticeable slowdown.
In the second version, uncomment the "new t2dStaticSprite" code inside the loop, and change it to loop only *50* times, not 500. This version is *significantly* slower than the other. It creates noticeable lurches in the animation every time I push the key that invokes this function. This is on a 2 GHz dual-core Intel Mac. Only 50 sprites. Even far fewer sprites can create a noticeable lurch in the animation if created at once on a somewhat older computer. (As I mentioned before, even 10 is enough to see the delay.) That's just 5 bullets with glows attached to them. In a shooter, this is a common occurrence. You *need* to be able to create (and delete -- also slow) lots of sprites in a single frame of animation in some instances.
The problem is not script. The problem is "new t2dStaticSprite" -- something internal to the engine is quite slow for this. I have the engine source, and can make mods, but I don't even know where to start. What files should I be looking at to consider trying possible engine-level speed-ups?
function doStarFieldTest()
{
$gMyTestStartTime = getRealTime();
%newSprite = new t2dStaticSprite()
{
scenegraph = gameSceneGraph; // NOTE: This name must be set in Torque's GUI editor!
// class = Asteroid;
imageMap = "star2ImageMap";
layer = $gAsteroidLayer;
size = "0.3 0.3";
};
for (%n = 1; %n < 500; %n++)
{
/*
%newSprite = new t2dStaticSprite()
{
scenegraph = gameSceneGraph; // NOTE: This name must be set in Torque's GUI editor!
imageMap = "star2ImageMap";
layer = $gAsteroidLayer;
size = "0.3 0.3";
};
*/
%newSprite.setLinearVelocity( 0, 15 );
// Set a random location
%xLoc = GetRandomFloat(-70, 70);
%yLoc = GetRandomFloat(-70p, 70);
%newSprite.setPosition( %xLoc, %yLoc );
}
echo(" Time to create 50 stars: " @ (getRealTime() - $gMyTestStartTime));
}I added key bindings to call this any time a key was pressed. In one version, I left it as-is, where only *one* sprite is created, but then the code above is called on it 500 times. This was a test to see if executing these lines of script 500 times was what was creating the noticeable slowdown.
In the second version, uncomment the "new t2dStaticSprite" code inside the loop, and change it to loop only *50* times, not 500. This version is *significantly* slower than the other. It creates noticeable lurches in the animation every time I push the key that invokes this function. This is on a 2 GHz dual-core Intel Mac. Only 50 sprites. Even far fewer sprites can create a noticeable lurch in the animation if created at once on a somewhat older computer. (As I mentioned before, even 10 is enough to see the delay.) That's just 5 bullets with glows attached to them. In a shooter, this is a common occurrence. You *need* to be able to create (and delete -- also slow) lots of sprites in a single frame of animation in some instances.
The problem is not script. The problem is "new t2dStaticSprite" -- something internal to the engine is quite slow for this. I have the engine source, and can make mods, but I don't even know where to start. What files should I be looking at to consider trying possible engine-level speed-ups?
#2
06/09/2009 (12:23 am)
Original source image is 5x5 pixels. Rounded up to 8x8 of course. I'm setting it to size 0.3 in these tests, but I"m fairly sure size of both source and dest is irrelevant to this slowdown occurrence.
#3
06/09/2009 (1:27 am)
Have you tried moving all this code into an engine function? That usually speeds things up by quite a bit when dealing with frequently-called events such as this.
#4
Adding 500 sprites is going to obviously eat cycles but I would not expect you to have a noticable hitch with only 50 sprites created.
Things to watch out for would be an image-map that's set to load when used (preLoad=false) so that the first sprite triggers the image-map to load which takes time and not something I'd want to be doing during game-play, only during level load.
I am presuming that you're running a RELEASE build for this testing. DEBUG builds can have some considerable hits for certain actions.
Although I think it's unlikely, you might want to check the size of the scene containers. By the size of the sprites (0.3,0.3) I don't think it's that but there's a default for the scene container (set-up as part of the scene-graph). However, you would notice this when these sprites moved as well as the scene-container is updated when an object moves.
Apart from that, I can't think of anything that should be giving you such a hit. You don't seem to have behaviors and I presume you've not got scripts set-up on callbacks that are being called, perhaps "t2dSceneObject::onAdd()".
Please feel free to send me a simple example project that demonstrates this and I'll find some time to look at it.
06/09/2009 (6:05 am)
Vern,Adding 500 sprites is going to obviously eat cycles but I would not expect you to have a noticable hitch with only 50 sprites created.
Things to watch out for would be an image-map that's set to load when used (preLoad=false) so that the first sprite triggers the image-map to load which takes time and not something I'd want to be doing during game-play, only during level load.
I am presuming that you're running a RELEASE build for this testing. DEBUG builds can have some considerable hits for certain actions.
Although I think it's unlikely, you might want to check the size of the scene containers. By the size of the sprites (0.3,0.3) I don't think it's that but there's a default for the scene container (set-up as part of the scene-graph). However, you would notice this when these sprites moved as well as the scene-container is updated when an object moves.
Apart from that, I can't think of anything that should be giving you such a hit. You don't seem to have behaviors and I presume you've not got scripts set-up on callbacks that are being called, perhaps "t2dSceneObject::onAdd()".
Please feel free to send me a simple example project that demonstrates this and I'll find some time to look at it.
#5
06/09/2009 (8:36 am)
Can you try cloning cloneWithBehaviors() a template object instead of making new sprites. at least on the iPhone it seemed to have better performance. worth giving it a shot.
#6
To give you some numbers, calling "new t2dStaticSprite" only once, and doing the loop above 500 times (i.e. unmodified from how I posted it), takes only about 23 milliseconds. But uncommenting "new t2dStaticSprite" from within the loop, and doing the loop only *50* times, takes more around 240 milliseconds if I remember correctly. This is 10x *less* script being executed, and it's an order of over 10x slower.
Melv -- yes, it's a release build. And the imageMap is set to preload.
Melv -- as for sending you a project, just plug the above code into any project you've got. I'm quite sure you should be able to reproduce it with any imageMap you've got handy. Just set up a Keyboard bindCmd to call doStarFieldTest() with any ImageMap you've got handy. Push that key when you run your project, and you should see the pause each time you push it. If that's not sufficient for you to reproduce it, let me know what you're seeing. Just be sure to uncomment the "new t2dStaticSprite" call that's *inside* the loop, and you should see a pause with even 50 or 100 sprites, and a huge (4-second or so) delay with 500, even on a fast computer.
The only thing I've *not* done is test this on Windows. So far I've assumed the pause would be the same, but this is unconfirmed. I might test this next.
06/09/2009 (10:18 am)
Dave -- The problem is that the time-consuming part already *is* in the engine. The "new t2dStaticSprite" method is what causes the slowdown. I realize my post may be a little confusing, but re-read it, and you'll find that my little test actually proves it's not an issue of script being what's slow, but t2dStaticSprite.To give you some numbers, calling "new t2dStaticSprite" only once, and doing the loop above 500 times (i.e. unmodified from how I posted it), takes only about 23 milliseconds. But uncommenting "new t2dStaticSprite" from within the loop, and doing the loop only *50* times, takes more around 240 milliseconds if I remember correctly. This is 10x *less* script being executed, and it's an order of over 10x slower.
Melv -- yes, it's a release build. And the imageMap is set to preload.
Melv -- as for sending you a project, just plug the above code into any project you've got. I'm quite sure you should be able to reproduce it with any imageMap you've got handy. Just set up a Keyboard bindCmd to call doStarFieldTest() with any ImageMap you've got handy. Push that key when you run your project, and you should see the pause each time you push it. If that's not sufficient for you to reproduce it, let me know what you're seeing. Just be sure to uncomment the "new t2dStaticSprite" call that's *inside* the loop, and you should see a pause with even 50 or 100 sprites, and a huge (4-second or so) delay with 500, even on a fast computer.
The only thing I've *not* done is test this on Windows. So far I've assumed the pause would be the same, but this is unconfirmed. I might test this next.
#7
>>Things to watch out for would be an image-map that's set to load when used (preLoad=false) so that the first sprite triggers the image-map to load which takes time and not something I'd want to be doing during game-play, only during level load.<<
BTW, off-topic from this conversation, but just a feature request for future version of Torque: a "load" method that I can call to load sprites I know I'll be using in my level, but the engine doesn't know I'll be using, so that I don't have to pre-load all important sprites at start-up. :-)
06/09/2009 (10:21 am)
Melv wrote:>>Things to watch out for would be an image-map that's set to load when used (preLoad=false) so that the first sprite triggers the image-map to load which takes time and not something I'd want to be doing during game-play, only during level load.<<
BTW, off-topic from this conversation, but just a feature request for future version of Torque: a "load" method that I can call to load sprites I know I'll be using in my level, but the engine doesn't know I'll be using, so that I don't have to pre-load all important sprites at start-up. :-)
#8
I now have no idea why the exact same code was behaving very, very slowly last night, and *consistently* so, and now this morning is working without a hitch. I will look into it more.
06/09/2009 (10:34 am)
Okay, WEIRD. I ran the test again, creating *100* stars at once, and now it's working without a hitch!!! Time to create 100 stars is merely 20 millisecond on average. What the heck?!!I now have no idea why the exact same code was behaving very, very slowly last night, and *consistently* so, and now this morning is working without a hitch. I will look into it more.
#9
The idea was that people load-up the sprites they want, whenever they want, typically upon level-load. Then they can be cloned or used as is.
With all that said, I'm fine with the idea of an explicit load if it makes things easier for some folks.
06/09/2009 (10:40 am)
Quote:I'm confused by this. That's what preload=false is for. You won't get anything loaded until you semi-explicitly want it to by using the image-map via a sprite etc. An explicit load (on an image-map) is easy to do but would obviously have to ignore the "preload" flag and the subsequent unloading. That's not really different than using the associated sprite in question.
BTW, off-topic from this conversation, but just a feature request for future version of Torque: a "load" method that I can call to load sprites I know I'll be using in my level, but the engine doesn't know I'll be using, so that I don't have to pre-load all important sprites at start-up. :-)
The idea was that people load-up the sprites they want, whenever they want, typically upon level-load. Then they can be cloned or used as is.
With all that said, I'm fine with the idea of an explicit load if it makes things easier for some folks.
#10
06/09/2009 (10:40 am)
I can only hypothesize that my brain was fried last night from too much coding, and so it was "user error". If so, my sincere apologies! I'm normally very careful with my tests. If I learn differently in the coming weeks, I'll follow up this thread with my findings, otherwise for now I'm chalking it up to user error. It seems that, indeed, "new t2dStaticSprite" is very fast!
#11
Turns out I have a lot of sprites like this (not just bullets), some of which take up a fair amount of VRAM, and they might not always be present in the sceneGraph, but *are* still nonetheless used a lot, and it'd be great to be able to tell the engine before that level starts "Hey, Make sure this list of sprites are preloaded, and not unloaded during the course of this level." But then it'd be fine to unload them once that level is over... they might not be used at all on certain other levels. I don't know of any way of doing this now.
I guess maybe my issue is more with unload than with preload. Speaking of which, does unload() help free up VRAM, or just RAM?
06/09/2009 (11:39 am)
Melv, as for preload: the problem is that any sprite that doesn't have preload=true (and is allowed to be unloaded) will be unloaded as soon as there are none of it in the sceneGraph. So if it is a certain type of bullet, for instance, it will constantly be loaded/unloaded throughout the course of the level, depending on whether it's currently present or not. (i.e. a spite shoots that bullet, so it's loaded. Then it moves off the screen, is deleted, and unloaded.)Turns out I have a lot of sprites like this (not just bullets), some of which take up a fair amount of VRAM, and they might not always be present in the sceneGraph, but *are* still nonetheless used a lot, and it'd be great to be able to tell the engine before that level starts "Hey, Make sure this list of sprites are preloaded, and not unloaded during the course of this level." But then it'd be fine to unload them once that level is over... they might not be used at all on certain other levels. I don't know of any way of doing this now.
I guess maybe my issue is more with unload than with preload. Speaking of which, does unload() help free up VRAM, or just RAM?
#12
I agree, this reference counting can lead to image-map thrashing however a method that's common is to template objects so you load-up all your templates which in turn get all the image-maps ready and you can clone them as you require them. You can also setup your templates whenever you want.
I do understand however that you may want to go image-map centric here and so an explicit load is possible. The problem of course and the main reason why I didn't do it in the first place is that an explicit unload is problematic at best. You have to either ignore the unload if there are references to the image-map or just go ahead and unload and wait for the crash/errors. At best you have to ensure that every client of the image-map is gone before you unload.
I can certainly see why it could be an advantage though.
In terms of unload, the TGE core that TGB was based upon didn't provide enough support for these dynamic textures (image-map generated bitmap packing) resulting in them having to stay in memory in case the texture-manager needed to refresh textures (going full-screen etc).
I made modifications and have supplied the code changes to several people now that change the behavior of the core texture-manager and the image-map so that this bitmap doesn't have to stay in memory. This frees anywhere from 40%-70% (sometimes more) memory.
This code modification has been tried on v1.7.4 build and have been incorporated into a handful of released games. It'll also be going into iTGB shortly.
06/09/2009 (11:49 am)
Vern,I agree, this reference counting can lead to image-map thrashing however a method that's common is to template objects so you load-up all your templates which in turn get all the image-maps ready and you can clone them as you require them. You can also setup your templates whenever you want.
I do understand however that you may want to go image-map centric here and so an explicit load is possible. The problem of course and the main reason why I didn't do it in the first place is that an explicit unload is problematic at best. You have to either ignore the unload if there are references to the image-map or just go ahead and unload and wait for the crash/errors. At best you have to ensure that every client of the image-map is gone before you unload.
I can certainly see why it could be an advantage though.
In terms of unload, the TGE core that TGB was based upon didn't provide enough support for these dynamic textures (image-map generated bitmap packing) resulting in them having to stay in memory in case the texture-manager needed to refresh textures (going full-screen etc).
I made modifications and have supplied the code changes to several people now that change the behavior of the core texture-manager and the image-map so that this bitmap doesn't have to stay in memory. This frees anywhere from 40%-70% (sometimes more) memory.
This code modification has been tried on v1.7.4 build and have been incorporated into a handful of released games. It'll also be going into iTGB shortly.
#13
i'm about to send my game to testing to a known portal, do you think you could provide me with that code change to the engine? because right now, my game is eating up to 300MB of RAM from the get go, and i think that maybe that codechange you're talkin bout here, may help me reduce that footprint, and maybe make my game work on slower machines?... i own the pro version of TGB, so i dont think theres a problem for that.
thank you very much.... in advance.
06/09/2009 (3:15 pm)
Hello there, Melv.i'm about to send my game to testing to a known portal, do you think you could provide me with that code change to the engine? because right now, my game is eating up to 300MB of RAM from the get go, and i think that maybe that codechange you're talkin bout here, may help me reduce that footprint, and maybe make my game work on slower machines?... i own the pro version of TGB, so i dont think theres a problem for that.
thank you very much.... in advance.
#14
300MB is actually not too terrible, Ehrlichmann. If your TGB project starts using nearly a gigabyte, like I've seen mentioned somewhere, it's time for drastic changes. It depends on what you're doing, but I'd agree half that may be more reasonable (if we assume Vista-infection+less than 1GB RAM).
06/09/2009 (10:31 pm)
While on the subject of really cool optimisations - is it possible we could get a 1.7.5 hotfix soon-ish with those latest tweaks in?300MB is actually not too terrible, Ehrlichmann. If your TGB project starts using nearly a gigabyte, like I've seen mentioned somewhere, it's time for drastic changes. It depends on what you're doing, but I'd agree half that may be more reasonable (if we assume Vista-infection+less than 1GB RAM).
#15
06/09/2009 (10:49 pm)
Quote:While on the subject of really cool optimisations - is it possible we could get a 1.7.5 hotfix soon-ish with those latest tweaks in?I had the same thought. At least the code Melv mentioned would help many people :)
#16
and, thats in the build i have right now, cuz whenever i use the editor, it takes something like 700MB (sometimes more), and im devvin' on a machine with 512MB of RAM, so you know now how painfull it is for me to work in that environment (and no, im not changing that rigg now, since it allows me to try my game on a presumable low end machine... basically, it forces me to try to keep things low while coding and stuff...)
06/09/2009 (10:50 pm)
well, 300MB may not be terrible ... but when you know it could be half of that, then you start to wonder what are you doing wrong.and, thats in the build i have right now, cuz whenever i use the editor, it takes something like 700MB (sometimes more), and im devvin' on a machine with 512MB of RAM, so you know now how painfull it is for me to work in that environment (and no, im not changing that rigg now, since it allows me to try my game on a presumable low end machine... basically, it forces me to try to keep things low while coding and stuff...)
#17
I'm slightly spoiled at home (which is my office now), since all my computers are 64-bit with 4GB RAM. My weakest device is my iPod touch. I tend to give away yesteryear's hardware :P
I have been thinking of getting an Eee someday, as that would both be cheap and a good example of weak hardware you find among people who only play casual games. My gaming PC is over two years old, but still miles ahead of what the casual crowd has if they didn't upgrade in the past year.
@Melv: How much difference has the new texture manager made on iTGB projects? I'd like to see some actual numbers from released projects, if there are any :)
06/09/2009 (11:15 pm)
Ouch. I feel your pain, having suffered from trying to do *anything* on 512MB laptops from before the war (the one between media providers, that is).I'm slightly spoiled at home (which is my office now), since all my computers are 64-bit with 4GB RAM. My weakest device is my iPod touch. I tend to give away yesteryear's hardware :P
I have been thinking of getting an Eee someday, as that would both be cheap and a good example of weak hardware you find among people who only play casual games. My gaming PC is over two years old, but still miles ahead of what the casual crowd has if they didn't upgrade in the past year.
@Melv: How much difference has the new texture manager made on iTGB projects? I'd like to see some actual numbers from released projects, if there are any :)
#18
I can provide anyone with the four C++ source files required that drastically reduce the memory overhead. They directly replace stock TGB v1.7.4 resource-manager and image-map files.
I'll discuss the idea of a hot-fix internally later today but obviously I can't promise anything.
06/10/2009 (12:18 am)
@Ronny: The memory management mods haven't yet been integrated but they're due in (iTGB 1.3).I can provide anyone with the four C++ source files required that drastically reduce the memory overhead. They directly replace stock TGB v1.7.4 resource-manager and image-map files.
I'll discuss the idea of a hot-fix internally later today but obviously I can't promise anything.
#20
Any form of hot-fix will take a little time but I just want to say though that if you do have the pro version (or are considering getting the pro version) then I am more than happy to provide the source-file replacements for you.
I'm pretty confident that these changes are solid as they have been tested many times on several released games and the memory reductions seen are huge.
06/10/2009 (2:11 am)
:)Any form of hot-fix will take a little time but I just want to say though that if you do have the pro version (or are considering getting the pro version) then I am more than happy to provide the source-file replacements for you.
I'm pretty confident that these changes are solid as they have been tested many times on several released games and the memory reductions seen are huge.
Torque 3D Owner Aun Taraseina