简单而简短的问题,
我从服务器上下载了一个纹理(185MB纹理图集)。但是使用Texture2D.LoadImage
创建字节之外的纹理会阻塞应用程序大约4秒。如何异步创建此纹理?
// Load texture atlas texture data
HttpWebRequest atlasTextureRequest = WebRequest.CreateHttp(EntityDatabase.ATLAS_TEXTURE_PATH);
WebResponse atlasTextureResponse = await atlasTextureRequest.GetResponseAsync();
byte[] atlasTextureData = await EntityDatabase.ReadStreamAsync(
atlasTextureResponse.GetResponseStream(),
(int)atlasTextureResponse.ContentLength
);
EntityDatabase.TextureAtlasTexture = new Texture2D(1, 1);
EntityDatabase.TextureAtlasTexture.LoadImage(atlasTextureData);
发布于 2021-09-11 03:43:16
操作:
如何异步创建此纹理?
简短的回答
更长的答案
通过.NET 4.x为我们带来async/await
支持允许异步I/O和异步计算。一般来说,它们的使用没有障碍,但在Unity 3D 中有,特别是compute。
下面的代码是受I/O限制的,并且是异步I/O的一个例子,Unity不应该对此有任何疑虑。造成问题的部分将是你的纹理创建。继续往下读。
WebResponse atlasTextureResponse = await atlasTextureRequest.GetResponseAsync();
byte[] atlasTextureData = await EntityDatabase.ReadStreamAsync(
atlasTextureResponse.GetResponseStream(),
(int)atlasTextureResponse.ContentLength
);
在Unity中创建纹理是受CPU限制的,并且是计算操作的一个示例。要在后台执行计算操作,通常需要执行以下操作:
var stuff =
await Task.Run (() => ConstructSomethingThatTakesALongTimeSynchonosouly ();
...which很好,但是我们想要创建一个Texture2D
,所以我们可以尝试这样做:
public class LoadTextureAsyncExample : MonoBehaviour
{
// Start runs in the main thread
private async void Start()
{
var textureBytes = await DownloadTextureAsync();
_texture = await Task.Run(() => CreateTexture2D(textureBytes));
}
private async Task<byte[]> DownloadTextureAsync()
{
// essentially your code above.
// This method will be a mixture of main/pool thread
}
private Texture2D CreateTexture2D(byte[] textureBytes)
{
// this method is running in a thread pool thread
var texture = new Texture2D(1, 1);
texture.LoadRawTextureData(textureBytes);
return texture;
}
...now虽然CreateTexture2D
将在线程池线程中运行,并且不会阻塞主线程,但代码将在创建Texture2D
时失败,因为它运行在与主线程不同的线程中。
必须在主线程中创建由C#创建的纹理。您可以尝试设置Texture.allowThreadedTextureCreation = true
,但这也不起作用。Unity在下面说得最好。
统一(我的重点):
Texture.allowThreadedTextureCreation Description允许统一内部在任何线程(而不是专用渲染线程)上执行纹理创建。注意:用于创建纹理的C#应用编程接口(例如,新Texture2D)必须始终在主线程上调用。此设置不会更改该requirement.
发布于 2021-09-11 07:55:29
在Micky's answer上扩展
目前,加载纹理的唯一内置(不需要额外的本机代码)非阻塞方式是使用
Coroutine中的UnityWebRequestTexture.GetTexture
API中的示例
void Start()
{
StartCoroutine(GetText(SOME_URL));
}
IEnumerator DownloadTexture(string url)
{
using (UnityWebRequest uwr = UnityWebRequestTexture.GetTexture(url))
{
yield return uwr.SendWebRequest();
if (uwr.result != UnityWebRequest.Result.Success)
{
Debug.Log(uwr.error);
}
else
{
// Get downloaded asset bundle
var texture = DownloadHandlerTexture.GetContent(uwr);
}
}
}
这也适用于设备上的本地文件路径。
如果不能直接这样做,您仍然可以将临时文件异步下载并存储在您的设备上,然后使用UnityWebRequestTexture.GetTexture
加载本地存储的文件,然后再次删除本地文件。
https://stackoverflow.com/questions/69139083
复制相似问题