Problem with FBO->PBO->main memory transfer

Hello people,

I’m trying to get my code to work in Linux, but I have yet to see it work unfortunately. I use 2 color attachments on an FBO to do some GPGPU work and I want to use asynchronous PBO transfers to transfer the results from the FBO back to main memory so that I can do something with it. My code works perfectly on my macbook pro (Nvidia 9600GT) but it fails to properly execute on Ubuntu using the latest FGLRX driver with an AMD-Ati HD4870.


glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
glReadBuffer(colorAttachment);
glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, pbo);
glBufferData(GL_PIXEL_PACK_BUFFER_ARB, 4*sizeof(float), NULL, GL_STREAM_READ);
glReadPixels (0, 0, 1, 1, GL_RGBA, GL_FLOAT, 0);

mem = glMapBuffer(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY);   
assert(mem);
  
for (i = 0; i < 4; i++)
{
  ((float *)output->data)[i] = ((float *)mem)[i];
}
  
glUnmapBuffer(GL_PIXEL_PACK_BUFFER_ARB);
glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, 0);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);

I call this code twice but with different PBOs and different color attachments to retrieve all the results. This works as it should on Mac OS X, but Linux only shows me the data on the first call properly. The second call I make to retrieve the data in a different color attachment doesn’t work. Anyone got an idea as to why Linux gives me something different than Mac OS X? (any comments/improvements on my code are always welcome :slight_smile: )

The code seems fine.

I’m somewhat curious as to exactly what you expect to get from using PBOs here. If the very first thing after glReadPixels is a map buffer call to that buffer object, then it’s not going to be any different from just calling glReadPixels to your own memory. The purpose of using PBOs is to make glReadPixels (and other pixel transfers) asynchronous. In order for the async call to be useful, you must put some distance between glReadPixels and any code that accesses the buffer.

Yea that’s true. I did some benchmarking and I found out that PBO transfers do seem to be the fastest way to read from textures. And using this method I still have the possibility to do something inbetween the read/map calls.

Well, it seems that internal non-clamped formats like GL_RGBA32F and GL_RGBA16F don’t work with PBO transfers in Linux/ATI driver version 9.10. When I used the same code for GL_RGBA, it worked perfectly.

I sure hope this is not a driver bug :S

hi,

would it be possible to send us a standalone test case so that we can investigate your issue ?

regards,

Pierre B.
AMD Fellow.

This code should display something like this:
Normal readback : 201.000000 201.000000 201.000000 201.000000
PBO readback : 201.000000 201.000000 201.000000 201.000000
Normal readback : 201.000000 201.000000 201.000000 201.000000
PBO readback : 201.000000 201.000000 201.000000 201.000000

But it instead does something like this:
Normal readback : 201.000000 201.000000 201.000000 201.000000
PBO readback : 201.000000 201.000000 201.000000 201.000000
Normal readback : 201.000000 201.000000 201.000000 201.000000
PBO readback : -1619085020624977094633163538024103936.000000 nan nan -24778363146118103040.000000

The last numbers from the PBO readback are kinda random actually. Sometimes it gives me the same numbers back when I execute the test a couple of times without too much pause inbetween. But when I wait a bit it gives me other numbers, never the right ones though :slight_smile:


#include <GL/glut.h>
#include <GL/glu.h>
#include <GL/gl.h>

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

static int width  = 1;
static int height = 1;

static GLuint texidFBO;
static GLuint texidInput;
static GLuint pboid[1];
static GLuint fbo;
static GLuint shaderProgram;

const char* vertSource = "" \
  "#version 120
" \
  "
" \
  "void main()
" \
  "{
" \
  "  gl_Position    = ftransform();
" \
  "  gl_TexCoord[0] = gl_MultiTexCoord0;
" \
  "}
";

const char* fragSource = "" \
  "#version 120
" \
  "#extension GL_ARB_texture_rectangle : enable
" \
  "
" \
  "uniform sampler2DRect input_image;
" \
  "
" \
  "void main()
" \
  "{
" \
  "  gl_FragData[0] = vec4(texture2DRect(input_image, gl_TexCoord[0].xy)+200.0);
" \
  "}
";

GLuint compileProgram(void)
{
  GLuint program, fragment_shader, vertex_shader;

  program = glCreateProgramObjectARB();
  
  vertex_shader = glCreateShaderObjectARB(GL_VERTEX_SHADER);
  glShaderSourceARB(vertex_shader, 1, &vertSource, NULL);
  glCompileShaderARB(vertex_shader);
  
  fragment_shader = glCreateShaderObjectARB(GL_FRAGMENT_SHADER);
  glShaderSourceARB(fragment_shader, 1, &fragSource, NULL);
  glCompileShaderARB(fragment_shader);
  
  glAttachObjectARB(program, vertex_shader);
  glAttachObjectARB(program, fragment_shader);
  
  glLinkProgramARB(program);
  glValidateProgramARB(program);
  
  glDeleteObjectARB(vertex_shader);
  glDeleteObjectARB(fragment_shader);

  return program;
}

void initGL(int w, int h)
{
  int i;
  GLfloat texdata[4*width*height];

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glOrtho(0.0, width, height, 0.0, -1.0, 1.0);
  
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  glViewport(0,0, width, height);

  glEnable(GL_TEXTURE_RECTANGLE_ARB);
  
  for (i = 0; i < 4*width*height; i++)
  {
    texdata[i] = 0.f;
  }
  
  glGenTextures(1, &texidFBO);
  glBindTexture(GL_TEXTURE_RECTANGLE_ARB, texidFBO);
  glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA32F_ARB, width, height, 0, GL_RGBA, GL_FLOAT, texdata);
  
  glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
  glGenFramebuffersEXT(1, &fbo);
  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
  
  glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_ARB, texidFBO, 0);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  
  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
  glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
  
  glGenBuffers(1, pboid);
}

void initTextureAndShader(void)
{
  GLfloat texdata[4*width*height];
  int i;
  GLuint input_image;
  
  for (i = 0; i < 4*width*height; i++)
  {
    texdata[i] = 1.f;
  }
  
  texidInput = 0;
  
  glGenTextures(1, &texidInput);
  glBindTexture(GL_TEXTURE_RECTANGLE_ARB, texidInput);
  glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGB32F_ARB, width, height, 0, GL_RGB, GL_FLOAT, texdata);
  
  glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
  
  shaderProgram = compileProgram();
  
  glUseProgramObjectARB(shaderProgram);
  input_image = glGetUniformLocationARB(shaderProgram, "input_image");
  glUniform1iARB(input_image, 0);
  glUseProgramObjectARB(0);
}

void doRender(void)
{
  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
  
  glEnable(GL_TEXTURE_RECTANGLE_ARB);
  glBindTexture(GL_TEXTURE_RECTANGLE_ARB, texidInput);
 
  glUseProgramObjectARB(shaderProgram);
  
  glBegin(GL_QUADS);
  glTexCoord2f(width, 0);
  glVertex2f(0, 0);
  glTexCoord2f(0, 0);
  glVertex2f(width, 0);
  glTexCoord2f(0, height);
  glVertex2f(width, height);
  glTexCoord2f(width, height);
  glVertex2f(0, height);
  glEnd();
  
  glUseProgramObjectARB(0);
  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
}

void readBackWithoutPBO(void)
{
  GLfloat data[4*width*height];

  glBindTexture(GL_TEXTURE_RECTANGLE_ARB, texidFBO);
  glGetTexImage(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, GL_FLOAT, data);
  glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
  
  printf("Normal readback : %f %f %f %f
", data[0], data[1], data[2], data[3]);
}

void readBackWithPBO(void)
{
  int i;
  GLfloat* mem;

  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
  glReadBuffer(GL_COLOR_ATTACHMENT0_EXT);
  
  glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, pboid[0]);
  glBufferData(GL_PIXEL_PACK_BUFFER_ARB, 4*sizeof(GLfloat), NULL, GL_STREAM_READ);
  glReadPixels(0, 0, 1, 1, GL_RGBA, GL_FLOAT, NULL);
  
  mem = (GLfloat*)glMapBuffer(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY);
  assert(mem);
  
  printf("PBO readback : %f %f %f %f
", ((GLfloat *)mem)[0], ((GLfloat *)mem)[1], ((GLfloat *)mem)[2], ((GLfloat *)mem)[3]);
  
  glUnmapBuffer(GL_PIXEL_PACK_BUFFER_ARB);
  
  glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, 0);
  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
}

int main ( int argc, char** argv ) 
{
  glutInit(&argc, argv);
  glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE);
  glutInitWindowSize(width, height);
  glutCreateWindow("PBO transfer test");
  
  initGL(width, height);
  initTextureAndShader();
  
  doRender();
  readBackWithoutPBO();
  readBackWithPBO();

  readBackWithoutPBO();
  readBackWithPBO();

  return 0;
}