PDA

View Full Version : can't get imageStore to work.



yogi_smith
01-22-2014, 12:57 PM
Hi,

I was hopping someone can help me out here.
I am trying to get imageStore to work from a simple fragment shader but I had no success until now. I am wondering if I am doing something fundamentally wrong.
In the code bellow I am sending 200,000 points to render and I am trying to use their index ( gl_VertexID ) to fill in the image2D (1024x1024) like this:
imageStore(image_out, ivec2(vertexIndex % imageWidth, vertexIndex / imageHeight ) , vec4(1.,1.,1.,1.));

When I read the pixels from the texture using "glGetTexImage" I get what seems to be random results, while I was expecting to see white color for at least the first 190 lines of the image.
Some time the image will show parts of the underlying windows, or different pass renders, so I am sure I am doing something very wrong here.
This is a very simplified version of my real shader. I only tried it for debug and I still could not get it to work :( . There are no compile or link errors.

Here is the code:

1) create the texture that will binded to the image:

int imageWidth = 1024;
int imageHeight =1024;
glGenTextures(1, &glBakeTexture);
glBindTexture(GL_TEXTURE_2D, glBakeTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, imageWidth , imageHeight , 0 , GL_RGBA, GL_FLOAT , NULL);
glBindTexture(GL_TEXTURE_2D, 0);


2) The following is how I render to image:

shader->bind();


shader->setUniformValue( "mvp", mvp );
// the width of the image2D
shader->setUniformValue("imageWidth", imageWidth);
// the height of the image2D
shader->setUniformValue("imageHeight", imageHeight);

//glActiveTexture( GL_TEXTURE0 + 2 ); // tried using glActiveTexture that didn't help much
glBindImageTexture(2, glBakeTexture, 0, false, 0, GL_WRITE_ONLY, GL_RGBA8);

glBindVertexArray( m_vao );


// draw points
glDrawArrays(GL_POINTS, 0, (GLsizei)numberOfPoints); // about 200,000 points


glBindVertexArray(0);


shader->release();

// release textures etc...




3) read the image using "glGetTexImage"

// tried both of the following barriers and also "ALL_BIT"
//glMemoryBarrier(GL_TEXTURE_FETCH_BARRIER_BIT);
glMemoryBarrier(GL_TEXTURE_UPDATE_BARRIER_BIT);


glBindTexture(GL_TEXTURE_2D, glBakeTexture);
int numBytes = imageWidth * imageHeight * 4 * sizeof(float);
unsigned char *pixels = (unsigned char*)malloc(numBytes); // allocate image data into RAM
glGetTexImage(GL_TEXTURE_2D , 0 , GL_RGBA , GL_FLOAT , pixels); // after this call the pixels seems wrong !

free(pixels);


4) The vertex shader:
#version 430


layout (location = 0) in vec3 in_Position;


uniform mat4 mvp;


flat out int vertexIndex;


void main( void )
{
vec4 worldPos = vec4(in_Position, 1.0);
gl_Position = mvp * worldPos;


vertexIndex = gl_VertexID;
}


5) Fragment shader :
#version 430

uniform int imageWidth;
uniform int imageHeight;

in vec4 worldPos;

// (I made sure vertexIndex variable has correct value by doing some color testing...)
flat in int vertexIndex;


// Output image
layout (binding = 2, rgba8) writeonly uniform image2D image_out;


void main( void )
{
imageStore(image_out, ivec2(vertexIndex % imageWidth, vertexIndex / imageHeight ) , vec4(1.,1.,1.,1.));
}



Am I forgetting something or making an obvious mistake here ?

Thanks in advance !

yogi_smith
01-23-2014, 06:54 AM
After a lot of hair pulling I got some working version. I am going to post the changes I made in case someone will find it helpful in the future. I ended up using FBOs, glReadPixels and memoryBarier() (in the shader), seems that made a big difference.

1) create the FBO that will hold the texture that will be referenced by the image2D:

int imageWidth = 1024;
int imageHeight =1024;
glGenFramebuffers(1, &glBakeFbo);
glBindFramebuffer(GL_FRAMEBUFFER, glBakeFbo);


glGenTextures(1, &glBakeTexture);
glBindTexture(GL_TEXTURE_2D, glBakeTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, imageWidth , imageHeight , 0 , GL_RGBA, GL_UNSIGNED_BYTE , NULL);
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, imageWidth, imageHeight);


glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, glBakeTexture, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, glBakeTexture, 0);


glBindTexture(GL_TEXTURE_2D, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);


2) The following is how I render to image:



static const GLfloat white[] = { 1.0f, 1.0f, 1.0f, 1.0f };
static const GLenum attachments[] = { GL_COLOR_ATTACHMENT0 };

// bind the FBO
glBindFramebuffer(GL_FRAMEBUFFER, glBakeFbo);
glDrawBuffers(1, attachments);
glClearBufferfv(GL_COLOR, 0, white);
glViewport(0, 0, imageWidth, imageHeight);

glDisable(GL_DEPTH_TEST);
glDepthMask( GL_FALSE );
glDisable(GL_BLEND);


shader->bind();
shader->setUniformValue( "mvp", mvp );
// the width of the image2D
shader->setUniformValue("imageWidth", imageWidth);
// the height of the image2D
shader->setUniformValue("imageHeight", imageHeight);

int id = shader->uniformLocation("image_out");
// bind the 2D image.
glBindImageTexture(id, glBakeTexture, 0, false, 0, GL_WRITE_ONLY, GL_RGBA8);

// This was also important to add otherwise I got artifacts.
glDrawBuffer(GL_NONE);

glBindVertexArray( m_vao );


// draw points
glDrawArrays(GL_POINTS, 0, (GLsizei)numberOfPoints); // about 200,000 points


glBindVertexArray(0);


shader->release();
glBindFramebuffer(GL_FRAMEBUFFER, 0);

// release textures etc...




3) read the image from FBO using "glReadPixels"


glBindFramebuffer(GL_FRAMEBUFFER, glBakeFbo);
glBindFramebuffer(GL_READ_FRAMEBUFFER, glBakeFbo);


glMemoryBarrier(GL_ALL_BARRIER_BITS);

int numBytes = imageWidth * imageHeight * 4;
unsigned char *pixels = (unsigned char*)malloc(numBytes); // allocate image data into RAM


glReadPixels( 0, 0, imageWidth, imageHeight, GL_RGBA , GL_UNSIGNED_BYTE , pixels);
free(pixels);


4) The vertex shader:

#version 430

layout (location = 0) in vec3 in_Position;

uniform mat4 mvp;

flat out int vertexIndex;

void main( void )
{
vec4 worldPos = vec4(in_Position, 1.0);
gl_Position = mvp * worldPos;

vertexIndex = gl_VertexID;
}


5) Fragment shader few changes here:

#version 430

uniform int imageWidth;
uniform int imageHeight;

in vec4 worldPos;

flat in int vertexIndex;

// Output image
layout (binding = 2 , rgba8) writeonly uniform image2D image_out;

void main( void )
{
memoryBarrier();
imageStore(image_out, ivec2(vertexIndex % imageWidth, vertexIndex / imageHeight ) , vec4(1.,0.,0.,1.0));
memoryBarrier();
}



Hope this helps anyone....
I was expecting it to work in other ways but this is the only way I got it to work...