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

在下一版的KlayGE中,D3D9插件光荣退休,OpenGL插件将要取代它在WinXP下的位置。众所周知,ATI的OpenGL驱动一直毛病众多。因此解决OpenGL插件在ATI卡上出现的错误成了当务之急。本文就讨论一下在增强OpenGL插件的过程中遇到的陷阱及其解决方法。测试的显卡为HD5870,所有问题在9.12-10.10的驱动中都会出现。

glBlitFramebufferEXT

在以前的版本里,OpenGL插件在ATI显卡上的文字显示总是乱码。一开始怀疑是vbo或者shader造成的,最后定位在texture本身。在texture的局部拷贝过程中,OpenGL插件用的是GL_EXT_framebuffer_blit扩展的glBlitFramebufferEXT。ATI驱动对GL_EXT_framebuffer_blit的支持似乎并不完善,结果是虽然什么错误都没发生,但拿目标texture来使用的时候总是一团乱麻,如下图所示:

错误的text结果

如果这个时候用glGetTexImage获取texture数据,却又是正确的。绕开这个问题的方法就是用pbo把源texture和目标texture都map出来,然后memcopy;或者更传统的gluScaleImage。此问题修好之后,所有例子的文字显示在ATI卡上恢复了正常:

正确的Text结果

glClearBufferfi/glClearBufferfv/glClearBufferiv

在一些例子,比如Parallax中,出现了画面空白的现象。clear color buffer能起作用,但depth/stencil buffer却没有被正确地清除。在支持OpenGL3及以上的情况下,OpenGL插件会默认调用glClearBuffer*的函数来直接清除buffer,而不用传统的方法:先调用glClearColor,glClearDepth,glClearStencil设置清除的值,然后调用glClear来完成清除的工作。在ATI卡上,似乎glClearBufferfi/glClearBufferfv/glClearBufferiv这些用来清除depth/stencil的函数并没有起作用。解决方法只能是退回到传统的Clear方法。

这么做之后,绝大部分例子都能正常渲染了:

正确的Parallax结果

glBeginConditionalRender

Deferred shading的例子用query conditional来判断一个光源是否可见。原先的版本在ATI卡上一执行就发生系统死锁,必须强制冷启动才能解决,破坏性极大,而且完全没有机会调试。(难道是因为query conditional是NV提出来的缘故?)一次凑巧看了http://www.g-truc.net/post-0318.html,确定就是因为query conditional造成了死锁。

把相关语句绕开之后Deferred shading恢复正常了。正确的DeferredShading结果

GLSL

GLSL的问题其实不能算是陷阱,但值得关注。ATI驱动的GLSL编译器可谓中规中矩,符合GLSL标准的shader一般没啥问题,但不符合标准的会死得很难看。NVIDIA的GLSL编译器则大不相同,兼容很多NV自己的“扩展”,比如支持数组的varying。所以其实在ATI卡上能工作的GLSL基本也能在NV的卡上工作。如果你希望你的GLSL可以在ATI平台上跑,我的推荐是用AMD GPU ShaderAnalyzer测试一下(GPU ShaderAnalyzer是个纯软件的分析器,在NV的平台上也能运行)。如果GLSL有问题,就会立刻显现出来。

经过一系列的尝试,Cg到GLSL的转化终于可以在ATI平台上顺利执行,包括Geometry shader:

正确的Geometry shader结果

以上就是我在KlayGE中遇到的ATI OpenGL驱动陷阱。希望对大家以后遇到的问题有所帮助。