目前,使用texImage2d
上传大型4096x4096纹理非常慢,在将纹理发送到GPU并最终导致卡顿时锁定主线程。
根据我的阅读,WebGL2能够使用PBO(Pixel Buffer Objects)以更有效的方式在GPU上创建纹理。但是,我无法在网上找到任何有关如何操作的例子。
我已经找到了如何在OpenGL中实现此目的的良好描述,但我不确定如何继续使用WebGL API。
我想使用Canvas
或ImageBitmap
作为纹理数据的来源。
到目前为止,我正在通过将纹理绘制到画布上进行测试,然后使用arrayBuffer
后跟canvas.toBlob()
和FileReader
将图像转换为readAsArrayBuffer
。然后,一旦我有一个有效的缓冲区,我尝试创建PBO并上传它。
我的代码的相关部分如下所示:
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
我真的不知道我是否正确接近它,所以任何帮助都会非常感激。
答案 0 :(得分:4)
我可能错了,但如果WebGL中用于上传数据的PBO比texImage2D
更快,我会感到惊讶。 PBO本身存在于另一个过程中。要将数据发送到该流程,需要使用gl.bufferData
将数据从JavaScript流程复制到GPU流程。在幕后,复制对于两种方法都是相同的。
原生OpenGL ES可以更快的原因是因为你可以调用glMapBufferRange
将PBO映射到进程的内存中,但是没有办法在浏览器中高效安全地执行此操作,因此没有{{ WebGL2中的1}}
来自规范
gl.mapBufferRange
和
5.14没有MapBufferRange
从WebGL 2.0 API中删除
// 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
,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
入口点来读取缓冲区数据,而不是使用MapBufferRange
。
对于上传4096x4096纹理,可以考虑制作一个空纹理(将getBufferSubData
传递给null
然后使用texImage2D
每帧上传一部分纹理以避免任何口吃?
至于问题本身,通过PBO上传纹理数据就是使用texSubImage2d
将数据复制到PBO。
gl.bufferData
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; }