I have a set of RGB Frames which i'm displaying using openGL's PBO concept. Below is my code:
#include <stdio.h>
#include <stdlib.h>
#include "glew.h"
#include "glfw.h"
#include "glaux.h"
#include "logodata.h"
PFNGLGENBUFFERSARBPROC pglGenBuffersARB = 0; // VBO Name Generation Procedure
PFNGLBINDBUFFERARBPROC pglBindBufferARB = 0; // VBO Bind Procedure
PFNGLBUFFERDATAARBPROC pglBufferDataARB = 0; // VBO Data Loading Procedure
PFNGLBUFFERSUBDATAARBPROC pglBufferSubDataARB = 0; // VBO Sub Data Loading Procedure
PFNGLDELETEBUFFERSARBPROC pglDeleteBuffersARB = 0; // VBO Deletion Procedure
PFNGLGETBUFFERPARAMETERIVARBPROC pglGetBufferParameterivARB = 0; // return various parameters of VBO
PFNGLMAPBUFFERARBPROC pglMapBufferARB = 0; // map VBO procedure
PFNGLUNMAPBUFFERARBPROC pglUnmapBufferARB = 0; // unmap VBO procedure
#define glGenBuffersARB pglGenBuffersARB
#define glBindBufferARB pglBindBufferARB
#define glBufferDataARB pglBufferDataARB
#define glBufferSubDataARB pglBufferSubDataARB
#define glDeleteBuffersARB pglDeleteBuffersARB
#define glGetBufferParameterivARB pglGetBufferParameterivARB
#define glMapBufferARB pglMapBufferARB
#define glUnmapBufferARB pglUnmapBufferARB
int index;
int pboSupported;
int pboMode;
GLuint pixBuffObjs[2];
HDC hDC = NULL;
GLuint texture;
char *FileName;
unsigned char *guibuffer;
AUX_RGBImageRec texture1;
unsigned long long pos=0;
GLuint myPBO;
GLuint logoPBO;
unsigned char *logoBuff;
void initGL(void)
{
int maxSz;
int maxwidth = 416;
int maxheight = 240;
if( !glfwInit() )
{
exit( EXIT_FAILURE );
}
// if( !glfwOpenWindow(4096, 2118, 0,0,0,0,0,0, GLFW_WINDOW ) )
if( !glfwOpenWindow(maxwidth, maxheight, 0,0,0,0,0,0, GLFW_WINDOW ) ) //GLFW_FULLSCREEN
{
glfwTerminate();
exit( EXIT_FAILURE );
}
glfwSetWindowTitle("sample");
glGenBuffersARB = (PFNGLGENBUFFERSARBPROC)wglGetProcAddress("glGenBuffersARB");
glBindBufferARB = (PFNGLBINDBUFFERARBPROC)wglGetProcAddress("glBindBufferARB");
glBufferDataARB = (PFNGLBUFFERDATAARBPROC)wglGetProcAddress("glBufferDataARB");
glBufferSubDataARB = (PFNGLBUFFERSUBDATAARBPROC)wglGetProcAddress("glBufferSubDataARB");
glDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC)wglGetProcAddress("glDeleteBuffersARB");
glGetBufferParameterivARB = (PFNGLGETBUFFERPARAMETERIVARBPROC)wglGetProcAddress("glGetBufferParameterivARB");
glMapBufferARB = (PFNGLMAPBUFFERARBPROC)wglGetProcAddress("glMapBufferARB");
glUnmapBufferARB = (PFNGLUNMAPBUFFERARBPROC)wglGetProcAddress("glUnmapBufferARB");
// check once again PBO extension
if(glGenBuffersARB && glBindBufferARB && glBufferDataARB && glBufferSubDataARB &&
glMapBufferARB && glUnmapBufferARB && glDeleteBuffersARB && glGetBufferParameterivARB)
{
pboSupported = 1;
pboMode = 1; // using 1 PBO
printf( "Video card supports GL_ARB_pixel_buffer_object.");
glGenBuffersARB(1, &pixBuffObjs[0]);
}
else
{
pboSupported = 0;
pboMode = 0; // without PBO
printf("Video card does NOT support GL_ARB_pixel_buffer_object.");
}
glGetIntegerv(GL_MAX_TEXTURE_SIZE,&maxSz);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // This Will Clear The Background Color To Black
glClearDepth(1.0); // Enables Clearing Of The Depth Buffer
glDepthFunc(GL_LESS); // The Type Of Depth Test To Do
glEnable(GL_DEPTH_TEST); // Enables Depth Testing
glShadeModel(GL_SMOOTH); // Enables Smooth Color Shading
glMatrixMode(GL_PROJECTION);
//glLoadIdentity();
hDC= wglGetCurrentDC();
#if 1
{ // TSS
HWND hCurrentWindow = GetActiveWindow();
char szTitle[256]="sample";
//SetWindowText(hCurrentWindow, );
// SetWindowLongA (hCurrentWindow , GWL_STYLE, (GetWindowLongA (hCurrentWindow , GWL_STYLE) & ~(WS_CAPTION)));
SetWindowLongA (hCurrentWindow, GWL_STYLE, (WS_VISIBLE));
}
#endif
glEnable(GL_TEXTURE_2D);
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
}
int GL_Disply()
{
FILE *fptr=fopen("F:\\myRGBvideo.rgb","rb");
fseek(fptr,pos,SEEK_SET);
fread(guibuffer,sizeof(unsigned char),sizeof(unsigned char)*416*240*3,fptr);
pos+=416*240*3;
texture1.sizeX =416;
texture1.sizeY =240;
texture1.data = guibuffer;
glDepthFunc(GL_ALWAYS);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
glDisable(GL_LIGHTING);
//glEnable(GL_TEXTURE_2D);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE);
#if 0
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, texture1.sizeX, texture1.sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, guibuffer);
#else
glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, myPBO);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, texture1.sizeX, texture1.sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
#endif
glBegin(GL_QUADS);
//glNormal3f( 0.0f, 0.0f, 0.0f);
glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, -1.0f, 0.0f);
glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, -1.0f, 0.0f);
glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, 1.0f, 0.0f);
glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, 1.0f, 0.0f);
glEnd();
//disp logo
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, LOGO_WIDTH, LOGO_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, logoBuff);
glBegin(GL_QUADS);
glTexCoord2f(0.0f, 1.0f); glVertex3f(0.8f, 0.8f, 0.0f);
glTexCoord2f(1.0f, 1.0f); glVertex3f( 0.97f, 0.8f, 0.0f);
glTexCoord2f(1.0f, 0.0f); glVertex3f( 0.97f, 0.97f, 0.0f);
glTexCoord2f(0.0f, 0.0f);glVertex3f(0.8f, 0.97f, 0.0f);
glEnd();
glDisable(GL_BLEND);
glEnable(GL_DEPTH_TEST);
// Swap front and back rendering buffers
glfwSwapBuffers();
//glDeleteTextures(1, &texture);
fclose(fptr);
}
int main(int argc, char *argv[])
{
initGL(); // GL initialization
#if 0
/* CPU memory allocation using C - malloc */
guibuffer=(unsigned char*)malloc(sizeof(unsigned char)*416*240*3);
#else
/*GPU memory allocation using C*/
glGenBuffersARB(1, &myPBO);
glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, myPBO);
glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 416*240*3, NULL, GL_STREAM_DRAW_ARB);
#endif
//Allocating memory for RGBA logo data present in logodata.h
logoBuff=(unsigned char*)malloc(400*312*4*sizeof(unsigned char));
memcpy(logoBuff,TELLogo_RGB,400*312*4);
for(index=0;index<200;index++)
{
guibuffer=(unsigned char*)glMapBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, GL_READ_WRITE_ARB);
glUnmapBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB);
printf("frame %d displayed\r",index);
GL_Disply();
}
free(logoBuff);
return 0;
}
When the above code is run on a system having graphics card details listed below the display is normal and works fine:
First System:
Graphics card: Nvidia GEForce 580
OpenGL Version: 4.3
Second system:
Graphics card: NVidia GEForce 310
OpenGL Version: 3.3
Third system:
Graphics card: MSI
OpenGL Version: 4.2
However when the same code is run on my laptop with same system configuration but with the graphic cards details as shown below, i get a kind of Jerk (slight stuck) for some streams (some streams are working fine). I have checked same streams on different systems listed above and they seem to work fine.
Graphics card: Intel HD Graphics 4000
openGL Version: 4.0
What might be the problem that is causing this issue?
Your code is seriously broken. If it works at all, than only by accident:
Your code then writes to
guibuffer
inGL_Display
, and then tries to source the data in the PBO to do the texture update.You must be aware that, after the
glUnmapBufferARB()
, the pointer you got by the mapping becomes invalid. Writing to it is just undefined behavior. You might be lucky here as the nvidia driver might use some client-memory for the buffer and the pointer by accident continues to point to the real buffer - but that would be more or less an obscure coincidence. Note that you also can not just leave the buffer mapped all the time, since while it is mapped, you cannot use the GL object. At least not without the quite new GL_ARB_BUFFER_STORAGE extension (in core since 4.4), and even there, you would have to manually synchronize acces of the client and the GL - and the underlying problem would remain the same, anyways.To do this right, the simplest solution would be to per frame do the following: map the buffer, read the file into int, unmap it, update the texture, draw.
However, that would probably be a bit faster than not using PBOs at all, but peformance will not be optimal. You could improve this by orphaning the buffer before mapping it. It will tell the GL that you do not care about the contents of the buffer anymore (all pending GL operations which work with this data will be completed, of course) and allows the GL to supply a new storage immediately, while the old buffer contents can be internally used as long they are needed. To do the orphaning, just use
glBufferData()
with the correct size andNULL
as the data pointer again. The orphaning could also be done using the more modern (and actually preferred)glMapBufferRange()
function with theGL_MAP_INVALIDATE_BUFFER_BIT
access bit set. See the GL_MAP_BUFFER_RANGE extension for details.Another solution for fully asynchronous resource updates is using a ring of PBOs, so that you can upload the next frame to another PBO while the GPU is still sourcing data from the current PBO. This will basically work out like the orphaning method, but push less pressure on the GL and especially it's buffer management, so less overhead is to be expected (although it is unlikely to be relevant in your use case).
Besides the flaws in your PBO logic, there are several other points I'd like to mention:
GL_ELEMENT_ARRAY_BUFFER
. It is perfectly legit to later use this buffer asGL_PIXEL_UNPACK_BUFFER
, but the GL might use this information to optimize for the expected usage (especially by chosing the location of the buffer in VRAM or in client memory).GL_READ_WRITE
whereGL_WRITE_ONLY
is sufficient and could be a more efficient path (and would actually be required for the suggested orphaning viaglMapBufferRange
)glTexSubImage2D()
instead ofglTexImage2D()
for the texture updates.