在OpenGL/GLSL 4.3中读取和更新纹理缓冲区

8

由于我不知道哪里出了问题,有些不太清楚。我可能对某些事情存在着巨大的误解,或者代码中存在某种错误或驱动程序存在一些错误。我正在使用上周发布的最新Catalyst beta驱动程序在AMD Radeon 5850上运行。

好的,我开始实现OIT-rendering并想要使用存储在shader storage缓冲对象中的结构数组。嗯,数组中的索引在内存中反映/移动得很不正确,我几乎认为这是一个驱动程序错误——因为他们刚刚开始支持这种东西+是beta驱动程序。因此,我退回到使用来自纹理缓冲对象的glsl图像,这应该至少在一段时间之前就已经被支持了。

仍然没有正确地工作。所以我创建了一个简单的测试项目,摸索了一下,现在我认为我已经找到了问题所在。

好的!首先,我初始化缓冲区和纹理。

//Clearcolor and Cleardepth setup, disabling of depth test, compile and link shaderprogram etc.
...
//
GLint tbo, tex;
datasize = resolution.x * resolution.y * 4 * sizeof(GLfloat);
glGenBuffers(1, &tbo);
glBindBuffer(GL_TEXTURE_BUFFER, tbo);
glBufferData(GL_TEXTURE_BUFFER, datasize, NULL, GL_DYNAMIC_COPY);
glBindBuffer(GL_TEXTURE_BUFFER, 0);

glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_BUFFER, tex);
glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32F, tex);
glBindTexture(GL_TEXTURE_BUFFER, 0);
glBindImageTexture(2, tex, 0, GL_TRUE, 0, GL_READ_WRITE, GL_RGBA32F);

然后就是渲染循环-更新和绘制,更新和绘制...之间有延迟,这样我有时间看更新做了什么。

更新的代码像这样...

ivec2 resolution; //Using GLM
resolution.x = (GLuint)(iResolution.x + .5f);
resolution.y = (GLuint)(iResolution.y + .5f);

glBindBuffer(GL_TEXTURE_BUFFER, tbo);
void *ptr = glMapBuffer(GL_TEXTURE_BUFFER, GL_WRITE_ONLY);
color *c = (color*)ptr; //color is a simple struct containing 4 GLfloats.
for (int i = 0; i < resolution.x*resolution.y; ++i)
{
  c[i].r = c[i].g = c[i].b = c[i].a = 1.0f;
}
glUnmapBuffer(GL_TEXTURE_BUFFER); c = (color*)(ptr = NULL);
glBindBuffer(GL_TEXTURE_BUFFER, 0);

而这个绘画就是这样的...

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

glMemoryBarrier(GL_ALL_BARRIER_BITS);
ShaderProgram->Use(); //Simple shader program class
quad->Draw(GL_TRIANGLES); //Simple mesh class containing triangles (vertices) and colors
glFinish();
glMemoryBarrier(GL_ALL_BARRIER_BITS);

我刚刚加了一些内存屏障,以确保更加安全,这不会对性能造成太大影响,对吧?嗯,实际上无论是否使用内存屏障,结果都是一样的...

Shader程序是一个简单的顶点着色器和一个执行测试的片段着色器。

顶点着色器

#version 430

in vec3 in_vertex;

void main(void)
{
    gl_Position = vec4(in_vertex, 1.0);
}

片段着色器(我猜在这里不需要一致性和memoryBarrier(),因为我在绘图/片段着色器执行之间在CPU上进行了处理...但这会有影响吗?)

#version 430

uniform vec2 iResolution;
layout(binding = 2, rgba32f) coherent uniform imageBuffer colorMap;

out vec4 FragColor;

void main(void)
{
    ivec2 res = ivec2(int(iResolution.x + 0.5), int(iResolution.y + 0.5));
    ivec2 pos = ivec2(int(gl_FragCoord.x + 0.5), int(gl_FragCoord.y + 0.5));
    int pixelpos = pos.y * res.x + pos.x;

    memoryBarrier();
    vec4 prevPixel = imageLoad(colorMap, pixelpos);

    vec4 green = vec4(0.0, 1.0, 0.0, 0.0);
    imageStore(colorMap, pixelpos, green);
    FragColor = prevPixel;
}

期望:一个白屏!因为即使在实际的着色器中加载图像后,我也会在每次绘制之间将“白色”写入整个缓冲区,而不仅仅是将绿色写入图像。
结果:第一帧是绿色的,其余都是黑色的。有一部分人认为可能存在一个太快无法看到的白色帧或一些vsync问题,但这是逻辑上的问题吗? :P
好吧,然后我尝试了一个新的事情,并将更新块(我在其中将“白色”写入整个缓冲区)移动到初始位置。
期望:首帧为白色,随后是绿色屏幕。
结果:哦,它肯定是绿色的!即使第一帧带有一些白/绿色的伪影,有时只有绿色。这可能是由于(缺乏)vsync之类的原因,我还没有检查过。尽管如此,我认为我得到了我想要的结果。
我可以从中得出的结论是我的更新有问题。它是否将缓冲区与纹理引用取消连接或其他原因?如果是这种情况,那么第一帧还正常不奇怪吗?仅在第一个imageStore命令(嗯,第一帧)之后,纹理变成全黑 - bind()-map()-unmap()-bind(0)在第一次工作,但之后不会。我对glMapBuffer的印象是它将缓冲区数据从GPU复制到CPU内存,让您对其进行更改,并且Unmap将其复制回来。嗯,刚才我想到它可能不会从GPU复制缓冲区到CPU然后再复制回来,而只有一个方向?GL_WRITE_ONLY应该改为GL_READ_WRITE,可能是这样吗?嗯,我两者都尝试过。假设其中一个是正确的,那么当使用其中一个时,“测试1”中的屏幕不总是白色吗?
啊,我错了什么?
编辑: 好吧,我还是不知道...显然 glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32F, tex); 应该是 glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32F, tbo);,但我认为tbo和tex具有相同的值,因为它们按相同的顺序生成。因此,在这个实现中它能正常工作。 不过我已经找到了解决方法,虽然我并不太满意,因为我真的认为上述方法应该可行。另一方面,新的解决方案在性能方面可能更好一些。 我放弃了使用glMapBuffer(),改用通过使用glBufferSubData()和glgetBufferSubData()在CPU上保留tbo内存的副本,并用它们在CPU/GPU之间发送数据。这样可以正常工作,因此我将继续使用该解决方案。
但是,是的,问题仍然存在 - 为什么glMapBuffer()无法使用我的纹理缓冲对象?
1个回答

4

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接