Unity3D Texture2D.LoadRawTextureData causing massive memory leak? - unity3d

In one application (Server), I am capturing the display:
public byte[] GetFrame()
{
int width = Screen.width;
int height = Screen.height;
Texture2D tex = new Texture2D(width, height, TextureFormat.RGB24, false);
tex.ReadPixels(new Rect(0, 0, width, height), 0, 0);
byte[] bytes = tex.GetRawTextureData();
Destroy(tex);
return bytes;
}
I send this frame over the network to another application (Client), which loads the received frame and puts it as texture on a plane or whatever (some other 3D primitive):
void DisplayFrame(byte[] frame)
{
Texture2D texture = new Texture2D(Screen.width, Screen.height, TextureFormat.RGB24, false);
texture.LoadRawTextureData(frame);
texture.Apply();
GetComponent<Renderer>().material.mainTexture = texture;
ScreenUpdate = true;
Destroy(texture);
}
This works and the image is displayed as I expected. However, when watching the RAM, I notice that the Client RAM goes nuts... over 8GB.
The interesting thing is that if I set frame = null; after calling Destroy(texture); in DisplayFrame on the Client, the user only sees a black screen.
I don't understand what is happening here... It's as if the frame is staying in memory and each new received frame just increases the memory used.
Does anyone have any ideas?

It turns out that if I move the Destroy(texture); from the bottom of DisplayFrame(byte[] frame) to the top of the function, the memory leak is resolved.
It's as if Destroy is being blocked because the texture is still being applied to the material when the next iteration comes around. I'm not clear on the issue here, so if someone with more knowledge could chip in, that would be great.

Related

I want to implement the automatic capture function that shows the game view in Unity

I'm using this as a capture.
ScreenCapture.CaptureScreenshot($"Screenshot{_shotIndex}.png", size);
But the CpatureScreenshot() is too slow.
So I tried different scripts but failed. One of them is that I succeeded in capturing the scene view.
public void TakeTransparentScreenshot(Camera cam, int width, int height, string savePath)
{
// Depending on your render pipeline, this may not work.
var bak_cam_targetTexture = cam.targetTexture;
var bak_cam_clearFlags = cam.clearFlags;
var bak_RenderTexture_active = RenderTexture.active;
var tex_transparent = new Texture2D(width, height, TextureFormat.ARGB32, false);
// Must use 24-bit depth buffer to be able to fill background.
var render_texture = RenderTexture.GetTemporary(width, height, 24, RenderTextureFormat.ARGB32);
var grab_area = new Rect(0, 0, width, height);
RenderTexture.active = render_texture;
cam.targetTexture = render_texture;
cam.clearFlags = CameraClearFlags.SolidColor;
// Simple: use a clear background
cam.backgroundColor = Color.clear;
cam.Render();
tex_transparent.ReadPixels(grab_area, 0, 0);
tex_transparent.Apply();
// Encode the resulting output texture to a byte array then write to the file
byte[] pngShot = ImageConversion.EncodeToPNG(tex_transparent);
File.WriteAllBytes(savePath, pngShot);
cam.clearFlags = bak_cam_clearFlags;
cam.targetTexture = bak_cam_targetTexture;
RenderTexture.active = bak_RenderTexture_active;
RenderTexture.ReleaseTemporary(render_texture);
Texture2D.Destroy(tex_transparent);
}
How do I capture a screenshot in Unity3d with a transparent background?
I need to capture the game view, but this script does the scene view.
Inevitably I tried to implement a feature that was automatically captured using CaptureScreenshot() and Coroutine. it was too slow to work.

Loading image from Bytes into Texture2D generates low quality results

I'm using Unity and using an Image object to display an image by loading its bytes (coming from a JSON request) in a Texture2D object, but the resulting image is blurry and pixelated, in a very low quality. This is the code:
Texture2D myTexture = new Texture2D(2, 2, TextureFormat.ARGB32, false);
myTexture.filterMode = FilterMode.Point;
myTexture.LoadImage(Bytes);
myImage.GetComponent<RawImage>().texture = myTexture;
And the output looks like this:
Any idea on how to improve the quality on this? There should be a way to make it look better, if I import an asset into Unity (image) and set it with filter mode Point, it actually looks pretty good, but in this case, it just makes it worse. The original image is pretty detailed:
Try with image exact width and hight in
Texture2D myTexture = new Texture2D(width, height, TextureFormat.ARGB32, false);
or
Texture2D myTexture = new Texture2D(1, 1, TextureFormat.ARGB32, false);

Unity Serialize and Encoding sprite

I'm trying to download and save file, on android device. It's works fine on PC, but i have a visual bug at my android phone. Look at screen please
My code:
It's how i download and serialize it
Icon = Sprite.Create(texture2dd, new Rect(0.0f, 0.0f, texture2dd.width, texture2dd.height), new Vector2(0.5f, 0.5f), 100.0f);
byte[] texturebytes = Icon.texture.GetRawTextureData();
File.WriteAllText(Application.persistentDataPath + "/icon", Encoding.Default.GetString(texturebytes));
File.WriteAllText(Application.persistentDataPath + "/iconinfo", Icon.texture.width + "###" + Icon.texture.height);
And thi is how I try to load it later:
string[] info = File.ReadAllText(path + "info").Split(new string[] { "###" }, StringSplitOptions.None);
int width, height;
int.TryParse(info[0], out width);
int.TryParse(info[1], out height);
byte[] bytesIcon = Encoding.Default.GetBytes(File.ReadAllText(path));
Texture2D iconText = new Texture2D(width, height, TextureFormat.ARGB32, false);
iconText.LoadRawTextureData(bytesIcon);
iconText.Apply();
return Sprite.Create(iconText, new Rect(0, 0, width, height), new Vector2(0.5f, 0.5f));
I think problem in Encoding type, but i tryed all Encoding types, and it's still don't work, and load some bug-texture.
Instead of using GetRawTextureData() and LoadRawTextureData you should save it actually as a .png or .jpg format! The "RawTextureData" is very huge compared to the pure .jpg or .png file data.
Instead use EncodeToPNG (or EncodeToJPG if the quality is not that important - than remember to also adopt the file ending) and LoadImage.
Additionally LoadImage actually "knows" the image size (because it is encoded into the png or jpg file) so there is no need for your iconinfo file at all!
Something like
// ...
Icon = Sprite.Create(texture2dd, new Rect(0.0f, 0.0f, texture2dd.width, texture2dd.height), Vector2.one * 0.5f, 100.0f);
byte[] texturebytes = Icon.texture.EncodeToPNG();
File.WriteAllText(Application.persistentDataPath + "/icon.png", Encoding.Default.GetString(texturebytes));
// ...
(Maybe also checkout this answer for other ways to write the file.)
and
// ...
byte[] bytesIcon = Encoding.Default.GetBytes(File.ReadAllText(path));
// as the example in the documentation states:
// Texture size does not matter, since
// LoadImage will replace it with incoming image size.
Texture2D iconText = new Texture2D(2, 2);
iconText.LoadImage(bytesIcon);
// Also from the documentation:
// Texture will be uploaded to the GPU automatically; there's no need to call Apply.
return Sprite.Create(iconText, new Rect(0, 0, iconText.width, iconText.height), Vector2.one * 0.5f);
And yes another issue might still be that you used Encoding.Default (see here) so maybe you should also use a fixed encoding like Encoding.UTF8.
Though for loading the file I would actually prefere to use a UnityWebRequest which can be also used for a file from the local filestorage!

Unity texture from disk has low resolution

I am trying to load a texture(and create a sprite from it eventually) from disk but sprite renders as low resolution image.
What I am doing:
-> Download the image from url. Once the image is downloaded, I save the texture as png to disk so that next time it doesn't requires a download.
WWW www = new WWW(url);
yield return www;
if (www.isDone)
{
if (string.IsNullOrEmpty(www.error))
{
Sprite img = Sprite.Create(www.texture, new Rect(0, 0, www.texture.width, www.texture.height), new Vector2(0, 0));
reward.RewardSprite = img;
byte[] bytes = www.texture.EncodeToPNG();
FileManager.SaveRewardImage(reward.rewardId, bytes);
}
else
{
Debug.Log(www.error);
}
}
-> Load from disk
string path = string.Format("Cache\\Venue\\{0}", nameWithoutExtension);
return Resources.Load<Texture2D>(path);
The first time when the texture loads from url, its resolution seems fine(because its the original one). When it loads from cache, it attenuates to a lower one.
Can someone tell me what am I missing, or even if there is way around it?
Thanks in advance.
You can overload your texture in your sprite creation in the same way that you do with the rectangle and specify in TextureFormat the format you need:
Sprite img = Sprite.Create(
new Texture2D (www.texture.width, www.texture.height, TextureFormat format, bool mipmap),
new Rect(0, 0, www.texture.width, www.texture.height), new Vector2(0, 0));

Scale a PNG in Unity5? - Bountie

Surprisingly in Unity, for years the only way to simply scale an actual PNG is to use the very awesome library http://wiki.unity3d.com/index.php/TextureScale
Example below
How do you scale a PNG using Unity5 functions? There must be a way now with new UI and so on.
So, scaling actual pixels (such as in Color[]) or literally a PNG file, perhaps downloaded from the net.
(BTW if you're new to Unity, the Resize call is unrelated. It merely changes the size of an array.)
public WebCamTexture wct;
public void UseFamousLibraryToScale()
{
// take the photo. scale down to 256
// also crop to a central-square
WebCamTexture wct;
int oldW = wct.width; // NOTE example code assumes wider than high
int oldH = wct.height;
Texture2D photo = new Texture2D(oldW, oldH,
TextureFormat.ARGB32, false);
//consider WaitForEndOfFrame() before GetPixels
photo.SetPixels( 0,0,oldW,oldH, wct.GetPixels() );
photo.Apply();
int newH = 256;
int newW = Mathf.FloorToInt(
((float)newH/(float)oldH) * oldW );
// use a famous Unity library to scale
TextureScale.Bilinear(photo, newW,newH);
// crop to central square 256.256
int startAcross = (newW - 256)/2;
Color[] pix = photo.GetPixels(startAcross,0, 256,256);
photo = new Texture2D(256,256, TextureFormat.ARGB32, false);
photo.SetPixels(pix);
photo.Apply();
demoImage.texture = photo;
// consider WriteAllBytes(
// Application.persistentDataPath+"p.png",
// photo.EncodeToPNG()); etc
}
Just BTW it occurs to me I'm probably only talking about scaling down here (as you often have to do to post an image, create something on the fly or whatever.) I guess, there would not often be a need to scale up in size an image; it's pointless quality-wise.
If you're okay with stretch-scaling, actually there's simpler way by using a temporary RenderTexture and Graphics.Blit. If you need it to be Texture2D, swapping RenderTexture.active temporarily and read its pixels to Texture2D should do the trick. For example:
public Texture2D ScaleTexture(Texture src, int width, int height){
RenderTexture rt = RenderTexture.GetTemporary(width, height);
Graphics.Blit(src, rt);
RenderTexture currentActiveRT = RenderTexture.active;
RenderTexture.active = rt;
Texture2D tex = new Texture2D(rt.width,rt.height);
tex.ReadPixels(new Rect(0, 0, tex.width, tex.height), 0, 0);
tex.Apply();
RenderTexture.ReleaseTemporary(rt);
RenderTexture.active = currentActiveRT;
return tex;
}