Try this short, stand-alone GLUT test program:
#include <stdio.h>
#include <math.h>
#include <string.h>
#define GL_GLEXT_PROTOTYPES 1
#include <GL/gl.h>
#include <GL/glext.h>
#include <GL/glu.h>
#include <GL/glut.h>
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
const int TF_NUM_OUT = 2;
const int TF_OUT_COUNT = 3;
const int TF_OUT_SIZE = TF_OUT_COUNT * sizeof(float);
GLuint Tf_pgm = 0; // Transform feedback program
GLuint Tf_obj = 0; // Transform feedback object
GLuint Tf_outbuf[ TF_NUM_OUT ]; // Transform feedback output buffer
const int NUM_ATTR = 1;
GLuint Vbo_handle[ NUM_ATTR ]; // Positions
static const char TF_VERT_SHADER[] = R"MAGIC(
#version 330
in vec3 vertex;
out vec3 pos;
void main(void)
{
pos = vertex;
};
)MAGIC";
static const char TF_GEOM_SHADER[] = R"MAGIC(
#version 400
layout( points ) in ;
layout( points, max_vertices = 1 ) out;
// Inputs
in vec3 pos[1];
// Outputs
layout( stream=0 ) out float stream0;
layout( stream=1 ) out float stream1;
void main(void)
{
int route = ( pos[0].z >= 1 ) ? 1 : 0;
switch ( route )
{
case 0 : stream0 = 1234; EmitStreamVertex(0); break;
case 1 :
default : stream1 = 5678; EmitStreamVertex(1); break;
}
};
)MAGIC";
//----------------------------------------------------------------------------
void check( const char *s )
{
GLenum err ;
bool found = false;
while ( (err = glGetError()) != GL_NO_ERROR )
{
found = true;
fprintf ( stderr, "%s: OpenGL Error: %s
",
s ? s : "", gluErrorString ( err ) );
}
if ( found )
exit(1);
}
//----------------------------------------------------------------------------
void dumpShaderLog( bool is_shader, GLuint obj )
{
int maxlen = 0;
if ( is_shader ) glGetShaderiv ( obj, GL_INFO_LOG_LENGTH, &maxlen );
else glGetProgramiv( obj, GL_INFO_LOG_LENGTH, &maxlen );
if ( maxlen > 0 )
{
char *log = new char [ maxlen ] ;
int len ;
if ( is_shader ) glGetShaderInfoLog ( obj, maxlen, &len, log );
else glGetProgramInfoLog( obj, maxlen, &len, log );
if( len > 0 && log[0] != '\0' )
fprintf( stderr, "%s
", log ) ;
delete [] log ;
}
}
//----------------------------------------------------------------------------
#define getGL( e, str_width ) \
do \
{ \
GLint v; \
glGetIntegerv( e, &v ); \
printf( "%-*s = %d
", str_width, #e, v ); \
} \
while ( 0 )
//----------------------------------------------------------------------------
void queryGL()
{
int w = 48;
printf( "
" );
getGL( GL_MAX_GEOMETRY_OUTPUT_VERTICES , w );
getGL( GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS , w );
getGL( GL_MAX_VERTEX_STREAMS , w );
getGL( GL_MAX_TRANSFORM_FEEDBACK_BUFFERS , w );
getGL( GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS, w );
printf( "
" );
}
//----------------------------------------------------------------------------
void initTFProgram( GLuint &tf_pgm )
{
// Read shader text
const char *vert_src = TF_VERT_SHADER;
const char *geom_src = TF_GEOM_SHADER;
// Compile shaders
GLint compiled;
GLuint vs = glCreateShader( GL_VERTEX_SHADER );
GLuint gs = glCreateShader( GL_GEOMETRY_SHADER );
glShaderSource ( vs, 1, &vert_src, 0 );
glShaderSource ( gs, 1, &geom_src, 0 );
glCompileShader( vs );
glGetShaderiv ( vs, GL_COMPILE_STATUS, &compiled );
if ( !compiled )
{
fprintf( stderr, "TF vertex shader: Failed to compile.
" );
dumpShaderLog( true, vs );
exit(1);
}
glCompileShader( gs );
glGetShaderiv ( gs, GL_COMPILE_STATUS, &compiled );
if ( !compiled )
{
fprintf( stderr, "TF geometry shader: Failed to compile.
" );
dumpShaderLog( true, gs );
exit(1);
}
GLuint pgm = glCreateProgram();
glBindAttribLocation( pgm, 0, "vertex" );
//--------------------------------------------------------------------------
// Designate TRANSFORM FEEDBACK input varyings, ordering, and storage mode
//--------------------------------------------------------------------------
const GLchar *strs[] = { "stream0", "gl_NextBuffer", "stream1" };
glTransformFeedbackVaryings( pgm, ARRAY_SIZE(strs), strs,
GL_INTERLEAVED_ATTRIBS );
// Link program
glAttachShader ( pgm, vs );
glAttachShader ( pgm, gs );
glLinkProgram ( pgm ) ;
GLint linked;
glGetProgramiv ( pgm, GL_LINK_STATUS, & linked ) ;
if ( !linked )
{
fprintf( stderr, "TF program: Failed to link.
" );
dumpShaderLog( false, pgm );
exit(1);
}
tf_pgm = pgm;
check( "initTFProgram" );
}
//----------------------------------------------------------------------------
void initTFObject( GLuint &tf, GLuint outbuf[ TF_NUM_OUT ] )
{
//--------------------------------------------------------------------------
// Create TRANSFORM FEEDBACK object
//--------------------------------------------------------------------------
glGenTransformFeedbacks ( 1, &tf );
glBindTransformFeedback ( GL_TRANSFORM_FEEDBACK, tf );
// Transform feedback:
// INPUTS : are program varying(s) (VS or GS output(s))
// OUTPUTS: are buffer(s)
// INPUT: (varying)
// NOTE: Input varyings, ordering, and storage mode specified above
// before we linked the program. Those are "program state".
// OUTPUT: (buffer)
for ( int i = 0; i < TF_NUM_OUT; i++ )
{
glGenBuffers ( 1, &outbuf[i] );
glNamedBufferDataEXT ( outbuf[i], TF_OUT_SIZE, 0, GL_DYNAMIC_DRAW );
glBindBufferBase ( GL_TRANSFORM_FEEDBACK_BUFFER, i, outbuf[i] );
}
// NOTE: If had wanted to output to a subrange of a buffer, could have
// used glBindBufferRange.
glBindTransformFeedback ( GL_TRANSFORM_FEEDBACK, 0 );
check( "initTFObject" );
}
//----------------------------------------------------------------------------
void initBatch()
{
static const GLfloat pos [ TF_OUT_COUNT*3 ] = { 0,0,0,
0,0,1,
0,0,2 };
// Create and fill VBOs
glGenBuffers( NUM_ATTR, Vbo_handle );
GLenum gl_target = GL_ARRAY_BUFFER;
// Positions...
glBindBuffer( gl_target, Vbo_handle[0] );
glBufferData( gl_target, sizeof(pos) , pos , GL_STATIC_DRAW );
}
//----------------------------------------------------------------------------
void init()
{
queryGL();
// Create transform feedback program and object
initTFProgram( Tf_pgm );
initTFObject ( Tf_obj, Tf_outbuf );
initBatch ();
check( "init" );
}
//----------------------------------------------------------------------------
void classifyPoints()
{
//--------------------------------------------------------------------------
// Write to TRANSFORM FEEDBACK buffer with vertex shader executions
//--------------------------------------------------------------------------
glEnable ( GL_RASTERIZER_DISCARD );
glUseProgram( Tf_pgm );
glBindTransformFeedback ( GL_TRANSFORM_FEEDBACK, Tf_obj );
glBeginTransformFeedback( GL_POINTS );
// Render batch (classic VBOs)
glBindBuffer ( GL_ARRAY_BUFFER, Vbo_handle[0] );
glVertexAttribPointer ( 0, 3, GL_FLOAT, false, 0, 0 );
glEnableVertexAttribArray( 0 );
glDrawArrays ( GL_POINTS, 0, 3 );
glEndTransformFeedback();
glUseProgram( 0 );
glDisable ( GL_RASTERIZER_DISCARD );
check( "classifyPoints" );
}
//----------------------------------------------------------------------------
float *mapBuffer( int i )
{
return (GLfloat *) glMapNamedBufferRangeEXT( Tf_outbuf[i], 0, TF_OUT_SIZE,
GL_MAP_READ_BIT );
}
//----------------------------------------------------------------------------
void unmapBuffer( int i )
{
glUnmapNamedBufferEXT( Tf_outbuf[i] );
}
//----------------------------------------------------------------------------
void display()
{
glClearColor(0.4, 0.4, 0.8, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Clear TF output
memset( mapBuffer(0), '\0', TF_OUT_SIZE ); unmapBuffer(0);
memset( mapBuffer(1), '\0', TF_OUT_SIZE ); unmapBuffer(1);
// Generate queries to determine how many entries written
GLuint outquery[ TF_NUM_OUT ];
glGenQueries( TF_NUM_OUT, outquery );
for ( int i = 0; i < TF_NUM_OUT; i++ )
glBeginQueryIndexed( GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, i,
outquery[i] );
// Transform feedback (bin points into 2 different lists)
classifyPoints();
// Query/print how many entries written
GLint counts[ TF_NUM_OUT ];
for ( int i = 0; i < TF_NUM_OUT; i++ )
{
glEndQueryIndexed ( GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, i );
glGetQueryObjectiv( outquery[i], GL_QUERY_RESULT, &counts[i] );
printf( "Num primitives written for output %d = %d
", i, counts[i] );
}
glDeleteQueries( TF_NUM_OUT, outquery );
// Dump TF output
for ( int i = 0; i < TF_NUM_OUT; i++ )
{
printf( "Transform feedback output %d =", i );
GLfloat *p = mapBuffer(i);
for ( int j = 0; j < counts[ i ]; j++ )
{
printf( " %g", p[j] );
}
unmapBuffer(i);
printf( "
" );
}
glutSwapBuffers();
glutPostRedisplay();
check( "display" );
exit(0);
}
//----------------------------------------------------------------------------
void reshape(int w, int h)
{
glViewport(0, 0, w, h);
}
//----------------------------------------------------------------------------
void keyfunc( unsigned char key, int x, int y )
{
switch(key)
{
case 27 : exit(0); break; // ESCape
}
}
//----------------------------------------------------------------------------
int main(int argc, char **argv)
{
glutInit( &argc, argv );
glutInitWindowSize( 640, 480 );
glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH );
glutCreateWindow( "Transform Feedback" );
glutReshapeFunc ( reshape );
glutDisplayFunc ( display );
glutKeyboardFunc( keyfunc );
init();
glutMainLoop();
return 0;
}
Compile with:
g++ -std=c++11 -o tf_test tf_test.cxx -lglut -lGLU -lGL
It should generate this output:
GL_MAX_GEOMETRY_OUTPUT_VERTICES = 1024
GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS = 1024
GL_MAX_VERTEX_STREAMS = 4
GL_MAX_TRANSFORM_FEEDBACK_BUFFERS = 4
GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS = 128
Num primitives written for output 0 = 1
Num primitives written for output 1 = 2
Transform feedback output 0 = 1234
Transform feedback output 1 = 5678 5678
Works fine on NVidia. That said, that does “not” necessarily means there’s no errors in it. But if it doesn’t work for you on Intel, it could be that you’re fighting a driver bug.