c# - 在 C# for Unity 中非阻塞加载和复制大型 Texture2D

标签 c# android performance unity3d texture2d

我正在为 Android 构建一个 Unity 应用程序,它处理动态加载大量大纹理(所有图像的大小都超过 6MB,如 png)。这些纹理可以来自 Amazon S3 服务器,在这种情况下它们作为流到达,也可以来自用户的设备本身。

在这两种情况下,我都能够毫无问题地异步获取原始数据或纹理。在第一个中,我查询服务器并获得数据流回调,在第二个中,我使用 WWW 类通过“file://”协议(protocol)获取纹理。

当我想将此数据复制到 Texture2D 到我可以使用的某个地方时,例如复制到 Texture2D 私有(private)成员上,问题就会发生。

对于流,我将其转换为 byte[] 并尝试调用 LoadImage(),对于 WWW 类,我只是尝试使用 myTexture = www.texture 复制它。两次我都在加载或复制纹理时得到一个巨大的框架。我想消除这个框架,因为该应用程序无法随附它。

using (var stream = responseStream)
{
   byte[] myBinary = ToByteArray(stream);
   m_myTexture.LoadImage(myBinary);  // Commenting this line removes frame out
}

...

WWW www = new WWW("file://" + filePath);
yield return www;
m_myTexture = www.texture;  // Commenting this line removes frame out

不幸的是,Unity 似乎不喜欢在与主线程不同的线程上运行这些操作,因此当我尝试时会抛出异常。

是否有任何方法可以将这些操作分块以便它需要多个帧?或者做一些不会拖延主线程的快速内存复制操作?

提前致谢!

PS:我在以下存储库中创建了一个问题的工作示例:https://github.com/NeoSouldier/Texture2DTest/

最佳答案

www.texture已知在下载大型纹理时会导致打嗝。

你应该尝试的事情:

1。使用WWW 的 LoadImageIntoTexture函数用下载数据中的图像替换现有 Texture2D 的内容。如果问题仍未解决,请继续阅读。

WWW www = new WWW("file://" + filePath);
yield return www;
///////m_myTexture = www.texture;  // Commenting this line removes frame out
www.LoadImageIntoTexture(m_myTexture);

2。使用www.textureNonReadable变量

使用 www.textureNonReadable而不是 www.texture还可以加快您的加载时间。我时常看到这种情况发生。

3.使用函数Graphics.CopyTexture从一个纹理复制到另一个。这应该很快。如果问题仍然没有解决,请继续阅读。

//Create new Empty texture with size that matches source info
m_myTexture = new Texture2D(www.texture.width, www.texture.height, www.texture.format, false);
Graphics.CopyTexture(www.texture, m_myTexture);

4.使用Unity的UnityWebRequest应用程序接口(interface)。这取代了 WWW类(class)。您必须拥有 Unity 5.2 及更高版本才能使用它。它有 GetTexture为下载纹理而优化的函数。

using (UnityWebRequest www = UnityWebRequest.GetTexture("http://www.my-server.com/image.png"))
{
    yield return www.Send();
    if (www.isError)
    {
        Debug.Log(www.error);
    }
    else
    {
        m_myTexture = DownloadHandlerTexture.GetContent(www);
    }
}

如果上述三个选项都没有解决卡住问题,另一种解决方案是使用 GetPixel 在协程函数中一个一个地复制像素。和 SetPixel功能。您添加一个计数器并设置您希望它等待的时间。随着时间的推移,它间隔纹理复制。

5。使用 GetPixel 逐一复制 Texture2D 像素和 SetPixel功能。示例代码包括来自 Nasa 的 8K 纹理,用于测试目的。复制 Texture 时不会阻塞。如果是,请减小 copyTextureAsync 函数中的 LOOP_TO_WAIT 变量的值。您还可以选择提供一个函数,该函数将在完成复制 Texture 时调用。

public Texture2D m_myTexture;

void Start()
{
    //Application.runInBackground = true;
    StartCoroutine(downloadTexture());
}

IEnumerator downloadTexture()
{
    //http://visibleearth.nasa.gov/view.php?id=79793
    //http://eoimages.gsfc.nasa.gov/images/imagerecords/79000/79793/city_lights_africa_8k.jpg

    string url = "http://eoimages.gsfc.nasa.gov/images/imagerecords/79000/79793/city_lights_africa_8k.jpg";
    //WWW www = new WWW("file://" + filePath);
    WWW www = new WWW(url);
    yield return www;

    //m_myTexture = www.texture;  // Commenting this line removes frame out

    Debug.Log("Downloaded Texture. Now copying it");

    //Copy Texture to m_myTexture WITHOUT callback function
    //StartCoroutine(copyTextureAsync(www.texture));

    //Copy Texture to m_myTexture WITH callback function
    StartCoroutine(copyTextureAsync(www.texture, false, finishedCopying));
}


IEnumerator copyTextureAsync(Texture2D source, bool useMipMap = false, System.Action callBack = null)
{

    const int LOOP_TO_WAIT = 400000; //Waits every 400,000 loop, Reduce this if still freezing
    int loopCounter = 0;

    int heightSize = source.height;
    int widthSize = source.width;

    //Create new Empty texture with size that matches source info
    m_myTexture = new Texture2D(widthSize, heightSize, source.format, useMipMap);

    for (int y = 0; y < heightSize; y++)
    {
        for (int x = 0; x < widthSize; x++)
        {
            //Get color/pixel at x,y pixel from source Texture
            Color tempSourceColor = source.GetPixel(x, y);

            //Set color/pixel at x,y pixel to destintaion Texture
            m_myTexture.SetPixel(x, y, tempSourceColor);

            loopCounter++;

            if (loopCounter % LOOP_TO_WAIT == 0)
            {
                //Debug.Log("Copying");
                yield return null; //Wait after every LOOP_TO_WAIT 
            }
        }
    }
    //Apply changes to the Texture
    m_myTexture.Apply();

    //Let our optional callback function know that we've done copying Texture
    if (callBack != null)
    {
        callBack.Invoke();
    }
}

void finishedCopying()
{
    Debug.Log("Finished Copying Texture");
    //Do something else
}

关于c# - 在 C# for Unity 中非阻塞加载和复制大型 Texture2D,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40450867/

相关文章:

android - Flutter:WAITING另一个flutter命令释放启动锁

java - 为什么 ArrayList 比 java 中的 HashSet 快?

c++ - 是否有理由不使用链接时间优化 (LTO)?

c# - xamarin.android visual studio 2015 中的电子邮件地址验证编码?

c# - 实例方法如何转换为委托(delegate)?

c# - 我如何在 C# 中表示像 1/3 这样的分数?

java - 显示货币格式问题

c# - VB6 使用.Net dll。无法读取 app.config

java - 在字符串 "},"之后添加新行

java - Java 中的垃圾收集器和性能下降