WebGL如何避免长时间着色器编译暂停选项卡

WebGL如何避免长时间着色器编译暂停选项卡,webgl,webgl2,Webgl,Webgl2,我有一个巨大的着色器,它需要一分钟以上的时间来编译,在编译过程中会完全阻塞整个浏览器。据我所知,着色器编译不能是异步的,所以您可以在等待编译完成时运行其他WebGL命令 我已经尝试了以下方法: 一段时间内不要使用该特定着色器-这不起作用,因为大多数其他WebGL命令将等待它完成,即使该着色器程序从未处于活动状态 使用另一个上下文-同上,但即使来自另一个上下文的WebGL命令也会导致暂停 在web worker中使用OffscreenCanvas——这也不能避免暂停,即使它在worker中,也会

我有一个巨大的着色器,它需要一分钟以上的时间来编译,在编译过程中会完全阻塞整个浏览器。据我所知,着色器编译不能是异步的,所以您可以在等待编译完成时运行其他WebGL命令

我已经尝试了以下方法:

  • 一段时间内不要使用该特定着色器-这不起作用,因为大多数其他WebGL命令将等待它完成,即使该着色器程序从未处于活动状态
  • 使用另一个上下文-同上,但即使来自另一个上下文的WebGL命令也会导致暂停
  • 在web worker中使用OffscreenCanvas——这也不能避免暂停,即使它在worker中,也会暂停整个浏览器。即使我在命令链接程序以发出任何其他WebGL命令后等待几分钟,浏览器也会暂停(就好像在这段时间内什么也没发生一样)
另一个问题是,它有时会使WebGL(上下文丢失)崩溃,这会使页面(或worker)上的所有上下文都崩溃

我可以做些什么来避免浏览器停滞吗

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

这就是我的程序初始化的样子,它可以被改变吗

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后等待几分钟,即使在worker中也无济于事

最后要注意的是:我可以让使用OpenGL运行的windows游戏不受此影响(游戏正在运行,我开始在浏览器中编译此着色器,并且在浏览器暂停时游戏继续运行正常)

更新 Chromium添加了扩展,允许您查询着色器是否已完成编译

不幸的是,截至2021年1月,只有Chromium(Chrome/Edge/Brave/等)实施了该计划


原始答案 没有好的解决办法

Windows上的浏览器使用DirectX,因为OpenGL在许多机器上默认不提供,而且浏览器所需的许多其他功能与OpenGL不兼容

DirectX编译着色器需要很长时间。只有微软能解决这个问题。Microsoft提供了HLSL着色器编译器的源代码,但它仅适用于DX12

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

  • 它们不是便携式的

    一个网页必须提供100或1000种二进制着色器的变体。每种类型的GPU*每种类型的驱动程序*每种平台(iOS、Android、PI、Mac、Windows、Linux、Fire等)各一个。网页应该到处加载,所以着色器二进制文件不是web的解决方案

  • 这将是一个巨大的安全问题

    让用户下载随机的二进制blob并交给OS/GPU执行将是巨大的漏洞来源。1

  • 请注意,某些浏览器(特别是Chrome浏览器)在后台本地缓存着色器二进制文件,但这无助于首次编译

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

    这是一条两年前的线索

    这只是我个人的观点,但我猜异步扩展没有太多进展的原因是它的实现比听起来要复杂得多,而且有很多具有复杂着色器的站点存在并且似乎可以工作


    1您作为文本GLSL传递给WebGL的着色器由浏览器编译,检查各种问题,如果任何WebGL规则被破坏,将被拒绝,然后重新写入以确保安全,插入错误解决方法,重新写入变量名称,添加夹紧指令,有时展开循环,各种各样的事情来确保你不会撞坏司机。您可以使用
    WEBGL\u debug\u着色器
    扩展查看实际发送到驱动程序的着色器

    二进制着色器是您给驱动程序的一个blob,您没有机会检查它或验证它是否做了坏事,因为它是驱动程序专有的二进制。没有关于其中内容、格式的文档,它们可以随每个GPU和每个驱动程序而改变。你只要相信司机就行了。司机不值得信赖。最重要的是,它是在您的计算机上执行的不受信任的代码。这和下载random.exe并执行它们没有什么不同,因此不会发生这种情况

    至于WebGPU,不,WebGL没有更多的安全风险。即使它使用二进制格式,该二进制格式也适用于WebGPU本身,而不是驱动程序。WebGPU将读取二进制文件,检查是否遵循所有规则,然后生成与用户GPU匹配的着色器。生成的着色器可以是GLSL、HLSL、MetalSL、SPIR-V,但与WebGL类似,只有在验证所有规则均已遵循后,它才会编写着色器,然后它编写的着色器(与WebGL一样)将包括变通方法、夹紧以及使着色器安全所需的任何其他方法。请注意,截至2018年11月30日,WebGPU的着色器格式尚未确定。谷歌和Mozilla正在推动二进制SPIR-V的子集,这是文本HLSL的一种变体

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

    在Windows上,即使Chrome没有这样做,Windows也会这样做。这主要是因为大多数GPU(可能都在2018年)不能像CPU那样多任务。这意味着,如果你给他们30分钟的工作时间,他们将不用花时间去做