0
votes

I'm trying to do a compute pass where I render to a texture that will be used in a draw pass later on. My initial implementation was based on shader storage buffer objects and was working nicely. But I want to apply a computation method that is going to take advantage of the blend hardware of the GPU so I started porting the SSBO implementation to RTT one. Unfortunately the code has stopped working. Now when I read back the texture it is getting wrong values.

Here is my texture and frame buffer setup code:

glGenFramebuffers(1, &m_fbo);
glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);

// Create render textures
glGenTextures(NUM_TEX_OUTPUTS, m_renderTexs);
m_texSize = square_approximation(m_numVertices);
cout << "Textures size: " << glm::to_string(m_texSize) << endl;
GLenum drawBuffers[NUM_TEX_OUTPUTS];
for (int i = 0 ; i < NUM_TEX_OUTPUTS; ++i)
{
    glBindTexture(GL_TEXTURE_2D, m_renderTexs[i]);
    // 1st 0: level, 2nd 0: no border, 3rd 0: no initial data
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_texSize.x, m_texSize.y, 0, GL_RGBA, GL_FLOAT, 0);
    // XXX: do we need this?
    // Poor filtering. Needed !
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glBindTexture(GL_TEXTURE_2D, 0);

    // 0: level
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D, m_renderTexs[i], 0);
    drawBuffers[i] = GL_COLOR_ATTACHMENT0 + i;
}
glDrawBuffers(NUM_TEX_OUTPUTS, drawBuffers);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
{
    cout << "Error when setting frame buffer" << endl;
    // throw exception?
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);

And this is the code to start the compute pass:

m_shaderProgram.use();

// setup openGL
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
glViewport(0, 0, m_texSize.x, m_texSize.y); // setup viewport (equal to textures size)
// make a single patch have the vertex, the bases and the neighbours
glPatchParameteri(GL_PATCH_VERTICES, m_maxNeighbours + 5);

// Wait all writes to shader storage to finish
glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);

glUniform1i(m_shaderProgram.getUniformLocation("curvTex"), m_renderTexs[2]);
glUniform2i(m_shaderProgram.getUniformLocation("size"), m_texSize.x, m_texSize.y);
glUniform2f(m_shaderProgram.getUniformLocation("vertexStep"), (umax - umin)/divisoes,
            (vmax-vmin)/divisoes);

// Bind buffers 
glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ibo);
glBindBufferBase(GL_UNIFORM_BUFFER, m_mvp_location, m_mvp_ubo);

// Make textures active
for (int i = 0; i < NUM_TEX_OUTPUTS; ++i)
{
    glActiveTexture(GL_TEXTURE0 + i);
    glBindTexture(GL_TEXTURE_2D, m_renderTexs[i]);
}

// no need to pass index array 'cause ibo is bound already
glDrawElements(GL_PATCHES, m_numElements, GL_UNSIGNED_INT, 0);

I then read back the textures using the following:

bool readTex(GLuint tex, void *dest)
{
    glBindTexture(GL_TEXTURE_2D, tex);
    glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_FLOAT, dest);
    glBindTexture(GL_TEXTURE_2D, 0);

    // TODO: check glGetTexImage return values for error
    return true;
}

for (int i = 0; i < NUM_TEX_OUTPUTS; ++i)
{
    if (m_tensors[i] == NULL) {
        m_tensors[i] = new glm::vec4[m_texSize.x*m_texSize.y];
    }
    memset(m_tensors[i], 0, m_texSize.x*m_texSize.y*sizeof(glm::vec4));
    readTex(m_renderTexs[i], m_tensors[i]);
}

Finally, the fragment shader code is:

#version 430
#extension GL_ARB_shader_storage_buffer_object: require

layout(pixel_center_integer) in vec4 gl_FragCoord;

layout(std140, binding=6) buffer EvalBuffer {
    vec4 evalDebug[];
};

uniform ivec2 size;

in TEData {
    vec4 _a;
    vec4 _b;
    vec4 _c;
    vec4 _d;
    vec4 _e;
};

layout(location = 0) out vec4 a;
layout(location = 1) out vec4 b;
layout(location = 2) out vec4 c;
layout(location = 3) out vec4 d;
layout(location = 4) out vec4 e;

void main()
{
    a= _a;
    b= _b;
    c= _c;
    d= _d;
    e= _e;
    evalDebug[gl_PrimitiveID] = gl_FragCoord;
}

The fragment coordinates are correct (each fragment is pointing to a x,y coordinate in the texture), so are all the input values (_a to _e), but I do not see them outputted correctly to the textures when reading back. I also tried accessing the texture in the shader to see if it was only a read-back error, but my debug SSBO returned all zeroes.

Am I missing some setup step? I've tested both on linux and windows (titan and 540M geforces) and I'm using openGL 4.3.

1
Can you elaborate on what "when I read back the texture it is getting wrong values" means? - derhass
sure. I do the same computation on CPU and the values I get when reading the texture do not match. But if I read values _a to _e using the evalDebug SSBO they do match. So the shaders are computing things right, but writing to texture is not working. - monstropizza
Well, make sure the texture format you are using matches your requirements. Currently, you are sing an 8 bit normalized unsigned integer texture format, and the GL_FLOAT type parameter isn't going to change that. Only the internalFormat for the texture matters. - derhass
Ha! that was a really dumb mistake. Thanks for pointing it out... After changing to glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, m_texSize.x, m_texSize.y, 0, GL_RGBA, GL_FLOAT, 0); it worked. - monstropizza

1 Answers

1
votes

As derhass pointed out in the comments above, the problem was with the texture format. I assumed that by passing GL_FLOAT as the data type it would use 32bit floats for each of the RGBA channels. It was not so. As derhass said, the data type parameter here does not change the texture format. I had to change the internalFormat parameter to what I wanted (GL_RGBA32F) so that it would work as expected. So, after changing glTexImage2D call to:

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, m_texSize.x, m_texSize.y, 0, GL_RGBA, GL_FLOAT, 0);

I was able to correctly render the results to the texture and read it back. :)