Search Unity

Coroutine Texture Loading

Discussion in 'Scripting' started by Frieza, Oct 1, 2009.

  1. Frieza

    Frieza

    Joined:
    Aug 29, 2008
    Posts:
    68
    Hi Guys,

    I'm currently working on a program that requires images to be loaded off of a webserver on our LAN (don't ask).

    What I have the program do is load very low resolution versions of these images via the WWW class first, and if you happen to step close to the relevant texture it will load a high resolution version of the texture.

    Now, this functionally works fine except that when loading the high resolution texture the entire program freezes for a second as it does so.

    I am calling my function like so:

    Code (csharp):
    1.  
    2. StartCoroutine("DoImageLoad");
    3.  
    and the function itself looks like this:

    Code (csharp):
    1.  
    2. IEnumerator DoImageLoad()
    3. {
    4.     WWW txTexture = new WWW(m_szTexturePath);
    5.     yield return txTexture;
    6.     renderer.material.mainTexture = txTexture.texture;
    7.     m_bHighResTexLoaded = true;
    8. }
    9.  
    Any idea how I can get around the texture load stage from blocking the rest of the program running? I was hoping that it would load in the background (via coroutine) and not block.

    Thanks in advance!
     
  2. Dreamora

    Dreamora

    Joined:
    Apr 5, 2008
    Posts:
    26,601
    you don't load anything in the background through coroutines.

    coroutines run in the same thread as the engine (anything that accesses the unity engine does, the engine is not thread safe)

    all you do is run the code asyncronous.

    the locking is a consequence of the loading of the texture ... how massiv is the texture that you are able to block it for so long?
    Did you consider using asset bundles instead where the texture could be stored compressed?

    if nothing else helps you can still store the texture data as pure binary or just parse the image yourself and build the texture progressively.
     
  3. Frieza

    Frieza

    Joined:
    Aug 29, 2008
    Posts:
    68
    Ahh right thanks for that. Hmmm it's a bit bad that it's not really threadsafe. The images are pretty huge (but no bigger than 2048x2048).

    I might be able to do it with threads anyhow if I'm pretty careful with what I do, it'll need a lot of testing I guess.

    Reducing the quality of the textures is not an option (when you're close, you need insane detail) although making my own method of texture loading may be an option.

    The problem with this program is that it is actually dynamic as it runs, it's basically an art gallery (in stereoscopic -- runs on a massive screen here) with high resolution artwork on the walls (and sculptures here and there) which is all content managed from a webserver.

    People can plonk in a new item whenever they wish, and of course if people want to get right in close they are able to.

    Functionally it all works, but I'm just trying to overcome the lag of the texture loading. (Obviously, it must have time to load, but I'dd rather show the low res until the high res has fully loaded without the program freezing).

    I'll attempt something with threads first, and if that doesn't work then do manual loading. Thanks!
     
  4. Frieza

    Frieza

    Joined:
    Aug 29, 2008
    Posts:
    68
    Managed to REDUCE my Lag a bit, but still not completely gone. I'm now downloading the data via threading, and once it is done then assigning the texturue to the main material in the main thread.

    However, it is extremely unsafe to do the actual assigning of texture to material in another thread (can crash Unity3d/the game) so I must do the texture assignment while in the main thread (or figure out how to 'threadsafe' this bit myself).

    There still remains some lag when assigning a texture to the material itself which confuses me a little, I am assuming a bunch of texture data is being copied around rather than just being pointed at. I might try assigning a texture to a seperate material in a new thread and then assigning that material to the renderer in the main thread instead, not sure if this will make any difference or not.

    Edit: Scrap that, you positively cannot assign a texture to ANY material in a seperate thread. Hmmmm!
     
  5. extragotcha

    extragotcha

    Joined:
    Mar 20, 2009
    Posts:
    42
    This is a most interesting thread to me as I work in visualization and often need to load large images at runtime. Is there any way to yield the Apply() which is what currently freezes/kills my framerate ?

    How cool would Texture2D.isApplied() be for us, viz lovers, huh ?

    It sure would make my 2010 year heavenly :)
     
  6. Dreamora

    Dreamora

    Joined:
    Apr 5, 2008
    Posts:
    26,601
    Apply actually is always blocking as apply includes the upload to the graphic card which stalls the pipeline.

    If you want fluent framerate avoid apply as much as possible or restrict it to realistic sizes unless you can afford hardware that can handle it fast enough.
    Alternatively, see if you can replicate your current cpu driven code through shaders to prevent the download - change - reupload completely.
     
  7. extragotcha

    extragotcha

    Joined:
    Mar 20, 2009
    Posts:
    42
    dreamora, thank you very much for your reply.

    I'm left with simple question :

    Is there any way of getting a large texture loaded at runtime (not from assetBundle) without freezing the framerate at some point ?

    Thanks again.
     
  8. Gabriel-Lanzer-Kannenberg

    Gabriel-Lanzer-Kannenberg

    Joined:
    Sep 1, 2014
    Posts:
    1
    For those who may ever stumble in this problem once more. I have two things to say: You could do it with threads or with Asset Bundles. Let me, quickly, explain this:

    - Asset Bundles can be built from inside Unity. They can contain assets which can be loaded in-game at realtime and async. Thing is, they must be pre-processed in Unity, that actually compiles and serializes everything in a AssetBundle package. So the images must be processed and then can be used.

    - Thread loading is the go. But you may have tried that and realized you cannot perform Apply on any Texture2D from outside of the main thread. What I have done is: To used an external library to perform the allocation of the image in memory (used FreeImage for that). Then take chunks of that image and create small "Texture2Ds", applying one of them at the time (to avoid spikes on the processing - I have done so with 256x256 images). Then, for each one of them, use the Graphics.Blit to copy it to a bigger Render Texture (you will have to make a blit shader yourself and, taking in account the difference of size from the fragment image to the original image, you must use a mask to override only a piece of the texture as the shader will wrap the UVs for each fragment).

    You could also use something like Granite SDK... But it ends up doing a lot more stuff and the images must be processed in the editor just as with the Asset Bundles.

    I have posted a tweet with a GIF of the thread method in action.

    Please feel free to comment other methods as well!
     
    WildStyle69 likes this.