WebGL - 何时调用gl.Flush?

3

我今天注意到有一个叫做Flush()的方法,但没有找到详细的文档。

它具体是做什么的?需要吗?

2个回答

16

gl.flush 在WebGL中确实有其用途,但它是驱动程序和浏览器特定的。

例如,由于Chrome的GPU架构是多进程的,因此您可以执行以下操作:

function var loadShader = function(gl, shaderSource, shaderType) {
  var shader = gl.createShader(shaderType);
  gl.shaderSource(shader, shaderSource);
  gl.compileShader(shader);
  return shader;
}

var vs = loadShader(gl, someVertexShaderSource, gl.VERTEX_SHADER);
var fs = loadShader(gl, someFragmentShaderSource, FRAGMENT_SHADER);
var p = gl.createProgram();
gl.attachShader(p, vs);
gl.attachShader(p, fs);
gl.linkProgram(p);

此时,所有命令可能都在命令队列中等待执行,因此,请执行一次刷新操作

gl.flush();

现在,由于我们知道编译和链接程序的速度取决于其大小和复杂程度,因此我们可以等待一段时间再尝试使用它们并做其他事情。

setTimeout(continueLater, 1000);  // continue 1 second later

现在可以做一些其他的事情,比如设置页面或用户界面等。

1秒钟后,continueLater将被调用。很可能是我们的着色器编译和链接完成了。

function continueLater() {

  // check results, get locations, etc.
  if (!gl.getShaderParameter(vs, gl.COMPILE_STATUS) ||
      !gl.getShaderParameter(fs, gl.COMPILE_STATUS) ||
      !gl.getProgramParameter(p, gl.LINK_STATUS)) {
     alert("shaders didn't compile or program didn't link");
     ...etc... 
   }

   var someLoc = gl.getUniformLocation(program, "u_someUniform");
   ...etc...
}

我认为谷歌地图使用了这种技术,因为他们需要编译许多非常复杂的着色器,并且希望页面保持响应。如果他们立即调用 gl.compileShadergl.linkProgram 并立即调用其中一个查询函数,如 gl.getShaderParametergl.getProgramParametergl.getUniformLocation,那么程序将会在首次验证着色器并将其发送到驱动程序进行编译时冻结。通过不立即进行查询而等待一段时间,他们可以避免 UX 中的暂停。
不幸的是,据我所知,这仅适用于 Chrome,因为其他浏览器不是多进程的,我认为所有驱动程序都会同步编译/链接。
可能还有其他原因要调用 gl.flush,但这非常依赖于驱动程序/操作系统/浏览器。例如,假设您将要绘制 1000 个对象,并且需要进行 5000 次 WebGL 调用才能完成。它可能需要更多,但只是为了有一个数字,我们选择了 5000。每个对象需要 4 次对 gl.uniformXXX 的调用和 1 次对 gl.drawXXX 的调用。
这些 5000 个调用可能都适合浏览器(Chrome)或驱动程序的命令缓冲区。如果没有 flush,它们将不会开始执行,直到浏览器为您发出 gl.flush 命令(这样它就可以在屏幕上组合您的结果)。这意味着 GPU 可能会空闲,而您则发出 1000、2000、3000 等命令,因为它们只是停留在缓冲区中。gl.flush 告诉系统“嘿,我添加了那些命令,请确保开始执行它们”。因此,您可能会决定在每个 1000 个命令之后调用一次 gl.flush
问题在于,gl.flush 并不是免费的,否则您会在每个命令之后立即调用它,以确保尽快执行。此外,每个驱动程序/浏览器的工作方式都不同。在某些驱动程序上,每隔几百或几千个 WebGL 调用调用一次 gl.flush 可能是一个好选择。在其他驱动程序上,这可能是浪费时间。
对不起,这可能是太多的信息了 :p

2
更多的信息会更好,我没有理解被接受的答案。 - pailhead

5
假设它在语义上等同于经典的GL glFlush,那么几乎不需要。OpenGL是异步API-您排队等待要完成的工作,并在可以完成时完成。 glFlush仍然是异步的,但会尽快清空任何累积的缓冲区,无论需要多长时间;它基本上告诉驱动程序“如果您计划因任何原因而保留任何内容,请不要这样做”。
通常只有出于与该系统上其他显示机制的耦合相关的特定于平台的原因才会执行此操作。例如,一个平台可能需要对所有GL工作进行排序,以便在将其绘制到屏幕上的容器之前不进行排队。另一个平台可能允许从后台线程管道传输资源到主OpenGL上下文,但不能保证它们在后续调用中一定可用,直到您刷新为止(例如,如果多线程最终创建两个可能存在一个队列的独立队列,则刷新可能会在两者之间插入同步障碍)。
任何具有双缓冲区或根据WebGL模型具有后缓冲区的平台都将自动确保缓冲区及时进行。排队旨在提高性能,但不应产生任何负面的可观察后果。因此,您不必手动执行任何操作。
如果您拒绝刷新并且即使在语义上也不一定需要,但是您的图形基于实时显示,那么您最多可能会遭受几分之一秒的延迟。

1
glFlush() 不会阻塞,直到工作完成。它只保证工作将在有限的时间内执行。你所描述的是 glFinish() 的行为。 - Reto Koradi
1
@RetoKoradi,您说得完全正确,我不得不对我的记忆提出重要的问题。我将编辑答案,假设这将导致勾号消失;如果失败,那么我将删除此答案并编写一个单独的答案,因为正如您隐含地知道的那样,这不是问题的正确答案。 - Tommy
好的,我想由于编辑而失去了“正确答案”的分数,好像没人在意那个,所以希望我的更新答案更接近正确。它是相同的声调,使用调度与异步和特定活动排序,因此所有其它东西可能都可以保留,但显然需要请求不同的特定行为。 - Tommy
2
不用担心分数问题。我只是想确保我们不会在一个得到最高票和被接受的答案中出现错误的信息。感谢您的修正。 - Reto Koradi
1
由于进一步的负面投票,我将其标记为社区维基,因为显然社区的意见非常宝贵。 - Tommy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接