问题描述
当前,使用texImage2d
上传大型4096x4096纹理非常慢,在将纹理发送到GPU时锁定了主线程,最终导致结结.
Currently, uploading large 4096x4096 textures using texImage2d
is quite slow, locking the main thread while the texture is sent to the GPU and ultimately causing stuttering.
据我了解,WebGL2能够使用PBO(像素缓冲对象)以更有效的方式在GPU上创建纹理.但是,我无法在网上找到任何有关此操作的示例.
From what I've read, WebGL2 has the ability to use PBO's (Pixel Buffer Objects) to create a texture on the GPU in a more efficient manner. However, I am unable to find any examples online of how to do so.
我在 OpenGL 中找到了有关如何实现此目标的很好的描述,但是不确定如何继续使用WebGL API.
I have found a good description of how to achieve this in OpenGL, but am unsure how to proceed using the WebGL API.
我想使用Canvas
或ImageBitmap
作为纹理数据的源.
I would like to use either a Canvas
or an ImageBitmap
as the source for the texture data.
到目前为止,我正在通过将纹理绘制到画布上,然后使用canvas.toBlob()
,然后是FileReader
和readAsArrayBuffer
将图像转换为arrayBuffer
进行测试.然后,一旦我实际上有了一个有效的缓冲区,便尝试创建PBO并上载它.
So far I am testing by drawing the texture to a canvas, then converting the image to an arrayBuffer
using canvas.toBlob()
followed by FileReader
and readAsArrayBuffer
. Then once I actually have a valid buffer, I attempt to create the PBO and upload it.
我的代码的相关部分如下所示:
The relevant part of my code looks like this:
var buf = gl.createBuffer();
var view = new Uint8Array(ArrayBuffer);
gl.bindBuffer(gl.PIXEL_UNPACK_BUFFER, buf);
gl.bufferData(gl.PIXEL_UNPACK_BUFFER, view, gl.STATIC_DRAW);
gl.texImage2D(gl.TEXTURE_2D, 0, this.format, this.width, this.height, 0, this.format, this.type, 0);
但这会返回错误:
GL_INVALID_OPERATION : glTexImage2D: pixel unpack buffer is not large enough
我真的不知道我是否能够正确地使用它,因此任何帮助将不胜感激.
I really have no idea if i'm even approaching it correctly, so any help would be greatly appreciated.
推荐答案
我可能是错的,但是如果WebGL中用于上传数据的PBO的速度比texImage2D
快,我会感到惊讶. PBO本身存在于另一个过程中.要使您的数据进入该进程,需要使用gl.bufferData
将数据从JavaScript进程复制到GPU进程.对于两种方法,在后台复制都是相同的.
I could be wrong but I'd be surprised if PBOs in WebGL for uploading data are any faster than texImage2D
. The PBO itself exists in another process. To get your data to that process requires copying data from the JavaScript process to the GPU process using gl.bufferData
. Behind the scenes that copy is the same for both methods.
在本机OpenGL ES中更快的原因是,您可以调用glMapBufferRange
将该PBO映射到进程的内存中,但是无法在浏览器中高效且安全地执行此操作,因此在浏览器中没有gl.mapBufferRange
WebGL2
The reason it can be faster in native OpenGL ES is because you can call glMapBufferRange
to map that PBO into your process's memory but there is no way to do that efficiently and securely in browsers so there is no gl.mapBufferRange
in WebGL2
根据规格
// MapBufferRange, in particular its read-only and write-only modes,
// can not be exposed safely to JavaScript. GetBufferSubData
// replaces it for the purpose of fetching data back from the GPU.
和
MapBufferRange
,FlushMappedBufferRange
和UnmapBuffer
入口点已从WebGL 2.0 API中删除.以下枚举值也被删除:BUFFER_ACCESS_FLAGS
,BUFFER_MAP_LENGTH
,BUFFER_MAP_OFFSET
,MAP_READ_BIT
,MAP_WRITE_BIT
,MAP_INVALIDATE_RANGE_BIT
,MAP_INVALIDATE_BUFFER_BIT
,MAP_FLUSH_EXPLICIT_BIT
和MAP_UNSYNCHRONIZED_BIT
.
The MapBufferRange
, FlushMappedBufferRange
, and UnmapBuffer
entry points are removed from the WebGL 2.0 API. The following enum values are also removed: BUFFER_ACCESS_FLAGS
, BUFFER_MAP_LENGTH
, BUFFER_MAP_OFFSET
, MAP_READ_BIT
, MAP_WRITE_BIT
, MAP_INVALIDATE_RANGE_BIT
, MAP_INVALIDATE_BUFFER_BIT
, MAP_FLUSH_EXPLICIT_BIT
, and MAP_UNSYNCHRONIZED_BIT
.
可以使用getBufferSubData
入口点读取缓冲区数据,而不是使用MapBufferRange
.
Instead of using MapBufferRange
, buffer data may be read by using the getBufferSubData
entry point.
要上传4096x4096纹理,可以考虑制作一个空纹理(将null
传递到texImage2D
,然后使用texSubImage2d
每帧上传一部分纹理,以避免结结?
For uploading a 4096x4096 texture maybe consider making an empty texture (passing null
to texImage2D
then using texSubImage2d
to upload a portion of the texture per frame to avoid any stutter?
对于问题本身,通过PBO上传纹理数据是使用gl.bufferData
将数据复制到PBO的问题.
As for the question itself, uploading texture data through a PBO is a matter of using gl.bufferData
to copy the data to the PBO.
const vs = `#version 300 es
in vec4 position;
out vec2 v_texcoord;
void main() {
gl_Position = position;
v_texcoord = position.xy * .5 + .5;
}
`;
const fs = `#version 300 es
precision mediump float;
in vec2 v_texcoord;
uniform sampler2D u_tex;
out vec4 outColor;
void main() {
// twizzle colors to show we went through shader
outColor = texture(u_tex, v_texcoord).gbra;
}
`;
const gl = document.querySelector("canvas").getContext("webgl2");
// compiles shaders, links program, looks up locations
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);
// make a 2d canvas
const ctx = document.createElement("canvas").getContext("2d");
ctx.translate(150, 75);
ctx.rotate(Math.PI * .25);
ctx.fillStyle = "red";
ctx.fillRect(-50, -50, 100, 100);
ctx.lineWidth = 20;
ctx.strokeStyle = "yellow";
ctx.strokeRect(-50, -50, 100, 100);
const pbo = gl.createBuffer();
const data = ctx.getImageData(0, 0, 300, 150).data;
gl.bindBuffer(gl.PIXEL_UNPACK_BUFFER, pbo);
gl.bufferData(gl.PIXEL_UNPACK_BUFFER, data, gl.STATIC_DRAW);
// data is now in PBO
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
// take data from PBO
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 300, 150, 0,
gl.RGBA, gl.UNSIGNED_BYTE, 0);
gl.generateMipmap(gl.TEXTURE_2D);
// calls gl.createBuffer, gl.bindBuffer, gl.bufferData for each array
const bufferInfo = twgl.createBufferInfoFromArrays(gl, {
position: [
-1, -1, 0,
1, -1, 0,
-1, 1, 0,
-1, 1, 0,
1, -1, 0,
1, 1, 0,
],
});
gl.useProgram(programInfo.program)
// calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
// calls gl.activeTexture, gl.bindTexture, gl.uniform?
twgl.setUniforms(programInfo, {
u_tex: tex,
});
// calls gl.drawArrays or gl.drawElements
twgl.drawBufferInfo(gl, bufferInfo);
canvas { border: 1px solid black; }
<script src="https://twgljs.org/dist/3.x/twgl.min.js"></script>
<canvas></canvas>
这篇关于如何使用像素缓冲区对象在WebGL2中上载纹理?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!