WebGL:异步操作?
WebGL: async operations?
我想知道是否有任何可以利用的 WebGL 异步调用?
我已经查看了 Spec v1 和 Spec v2,它们没有提及任何内容。在 V2 中,有一个 WebGL 查询机制,我认为这不是我正在寻找的。
在网络上搜索没有得出任何明确的结论。有这个例子,并不清楚同步和异步版本有何不同。 http://toji.github.io/shader-perf/
最终我希望能够异步执行所有这些操作:
- 读取像素
- texSubImage2D 和 texImage2D
- 着色器编译
- 程序链接
- 平局???
有一个 glFinish 操作,它的文档说:"does not return until the effects of all previously called GL commands are complete."。对我来说,这意味着可以通过调用 Finish()?
等待异步操作
网络上的一些帖子建议调用 getError() 也会强制同步,并不是每次调用后都非常希望做的事情。
这取决于你对异步的定义。
在 Chrome 中(Firefox 现在可能也这样做了?不确定)。 Chrome 在与 JavaScript 不同的进程中运行所有 GPU 代码。这意味着您的命令是 运行 异步的。甚至 OpenGL 本身也设计为异步的。函数 (WebGL/OpenGL) 将命令插入命令缓冲区。那些是由其他一些 thread/process 执行的。您通过调用 gl.flush
告诉 OpenGL "hey, I have new commands for you to execute!"。它异步执行这些命令。如果您不调用 gl.flush
,当发出太多命令时,它会定期为您调用。当当前 JavaScript 事件退出时,它也会被调用,假设您调用任何渲染命令到 canvas (gl.drawXXX, gl.clear).
从这个意义上讲,WebGL 的一切都是异步的。如果您不查询某些内容 (gl.getXXX, gl.readXXX),则处理(绘制)内容与您的 JavaScript 不同步。毕竟 运行 与 CPU.
分开,WebGL 让您可以访问 GPU
知道在 Chrome 中利用它的一种方法是通过提交着色器来异步编译着色器
for each shader
s = gl.createShader()
gl.shaderSource(...);
gl.compileShader(...);
gl.attachShader(...);
gl.linkProgram(...)
gl.flush()
GPU 进程现在将编译您的着色器。因此,比方说,250 毫秒后,您才开始询问它是否成功并查询位置,然后如果编译时间少于 250 毫秒,并且 link 着色器全部发生异步。
在 WebGL2 中至少有一个更明确的异步操作,即遮挡查询,其中 WebGL2 可以告诉您为一组绘制调用绘制了多少像素。如果没有被抽到,那么你的抽奖就被遮挡了。要获得答案,您会定期检查答案是否准备就绪。通常你检查下一帧,事实上 WebGL 规范要求答案在下一帧之前不可用。
否则,目前(2018 年 8 月),没有明确的异步 APIs。
更新
HankMoody 在评论中提到 texImage2D
是同步的。同样,它 取决于您对异步 的定义。添加命令及其数据需要时间。像 gl.enable(gl.DEPTH_TEST)
这样的命令只需要添加 2-8 个字节。像 gl.texImage2D(..., width = 1024, height = 1024, RGBA, UNSIGNED_BYTE)
这样的命令必须添加 4meg!。上传 4meg 后,其余部分是异步的,但上传需要时间。这两个命令都是一样的,只是添加 2-8 个字节比添加 4meg 花费的时间少得多。
更清楚的是,在上传 4 meg 之后,许多其他事情会异步发生。使用 4 meg 调用驱动程序。驱动程序复制该 4meg。驱动程序安排 4meg 稍后使用,因为如果纹理已在使用中,它无法立即上传数据。或者它会立即将它上传到一个新区域,然后在实际使用该新数据的绘制调用之前交换纹理指向的内容。其他驱动程序只是复制数据并存储它,然后等到在绘制调用中使用纹理以实际更新纹理。这是因为 texImage2D 具有疯狂的语义,您可以按任何顺序上传不同大小的 mip,因此驱动程序直到绘制时间才能知道 GPU 内存中实际需要什么,因为它不知道您将调用 texIamge2D 的顺序。本段中提到的所有这些东西都是异步发生的。
但这确实提供了更多信息。
gl.texImage2D
和相关命令必须完成大量工作。一个是他们必须遵守 UNPACK_FLIP_Y_WEBGL
和 UNPACK_PREMULTIPLY_ALPHA_WEBGL
,所以他们需要复制多个 megs 的数据来翻转它或预乘它。其次,如果您向他们传递视频、canvas 或图像,他们可能需要进行大量转换,甚至需要重新解析来自源的图像,尤其是根据 UNPACK_COLORSPACE_CONVERSION_WEBGL
。这是否以类似异步的方式发生取决于浏览器。由于您没有直接访问 image/video/canvas 的权限,因此浏览器可能会执行所有这些异步操作,但必须以一种或另一种方式完成所有工作。
为了完成大部分异步工作,添加了 ImageBitmap
API。与大多数 Web APIs 一样,它未指定,但想法是您首先执行 fetch
(异步)。然后您请求创建一个 ImageBitmap
并为其提供颜色转换、翻转、预乘 alpha 选项。这也会发生异步。然后将结果传递给 gl.texImage2D
,希望浏览器能够在到达最后一步之前完成所有繁重的部分。
示例:
// note: mode: 'cors' is because we are loading
// from a different domain
async function main() {
const response = await fetch('https://i.imgur.com/TSiyiJv.jpg', {mode: 'cors'})
if (!response.ok) {
return console.error('response not ok?');
}
const blob = await response.blob();
const bitmap = await createImageBitmap(blob, {
premultiplyAlpha: 'none',
colorSpaceConversion: 'none',
});
const gl = document.querySelector("canvas").getContext("webgl");
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
{
const level = 0;
const internalFormat = gl.RGBA;
const format = gl.RGBA;
const type = gl.UNSIGNED_BYTE;
gl.texImage2D(gl.TEXTURE_2D, level, internalFormat,
format, type, bitmap);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
}
const vs = `
uniform mat4 u_worldViewProjection;
attribute vec4 position;
attribute vec2 texcoord;
varying vec2 v_texCoord;
void main() {
v_texCoord = texcoord;
gl_Position = u_worldViewProjection * position;
}
`;
const fs = `
precision mediump float;
varying vec2 v_texCoord;
uniform sampler2D u_tex;
void main() {
gl_FragColor = texture2D(u_tex, v_texCoord);
}
`;
const m4 = twgl.m4;
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);
const bufferInfo = twgl.primitives.createCubeBufferInfo(gl, 2);
const uniforms = {
u_tex: tex,
};
function render(time) {
time *= 0.001;
twgl.resizeCanvasToDisplaySize(gl.canvas);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.enable(gl.DEPTH_TEST);
const fov = 30 * Math.PI / 180;
const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
const zNear = 0.5;
const zFar = 10;
const projection = m4.perspective(fov, aspect, zNear, zFar);
const eye = [1, 4, -6];
const target = [0, 0, 0];
const up = [0, 1, 0];
const camera = m4.lookAt(eye, target, up);
const view = m4.inverse(camera);
const viewProjection = m4.multiply(projection, view);
const world = m4.rotationY(time);
uniforms.u_worldViewProjection = m4.multiply(viewProjection, world);
gl.useProgram(programInfo.program);
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
twgl.setUniforms(programInfo, uniforms);
gl.drawElements(gl.TRIANGLES, bufferInfo.numElements, gl.UNSIGNED_SHORT, 0);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main();
body { margin: 0; }
canvas { width: 100vw; height: 100vh; display: block; }
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<canvas></canvas>
不幸的是,截至 2018 年 8 月,这仅适用于 Chrome。火狐 bug is here。其他浏览器我不知道。
我想知道是否有任何可以利用的 WebGL 异步调用?
我已经查看了 Spec v1 和 Spec v2,它们没有提及任何内容。在 V2 中,有一个 WebGL 查询机制,我认为这不是我正在寻找的。
在网络上搜索没有得出任何明确的结论。有这个例子,并不清楚同步和异步版本有何不同。 http://toji.github.io/shader-perf/
最终我希望能够异步执行所有这些操作:
- 读取像素
- texSubImage2D 和 texImage2D
- 着色器编译
- 程序链接
- 平局???
有一个 glFinish 操作,它的文档说:"does not return until the effects of all previously called GL commands are complete."。对我来说,这意味着可以通过调用 Finish()?
等待异步操作网络上的一些帖子建议调用 getError() 也会强制同步,并不是每次调用后都非常希望做的事情。
这取决于你对异步的定义。
在 Chrome 中(Firefox 现在可能也这样做了?不确定)。 Chrome 在与 JavaScript 不同的进程中运行所有 GPU 代码。这意味着您的命令是 运行 异步的。甚至 OpenGL 本身也设计为异步的。函数 (WebGL/OpenGL) 将命令插入命令缓冲区。那些是由其他一些 thread/process 执行的。您通过调用 gl.flush
告诉 OpenGL "hey, I have new commands for you to execute!"。它异步执行这些命令。如果您不调用 gl.flush
,当发出太多命令时,它会定期为您调用。当当前 JavaScript 事件退出时,它也会被调用,假设您调用任何渲染命令到 canvas (gl.drawXXX, gl.clear).
从这个意义上讲,WebGL 的一切都是异步的。如果您不查询某些内容 (gl.getXXX, gl.readXXX),则处理(绘制)内容与您的 JavaScript 不同步。毕竟 运行 与 CPU.
分开,WebGL 让您可以访问 GPU知道在 Chrome 中利用它的一种方法是通过提交着色器来异步编译着色器
for each shader
s = gl.createShader()
gl.shaderSource(...);
gl.compileShader(...);
gl.attachShader(...);
gl.linkProgram(...)
gl.flush()
GPU 进程现在将编译您的着色器。因此,比方说,250 毫秒后,您才开始询问它是否成功并查询位置,然后如果编译时间少于 250 毫秒,并且 link 着色器全部发生异步。
在 WebGL2 中至少有一个更明确的异步操作,即遮挡查询,其中 WebGL2 可以告诉您为一组绘制调用绘制了多少像素。如果没有被抽到,那么你的抽奖就被遮挡了。要获得答案,您会定期检查答案是否准备就绪。通常你检查下一帧,事实上 WebGL 规范要求答案在下一帧之前不可用。
否则,目前(2018 年 8 月),没有明确的异步 APIs。
更新
HankMoody 在评论中提到 texImage2D
是同步的。同样,它 取决于您对异步 的定义。添加命令及其数据需要时间。像 gl.enable(gl.DEPTH_TEST)
这样的命令只需要添加 2-8 个字节。像 gl.texImage2D(..., width = 1024, height = 1024, RGBA, UNSIGNED_BYTE)
这样的命令必须添加 4meg!。上传 4meg 后,其余部分是异步的,但上传需要时间。这两个命令都是一样的,只是添加 2-8 个字节比添加 4meg 花费的时间少得多。
更清楚的是,在上传 4 meg 之后,许多其他事情会异步发生。使用 4 meg 调用驱动程序。驱动程序复制该 4meg。驱动程序安排 4meg 稍后使用,因为如果纹理已在使用中,它无法立即上传数据。或者它会立即将它上传到一个新区域,然后在实际使用该新数据的绘制调用之前交换纹理指向的内容。其他驱动程序只是复制数据并存储它,然后等到在绘制调用中使用纹理以实际更新纹理。这是因为 texImage2D 具有疯狂的语义,您可以按任何顺序上传不同大小的 mip,因此驱动程序直到绘制时间才能知道 GPU 内存中实际需要什么,因为它不知道您将调用 texIamge2D 的顺序。本段中提到的所有这些东西都是异步发生的。
但这确实提供了更多信息。
gl.texImage2D
和相关命令必须完成大量工作。一个是他们必须遵守 UNPACK_FLIP_Y_WEBGL
和 UNPACK_PREMULTIPLY_ALPHA_WEBGL
,所以他们需要复制多个 megs 的数据来翻转它或预乘它。其次,如果您向他们传递视频、canvas 或图像,他们可能需要进行大量转换,甚至需要重新解析来自源的图像,尤其是根据 UNPACK_COLORSPACE_CONVERSION_WEBGL
。这是否以类似异步的方式发生取决于浏览器。由于您没有直接访问 image/video/canvas 的权限,因此浏览器可能会执行所有这些异步操作,但必须以一种或另一种方式完成所有工作。
为了完成大部分异步工作,添加了 ImageBitmap
API。与大多数 Web APIs 一样,它未指定,但想法是您首先执行 fetch
(异步)。然后您请求创建一个 ImageBitmap
并为其提供颜色转换、翻转、预乘 alpha 选项。这也会发生异步。然后将结果传递给 gl.texImage2D
,希望浏览器能够在到达最后一步之前完成所有繁重的部分。
示例:
// note: mode: 'cors' is because we are loading
// from a different domain
async function main() {
const response = await fetch('https://i.imgur.com/TSiyiJv.jpg', {mode: 'cors'})
if (!response.ok) {
return console.error('response not ok?');
}
const blob = await response.blob();
const bitmap = await createImageBitmap(blob, {
premultiplyAlpha: 'none',
colorSpaceConversion: 'none',
});
const gl = document.querySelector("canvas").getContext("webgl");
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
{
const level = 0;
const internalFormat = gl.RGBA;
const format = gl.RGBA;
const type = gl.UNSIGNED_BYTE;
gl.texImage2D(gl.TEXTURE_2D, level, internalFormat,
format, type, bitmap);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
}
const vs = `
uniform mat4 u_worldViewProjection;
attribute vec4 position;
attribute vec2 texcoord;
varying vec2 v_texCoord;
void main() {
v_texCoord = texcoord;
gl_Position = u_worldViewProjection * position;
}
`;
const fs = `
precision mediump float;
varying vec2 v_texCoord;
uniform sampler2D u_tex;
void main() {
gl_FragColor = texture2D(u_tex, v_texCoord);
}
`;
const m4 = twgl.m4;
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);
const bufferInfo = twgl.primitives.createCubeBufferInfo(gl, 2);
const uniforms = {
u_tex: tex,
};
function render(time) {
time *= 0.001;
twgl.resizeCanvasToDisplaySize(gl.canvas);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.enable(gl.DEPTH_TEST);
const fov = 30 * Math.PI / 180;
const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
const zNear = 0.5;
const zFar = 10;
const projection = m4.perspective(fov, aspect, zNear, zFar);
const eye = [1, 4, -6];
const target = [0, 0, 0];
const up = [0, 1, 0];
const camera = m4.lookAt(eye, target, up);
const view = m4.inverse(camera);
const viewProjection = m4.multiply(projection, view);
const world = m4.rotationY(time);
uniforms.u_worldViewProjection = m4.multiply(viewProjection, world);
gl.useProgram(programInfo.program);
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
twgl.setUniforms(programInfo, uniforms);
gl.drawElements(gl.TRIANGLES, bufferInfo.numElements, gl.UNSIGNED_SHORT, 0);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main();
body { margin: 0; }
canvas { width: 100vw; height: 100vh; display: block; }
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<canvas></canvas>
不幸的是,截至 2018 年 8 月,这仅适用于 Chrome。火狐 bug is here。其他浏览器我不知道。