将Uint8Array渲染到Canvas的最快方法

时间:2016-10-24 16:16:33

标签: javascript canvas webgl

我收到带有RGBA值的Uint8Array,以及部分图片已更新的部分信息。将原始位图信息渲染回画布的最快方法是什么?我一直在使用2D渲染上下文来手动循环遍历数组并设置像素信息,但这非常慢:

let imageData = new ImageData(1920, 1080);
for (var i=0;i < args.frameBuffer.length; i+=4) {
    imageData.data[i]   = args.frameBuffer[i];
    imageData.data[i+1] = args.frameBuffer[i+1];
    imageData.data[i+2] = args.frameBuffer[i+2];
    imageData.data[i+3] = args.frameBuffer[i+3];
}
context.putImageData(imageData, 0, 0, args.dirtyX, args.dirtyY, args.dirtyWidth, args.dirtyHeight);

什么是更有效的方法呢? WebGL会比2D上下文更高效吗?原生或通过像Three.js这样的东西?

1 个答案:

答案 0 :(得分:0)

您可以使用gl.TexSubImage2D更新纹理,然后将整个纹理blit。但是TexSubImage2D存在性能问题,因为在更新下一帧的纹理之前必须等待先前的渲染才能完成。

更好的方法可以使用webgl的preserveDrawingBuffer位,然后使用TexImage2D以循环方式使用两个纹理。要仅渲染脏区,您需要根据脏坐标为DrawArrays设置顶点数据。

WebGL仍然需要大量复制数据,但这些复制路径可能比putImageData更好地优化。但是webgl可能会更慢,因为驱动程序经常针对纹理图像是静态的并用于多个渲染的用例进行优化。这使得渲染速度更快,但上传纹理数据的速度更慢。

为了解决昂贵的纹理上传问题,OpenGL有多个扩展支持快速图像上传,但渲染速度稍慢。太糟糕了,这些扩展对webgl来说不实用。

但是你的拷贝循环看起来很像你每次都接收整个帧缓冲区,即使它只是部分更新也很难。

如果你能做点什么

let imageData = new ImageData(args.frameBuffer, args.dirtyWidth, args.dirtyHeight);
context.putImageData(imageData, args.dirtyX, args.dirtyY);

当然,您可以使用

重复当前界面
let imageData = new ImageData(args.frameBuffer, 1920, Math.floor((args.frameBuffer.length + 4*1919)/(4*1920)));
context.putImageData(imageData, 0,0, args.dirtyX, args.dirtyY, args.dirtyWidth, args.dirtyHeight);