OSX 上的 WebGL 在所有浏览器中使用了错误的纹理

WebGL on OSX using wrong texture in all browsers

我在某些 OSX 机器上看到一个非常奇怪的问题,我的 WebGL 程序似乎使用了错误的纹理内容进行绘图。

我设置了精灵批次,我将一堆精灵四边形缓冲到单个绘制调用中。我还使用多纹理进一步减少绘制调用的数量,在同一个绘制调用中为多个纹理绘制精灵。

每个顶点有以下数据:

 // 16 bytes
 float x;
 float y;
 float u;
 float v;

 // 16 bytes
 float texture_offset_x;
 float texture_offset_y;
 float texture_scale_width;
 float texture_scale_height;

 // 24 bytes
 float t0;
 float t1;
 float t2;
 float t3;
 float t4;
 float t5;

 // 8 bytes
 float width_scale;
 float height_scale;

 // 4 bytes
 unsigned byte r;
 unsigned byte g;
 unsigned byte b;
 unsigned byte a;

 // Texture sampler index
 float texture_index;

片段着色器像这样组合绘制调用:

  "#version 100",
  "",
  "uniform lowp sampler2D sampler0;",
  "uniform lowp sampler2D sampler1;",
  "uniform lowp sampler2D sampler2;",
  "uniform lowp sampler2D sampler3;",
  "uniform lowp sampler2D sampler4;",
  "uniform lowp sampler2D sampler5;",
  "uniform lowp sampler2D sampler6;",
  "uniform lowp sampler2D sampler7;",
  "",
  "varying mediump vec2 vTextureCoord;",
  "varying lowp vec4 vTintColor;",
  "varying lowp float vTextureIndex;",
  "",
  "void main(void) {",
  "   lowp vec4 fragColors[8];",
  "   fragColors[0] = texture2D(sampler0, vTextureCoord);",
  "   fragColors[1] = texture2D(sampler1, vTextureCoord);",
  "   fragColors[2] = texture2D(sampler2, vTextureCoord);",
  "   fragColors[3] = texture2D(sampler3, vTextureCoord);",
  "   fragColors[4] = texture2D(sampler4, vTextureCoord);",
  "   fragColors[5] = texture2D(sampler5, vTextureCoord);",
  "   fragColors[6] = texture2D(sampler6, vTextureCoord);",
  "   fragColors[7] = texture2D(sampler7, vTextureCoord);",
  "",
  "   lowp float fragIncluded[8];",
  "",
  "   fragIncluded[0] = float(vTextureIndex <= 0.5);",
  "   fragIncluded[1] = float(vTextureIndex >= 0.5 && vTextureIndex < 1.5);",
  "   fragIncluded[2] = float(vTextureIndex >= 1.5 && vTextureIndex < 2.5);",
  "   fragIncluded[3] = float(vTextureIndex >= 2.5 && vTextureIndex < 3.5);",
  "   fragIncluded[4] = float(vTextureIndex >= 3.5 && vTextureIndex < 4.5);",
  "   fragIncluded[5] = float(vTextureIndex >= 4.5 && vTextureIndex < 5.5);",
  "   fragIncluded[6] = float(vTextureIndex >= 5.5 && vTextureIndex < 6.5);",
  "   fragIncluded[7] = float(vTextureIndex >= 6.5 && vTextureIndex < 7.5);",
  "",
  "   lowp vec4 fragColor = fragColors[0] * fragIncluded[0] + ",
  "                         fragColors[1] * fragIncluded[1] + ",
  "                         fragColors[2] * fragIncluded[2] + ",
  "                         fragColors[3] * fragIncluded[3] + ",
  "                         fragColors[4] * fragIncluded[4] + ",
  "                         fragColors[5] * fragIncluded[5] + ",
  "                         fragColors[6] * fragIncluded[6] + ",
  "                         fragColors[7] * fragIncluded[7];",
  "",
  "   gl_FragColor = fragColor * vTintColor;",
  "}"

大多数情况下一切正常。但是在某些 OSX 机器上,采样的纹理有时会出现错误。具体来说,这似乎最常发生在基于 NVidia 的 MacBook 上,但我也能够在 Intel HD 5000 上重现它。这种情况发生在所有 Chrome、Safari 和 Firefox 上,所以我很确定它不是与 WebGL 实现本身相关。这可能是 OSX 中的驱动程序错误吗?

我的场景是由一堆UI组成的。大多数 UI 元素是从一个 UI 精灵 sheet 绘制的,文本是从位图字体精灵 sheet 绘制的。该错误通常表现为使用 UI 精灵 sheet 纹理绘制文本。我已经通过着色器修改和 WebGL Inspector 验证了我在绘制调用时将正确的纹理绑定到正确的采样器。

我确实注意到,这似乎是由于在一个绘制调用中将纹理 A 绑定到采样器 1,并将纹理 B 绑定到采样器 2,而下一个绘制调用将它们反转,纹理 A 绑定到采样器 2 和纹理B 绑定到 sampler1.

目前可以在此 URL 现场重现此问题:https://tinytappers.bigvikinggames.com/

最容易看到的方法是打开两个底部菜单之一,打开时点击屏幕顶部的敌人。通常无论是点击本身,还是敌人的死亡动画都足以使下方菜单中的文本在纹理之间快速切换。同样,这仅适用于一些特定的 OSX 机器。

其他人可以重现吗?有谁知道发生了什么事?我很确定我传递给 WebGL 的数据是正确的。谁能发现数据有问题吗?

我想我可以解决这个问题。

我的代码试图变得聪明,并跟踪哪个纹理绑定到哪个纹理单元,然后仅在需要更改时才重新绑定它们。如果我在每次绘制调用之前总是明确地将纹理绑定到相应的纹理单元,问题就会消失。

在我执行此解决方法之前,在某些情况下我什至能够使 WebGL(在所有 3 个浏览器中)崩溃。在这种情况下,崩溃发生在 Apple Intel 5000 驱动程序中(在 NVidia 机器上没有崩溃)。

我仍然相信这是某种驱动程序错误。我认为纹理绑定在 Apple 的 OpenGL 实现深处的绘制调用之间不知何故被破坏了。除非假设绑定纹理将保持绑定是不安全的?

无论如何,我的解决方法目前有效,所以我会接受它。