为何GPU未对每个像素着色器调用都递增原子计数器?
原子计数器未按预期递增的原因分析
相关博客中的以下片段着色器,会将原子计数器的值写入帧缓存像素:
#version 420 compatibility layout(binding = 0, offset = 0) uniform atomic_uint ac; void main(void) { uint counter = atomicCounterIncrement(ac); float r = (counter/255) / 255.f; gl_FragColor = vec4(r, 0, 0, 1); }
因为渲染的图像尺寸是256×256像素,计数器取值范围为0到65535(256×256-1),所以代码里做了两次除以255的操作:先通过整数除法把计数器值压缩到0-255区间,再除以255.f转换成0-1的浮点数,对应红色通道的颜色值。
在GTX460 GPU上的运行结果:
在HD6970 GPU上的运行结果:
从图中能看出,颜色越深的像素对应的计数器值越小。核心问题是:为什么原子计数器没有对每个像素的着色器调用都执行递增?
问题根源
这主要是由GPU的执行机制和优化策略导致的:
- Early-Z/Hi-Z 剔除优化:GPU会在执行像素着色器之前,先做Early-Z测试——判断当前片元是否会被遮挡(比如被前方几何体挡住、超出视锥体范围)。如果片元被判定为不可见,GPU会直接跳过该片元的着色器调用,自然不会执行
atomicCounterIncrement,计数器也就不会递增。 - 片元合并与多采样优化:部分GPU驱动会对完全相同的片元做合并处理,或者在多采样抗锯齿(MSAA)场景下,多个采样点共享同一个着色器执行结果,这也会导致着色器调用次数少于像素总数,计数器递增次数随之减少。
- 内存可见性(次要原因):虽然
atomicCounterIncrement是原子操作,但如果没有配置正确的内存屏障,不同线程组之间的计数器值同步可能存在延迟,但这种情况更多表现为结果无序,而非计数缺失。
说白了,GPU不会为所有“理论上存在”的像素都运行一次着色器,只会为实际会出现在最终画面里的可见片元执行,这就导致计数器的递增次数不等于像素总数。
内容的提问来源于stack exchange,提问作者Zebrafish
相关产品推荐
相关产品推荐

