QGLWidget + OpenGL + CUDA

Hi everyone.

I’m currently trying to get a combination of an QGLWidget showing an OpenGL texture which was modified by a CUDA kernel to work :cool:.

So, once again, I want to have a texture, modify it in some ways with CUDA kernels working on it, and display it in a QGLWidget, having OpenGL as the interface to both of them.

Basically, I was trying to get some of NVIDIA’s examples (SobelFilter) to work in an QGLWidget.

My steps so far:

init():

  • load the texture (tried QImage and cutLoadPPM4ub)

  • generate pixel buffer object (pbo)

  • bind pbo and copy the data

  • register the buffer as cuda_resource

  • generate and bind texture object

display():

  • cudaGraphicsMapResources()

  • cudaGraphicsResourceGetMappedPointer()

  • CUDA kernel working on the date behind the pointer

  • cudaGraphicsUnmapResources()

  • bind texture and buffer

  • use glTexImage2D()

  • draw the texture onto a quad

Well, I tried many combinations of the above steps, going one line after another through the examples NVIDIA provided with their CUDA SDK.

But I can’t get it to work.

Most of the time I see a black square, nothing more. If I play around with the pointers to show and so on, I can sometimes see the picture of some uninitialized memory :).

But I newer saw the picture/texture after it was altered by the CUDA kernel, I newer saw it after the sobel filter.

If I give the pointer to the texture pixels to glTexImage2D() and don’t do the memset, I can see the picture. But without filtering, of course. Like this

glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, m_pQImage.width(), m_pQImage.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);

So I believe, that the steps until this point are not totally wrong.

  • Has anybody done sth. like that already and can provide some useful hints?

  • I also habe concerns about double buffering and the correct use of swapBuffers()…

Any help is appreciated :).

Here a the three most important functions of my QGLWidget subclass:

the init() function:

void GLWidget::initializeGL()

{

	makeCurrent();

	m_pQImage = QGLWidget::convertToGLFormat(m_pQImage);

	cudaGLSetGLDevice (cutGetMaxGflopsDeviceId() );

	glewInit();

	unsigned int w, h;

	const char* image_filename = getImagePath();

	if (cutLoadPPM4ub(image_filename, &pixels, &w, &h) != CUTTrue) {

		printf("Failed to load image file: %s\n", image_filename);

		exit(-1);

	}

	GLint bsize;

	setupTexture(m_pQImage.width(), m_pQImage.height(), pixels, 4);

	memset(pixels, 0x0, 4 * sizeof(Pixel) * m_pQImage.width() * m_pQImage.height());

	glGenBuffers(1, &pbo_buffer);

	glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo_buffer); 

	glBufferData(GL_PIXEL_UNPACK_BUFFER, 

					4 * sizeof(Pixel) * m_pQImage.width() * m_pQImage.height(), 

					pixels, GL_STREAM_DRAW);

	glGetBufferParameteriv(GL_PIXEL_UNPACK_BUFFER, GL_BUFFER_SIZE, &bsize); 

	if ((GLuint)bsize != (4 * sizeof(Pixel) * m_pQImage.width() * m_pQImage.height()))

	{

		printf("Buffer object (%d) has incorrect size (%d).\n", (unsigned)pbo_buffer, (unsigned)bsize);

		cudaThreadExit();

		exit(-1);

	}

	glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);

	// register this buffer object with CUDA

	cutilSafeCall(cudaGraphicsGLRegisterBuffer(&cuda_pbo_resource, pbo_buffer, cudaGraphicsMapFlagsWriteDiscard));	

	glGenTextures  (1, &m_pTexture);

	glBindTexture  (GL_TEXTURE_2D, m_pTexture);

	glTexImage2D   (GL_TEXTURE_2D, 0, GL_RGBA, m_pQImage.width(), m_pQImage.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);

	glBindTexture  (GL_TEXTURE_2D, 0);

	glPixelStorei  (GL_UNPACK_ALIGNMENT, 1);

	glPixelStorei  (GL_PACK_ALIGNMENT, 1);

}

the display() function:

void GLWidget::paintGL()

{

	qDebug() << "paintGL()";

	makeCurrent();

	// Sobel operation

	Pixel *data = NULL;

	// map PBO to get CUDA device pointer

	cutilSafeCall(cudaGraphicsMapResources(1, &cuda_pbo_resource, 0));

	size_t num_bytes; 

	cutilSafeCall(cudaGraphicsResourceGetMappedPointer((void **)&data, &num_bytes,  

							   cuda_pbo_resource));

	qDebug() << QString("CUDA mapped PBO: May access %1 bytes").arg(num_bytes);

	

	sobelFilter(data, m_pQImage.width(), m_pQImage.height(), SOBELDISPLAY_IMAGE, 1.0f);

	cutilSafeCall(cudaGraphicsUnmapResources(1, &cuda_pbo_resource, 0));

	glClear(GL_COLOR_BUFFER_BIT);

	glBindTexture(GL_TEXTURE_2D, m_pTexture);

	glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo_buffer);

	glTexImage2D(GL_TEXTURE_2D, 0, 0, 0, m_pQImage.width(), m_pQImage.height(), 

				   GL_LUMINANCE, GL_UNSIGNED_BYTE, NULL /*= OFFSET*/);

	glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);

	glDisable(GL_DEPTH_TEST);

	glEnable(GL_TEXTURE_2D);

	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);

	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

		

	glBegin(GL_QUADS);

		glTexCoord2f(0.0f, 0.0f);

		glVertex2f(-1.0f, -1.0f);

		glTexCoord2f(1.0f, 0.0f);

		glVertex2f(1.0f, -1.0f);

		glTexCoord2f(1.0f, 1.0f);

		glVertex2f(1.0f, 1.0f);

		glTexCoord2f(0.0f, 1.0f);

		glVertex2f(-1.0f, 1.0f);

	glEnd();

	glBindTexture(GL_TEXTURE_2D, 0);

	swapBuffers();

}

the reshape() function:

void GLWidget::resizeGL(int iWidth, int iHeight)

{

	makeCurrent();

	int iSide = qMin(iWidth, iHeight);

	glViewport((iWidth - iSide) / 2, (iHeight - iSide) / 2, iSide, iSide);

	glMatrixMode(GL_PROJECTION);

	glLoadIdentity();

	glOrtho(-0.5, +0.5, +0.5, -0.5, 4.0, 15.0);

	glMatrixMode(GL_MODELVIEW);

	setFixedSize(iWidth, iHeight);

}

I tried to build a little example, but you’ll have to set some path variables and the CUDA Build rules must be used of course.

UPDATED: For VS2008 you schould only have to set the Qt version:

[attachment=18572:QGLWidge…GLCUDAv2.zip]

THANKS!
QGLWidgetOpenGLCUDAv2.zip (572 KB)

Hi everyone.

I’m currently trying to get a combination of an QGLWidget showing an OpenGL texture which was modified by a CUDA kernel to work :cool:.

So, once again, I want to have a texture, modify it in some ways with CUDA kernels working on it, and display it in a QGLWidget, having OpenGL as the interface to both of them.

Basically, I was trying to get some of NVIDIA’s examples (SobelFilter) to work in an QGLWidget.

My steps so far:

init():

  • load the texture (tried QImage and cutLoadPPM4ub)

  • generate pixel buffer object (pbo)

  • bind pbo and copy the data

  • register the buffer as cuda_resource

  • generate and bind texture object

display():

  • cudaGraphicsMapResources()

  • cudaGraphicsResourceGetMappedPointer()

  • CUDA kernel working on the date behind the pointer

  • cudaGraphicsUnmapResources()

  • bind texture and buffer

  • use glTexImage2D()

  • draw the texture onto a quad

Well, I tried many combinations of the above steps, going one line after another through the examples NVIDIA provided with their CUDA SDK.

But I can’t get it to work.

Most of the time I see a black square, nothing more. If I play around with the pointers to show and so on, I can sometimes see the picture of some uninitialized memory :).

But I newer saw the picture/texture after it was altered by the CUDA kernel, I newer saw it after the sobel filter.

If I give the pointer to the texture pixels to glTexImage2D() and don’t do the memset, I can see the picture. But without filtering, of course. Like this

glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, m_pQImage.width(), m_pQImage.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);

So I believe, that the steps until this point are not totally wrong.

  • Has anybody done sth. like that already and can provide some useful hints?

  • I also habe concerns about double buffering and the correct use of swapBuffers()…

Any help is appreciated :).

Here a the three most important functions of my QGLWidget subclass:

the init() function:

void GLWidget::initializeGL()

{

	makeCurrent();

	m_pQImage = QGLWidget::convertToGLFormat(m_pQImage);

	cudaGLSetGLDevice (cutGetMaxGflopsDeviceId() );

	glewInit();

	unsigned int w, h;

	const char* image_filename = getImagePath();

	if (cutLoadPPM4ub(image_filename, &pixels, &w, &h) != CUTTrue) {

		printf("Failed to load image file: %s\n", image_filename);

		exit(-1);

	}

	GLint bsize;

	setupTexture(m_pQImage.width(), m_pQImage.height(), pixels, 4);

	memset(pixels, 0x0, 4 * sizeof(Pixel) * m_pQImage.width() * m_pQImage.height());

	glGenBuffers(1, &pbo_buffer);

	glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo_buffer); 

	glBufferData(GL_PIXEL_UNPACK_BUFFER, 

					4 * sizeof(Pixel) * m_pQImage.width() * m_pQImage.height(), 

					pixels, GL_STREAM_DRAW);

	glGetBufferParameteriv(GL_PIXEL_UNPACK_BUFFER, GL_BUFFER_SIZE, &bsize); 

	if ((GLuint)bsize != (4 * sizeof(Pixel) * m_pQImage.width() * m_pQImage.height()))

	{

		printf("Buffer object (%d) has incorrect size (%d).\n", (unsigned)pbo_buffer, (unsigned)bsize);

		cudaThreadExit();

		exit(-1);

	}

	glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);

	// register this buffer object with CUDA

	cutilSafeCall(cudaGraphicsGLRegisterBuffer(&cuda_pbo_resource, pbo_buffer, cudaGraphicsMapFlagsWriteDiscard));	

	glGenTextures  (1, &m_pTexture);

	glBindTexture  (GL_TEXTURE_2D, m_pTexture);

	glTexImage2D   (GL_TEXTURE_2D, 0, GL_RGBA, m_pQImage.width(), m_pQImage.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);

	glBindTexture  (GL_TEXTURE_2D, 0);

	glPixelStorei  (GL_UNPACK_ALIGNMENT, 1);

	glPixelStorei  (GL_PACK_ALIGNMENT, 1);

}

the display() function:

void GLWidget::paintGL()

{

	qDebug() << "paintGL()";

	makeCurrent();

	// Sobel operation

	Pixel *data = NULL;

	// map PBO to get CUDA device pointer

	cutilSafeCall(cudaGraphicsMapResources(1, &cuda_pbo_resource, 0));

	size_t num_bytes; 

	cutilSafeCall(cudaGraphicsResourceGetMappedPointer((void **)&data, &num_bytes,  

							   cuda_pbo_resource));

	qDebug() << QString("CUDA mapped PBO: May access %1 bytes").arg(num_bytes);

	

	sobelFilter(data, m_pQImage.width(), m_pQImage.height(), SOBELDISPLAY_IMAGE, 1.0f);

	cutilSafeCall(cudaGraphicsUnmapResources(1, &cuda_pbo_resource, 0));

	glClear(GL_COLOR_BUFFER_BIT);

	glBindTexture(GL_TEXTURE_2D, m_pTexture);

	glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo_buffer);

	glTexImage2D(GL_TEXTURE_2D, 0, 0, 0, m_pQImage.width(), m_pQImage.height(), 

				   GL_LUMINANCE, GL_UNSIGNED_BYTE, NULL /*= OFFSET*/);

	glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);

	glDisable(GL_DEPTH_TEST);

	glEnable(GL_TEXTURE_2D);

	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);

	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

		

	glBegin(GL_QUADS);

		glTexCoord2f(0.0f, 0.0f);

		glVertex2f(-1.0f, -1.0f);

		glTexCoord2f(1.0f, 0.0f);

		glVertex2f(1.0f, -1.0f);

		glTexCoord2f(1.0f, 1.0f);

		glVertex2f(1.0f, 1.0f);

		glTexCoord2f(0.0f, 1.0f);

		glVertex2f(-1.0f, 1.0f);

	glEnd();

	glBindTexture(GL_TEXTURE_2D, 0);

	swapBuffers();

}

the reshape() function:

void GLWidget::resizeGL(int iWidth, int iHeight)

{

	makeCurrent();

	int iSide = qMin(iWidth, iHeight);

	glViewport((iWidth - iSide) / 2, (iHeight - iSide) / 2, iSide, iSide);

	glMatrixMode(GL_PROJECTION);

	glLoadIdentity();

	glOrtho(-0.5, +0.5, +0.5, -0.5, 4.0, 15.0);

	glMatrixMode(GL_MODELVIEW);

	setFixedSize(iWidth, iHeight);

}

I tried to build a little example, but you’ll have to set some path variables and the CUDA Build rules must be used of course.

UPDATED: For VS2008 you schould only have to set the Qt version:

[attachment=24101:QGLWidge…GLCUDAv2.zip]

THANKS!

Well… after nearly three weeks of unsuccessful work on this I decided to create a topic here…

Now, one day later I found my “mistake”… you have to use glTexSubImage2D() instead of glTexImage2D() wit same parameters… even if you want to read the whole image.

I don’t get it… but here’s some working code, for anybody, that comes across this thread:

void GLWidget::initializeGL()

{

	makeCurrent();

	cudaGLSetGLDevice(cutGetMaxGflopsDeviceId() );

	int c=1;

	char* dummy = "";

	glutInit( &c, &dummy );

	glewInit();

	unsigned int w, h;

	char data[1024];

	strncpy(data, getImagePath().toLatin1(), sizeof(data) - 1);

	const char* image_filename = data;

	if (cutLoadPPM4ub(image_filename, &pixels, &w, &h) != CUTTrue) {

		printf("Failed to load image file: %s\n", image_filename);

		exit(-1);

	}

	GLint bsize;

	setupTexture(m_pQImage.width(), m_pQImage.height(), pixels, 4);

	memset(pixels, 0x0, 4 * sizeof(Pixel) * m_pQImage.width() * m_pQImage.height());

	// use OpenGL Path

	glGenBuffers(1, &pbo_buffer);

	glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo_buffer); 

	glBufferData(GL_PIXEL_UNPACK_BUFFER, 

					4 * sizeof(Pixel) * m_pQImage.width() * m_pQImage.height(), 

					pixels, GL_STREAM_DRAW);

	glGetBufferParameteriv(GL_PIXEL_UNPACK_BUFFER, GL_BUFFER_SIZE, &bsize);

	if ((GLuint)bsize != (4 * sizeof(Pixel) * m_pQImage.width() * m_pQImage.height()))

	{

		printf("Buffer object (%d) has incorrect size (%d).\n", (unsigned)pbo_buffer, (unsigned)bsize);

		cudaThreadExit();

		exit(-1);

	}

	glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);

	// register this buffer object with CUDA

	cutilSafeCall(cudaGraphicsGLRegisterBuffer(&cuda_pbo_resource, pbo_buffer, cudaGraphicsMapFlagsWriteDiscard));	

	glGenTextures  (1, &m_pTexture);

	glBindTexture  (GL_TEXTURE_2D, m_pTexture);

	glTexImage2D   (GL_TEXTURE_2D, 0, GL_RGBA, m_pQImage.width(), m_pQImage.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

	glBindTexture  (GL_TEXTURE_2D, 0);

	glPixelStorei  (GL_UNPACK_ALIGNMENT, 1);

	glPixelStorei  (GL_PACK_ALIGNMENT, 1);

}
void GLWidget::paintGL()

{

	makeCurrent();

	glMatrixMode(GL_PROJECTION);

	glLoadIdentity();

	glMatrixMode(GL_MODELVIEW);

	// Sobel operation

	Pixel *data = NULL;

	// map PBO to get CUDA device pointer

	cutilSafeCall(cudaGraphicsMapResources(1, &cuda_pbo_resource, 0));

	size_t num_bytes; 

	cutilSafeCall(cudaGraphicsResourceGetMappedPointer((void **)&data, &num_bytes,  

							   cuda_pbo_resource));

	qDebug() << QString("CUDA mapped PBO: May access %1 bytes").arg(num_bytes);

	

	sobelFilter(data, m_pQImage.width(), m_pQImage.height(), SOBELDISPLAY_IMAGE, 1.0f);

	cutilSafeCall(cudaGraphicsUnmapResources(1, &cuda_pbo_resource, 0));

	glClear(GL_COLOR_BUFFER_BIT);

	glBindTexture(GL_TEXTURE_2D, m_pTexture);

	glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo_buffer);

	glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_pQImage.width(), m_pQImage.height(), 

					GL_LUMINANCE, GL_UNSIGNED_BYTE, (char *)NULL);

	glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);

	glDisable(GL_DEPTH_TEST);

	glEnable(GL_TEXTURE_2D);

	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);

	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

		

	glBegin(GL_QUADS);

		glVertex2f(-1, -1); glTexCoord2f(0, 0);

		glVertex2f(-1,  1); glTexCoord2f(1, 0);

		glVertex2f( 1,  1); glTexCoord2f(1, 1);

		glVertex2f( 1, -1); glTexCoord2f(0, 1);

	glEnd();

	//glDisable(GL_TEXTURE_2D);

	glBindTexture(GL_TEXTURE_2D, 0);

	swapBuffers();

}

Cheers!

Well… after nearly three weeks of unsuccessful work on this I decided to create a topic here…

Now, one day later I found my “mistake”… you have to use glTexSubImage2D() instead of glTexImage2D() wit same parameters… even if you want to read the whole image.

I don’t get it… but here’s some working code, for anybody, that comes across this thread:

void GLWidget::initializeGL()

{

	makeCurrent();

	cudaGLSetGLDevice(cutGetMaxGflopsDeviceId() );

	int c=1;

	char* dummy = "";

	glutInit( &c, &dummy );

	glewInit();

	unsigned int w, h;

	char data[1024];

	strncpy(data, getImagePath().toLatin1(), sizeof(data) - 1);

	const char* image_filename = data;

	if (cutLoadPPM4ub(image_filename, &pixels, &w, &h) != CUTTrue) {

		printf("Failed to load image file: %s\n", image_filename);

		exit(-1);

	}

	GLint bsize;

	setupTexture(m_pQImage.width(), m_pQImage.height(), pixels, 4);

	memset(pixels, 0x0, 4 * sizeof(Pixel) * m_pQImage.width() * m_pQImage.height());

	// use OpenGL Path

	glGenBuffers(1, &pbo_buffer);

	glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo_buffer); 

	glBufferData(GL_PIXEL_UNPACK_BUFFER, 

					4 * sizeof(Pixel) * m_pQImage.width() * m_pQImage.height(), 

					pixels, GL_STREAM_DRAW);

	glGetBufferParameteriv(GL_PIXEL_UNPACK_BUFFER, GL_BUFFER_SIZE, &bsize);

	if ((GLuint)bsize != (4 * sizeof(Pixel) * m_pQImage.width() * m_pQImage.height()))

	{

		printf("Buffer object (%d) has incorrect size (%d).\n", (unsigned)pbo_buffer, (unsigned)bsize);

		cudaThreadExit();

		exit(-1);

	}

	glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);

	// register this buffer object with CUDA

	cutilSafeCall(cudaGraphicsGLRegisterBuffer(&cuda_pbo_resource, pbo_buffer, cudaGraphicsMapFlagsWriteDiscard));	

	glGenTextures  (1, &m_pTexture);

	glBindTexture  (GL_TEXTURE_2D, m_pTexture);

	glTexImage2D   (GL_TEXTURE_2D, 0, GL_RGBA, m_pQImage.width(), m_pQImage.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

	glBindTexture  (GL_TEXTURE_2D, 0);

	glPixelStorei  (GL_UNPACK_ALIGNMENT, 1);

	glPixelStorei  (GL_PACK_ALIGNMENT, 1);

}
void GLWidget::paintGL()

{

	makeCurrent();

	glMatrixMode(GL_PROJECTION);

	glLoadIdentity();

	glMatrixMode(GL_MODELVIEW);

	// Sobel operation

	Pixel *data = NULL;

	// map PBO to get CUDA device pointer

	cutilSafeCall(cudaGraphicsMapResources(1, &cuda_pbo_resource, 0));

	size_t num_bytes; 

	cutilSafeCall(cudaGraphicsResourceGetMappedPointer((void **)&data, &num_bytes,  

							   cuda_pbo_resource));

	qDebug() << QString("CUDA mapped PBO: May access %1 bytes").arg(num_bytes);

	

	sobelFilter(data, m_pQImage.width(), m_pQImage.height(), SOBELDISPLAY_IMAGE, 1.0f);

	cutilSafeCall(cudaGraphicsUnmapResources(1, &cuda_pbo_resource, 0));

	glClear(GL_COLOR_BUFFER_BIT);

	glBindTexture(GL_TEXTURE_2D, m_pTexture);

	glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo_buffer);

	glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_pQImage.width(), m_pQImage.height(), 

					GL_LUMINANCE, GL_UNSIGNED_BYTE, (char *)NULL);

	glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);

	glDisable(GL_DEPTH_TEST);

	glEnable(GL_TEXTURE_2D);

	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);

	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

		

	glBegin(GL_QUADS);

		glVertex2f(-1, -1); glTexCoord2f(0, 0);

		glVertex2f(-1,  1); glTexCoord2f(1, 0);

		glVertex2f( 1,  1); glTexCoord2f(1, 1);

		glVertex2f( 1, -1); glTexCoord2f(0, 1);

	glEnd();

	//glDisable(GL_TEXTURE_2D);

	glBindTexture(GL_TEXTURE_2D, 0);

	swapBuffers();

}

Cheers!