WebGL能否模拟异步调用gl.finish()?

12

WebGL非常好且异步,可以发送一长串渲染命令而无需等待它们完成。然而,如果出于某种原因您确实需要等待渲染完成,那么必须使用 gl.finish() 同步方式。毫无疑问,如果 gl.finish() 接受回调并立即返回将是更好的选择。

问题: 有可靠的仿真方法吗?

用例:我正在将大量顶点渲染到一个大型离屏画布上,然后使用 drawImage 将这个大型画布的部分复制到页面上的小型画布上。 我实际上没有使用 gl.finish(),但是 drawImage() 看起来效果相同。在我的应用程序中,重新渲染仅在用户执行操作(例如单击按钮)时触发,并且可能需要几百毫秒。 在渲染期间,如果浏览器仍然响应(允许滚动等),那将是很好的。 我特别寻找 Chrome 的解决方案,不过在 Firefox 和 Safari 中也能工作的解决方案也不错。

可能的(糟糕)答案:您可以尝试估计渲染需要多长时间,然后设置一个以调用 gl.finish() 开始的超时。 但是,对于所有顶点缓冲区大小和所有用户可靠地进行此估算将非常麻烦且不准确。

可能的(非)答案:requestAnimationFrame 解决了我要找的东西...但它并没有解决吧?

2018 年的可能答案:也许 ImageBitmap API 可以解决这个问题-请参见 MDN docs.


你无法使用“async”gl.finish()从同一页中呈现它,因为没有异步版本。如果绘制屏幕外图像被认为是“繁重”的工作,则可以使用Web Worker生成该图像,然后使用postMessage返回该图像,并将其绘制到画布上。这会稍微提高性能,但绘制仍然必须同步进行。减少画布的分辨率也可能加快速度。 - moka
@MaksimsMihejevs - 从Web Worker中渲染图像将是异步的,但它将使用CPU而不是GPU,因此速度会慢得多。(您无法从Web Workers访问WebGL。) - dan-man
我已经更多地谈论了Canvas 2D上下文,就像你提到的drawImage一样。 - moka
1个回答

5
你已经部分地回答了你的问题:drawImage()确实具有类似完成的行为,因为它强制所有未完成的绘画命令在读取图像数据之前完成。问题在于,即使gl.finish()能够实现你想要的等待渲染完成的功能,你使用它时仍然会出现与现在相同的行为。主线程将被阻塞,而用户无法与页面进行交互。
理想情况下,在这种情况下,您希望有一种回调方式,指示一组绘制命令何时完成,而不必阻止等待它们。不幸的是,没有这样的回调存在(考虑到浏览器内部的工作方式,提供这样的回调可能会非常困难!)。
在您的情况下,一个不错的中间地带可能是对您认为图像可能准备好的时间进行一些智能估计。例如,一旦您分派了绘制调用,请在调用drawImage之前通过3或4个requestAnimationFrame进行旋转。如果您始终观察到它需要更长时间(10帧?),那么就要转动更长时间。这将允许用户正常地继续与页面交互,并且在完成呈现时要么不产生延迟,因为内容已经完成呈现,要么因为您在呈现的中途执行了同步步骤而减少延迟。根据您网站的预期使用情况,非实时渲染可能甚至可以旋转一秒钟左右才进行呈现。
当然,这并不是一个完美的解决方案,我希望我能给你一个更好的答案。也许WebGL将来会获得查询此类状态的能力,因为在像你这样的情况下它将是有价值的,但是现在这很可能是你能做的最好的事情。

我刚刚阅读了你的webgl 2.0帖子。 我想问一下,同步对象是否可以解决这个问题? - dan-man

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