Quick Links: Download Gideros Studio | Gideros Documentation | Gideros community chat | DONATE
Speed of Texture loading — Gideros Forum

Speed of Texture loading

bowerandybowerandy Guru
edited March 2012 in General questions
I'm new to Gideros and evaluating whether I can jump ship from Corona SDK over here. I absolutely love the fast testing turnaround with Gideros player and the fact that you have a standard OOP framework built-in.

One problem I have, which might be a bit of a show-stopper for me is the speed of texture loading. Basically, I have some long animations that can't all fit into a single texture sheet so I need to page the next section in after the first one has finished playing (or better still, WHILE the first one is playing).

Since I can't find any multi-threading capability inside Gideros , I'm looking at loading the next texture in sequence dynamically after the animation of the first one has finished. Now, I hear you say, that will never work and you're right. However, it does work okay on Corona (just about) because the texture loading seems so much faster. For example with a 1024x1024 texture on an iPad 1:

Gideros:
TexturePack.new of 1024x1024 png texture takes 900-120ms
Texture.new of 800x500 png image takes 80ms

Corona
SpriteGrabber load of 1024x1024 png texture takes 50ms
display.newImage of 800x500 png image takes 40ms

You see that there is a lot of difference here, which makes me think that something is in need of optimizing somewhere. Has anyone else experienced this?

Comments

  • That should say:
    TexturePack.new of 1024x1024 png texture takes 900-1200ms
  • Take a look at Lua coroutines. Haven't tried them with Gideros yet but they may help you out in loading things asynchronously.
  • atilimatilim Maintainer
    edited March 2012
    Hi,

    After your post, I've created a 1024x1024 texture pack with 74 texture regions in it. The size of the resulting .png file is 433kb. On iPad 1, loading times are:
    Texture.new -> 220ms
    TexturePack.new -> 235ms

    And I think 900-1200ms is very high and there is something wrong there.

    In Gideros, we're using zlib+libpng to load png files. And in my tests, libpng takes ~%80 of the loading time.

    For some time, I was planning to use stb_image.c from http://nothings.org/ which is widely used (in game programming community) and it's a robust image loader. My main aim was to decrease the executable size, but it may decrease the image loading time also.

    Now I'll do some tests on Corona.
  • atilimatilim Maintainer
    @moopf, most probably coroutines cannot help. Because coroutines are not run on a separate thread: http://www.lua.org/pil/9.html

    You need a true multithreading library like Lua Lanes. http://kotisivu.dnainternet.net/askok/bin/lanes/
  • Hi @atilim, that's interesting, as the original poster wants to load the other page whilst the animations from the first page are still playing, I only mentioned coroutines as I thought there might be the spare time before to load whilst the animation is playing. Admittedly I've only played with coroutines on a very cursory way, haven't used them in anger myself.

    Seeing as you've mentioned Lanes, however, does that work with Gideros? ;)
  • Actually no, scratch that, of course there wouldn't be enough time as the act of loading would still block everything anyway. Didn't think that one through, did I :D
  • atilimatilim Maintainer
    edited March 2012
    :) exactly as you said: loading a texture on a coroutine would block the animation.

    I think it's worth to try Lua Lanes as a plugin :) My first guess is it will crash Gideros. On the other hand, making texture loading thread-safe is not that hard.
  • atilimatilim Maintainer
    edited March 2012
    Hi @bowerandy,

    On iPad 1, with Corona loading the same texture took 275ms. Is there a possibility that you've done this test on simulator (instead of real iPad 1)?
  • Hi @atilim,

    No I've definitely done the tests on a real iPad 1.

    However, now I come to experiment a bit more I find that only the first texture load (of the whole run) is stupidly fast under Corona (??). Subsequent ones are more around the 150-300ms mark. This agrees more with what you saw. Also, I'm afraid I reported the texture size incorrectly. I imagine that the load times will depend on the contents and size of the textures. The three PNG segments I'm using are 550K, 1.1Mb and 750K on disk. The actually sizes that I'm using are not 1024x1024. They are 1000x1800, 2048x2048, 1100x1900. Sorry about that.

    This means that running my animation at 15fps (which would *just* about be good enough) I get a small stutter between segments under Corona. I've thought of a way of covering these up by choosing the segment boundaries carefully and other disguises in the game which would mean that I could (barely) get by in Corona.

    I've rechecked the Gideros version along similar lines (i.e. by not only reporting the first segment load) and I'm getting better times between 500-800ms but the results are definitely significantly slower. The end result is that the "stutter" between segment is simply not disguisable under Gideros and is consistent with being about 1/3 the speed of the Corona version. So I have these approx times across all three textures:

    Gideros (approx times):
    TexturePack.new takes 800-1200ms

    Corona (approx times)
    SpriteGrabber load takes 150-300ms

    Also the times I'm seeing under Gideros are a bit all over the place. I think I may be being caught out by the garbage collector. I'm not sure when the Bitmaps and Textures are being freed after the first loop through. Under Corona I am explicitly releasing the objects but there doesn't appear to be this option in Gideros (unless I'm missing something). I really need to put together an example to demonstrate all this but it's a bit tricky as I'll have to manufacture some non-game artwork since I can't publicly expose the real stuff just yet.

    I had thought of a plugin solution, perhaps starting a separate thread in a plugin that calls back into Gideos and does the load in the Lua callback (protected by an NSLock). I haven't tried plugins yet so don't know if that would stand a chance of working? I'll take a look at Lua Lanes too since that sounds like a more general solution but it might be a bit beyond me as I stand right now.

    Thanks for looking at this and your suggestions so far.
  • BTW, I should say that this is only a test. If I was doing the thing for real I would vary the sizes of the animation segments to make each smaller and more consistent. I reckon if I could get the load speeds in Gideros to be about the same as those for Corona I could double the number of segments (thus halving the times) and everything would be okay.

    Would it be possible to write a plugin that uses stb_image.c as you suggest but actually fills in a proper Gideos Texture object? Is the format of this set out anywhere?
  • atilimatilim Maintainer
    Hi @bowerandy,

    Unfortunately, I couldn't say anything without images or code. If possible, can you send me the images (maybe with a big watermark on it) and test codes. So that I can test more and try to find the bottleneck.

    Currently, embedding is stb_image.c as a plugin is impossible. Because we haven't give access to the internals of rendering engine through plugins.

    In Gideros, if a Bitmap and Texture object doesn't have any reference, they will be garbage collected with the next cycle. And we don't call collectgarbage() internally. Also if a texture is still on memory, loading that texture again is very fast and consumes very little memory.
  • Hi @atilim,

    Okay, I've rebuilt my tests with some dummy graphics and created a Gideros project and a Corona project that try do do the same thing. Since the Corona project uses their built-in sprite library (via a module called SpriteGrabber) it operates a little differently from the Gideros version where the animation is "roll your own".

    I've reduced the size of each segment to incorporate 50 frames in each, out of a total of 255. Typically, the Corona times are a bit higher than I got before and the Gideros times are a bit lower. I'm not sure why this would be although the original graphics were rather more complex so would take longer to decompress, I guess. The ratio is still about 1.7:1 but I admit that is rather better than the 3:1 ratio I was seeing before.

    You can download the two projects here:
    http://dl.dropbox.com/u/2242074/GiderosTest.zip

    Here are the times for the first run only (to avoid GC and caching issues) of each test:

    Corona: 382, 414, 233, 185, 144
    Gideros: 648, 658, 333, 311, 328

    My feeling is that unless there is some way to speed up the Gideros load times substantially to be able to even beat the Corona ones then I'm going to have to look at some sort of background loading approach.
  • Hi @Atilim, I'm sure that @bowerandy might be facing some timing issues, could you post a sample that uses the sprite sheet he has provided. His sample has a missing Classes.lua for Corona and a missing customised animatedSprite.lua for Gideros. I think there can be some difference on how much time things take depending on how you do it. You know Gideros best and you have those tests you have run, if you post that code, it could be used as best practice.
    twitter: @ozapps | http://www.oz-apps.com | http://howto.oz-apps.com | http://reviewme.oz-apps.com
    Author of Learn Lua for iOS Game Development from Apress ( http://www.apress.com/9781430246626 )
    Cool Vizify Profile at https://www.vizify.com/oz-apps
  • atilimatilim Maintainer
    @bowerandy thank you, that'll help me a lot.

    @OZApps totally agree. I'll do it.
  • @OSApp, @Atilim,

    Sorry, my bad, I did miss some files out. Although only the ones for Corona, I think. I have updated the download so that the missing Corona files are present. You can get it from the same link:

    http://dl.dropbox.com/u/2242074/GiderosTest.zip

    One other question. Is there a way of forcing a GC in Gideros or explicitly unloading textures in the same way I do in Corona? I noticed with some of my previous tests with larger textures that the app would crash towards the end of the first loop, presumably because the iPad is running out of texture memory because old unreferenced textures are not being GC'd in time.

    Does Gideros/Lua using reference counting or a sweeping GC. I presume the latter, but if that is the case there needs to be some way to force a collection or for the GC to respond to a low memory situation. I imagine you already do this but perhaps you are only doing it for low system memory and not low texture memory?

    Best regards
  • atilimatilim Maintainer
    Hi @bowerandy,

    I've downloaded the zip file but it seems animatedsprite.lua is wrong. It refers to the file "../../../../../../../../Applications/Gideros Studio/Examples/Graphics/Texture Pack/animatedsprite.lua"

    Gideros and Lua uses sweeping GC and therefore handle cyclic references without a problem. If a Texture object is not referenced by any other object, I'll be garbage collected with the next GC cycle, or by explicitly calling collectgarbage() function.

    We don't call collectgarbage() internally. But now I'm putting a call to collectgarbage() to the low memory warning function of iOS.

    cheers
  • Sigh. Because I'm using DropBox and it sticks little green check mark overlays over the file icons, I was unable to see that the animatedsprite.lua that I was including was actually an alias (shortcut). Anyway I've updated the download (again) and it should hopefully have the correct file now. Sorry.

    http://dl.dropbox.com/u/2242074/GiderosTest.zip

    Regarding GC. In the meantime, before you've added the collectgarbage() call as a result of the low memory warning, could I add a plugin that just calls lua_gc() explicitly. I assume that will work.

    Best regards
  • atilimatilim Maintainer
    edited March 2012
    Thank you.

    Yes you can:

    1. Add a gc.cpp to Plugins with this content:
    #define HAVE_ENTER_FRAME
    #include "gideros.h"
    #include "lua.h"
    #include "lauxlib.h"
     
    int g_collectGarbage = 0;
     
    static void g_enterFrame(lua_State* L)
    {
    	if (g_collectGarbage)
    	{
    		lua_gc(L, LUA_GCCOLLECT, 0);
    		g_collectGarbage = 0;		
    	}
    }
     
    static void g_initializePlugin(lua_State* L)
    {
    }
     
    static void g_deinitializePlugin(lua_State* L)
    {
    }
     
    REGISTER_PLUGIN("GC", "1.0")
    2. Add these two lines to didReceiveMemoryWarning function of ViewController:
    extern int g_collectGarbage;
    g_collectGarbage = 1;
    cheers
  • atilimatilim Maintainer
    Yes, now GiderosTest.zip is working :)
  • @atilim, thanks for the GC code it works a treat and prevents those nasty memory-out crashes I was seeing!

    Although your solution is better in the general case, I still think that on occasion it might be helpful to be explicitly force a GC. In the case of my "Bouncy" example code, if I force a GC after each new texture load then the memory use is kept low and the times reported are always correct each time around the loop because there is no caching.

    With that in mind, here is my modified version of your GC plugin so in Lua one can use:
    require ("gc")
    ..
    gc.collectGarbage()
    #define HAVE_ENTER_FRAME
    #include "gideros.h"
    #include "lua.h"
    #include "lauxlib.h"
     
    int g_collectGarbage = 0;
     
    static int collectGarbage(lua_State *L)
    {
        lua_gc(L, LUA_GCCOLLECT, 0);
        return 0;
    }
     
    static void g_enterFrame(lua_State* L)
    {
    	if (g_collectGarbage)
    	{
    		collectGarbage(L);
    		g_collectGarbage = 0;		
    	}
    }
     
    static int loader(lua_State *L)
    {
        //This is a list of functions that can be called from Lua
        const luaL_Reg functionlist[] = {
            {"collectGarbage", collectGarbage},
            {NULL, NULL},
        };
        luaL_register(L, "gc", functionlist);
     
        //return the pointer to the plugin
        return 1;
    }
     
    static void g_initializePlugin(lua_State* L)
    {
        lua_getglobal(L, "package");
        lua_getfield(L, -1, "preload");
     
        lua_pushcfunction(L, loader);
        lua_setfield(L, -2, "gc");
     
        lua_pop(L, 2); 
    }
     
    static void g_deinitializePlugin(lua_State* L)
    {
    }
     
    REGISTER_PLUGIN("GC", "1.0")
  • Maybe I don't understand it, but why do you need a plugin for calling the garbadge collector? Why don't use just call it from your LUA script?
  • @MikeHart. That's because I didn't know one could do it! :\"> When I asked above @atilim didn't mention collectgarbage() so I assumed it wasn't possible. Anyway, thanks to your reply I've found it now, so I've replaced my version of that GC plugin code with @atilim's original.
  • I think all standard Lua commands can be called from a Hideros script. Anyway the reason why i had asked was because i thought it was a different way to use the GC.
  • atilimatilim Maintainer
    Hi @bowerandy,

    I've done a one-pass of optimization to the texture loading functions but the loading times remained same. :(
  • Not wishing to start an issue here or anything, but if your experiencing problems like this I'd suggest the problem might be more with your implementation rather than the library calls.

    Obviously I haven't seen your code or anything - but...
    If you have multiple pages of animation, couldn't you just load all the pages at the same time (store a table of page references) and then expand your sprite animation player so that it know's which page the required frame is on and pick that one.

    In my experience the devices can hold plenty of full screen images and the texture state change when uploading the texture to the GPU video memory is pretty fast.

    Failing that you could always use some kind of custom plugin (something which Corona DOESN'T have) to manage the images yourself at the OpenGL level. ???

    Just my $0.02

    Jon...
    WhiteTree Games - Home, home on the web, where the bits and bytes they do play!
    #MakeABetterGame! "Never give up, Never NEVER give up!" - Winston Churchill
Sign In or Register to comment.