Transform Feedback

I recently wrote the program displayed below to play around with transform feedback under GL 3.3. Unfortunately it doesn’t work and a weekend of searching the web + the forums didn’t deliver much useful information.
In the program below, the lines commented with “/* feedback */” contain a call to glDrawArrays which appears to generate an invalid value error, despite not matching any of the conditions for such an error in the man-page of glBeginTransformFeedback(no geometry shader, same primitive mode).
I appreciate any help, but please don’t comment on me dropping references to objects and not doing any cleanups. It is just for playing around and I didn’t want to clutter the code with cleanups.

The test program itself:


#include "OpenGL.h"
#include "window.h"
#include "matrix.h"
#include "tools.h"

#include <stdio.h>
#include <stdlib.h>


#define WIDTH  800
#define HEIGHT 600

#define NUM_PARTICLES 100

GLfloat initial_particles[ NUM_PARTICLES*8 ];

const char* vars[] = { "gl_Position", "velocity" };

void generate_initial_particles( );

int main( void )
{
    char *vert_source, *frag_source;

    GLfloat matrix[16];
    GLuint vao, vbo[2];
    GLuint vertex_shader, fragment_shader;
    GLuint feedback_program, draw_program;
    GLuint uniform_projection, uniform_modelview;

    /********************* window initialisation *********************/
    window_init( );

    if( !window_open( WIDTH, HEIGHT, 0, GL_FALSE ) )
        return -1;

    window_set_vsync( GL_TRUE );

    /********************* OpenGL initialisation *********************/
    glViewport( 0, 0, WIDTH, HEIGHT );

    /* VBOs & VAO */
    generate_initial_particles( );

    glGenVertexArrays( 1, &vao );
    glGenBuffers( 2, vbo );

    glBindVertexArray( vao );
    glBindBuffer( GL_ARRAY_BUFFER, vbo[1] );

    glBufferData( GL_ARRAY_BUFFER, NUM_PARTICLES*8*sizeof(GLfloat),
                  NULL, GL_STREAM_COPY );

    glBindBuffer( GL_ARRAY_BUFFER, vbo[0] );

    glBufferData( GL_ARRAY_BUFFER, NUM_PARTICLES*8*sizeof(GLfloat),
                  initial_particles, GL_STREAM_COPY );

    glEnableVertexAttribArray( 0 ); /* position */
    glEnableVertexAttribArray( 1 ); /* velocity */

    glVertexAttribPointer( 0, 4, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*8, 0 );
    glVertexAttribPointer( 1, 4, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*8,
                           (GLvoid*)(sizeof(GLfloat)*4) );

    /* feedback program */
    vert_source = load_file_into_string( "p_feedback.vert" );

    vertex_shader    = glCreateShader( GL_VERTEX_SHADER );
    feedback_program = glCreateProgram( );

    glShaderSource( vertex_shader, 1, (const GLchar**)&vert_source, NULL );

    free( vert_source );

    glCompileShader( vertex_shader );

    glAttachShader( feedback_program, vertex_shader );

    glTransformFeedbackVaryings( feedback_program, 2, vars,
                                 GL_INTERLEAVED_ATTRIBS );

    glLinkProgram( feedback_program );

    /* drawing program */
    vert_source = load_file_into_string( "p_draw.vert" );
    frag_source = load_file_into_string( "p_draw.frag" );

    vertex_shader    = glCreateShader( GL_VERTEX_SHADER );
    fragment_shader  = glCreateShader( GL_FRAGMENT_SHADER );
    draw_program     = glCreateProgram( );

    glShaderSource( vertex_shader,   1, (const GLchar**)&vert_source, NULL );
    glShaderSource( fragment_shader, 1, (const GLchar**)&frag_source, NULL );

    free( vert_source );
    free( frag_source );

    glCompileShader( vertex_shader );
    glCompileShader( fragment_shader );

    glAttachShader( draw_program, vertex_shader );
    glAttachShader( draw_program, fragment_shader );

    glLinkProgram( draw_program );



    uniform_projection = glGetUniformLocation( draw_program, "projection" );
    uniform_modelview  = glGetUniformLocation( draw_program, "modelview" );

    glUseProgram( draw_program );

    mat4_set_projection_perspective( matrix, 60.0f,
                                     (float)WIDTH/(float)HEIGHT,
                                     0.1f, 500.0f );

    glUniformMatrix4fv( uniform_projection, 1, GL_FALSE, matrix );

    mat4_set_identity( matrix );
    mat4_translate( matrix, 0.0, 0.0, -10.0f );

    glUniformMatrix4fv( uniform_modelview, 1, GL_FALSE, matrix );

    /********************* rendering loop *********************/
    glClearColor( 1.0f, 1.0f, 1.0f, 1.0f );
    glPointSize( 5.0f );

    while( window_handle_messages( ) )
    {
        GLuint temp;

        glClear( GL_COLOR_BUFFER_BIT );

        glBindBuffer( GL_ARRAY_BUFFER, vbo[0] );
        glBindBufferBase( GL_TRANSFORM_FEEDBACK_BUFFER, 0, vbo[1] );

        /* draw */
        glUseProgram( draw_program );
        glDrawArrays( GL_POINTS, 0, NUM_PARTICLES );

        /* feedback */
        glEnable( GL_RASTERIZER_DISCARD );
        glUseProgram( feedback_program );

        glBeginTransformFeedback( GL_POINTS );
        glDrawArrays( GL_POINTS, 0, NUM_PARTICLES );
        glEndTransformFeedback( );
        glDisable( GL_RASTERIZER_DISCARD );

        /* swap the buffers */
        temp   = vbo[0];
        vbo[0] = vbo[1];
        vbo[1] = temp;

        window_swap_buffers( );
    }

    window_close( );
    window_cleanup( );

    return 0;
}

void generate_initial_particles( )
{
    size_t i;

    for( i=0; i<NUM_PARTICLES; ++i )
    {
        initial_particles[ i*8     ] = 0.0f;
        initial_particles[ i*8 + 1 ] = 0.0f;
        initial_particles[ i*8 + 2 ] = 0.0f;
        initial_particles[ i*8 + 3 ] = 1.0f;

        initial_particles[ i*8 + 4 ] = 1.0f;
        initial_particles[ i*8 + 5 ] = 10.0f;
        initial_particles[ i*8 + 6 ] = 0.0f;
        initial_particles[ i*8 + 7 ] = 0.0f;
    }
}

The feedback vertex shader “p_feedback.vert”:


#version 330

layout(location=0) in vec4 V_POSITION;
layout(location=1) in vec4 V_VELOCITY;

out vec4 velocity;

void main( )
{
    velocity = V_VELOCITY - vec4(0.0,9.81,0.0,0.0) / 60.0;

    gl_Position = V_POSITION + V_VELOCITY / 60.0;
}

The drawing vertex shader “p_draw.vert”:


#version 330

uniform mat4 projection;
uniform mat4 modelview;

layout(location=0) in vec4 V_POSITION;
layout(location=1) in vec4 V_VELOCITY;

void main( )
{
    gl_Position = projection * modelview * V_POSITION;
}

The drawing fragment shader “p_draw.vert”:


#version 330

layout(location=0) out vec3 COLOR0;

void main( )
{
    COLOR0 = vec3( 1.0, 0.0, 0.0 );
}

OS: Debian Lenny GNU/Linux (i686)
Graphics Card: nvidia gtx 285
Driver version: 280.13

Thanks in advance,

Dave

layout(location=0) in vec4 V_POSITION;
layout(location=1) in vec4 V_VELOCITY;

is not matching with your defined TFB varyings of

const char* vars = { “gl_Position”, “velocity” };

Sorry, but this makes little sense.

glTransformFeedbackVaryings specifies varyings to record(i.e. what comes out of the shader). In the first quote, I specify attributes(i.e. what goes into the shader).

Varying and attribute names having to match would be a little strange, right?

yep, strike that. I was looking at the wrong vertex shader and noticed it didn’t have the right ‘varyings’.
As you say, the shader out vars and the ‘varyings’ need to match, which they seem to do.

I’d try avoiding gl_Position as a varying to be recorded.

Also, use glMapBuffer() to read-back the results, for debugging purposes.

You should reset the vertexatrribpointer after the buffer object swap.

Perhaps you should have a more intensive vertex array usage, instead of providing a “default” vertex array.

Thanks for the answers. Reconfiguring the vertex attributes between drawing and feedback somehow magically solves the problem(!?).

The reason why I only use one VAO here is -as previously stated- for shrinking the overall code size as I use this code purely for experimenting.

–dave

Reconfiguring the attributes isn’t magic. VertexAttribPointer captures the ARRAY_BUFFER_BINDING at the time of the call so even if you the bind different buffers in the drawing loop it will never affect the state captured by the VAO and the draw call will always read data from the original buffer binding.