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.
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.
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.
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!
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!
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 ?
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.
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!