4

目前,使用上传大型 4096x4096 纹理texImage2d非常慢,在将纹理发送到 GPU 时锁定主线程并最终导致卡顿。

根据我的阅读,WebGL2 能够使用 PBO(像素缓冲区对象)以更有效的方式在 GPU 上创建纹理。但是,我无法在网上找到任何有关如何执行此操作的示例。

我找到了关于如何在OpenGL中实现这一点的很好的描述,但我不确定如何继续使用 WebGL API。

我想使用 aCanvas或 anImageBitmap作为纹理数据的来源。

到目前为止,我正在通过将纹理绘制到画布上进行测试,然后将图像转换为arrayBufferusingcanvas.toBlob()后跟FileReaderand 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

我真的不知道我是否正确地接近它,所以任何帮助将不胜感激。

4

1 回答 1

8

我可能是错的,但如果 WebGL 中用于上传数据的 PBO 比texImage2D. PBO 本身存在于另一个过程中。要将数据传输到该进程需要使用gl.bufferData. 在幕后,这两种方法的复制是相同的。

在本机 OpenGL ES 中它可以更快的原因是因为您可以调用glMapBufferRange将该 PBO 映射到您的进程的内存中,但是在浏览器中没有办法有效和安全地做到这一点,所以gl.mapBufferRange在 WebGL2中没有

从规格

MapBufferRange,特别是它的只读和只写模式,不能安全地暴露给 JavaScript。GetBufferSubData 替换它以从 GPU 取回数据。

5.14 没有 MapBufferRange

WebGL 2.0 API 中删除了、MapBufferRangeFlushMappedBufferRange入口点。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可以使用getBufferSubData入口点读取缓冲区数据,而不是使用。

对于上传 4096x4096 纹理,可以考虑制作一个空纹理(传递nulltexImage2D然后使用texSubImage2d每帧上传一部分纹理以避免任何卡顿?

至于问题本身,通过 PBO 上传纹理数据是使用gl.bufferData将数据复制到 PBO 的问题。

const vs = `#version 300 es
void main() {
  gl_Position = vec4(0, 0, 0, 1);
  gl_PointSize = 128.0;
}
`;

const fs = `#version 300 es
precision mediump float;

uniform sampler2D u_tex;

out vec4 outColor;

void main() {
  // twizzle colors to show we went through shader
  outColor = texture(u_tex, gl_PointCoord.xy).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.arc(150, 75, 60, 0, Math.PI * 2);
ctx.fillStyle = "red";
ctx.fill();
ctx.lineWidth = 20;
ctx.strokeStyle = "yellow";
ctx.stroke();
ctx.fillStyle = "cyan";
ctx.font = "bold 48px sans-serif";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText("FFFF", 150, 75);

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);

// draw a single point, uniforms default to 0 so sampler
// will use texture unit 0
gl.useProgram(programInfo.program)
gl.drawArrays(gl.POINTS, 0, 1);
canvas { border: 1px solid black; }
<script src="https://twgljs.org/dist/4.x/twgl.min.js"></script>
<canvas></canvas>

于 2017-04-21T05:36:41.003 回答