Art*_*hur 9 c# performance android texture2d unity-game-engine
我正在为Android构建一个Unity应用程序,它可以动态加载大量的大纹理(所有图像的大小都超过6MB).这些纹理可以来自Amazon S3服务器,在这种情况下它们以流的形式出现,或者来自用户的设备本身.
在这两种情况下,我都可以异步地保持原始数据或纹理而不会出现问题.在第一个我查询服务器并获得带有数据流的回调,在第二个我使用WWW类来获取使用"file://"协议的纹理.
一旦我想将这些数据复制到Texture2D到我可以使用的某个地方,例如在Texture2D私有成员上,问题就会发生.
使用流我将其转换为byte []并尝试调用LoadImage(),而使用WWW类我只需尝试使用myTexture = www.texture进行复制.在纹理加载或复制时,我都会得到一个巨大的帧.我想彻底根除这个框架,因为App简直无法发布.
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
Run Code Online (Sandbox Code Playgroud)
不幸的是,Unity似乎不喜欢在主线程的单独线程上运行这些操作,并在我尝试时抛出异常.
有没有什么方法可以将这些操作分块,以便需要多个帧?或者做一些不会拖延主线程的快速memcopy操作?
提前致谢!
PS:我在以下回购中创建了一个问题的工作示例:https://github.com/NeoSouldier/Texture2DTest/
Pro*_*mer 11
在www.texture被称为当大纹理下载到引起打嗝.
你应该尝试的事情:
1.使用从下载的数据中WWW's 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);
Run Code Online (Sandbox Code Playgroud)
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);
Run Code Online (Sandbox Code Playgroud)
4,使用 Unity的UnityWebRequestAPI.这取代了WWW班级.您必须拥有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);
}
}
Run Code Online (Sandbox Code Playgroud)
如果上面的三个选项没有解决冻结问题,另一个解决方案是在带有GetPixel和SetPixel函数的协程函数中逐个复制像素.您需要添加计数器并设置等待时间.它随时间间隔纹理复制.
5.Texture2D使用GetPixel和SetPixel功能逐个复制像素.示例代码包括来自Nasa的8K纹理以用于测试目的.复制时不会阻止Texture.如果是,则减少函数中LOOP_TO_WAIT变量的值copyTextureAsync.您还可以选择提供在复制完成后调用的函数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
}
Run Code Online (Sandbox Code Playgroud)
最终,通过创建一个 C++ 插件(通过 Android Studio 2.2 构建)解决了这个问题,该插件使用“stb_image.h”来加载图像,并使用 OpenGL 来生成纹理并将一组扫描线映射到多个帧上的纹理上。然后通过Texture2D.CreateExternalTexture()将纹理交给Unity。
此方法不会使工作异步,而是将加载成本分散到多个帧上,从而删除同步块和后续帧。
我无法使纹理创建异步,因为为了使 OpenGL 函数正常工作,您需要从 Unity 的主渲染线程运行代码,因此必须通过 GL.IssuePluginEvent() 调用函数 - Unity 的文档使用以下项目解释了如何使用此功能:https://bitbucket.org/Unity-Technologies/graphicsdemos/
我已经清理了我正在处理的测试存储库,并在自述文件中编写了说明,以便尽可能轻松地理解我得出的最终解决方案。我希望它在某个时候对某人有用,并且他们不必像我一样花很长时间来解决这个问题!https://github.com/NeoSouldier/Texture2DTest/