WebGL如何避免长着色器编译使标签停滞

时间:2018-11-29 14:48:10

标签: webgl webgl2

我有一个巨大的着色器,需要花费一分钟以上的时间进行编译,从而在整个过程中完全停滞了整个浏览器。据我所知,着色器编译不能异步进行,因此可以在等待编译完成时运行其他WebGL命令。

我已经尝试了以下方法:

  • 一段时间不使用该特定着色器-这是行不通的,因为大多数其他WebGL命令将等待其完成,即使该着色器程序从未处于活动状态
  • 使用另一个上下文-与上面相同,但是即使来自另一个上下文的WebGL命令也会导致停顿
  • 在Web worker中使用OffscreenCanvas -这也无法避免停顿,即使它在worker中,也会停滞整个浏览器。即使我在命令链接程序后等待几分钟以发出任何其他WebGL命令,浏览器也会停止运行(好像在此期间什么都没发生)

另一个问题是,有时它会使WebGL(上下文丢失)崩溃,从而使页面(或工作线程)中的所有上下文崩溃。

有什么办法可以避免浏览器停顿吗?

我可以将着色器分为多个部分并分别编译吗?

这是我的程序初始化的样子,它可以以某种方式更改吗?

let vertexShader = gl.createShader(gl.VERTEX_SHADER);
let fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
let program = gl.createProgram();

gl.shaderSource(vertexShader, vertexSource);
gl.shaderSource(fragmentShader, fragmentSource);

gl.compileShader(vertexShader);
gl.compileShader(fragmentShader);

gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);

gl.linkProgram(program);

gl.useProgram(program);

let status = gl.getProgramParameter(program, gl.LINK_STATUS);
let programLog = gl.getProgramInfoLog(program);

调用linkProgram几分钟后等待,即使在工作人员中也无济于事。

最后要注意的一点是:例如使用不受此影响的OpenGL运行的Windows游戏(游戏正在运行,我开始在浏览器中编译此着色器,并且当浏览器停止运行时,游戏仍可以正常运行)

1 个答案:

答案 0 :(得分:5)

没有好的解决方案。

Windows上的浏览器使用DirectX,因为默认情况下,许多机器上都不提供OpenGL,并且浏览器所需的许多其他功能与OpenGL不兼容。

DirectX需要很长时间才能编译着色器。只有Microsoft可以解决此问题。 Microsoft已为HLSL着色器编译器提供了源代码,但仅适用于DX12。

有人建议允许网页提供二进制着色器,但是由于两个非常重要的原因,这种情况永远不会发生

  1. 它们不是便携式的

    一个网页必须提供100或1000多种二进制着色器的变体。适用于每种类型的GPU *每种驱动程序*每个平台(iOS,Android,PI,Mac,Windows,Linux,Fire等)。网页应该加载到任何地方,因此着色器二进制文件不是Web的解决方案。

  2. 这将是一个巨大的安全问题。

    让用户下载分配给OS / GPU的随机二进制Blob来执行将是漏洞利用的巨大来源。 1

请注意,某些浏览器(尤其是Chrome)会在后台本地缓存着色器二进制文件,但这对首次编译没有帮助。

因此,目前基本上没有解决方案。您可以制作更简单的着色器,也可以一次编译更少的着色器。人们要求使用异步扩展来编译着色器,但一直没有进展。

这是2年前的主题 https://www.khronos.org/webgl/public-mailing-list/public_webgl/1702/msg00039.php

仅是个人观点,但我猜测异步扩展没有太多动作的原因是,它实现的工作量比听起来要多,并且存在许多具有复杂着色器的站点并且似乎可以正常工作。


1 作为文本GLSL传递给WebGL的着色器由浏览器编译,检查各种问题,如果任何WebGL规则被破坏则拒绝,然后将它们重写为为了安全起见,请插入错误的解决方法,重新编写变量名,添加约束说明,有时展开循环,以确保您不会使驱动程序崩溃。您可以使用WEBGL_debug_shaders扩展名查看实际发送给驱动程序的着色器。

二进制着色器是您提供给驱动程序的Blob,由于它是驱动程序专有的二进制文件,因此您没有机会对其进行检查或验证其是否做得不好。没有关于其中的内容,格式的文档,它们可以随每个GPU和每个驱动程序而变化。您只需要信任驱动程序。司机不值得信赖。最重要的是,在您的计算机上执行的不受信任的代码。就像下载随机的.exe并执行它们一样,因此不会发生。

对于WebGPU,不,WebGL不再存在安全风险。即使使用二进制格式,该二进制格式也适用于WebGPU本身,而不适用于驱动程序。 WebGPU将读取二进制文件,检查是否遵循所有规则,然后生成与用户的GPU匹配的着色器。生成的着色器可以是GLSL,HLSL,MetalSL,SPIR-V,无论如何工作,但与WebGL相似,只有在验证所有规则后,它才会编写一个着色器,然后像WebGL一样,它编写的着色器将包括变通办法,夹紧除此之外,还需要确保着色器安全。请注意,截至今天2018/11/30,尚不确定WebGPU的着色器格式是什么。 Google和Mozilla正在推动以二进制形式Apple and Microsoft are pushing for WHLSL(是文本中的HLSL的变体)的SPIR-V的子集

请注意,当浏览器显示“ RATS!WebGL这是一个障碍”时,这并不意味着驱动程序崩溃了。而是几乎总是意味着GPU重置时间太长。在Chrome(不确定其他浏览器)中,当Chrome要求GPU(通过驱动程序)执行某项操作时,它将启动计时器。如果GPU在2到5秒内仍未完成(不确定实际的超时时间),则Chrome将终止GPU进程。这包括编译着色器,并且由于DirectX花费的时间最多,因此这就是DirectX上最多出现此问题的原因。

在Windows上,即使Chrome没有执行此操作,Windows也会执行此操作。这主要是因为大多数GPU(可能全部在2018年)无法像CPU一样执行多任务。这意味着,如果您给他们30分钟的工作时间,他们将在不中断的情况下完成30分钟的工作,这基本上会冻结您的计算机,因为您的计算机需要GPU绘制应用程序窗口等。在过去,Windows就像这样Chrome,如果时间过长,请重置GPU。过去,Linux和Mac只能冻结30分钟,否则会使操作系统崩溃,因为该操作系统希望能够绘制图形而不能。在过去8年中的某个时候,Mac和Linux在此方面做得更好。无论如何,Chrome都需要尝试主动,因此它使用自己的计时器并在耗时太长时将其杀死。