How can I generate mipmaps for a texture loaded from UnityWebRequest at runtime? Please note while I use ‘www’ as a reference, it is not using the WWW class.
I’m currently loading a texture from a UnityWebRequest using this code:
If that prints 0 into the log, then the Texture2D that GetContent is creating does not have mip maps enabled, so nothing you do after that will make it work as there’s no way to enable mipmaps post creation of the Texture2D object. You’ll likely need to do something like this:
Texture2D wwwTex = DownloadHandlerTexture.GetContent(www);
Texture2D newTex = new Texture2D(wwwTex.width, wwwTex.height);
newTex.SetPixels(wwwTex.GetPixels(0));
newTex.Apply(); // default is to update mipmaps
You could also use newTex.Resize(wwwTex.width, wwwTex.height); and reuse the “newTex” Texture2D rather than creating a new one every time.
Ok, whoa. Your blowing my mind right now. I went with a compute shader because it performed way better than getpixel and setpixel apply methods - but if what you say is true, then compatibility would be way better across more devices. I’m going to experiment with this, so thanks so much for sharing!
@bgolus Am I correct that the DownloadHanderTexture only supports jpg and png and therefore does not support mipmaps at all?
Which in case of low performance devices is really bad because LoadImage has a huge impact on performance and using it continuously is not an option.
So in essence, there is no way you get a mipmapped texture out of a download unless you spend CPU on it, am I right?
Yes, you need to copy all mips to the final texture and do not call Apply after that on such texture. Here is a quote from Texture2D.Apply() API reference:
No, it’s not something you should be using continuously even on desktop. It’s something to use every so often to load a texture once. After that you use the Texture2D asset it creates. You don’t need to call it every frame to use a single if that’s what you’re thinking. Also decompressing jpg and png files is surprisingly slow, even on high end PCs. These are formats that were designed around reducing the file size as much as possible, not necessarily for real time usage.
If you’re looking to load .jpg or .png files from a web address, yes. You can pack all of the mipmaps into a single texture, like this example from the wikipedia article on mipmaps.
But you’d still need to use Graphics.CopyTexture() to copy the sections of the image that are for each mip into the new texture. Similar to the examples from @codestage but using a single texture rather than one for each mip.
However I think the real answer you might be looking for is don’t use DownloadHanderTexture at all. Use DownloadHandlerAssetBundle and create asset bundles with pre-compressed texture asset with mips already in it. That’ll be far more efficient to load.
@bgolus Thanks for your awesome answer. I thought so. It’s actually not that easy Right now we are faking sharing a browser screen from one device to the others by reading its texture continuously and sending them over the internet. the clients basically start loading the next just after the previous image was finished. This is totally insane, I know, but for now its fine until we have a real streaming solution.
avoid using GetPixels() or even the untemplated textureSource.GetRawTextureData() because these will create a copy first, potentially doing a conversion from Color32 to Color, before you then copy that again into the destination with SetPixels/LoadRawTextureData(). Using the approach above is 20x faster on the CPU than calling SetPixels(GetPixels) on each mip.
So if you have a texture without mipmaps, you can copy it on the CPU, then Apply(true) to copy it to the GPU resource and generate the mipmaps at the same time.
//texture with mips
textureDestination = new Texture2D(width,height, format, true);
//copy on the CPU
textureDestination.LoadRawTextureData(
textureSource.GetRawTextureData<byte>())
//copy to GPU and generate mips
textureDestination.Apply(true)
If you will not change the texture anymore then you can use
textureSource.Apply(true,true)
This will make the texture unreadable on the CPU and remove the CPU copy (and memory usage) after it is uploaded/copied to video memory.
Graphics.CopyTexture() is only meant to copy between the textures’ GPU resources. It is not intended to keep the CPU copies in sync. Apply should indeed not be used on a destination texture after you call Graphics.CopyTexture().
Hi, I’m trying to use this approach to create a texture with mipmaps at runtime, but when I call the LoadRawTextureData method I’ve the following error.
UnityException: LoadRawTextureData: not enough data provided (will result in overread).
I guess that’s due to the different size of textures, the destination one having room for mipmaps, the source one not.
Moreover, shouldn’t the Apply method be called on the destination texture rather than on the source, like this?
//copy to GPU and generate mips
textureDestination.Apply(true)
Anyway, the problem is the error on the LoadRawTextureData, have you some hints about it?
Hi Riccardo,
you are right, I had a typo, the last Apply should indeed be called on the destination texture to upload the new content to the GPU. And you are also right that this only works if the source and destination have exactly the same raw data size. So it won’t work if the mips or dimensions are different. It’s probably possible to get a sub array and load that one in the very specific case that the destination has less mipmaps.
In fact, it’s the source the one with no mipmaps, not the destination. Anyway I tried to get the subarray and load it into the destination texture like this:
//texture with mips
textureDestination = new Texture2D(width,height, format, true);
//copy on the CPU
textureDestination.LoadRawTextureData(textureSource.GetRawTextureData<byte>().GetSubArray(0, width*height));
//copy to GPU and generate mips
textureDestination.Apply(true);
But I get the same error as before:
UnityException: LoadRawTextureData: not enough data provided (will result in overread).
So far, of all the proposed methods, the only one that worked for me is:
It would be great to have a solid, fast and smooth way to create a texture with mipmaps at runtime, that’s a really important feature for a game engine.