Posts tagged ‘frame buffer object’

The Ping-Pong technique

Introduction

In this post I’m going to discuss a common technique in GPGPU projects, this technique allows a shader to use two textures as input and output data so to be able to perform a computation based on more iterations.

The technique described in this post is based on Textures and the Frame Buffer Object, so I suggest you to read my two previous posts before to read this one.

The technique

The base concept of this technique is quite simple, all we need is a Frame Buffer Object and two textures linked to it.

The Ping-Pong technique is used for all the programs that need to store the result of a computation, so all the programs based on more iterations.

The first thing to do is to create a FBO and two textures (with same dimensions and internalFormat), then attach the two texturs to the FBO, after that it’s possible to use one texture as input, so read its values in the shader (vertex and fragment shaders can read one or more textures as a rule) and use the other one as output thanks to the glDrawBuffer function that sets the second texture as output buffer (so the fragments computed in the fragment shader will be wrote in the texture).

After the first iteration, it’s enough to swap the two textures so to use the computed values in the previous step as input for the next computation.

Some implementation details

The key element of this technique is the texture swapping, an easy way to do that is storing the Texture ids in a two elements array and access the array with two integer variables, for example read_tex and write_tex .

So to swap the textures all you have to do is swapping the values of these two variables.

Example code

This is the code used to init properly the FBO and the two textures:

int read_tex = 0;
int write_tex = 1;
GLuint tex[2];

glGenTextures(2, tex);
…
// create and init the FBO
GLuint fb;
glGenFramebuffersEXT(1, &fb);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb);
…
// create and init the the two textures - this is the code for the first one
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex[read_tex]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, W, H, 0, GL_RGBA, GL_FLOAT, data);
…
// attach the textures to the FBO
GLenum att_point[] = {GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT};
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, att_point[read_tex], GL_TEXTURE_2D, tex[read_tex], 0);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, att_point[write_tex], GL_TEXTURE_2D, tex[write_tex], 0);
…
// create, init and enable the shaders

now everything is ready for the computational loop:

for(int i = 0; i < num_it; i++)
{
	// set the second texture as output buffer for the shader
	glDrawBuffer(att_point[write_tex]);

	// attach the input texture to the first Texture Unit
	glActiveTexture(GL_TEXTURE0);
	glBindTexture(GL_TEXTURE_2D, tex[read_tex]);
	glUniform1i(tex0_loc, 0);

	// draw a square with the texture on it so to perform the computation
	ShaderDraw();

	// swap read and write indexes
	if(read_tex)
	{
		read_tex = 0;
		write_tex = 1;
	}
	else
	{
		read_tex = 1;
		write_tex = 0;
	}
}

Useful resources

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

Building blocks: the Frame Buffer object

Introduction

In order to perform computation with shaders you have to read and write data from/to textures, even if the former task is an usual operation for a shader, the latter requires some GL code, in fact you can’t directly write data to a texture with a shader, but just to the buffers provided to GL by the window system.

In this post I’m going to discuss how to render directly to textures, so how to use texture for computational purpose.

The Frame Buffer object

All we need to render directly to a texture is an extension called GL_EXT_framebuffer_object, this extensions allows us to draw to rendering destinations other than the buffers provided by GL.

The Frame buffer is an OpenGL object, so the first thing we need to do is to create an handle to it using the function glGenFramebuffersEXT, then we have to bind it to the current context with the function glBindFramebufferEXT.

After that we can attach one or more textures to the FBO using the function glFramebufferTexture2DEXT, each texture is so attached to a different attach point identified by the constants GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT, etc…

After we have set everything up, we can use the attached textures for reading data with the function glReadBuffer and for drawing data with the function glDrawBuffer.

Example code

this is an example code that shows how to set up a FBO, attach a texture to it and use this texture as target buffer for writing operations.

GLuint fb;
glGenFramebuffersEXT(1, &fb);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb);

… // create a texture with texture name tex

glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, tex, 0);

glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);

It’s important to remember that you can attach to a single FBO just textures of the same type (GL_TEXTURE_2D) and with the same dimension, if you need to work with textures of different type and/or dimension you have to use two or more FBOs.

Useful resources

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]