如何解决上传大 JPEG 到 WebGL 纹理而不停止主线程
我正在从服务器下载 JPEG 图像,我想将它们加载到 GPU 纹理中。如何在不停止主线程的情况下执行此操作?
我的代码如下:
- 使用 HTMLImageElement 下载 JPEG
- 使用 decode() 回调来确保在后台进行尽可能多的 JPEG 解码
- 创建一个画布,并将图像绘制到画布中
- 使用 getimageData() 从画布中提取 RGBA 缓冲区
- 使用 glTexImage2D 上传 RGBA 缓冲区(我使用的是 emscripten)
对于大纹理(在本例中为 5184 x 3456),纹理上传相对较快(例如 20 毫秒),但 getimageData() 非常慢(例如 400 毫秒)。
解决方法
以下解决方案适用于 Chrome。其他浏览器要么缺少 OffscreenCanvas,要么它们的 OffscreenCanvas 不支持“2d”上下文(至少在 2021 年 7 月 3 日是这样)。
您可以使用 Web Workers 解码 JPEG 并获取 ImageData 对象,这样主线程上唯一剩下的工作就是通过 glTexImage2D 上传到 GPU。
Web Worker 代码如下所示:
onmessage = function (ev) {
let data = ev.data;
if (data.type == "downloadBitmap") {
fetch(data.url)
.then(response => response.blob())
.then(blob => createImageBitmap(blob))
.then(bmp => {
let canvas = new OffscreenCanvas(bmp.width,bmp.height);
let cx = canvas.getContext('2d');
cx.drawImage(bmp,0);
let imgData = cx.getImageData(0,bmp.width,bmp.height);
postMessage({ type: "downloadBitmap",url: data.url,imgData: imgData });
},err => {
postMessage({ type: "downloadBitmap",error: err });
});
}
}
如果您使用的是 emscripten,那么不幸的是,您需要在调用 glTexImage2D 之前将 ImageData 复制到 emscripten 堆中。但是,如果您有幸使用 Javascript webgl 界面,那么您可以上传 ImageData 对象而无需此额外副本。
有趣的一点是,理论上您可以直接从 ImageBitmap 转到 WebGL 纹理,但是在我的测试中,这仍然会导致主线程出现大量暂停。所以将ImageBitmap绘制到canvas中,然后使用getImageData()提取RGBA缓冲区的过程确实是很有必要的。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。