转载请注明出处为KlayGE游戏引擎,本文的永久链接为http://www.klayge.org/?p=2526

前两年我曾经写过几篇关于AMD显卡上OpenGL驱动的陷阱,但原先我只在NV和AMD的卡上测试过KlayGE的例子,还从来没在Intel的集成显卡上测试过。前段时间曾经在Intel HD3000的笔记本上小测一下,结果惨不忍睹,所有的例子全部黑屏。最近在做KlayGE 4.3的最终测试和优化,就试图找一下失败的原因,以及修复的方法。这里把遇到的一些大坑总结一下,希望对遇到类似问题的朋友有所提示。

GLSL

所有例子都黑屏,最有可能的就是shader挂了。Debug下打开shader错误输出,果然,在NV和AMD驱动上都没事的GLSL遇到了编译失败。错误行是PS里的varying out vec4 v_gl_FragData。如果不用自定义的varying out,而用系统内建的gl_FragData就没事。所以判断应该就是这行造成的。翻阅gl spec之后,发现只有GLSL 1.2及以上才支持在PS里定义varying out。我这里生成的GLSL没有#version,所以Intel驱动把它当作最基础的#version 110来处理了。在NV和AMD上,如果没有#version,似乎当作是驱动支持的最高版本,但Intel的驱动没有这个容错能力,就按照glsl spec的说法,按照最低的版本。所以修正也很简单,在GLSL生成器里加上#version 120,就解决了。一些基本的例子恢复运行。

Vertex Array Object

在修正了GLSL之后,接着尝试运行复杂一点的例子。结果mesh全乱了。原先以为是顶点太多之类,结果简化到就剩个几百个顶点的teapot仍然出错。逐帧分析发现,第一帧是对的,接着就错了。Vertex Array Object(VAO)的使用符合这个模式,第一帧的时候调用glBindVertexBuffer绑定,接下去就只是使用VAO。于是在Intel卡上尝试暂时不用VAO,混乱的mesh恢复正常。在stackoverflow的一个帖子上发现别人也有遇到这样的问题,是因为Intel的VAO实现里,index buffer并不会被记录,需要每次都绑定。这与VAO的spec不符,应该是个驱动bug。测试一下果然如此,目前在Intel卡上仍然使用VAO,但每次使用前都绑一下IB,问题就解决了。至此,不少例子恢复运行。

sRGB

这是我迄今为止在图形系统中遇到的最奇葩的bug,它的表现不会让你想到是sRGB造成的。具体行为是,如果用了HDR的post process,那么屏幕左边一半是花屏,右边一半是黑屏。如果只有LDR的post process,那么颜色错乱。只有关闭了所有的render to texture,包括shadow map在内,结果才能看起来正常一些。因为LDR post process的最后一个环节是gamma correction和color grading,所以我就在gamma这里找问题。经过很长时间的尝试,最后发现如果不调用glEnable(GL_FRAMEBUFFER_SRGB),就不会颜色错乱。查看GL_EXT_framebuffer_sRGB发现,如果启用了framebuffer的sRGB,那么系统应该要检测目前绑在fbo上的color attachment是sRGB的,就把线性空间的颜色转换到sRGB空间,写入fbo。但在Intel的卡上,一个超大的bug使得这个行为变成了,只要启用了sRGB,就一定做转换,并且不是按像素来转换,而是把目标当作ABGR_sRGB这样的格式来强制转换。这解释了为何在HDR post process的时候,屏幕一边花一边黑了。ABGR16F是64bpp,而ABGR_sRGB是32bpp,Intel的驱动完全忽略目标像素格式,直接把32bit写道ABGR16F的纹理。这个实在太发指了,不管目标是不是float,不管目标有几个通道,都转一把,结果就是乱七八糟的颜色。所以这里我改成,只有目标是sRGB,才打开framebuffer的sRGB标志。绝大部分例子都正常了,包括以前shadow map有错的(连这都转!)。

尚未解决的问题

目前还有两个例子还不能得到完全正确的结果。如果有知道的朋友不妨提醒一下。

Model viewer

光照在model viewer里是缺失的,只有个ambient。奇怪的就是在其他deferred框架的例子里一切正常。似乎是stencil buffer不正确是的lighting的shader没被执行。

JudaTex viewer

judatex viewer里有些tile的是乱的,寻址到了错误的block。似乎是由于精度问题,在计算间接地址的时候得到了偏移的结果。