I have a 5 megapixel texture that I am having trouble updating. The texture is displayed on a rectangle not dissimilar to a video stream.
The OpenGL commands execute quickly, but the real texture update rate is sub optimal, perhaps only 3 actual frames per second. There is some change, but not much change when using a smaller texture (500x500).
The machine has a NVIDIA gtx 570
My initial efforts were to use glTexSubImage2D
and glBufferSubData
, but these performed slightly worse than the memory mapped scheme.
Is there any way to force the graphics card to update the texture? How is video streaming software written?
Render Loop
void glStream::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
program1.bind();
program1.setUniformValue("texture", 0);
program1.enableAttributeArray(vertexAttr1);
program1.enableAttributeArray(vertexTexr1);
program1.setAttributeArray(vertexAttr1, vertices.constData());
program1.setAttributeArray(vertexTexr1, texCoords.constData());
//
glECheck();
glBindTexture(GL_TEXTURE_2D, textures[0]);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB,pbos[0]);
void* memory = glMapBuffer(GL_PIXEL_UNPACK_BUFFER_ARB,GL_WRITE_ONLY);
device->fillBuffer((unsigned char *)memory,heightGL,widthGL); // takes 2ms (not long)
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER_ARB);
glTexSubImage2D(GL_TEXTURE_2D,0,0,0,widthGL,heightGL,GL_LUMINANCE,GL_UNSIGNED_BYTE, NULL);
glDrawArrays(GL_TRIANGLES, 0, vertices.size());
glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB,0);
glBindTexture(GL_TEXTURE_2D,0);
//
program1.disableAttributeArray(vertexTexr1);
program1.disableAttributeArray(vertexAttr1);
program1.release();
glECheck();//no errors
}
Texture Reservation
void glStream::reserveTextures()
{
assert(numGLFrames>0);
assert(glGenBuffers);
displayBuff = (GLubyte*) calloc(numGLFrames*widthGL*heightGL,sizeof(GLubyte));//GL_RGB8
memset(displayBuff,100,numGLFrames*widthGL*heightGL*sizeof(GLubyte));
glGenBuffers(1,&pbos[0]);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, pbos[0]);
glBufferData(GL_PIXEL_UNPACK_BUFFER_ARB,
numGLFrames*widthGL*heightGL*sizeof(GLubyte),
&displayBuff[0], GL_STREAM_DRAW);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, 0);
glGenTextures(1,&textures[0]);
glBindTexture(GL_TEXTURE_2D,textures[0]);
glTexImage2D(GL_TEXTURE_2D,0,GL_LUMINANCE,
widthGL,heightGL,0,GL_LUMINANCE,GL_UNSIGNED_BYTE,NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_FALSE);
glBindTexture(GL_TEXTURE_2D,0);
}
Initialization
void glStream::initializeGL()
{
GLenum err = glewInit();
if (GLEW_OK != err)
{
const char * emssage = (const char*)glewGetErrorString(err);
QMessageBox::information(0, "OpenGL 3.x Context Example",
emssage);
exit(20);
}
glDisable(GL_DEPTH_TEST);
QGLShader *vshader1 = new QGLShader(QGLShader::Vertex, this);
const char *vsrc1 =
"attribute vec2 coord2d; \n"
"attribute mediump vec4 texCoord;\n"
"varying mediump vec4 texc;\n"
"void main() \n"
"{ \n"
" gl_Position = vec4(coord2d, 0.0, 1.0); \n"
" texc = texCoord;\n"
"} \n";
vshader1->compileSourceCode(vsrc1);
QGLShader *fshader1 = new QGLShader(QGLShader::Fragment, this);
const char *fsrc1 =
"uniform sampler2D texture;\n"
"varying mediump vec4 texc;\n"
"void main(void)\n"
"{\n"
" gl_FragColor = texture2D(texture, texc.st);\n"
"}\n";
fshader1->compileSourceCode(fsrc1);
program1.addShader(vshader1);
program1.addShader(fshader1);
program1.link();
vertexAttr1 = program1.attributeLocation( "coord2d");
vertexTexr1 = program1.attributeLocation( "texCoord");
// Create the vertex buffer.
vertices.clear();
float u=1;
#define AVEC -u,u
#define BVEC -u,-u
#define CVEC u,u
#define DVEC u,-u
vertices << QVector2D(AVEC);vertices << QVector2D(BVEC);
vertices << QVector2D(CVEC);vertices << QVector2D(BVEC);
vertices << QVector2D(DVEC);vertices << QVector2D(CVEC);
// Create the texture vertex buffer
#define TAVEC 0,1
#define TBVEC 0,0
#define TCVEC 1,1
#define TDVEC 1,0
texCoords << QVector2D(TAVEC);texCoords << QVector2D(TBVEC);
texCoords << QVector2D(TCVEC);texCoords << QVector2D(TBVEC);
texCoords << QVector2D(TDVEC);texCoords << QVector2D(TCVEC);
glECheck();
reserveTextures();
}
Edit 1
I am confident that fill buffer comes up with a new texture because in some other part of the code I write this texture to a file and it is indeed different. Its a sad day when my File IO is faster then a OpenGL texture.
Edit 2 I tried out FRAPS and I verified that the render loop is going at ~18 FPS, but the visible updates are slower (maybe 3 FPS). What could account for such a discrepancy?
The PBO is much larger than the texture, in the call to glBufferData:
numGLFrames*widthGL*heightGL*sizeof(GLubyte)
You're allocating a PBO large enough for multiple textures (frames), but only ever reading/writing one frame's worth of data.
If you make the PBO the same size as the texture and use glTexImage2D instead of glTexSubImage2D, is it much faster?