Unity RenderTexture的保存和多线程

Unity中优化图片保存:避免卡顿与异步解决方案
这篇博客探讨了在Unity中如何避免在保存相机图片时导致的游戏卡顿问题。主要焦点在于EncodeToPNG函数消耗大量CPU资源。作者提出使用AsyncGPUReadbackRequest进行异步处理,但遇到不支持的设备问题。为了解决这个问题,作者提供了另一种方法,即先将RenderTexture转换为Texture2D,然后在多线程中处理GetPixels32和编码。通过这种方式,主线程的卡顿得到了缓解。博客还包含了错误处理和兼容性检查的建议。

在Unity中如果要保存相机图片,我们最常用的方法是首先把RenderTexture转化成Texture2D,如下:

Texture2D texture2D = null;
texture2D = new Texture2D(renderTexture.width, renderTexture.height, TextureFormat.ARGB32, false);
var previous = RenderTexture.active;
RenderTexture.active = renderTexture;
texture2D.ReadPixels(new Rect(0, 0, renderTexture.width, renderTexture.height), 0, 0);
RenderTexture.active = previous;
texture2D.Apply();
void Awake()
{
        targetRenderTexture = new RenderTexture((int)(normalSize), (int)(normalSize), 0 , RenderTextureFormat.ARGB32);//, RenderTextureFormat.Default
        
        int quality = QualitySettings.GetQualityLevel();
        if (quality > 0)
            targetRenderTexture.antiAliasing = 4;
        else
            targetRenderTexture.antiAliasing = 1;
}

RenderTexture创建部分

然后再把图片保存成二进制格式进行保存,如下:

byte[] bytes = txtureSmall.EncodeToPNG();
try
{
	string forder = GetFileDirectory(path);
	bool exists = System.IO.Directory.Exists(forder);
	if (!exists)
		System.IO.Directory.CreateDirectory(forder);

	System.IO.File.WriteAllBytes(path, data);
}
catch (Exception ee)
{
	Debug.Log("文件写入失败:" + path + " Error:" + ee.Message);
}

如果是在保存进度,场景切换等允许卡顿的地方操作是没有问题的。

如果是在不允许有卡顿感的操作中就是大问题了。

经过分析主要来自EncodeToPNG函数,耗费了大量CPU。

那么如果我们让他在别的进程运行,游戏就不会有卡顿感了。

马上进行测试:

		new System.Threading.Thread(() =>
        {
            这里处理上面的逻辑。
            
        }).Start();

发现不能在其他进程里处理RenderTexture,txtureSmall.EncodeToPNG,会报出
…… can only be called from the main thread.

那么怎么做呢,其实Unity提供了其他的方法来做这样事情。

下面做一个记录

		int nw = rendertxture.width;
        int nh = rendertxture.height;
        //filesavepath = filename;
        GraphicsFormat format = rendertxture.graphicsFormat;
        UnityEngine.Rendering.AsyncGPUReadbackRequest request = UnityEngine.Rendering.AsyncGPUReadback.Request(rendertxture, 0 );
        yield return 0;


        while (!request.done)
        {
            yield return new WaitForEndOfFrame();
        }

        if (request.hasError)
        {
            Debug.Log("GPU readback error detected.");
            yield break;
        }

        byte[] array = request.GetData<byte>().ToArray();

        Task.Run(() =>
        {
#if UNITY_EDITOR
            Debug.Log("保存进度图:" + filename);
#endif
            byte[] datas = ImageConversion.EncodeArrayToPNG(array, format, (uint)nw, (uint)nh);
            CFunc.StreamWriter(filename, datas);

        });

这样主线程就不会卡顿了。

经过一段时间测试后发现
AsyncGPUReadbackRequest的函数,可能会出现报错 This GfxDevice does not support asynchronous readback
发现报错了图片也能显示出来。匪夷所思
然后通过SystemInfo.supportsAsyncGPUReadback函数来判断发现也是False,说明不支持,不支持为说明图可以显示出来呢。

所以研究了下,直接可以通过拿出RenderText的Texture2D,获取到GetPixeds32,然后再放入多线程处理。代码如下,可能速度稍微慢一些。

	Texture2D saveToTexture(RenderTexture renderTexture)
    {
        Texture2D texture2D = null;
        texture2D = new Texture2D(renderTexture.width, renderTexture.height, TextureFormat.ARGB32, false);
        var previous = RenderTexture.active;
        RenderTexture.active = renderTexture;
        texture2D.ReadPixels(new Rect(0, 0, renderTexture.width, renderTexture.height), 0, 0);
        RenderTexture.active = previous;
        texture2D.Apply();
        return texture2D;
    }
	
	 		Texture2D txture = saveToTexture(targetRenderTexture);
	 		Color32[] array = txture.GetPixels32();
	 		//这里如果renderTexture不是RenderTextureFormat.ARGB32,在IOS中好像是BGRA,会导致获取保存的颜色变色,原因未知.
            GameObject.DestroyImmediate(txture);
            Task.Run(() =>
            {
#if UNITY_EDITOR
                Debug.Log("保存进度图:" + filename);
#endif
                byte[] bytes = ImageConversion.EncodeArrayToPNG(array, format, (uint)nw, (uint)nh);

                CFunc.StreamWriter(filename, bytes);
                pone.ok = true;



            });

相关技术资料
https://docs.unity3d.com/ScriptReference/ImageConversion.EncodeArrayToPNG.html
https://forum.unity.com/threads/asyncgpureadbackrequest-and-encodetopng.551314/
https://forum.unity.com/threads/using-the-job-system-to-save-images.522837/

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Thinbug

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值