最近发现自己的 WebGL 应用在部分 Android 手机上部分元素绘制不出来。于是找来多台手机测试,发现出问题的机器所用 GPU 都是 Mali 系列,因此怀疑 WebGL 异常和 GPU 相关。
将手机连接到电脑,通过 Chrome 的插件 ADB Plugin 来远程调试,发现控制台出现如下错误:
WebGL: INVALID_OPERATION: drawElements: no buffer is bound to enabled attribute
我把同样的绘制过程更改到程序里另一个功能近似的 shader 就没问题,因此初步定位问题出在 shader 里。
问题既然出在 attribute
上,那就从这里开始排查,首先我打印出绘制这个元素时所有启用的 attribute
有哪些,于是发现了异常。在顶点着色器代码中我定义了 4 个 attribute
:
attribute vec2 a_pos; attribute vec2 a_normal; attribute vec4 a_color; attribute vec4 a_data;
但是通过下面代码获取出来的只有 3 个:
var numAttributes = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES);
而其他渲染正常的手机上都是 4 个。于是我在网上搜索这个问题,找到了这篇文章,了解到这是编译器做了优化,把代码中不需要的属性自动优化掉了。仔细看了代码逻辑发现属性 a_color
确实没用上,具体逻辑是这样的,在顶点着色器中 a_color
是这样赋值给 v_color
的:
v_color = vec4(a_color.rgb, 0.0);
而在片元着色器里只用到了 v_color.a
,这就相当于只用了一个常量 0.0
。于是 a_color
就舍弃了。
修改方法
最优的修改方法当然是去掉顶点数据中的 a_color
属性,但是由于最初的设计这类元素与另一类元素共用了一套数据组织方案,因此想修改成本略高,于是想到这个快速解决办法:
v_color = vec4(a_color.rgb, floor(a_color.a / 10.0));
这样,既能用到 a_color
属性,又保证了 a_color.a
是 0.0
。编译之后果然问题解决了。
一些疑问
虽然问题解决了,但是还是有个疑问,shader 编译优化没有问题,但是优化之后报了错误是为什么呢。“no buffer is bound to enabled attribute” 这个错误本意是说启用了某个属性,但是没有数据来填充给它,但是这里的情况正好反过来,数据都是有的,而并不是所有预定义的属性都得到了启用。如果有谁对这个细节问题了解,也欢迎在评论里帮忙解答。
支持一下
2022年,翻问题的时候翻到了贾老师的文章
你在 grab?