Followers 0

# OpenGL AMD 6310 GLSL/FBO texture copy issue

## 1 post in this topic

I'm using OpenGL 2.0 and an FBO to copy some data from an RGBA texture to an RGB texture, and I ran into an issue where sometimes it "corrupts" the first few lowest order bits of some of the pixel components during the copy.

The texture copy is broken up into several steps, and I'm resizing the FBO.

At first I thought perhaps it was a problem related to the way that I was resizing the FBO, or with the way the the texture is being sampled, but the problem doesn't always occur, and when it does, it never occurs for every pixel copied nor does it ever occur for all of the components of each problematic pixel. In other words, it seems nearly random, except that it is indeed deterministic insomuch that the same error(s) occur if the same input float values are used during each run of the program.

Also, the problem never, ever occurs if I always use an FBO size of 1x1 (which is kind of misleading to know, because it made me think that it was a sampling issue, but, again, that is probably not the case since not every component of every problematic pixel is "corrupted"). Unfortunately, using an FBO size of 1x1 is absolutely useless in the real world where I'm going to be copying a texture containing anything more than a few pixels.

The problem happens on Windows 7 and Ubuntu, and the problem happens when I use MSVC++ or g++'s std rand() or Mersenne Twister to generate the input texture values (not that how I generate the values should matter, since copy operations are by definition independent of how the data to be copied was generated beforehand) .

I wrote a test program (see code below) where nothing changes between runs of the program other than the input texture values.

Does anyone have an AMD 6310 (or any other kind of hardware, really) that they can run this test program on? You'll have to run it a few times, as sometimes it produces an error, and sometimes it does not. I'm just curious if it ever produces the error on your hardware. I just can't spot the pattern, and my naive thinking is that this should either work all of the time, or never -- not so sporadically.

I'm also totally wiling to accept that it might, in the end, have something to do with the way that I'm using OpenGL to do the copy. This would be relieving actually, since it would mean that there's an easy and reliable solution. I hope this is the case.

I probably have some extraneous calls to glTexParameteri in there somewhere, but I was trying the "better safe than sorry" method while working on this test program.

In any case, the problem results in some of the pixel components having error that's on the order of like ~1e-8. Yes, that's a very small error, but it's totally unacceptable for what I'm doing.

#include <iostream>
using std::cout;
using std::endl;

#include <iomanip>
using std::ios_base;
using std::setprecision;
using std::fixed;

#include <vector>
using std::vector;

#include <string>
using std::string;

#include <utility>
using std::pair;

#include <cmath>
#include <cstdlib> // MSVC++ chokes if you don't include this before glut.h
#include <ctime>

#include <GL/glew.h>
#include <GL/glut.h>

// Automatically link in the GLUT and GLEW libraries if compiling on MSVC++
#ifdef _MSC_VER
#pragma comment(lib, "glew32")
#pragma comment(lib, "glut32")
#endif

float dist(float a, float b);
void get_chunk_sizes(const size_t num_pixels, vector< pair<size_t, size_t> > &chunk_sizes, const bool force_1x1_chunks, const bool force_square_chunks = false);
string float_bits_string(const float f);

int main(int argc, char **argv)
{
// This program uses an FBO and a fragment shader to copy RGBA pixels from an input array into an RGB output array.
// It breaks up the entire pixel copy process into many smaller chunks of a varying number of pixels per chunk.
// See line 165 to change the number of pixels in the array (right now it's hard-coded to be 7 pixels total).

// If the chunk sizes are forced to be 1x1 pixels, then there are never any problems with the copy.
// See line 186 to change whether the chunks are forced to be 1x1 pixels or not (right not they are not being forced as such).
//
// If the chunk sizes are not forced to be 1x1 pixels, then almost all of the time (but not quite always)
// there is a small problem with at least one component of one of the pixels during the copy:
//
// The copy is off by a small, non-zero value of practically constant magnitude (on the order of ~1e-8).
//
// Since the values of the pixel components are the only thing that change between runs of the program,
// the problem seems to be entirely dependent on the values of the pixel components themselves. This is totally
// unexpected -- it should always work or always fail to the same degree, regardless of the pixel component values.
// While looking at the bit patterns of the problematic pixel component values, it seems that it is always only the
// first three to five lowest order bits that do not get copied successfully.
//
// Note that if the values of the pixel components do not change between runs, then the same errors occur,
// and so the problem seems to be entirely deterministic. Right now the components are set via PRNG, and are done in a way
// so that all of the bits of precision are used (see lines 173 - 176).
// See line 86 to alter the PRNG seed.

// 1) Initialize pseudo-random number generator.
srand(time(0)); // srand(0);

// 2) Initialize OpenGL and related objects.
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA);
GLint glut_window_handle = glutCreateWindow("");

if(! ( GLEW_OK == glewInit() &&
GLEW_VERSION_2_0 &&
GLEW_ARB_framebuffer_object &&
GLEW_ARB_texture_rectangle ) )
{
return -1;
}

GLuint fbo_handle = 0;
GLuint tex_fbo_handle = 0;
GLuint tex_in_handle = 0;
GLuint tex_out_handle = 0;
const GLint tex_in_internal_format = GL_RGBA32F_ARB;
const GLint tex_in_format = GL_RGBA;
const GLint tex_out_internal_format = GL_RGB32F_ARB;
const GLint tex_out_format = GL_RGB;
const GLint var_type = GL_FLOAT;

string code;
code += "#version 110\n";
code += "uniform sampler2D input_tex;\n";
code += "void main(void)\n";
code += "{\n";
code += "    vec4 p = texture2D(input_tex, gl_TexCoord[0].st);\n";
code += "    gl_FragData[0].rgb = vec3(p.xyz);\n";
code += "}\n";

string error;

{
cout << error << endl;
return -2;
}

glGenTextures(1, &tex_in_handle);
glBindTexture(GL_TEXTURE_2D, tex_in_handle);
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);

glGenTextures(1, &tex_out_handle);
glBindTexture(GL_TEXTURE_2D, tex_out_handle);
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);

glGenFramebuffersEXT(1, &fbo_handle);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo_handle);
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);

glGenTextures(1, &tex_fbo_handle);
glBindTexture(GL_TEXTURE_2D, tex_fbo_handle);
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);

glUniform1i(glGetUniformLocation(shader_handle, "input_tex"), 0); // Use texture 0.

// 3) Set up input -- an array of RGBA float pixels with pseudorandom values.
size_t num_pixels = 7; // = rand() % 50 + 1;
size_t num_input_channels = 4;
vector<float> input(num_pixels*num_input_channels, 0);

for(size_t i = 0; i < num_pixels; i++)
{
size_t input_index = i*num_input_channels;

input[input_index + 0] = static_cast<float>(rand()) / static_cast<float>(RAND_MAX);
input[input_index + 1] = static_cast<float>(rand()) / static_cast<float>(RAND_MAX);
input[input_index + 2] = static_cast<float>(rand()) / static_cast<float>(RAND_MAX);
input[input_index + 3] = static_cast<float>(rand()) / static_cast<float>(RAND_MAX);
}

// 4) Break up processing of input into chunks.
vector< pair<size_t, size_t> > chunks;

#ifdef FORCE_1x1_CHUNKS
get_chunk_sizes(num_pixels, chunks, true, true);
#else
get_chunk_sizes(num_pixels, chunks, false, true);
#endif

size_t num_pixels_remaining = num_pixels;

size_t num_output_channels = 3;
vector<float> output(num_pixels*num_output_channels, 0);

for(size_t i = 0; i < chunks.size(); i++)
{
cout << "Pixels remaining: " << num_pixels_remaining << ", processing chunk size: " << chunks[i].first << " x " << chunks[i].second << " = " << chunks[i].first*chunks[i].second << endl;

const size_t tex_size_x = chunks[i].first;
const size_t tex_size_y = chunks[i].second;
const size_t index = num_pixels - num_pixels_remaining;
const size_t input_index = index*num_input_channels;
const size_t output_index = index*num_output_channels;

// Set the FBO size to match the current chunk size.
glBindTexture(GL_TEXTURE_2D, tex_fbo_handle);
glTexImage2D(GL_TEXTURE_2D, 0, tex_out_internal_format, tex_size_x, tex_size_y, 0, tex_out_format, var_type, 0);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, tex_fbo_handle, 0);

// Write to GPU memory.
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex_in_handle);
glTexImage2D(GL_TEXTURE_2D, 0, tex_in_internal_format, tex_size_x, tex_size_y, 0, tex_in_format, var_type, &input[input_index]);

// Calculate by "drawing".
glMatrixMode(GL_PROJECTION);
glOrtho(0, 1, 0, 1, 0, 1);
glMatrixMode(GL_MODELVIEW);
glViewport(0, 0, tex_size_x, tex_size_y);

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

glReadPixels(0, 0, tex_size_x, tex_size_y, tex_out_format, var_type, &output[output_index]);

num_pixels_remaining -= tex_size_x*tex_size_y;
}

// 5) Analyze largest distance between input and output -- it should be zero, but it is not zero
//    if the chunk sizes are not forced to be 1x1.
float largest_dist = 0;
cout << setprecision(18);

cout << endl << "Comparing input and output: " << endl;

for(size_t i = 0; i < num_pixels; i++)
{
size_t input_index = i*num_input_channels;
size_t output_index = i*num_output_channels;

float dist0 = dist(input[input_index + 0], output[output_index + 0]);
float dist1 = dist(input[input_index + 1], output[output_index + 1]);
float dist2 = dist(input[input_index + 2], output[output_index + 2]);

if(dist0 > largest_dist)
largest_dist = dist0;

if(dist1 > largest_dist)
largest_dist = dist1;

if(dist2 > largest_dist)
largest_dist = dist2;

if(dist0 != 0)
{
cout << endl;
cout << "**** Copy error at pixel " << i + 1  << " first component" << endl;
cout << "\tInput:  " << input[input_index + 0] << '\n' << "\tOutput: " << output[output_index + 0] << endl;
cout << "\tInput (as bits):  " << float_bits_string(input[input_index + 0]) << '\n' << "\tOutput (as bits): " << float_bits_string(output[output_index + 0]) << endl;
cout << endl;
}
else
{
cout << "OK at pixel " << i + 1  << " first component" << endl;
//            cout << "\tInput:  " << input[input_index + 0] << '\n' << "\tOutput: " << output[output_index + 0] << endl;
}

if(dist1 != 0)
{
cout << endl;
cout << "**** Copy error at pixel " << i + 1 << " second component" << endl;
cout << "\tInput:  " << input[input_index + 1] << '\n' << "\tOutput: " << output[output_index + 1] << endl;
cout << "\tInput (as bits):  " << float_bits_string(input[input_index + 1]) << '\n' << "\tOutput (as bits): " << float_bits_string(output[output_index + 1]) << endl;
cout << endl;
}
else
{
cout << "OK at pixel " << i + 1 << " second component" << endl;
//            cout << "\tInput:  " << input[input_index + 1] << '\n' << "\tOutput: " << output[output_index + 1] << endl;
}

if(dist2 != 0)
{
cout << endl;
cout << "**** Copy error at pixel " << i + 1 << " third component" << endl;
cout << "\tInput:  " << input[input_index + 2] << '\n' << "\tOutput: " << output[output_index + 2] << endl;
cout << "\tInput (as bits):  " << float_bits_string(input[input_index + 2]) << '\n' << "\tOutput (as bits): " << float_bits_string(output[output_index + 2]) << endl;
cout << endl;
}
else
{
cout << "OK at pixel " << i + 1 << " third component" << endl;
//            cout << "\tInput:  " << input[input_index + 2] << '\n' << "\tOutput: " << output[output_index + 2] << endl;
}

}

if(0 != largest_dist)
cout << "\nLargest copy error: " << largest_dist << endl;
else
cout << "\nNo copy errors." << endl;

// 6) Cleanup OpenGL and related objects.
glDeleteTextures(1, &tex_in_handle);
glDeleteTextures(1, &tex_out_handle);
glDeleteTextures(1, &tex_fbo_handle);
glDeleteFramebuffersEXT(1, &fbo_handle);
glUseProgram(0);
glutDestroyWindow(glut_window_handle);

return 0;
}

float dist(float a, float b)
{
return fabsf(b - a);
}

{
error = "";

const char *cch = 0;
GLint status = GL_FALSE;

if(GL_FALSE == status)
{
error = "Fragment shader compile error.\n";
vector<GLchar> buf(4096, '\0');

for(size_t i = 0; i < buf.size(); i++)
if(0 != buf[i])
error += buf[i];

error += '\n';

return false;
}

if(GL_FALSE == status)
{
vector<GLchar> buf(4096, '\0');

for(size_t i = 0; i < buf.size(); i++)
if(0 != buf[i])
error += buf[i];

error += '\n';

return false;
}

// Cleanup.

return true;
}

void get_chunk_sizes(const size_t num_pixels, vector< pair<size_t, size_t> > &chunk_sizes, const bool force_1x1_chunks, const bool force_square_chunks)
{
chunk_sizes.clear();

size_t num_pixels_remaining = num_pixels;
GLint max_tex_size = 0;

glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_tex_size);

size_t curr_tex_x = max_tex_size;
size_t curr_tex_y = max_tex_size;

if(true == force_1x1_chunks)
curr_tex_x = curr_tex_y = 1;

while(0 < num_pixels_remaining)
{
if(num_pixels_remaining < curr_tex_x*curr_tex_y)
{
if(true == force_square_chunks)
{
curr_tex_x /= 2;
curr_tex_y /= 2;
}
else
{
if(curr_tex_x == curr_tex_y)
curr_tex_y /= 2;
else
curr_tex_x /= 2;
}
}
else
{
pair<size_t, size_t> p(curr_tex_x, curr_tex_y);
chunk_sizes.push_back(p);
num_pixels_remaining -= curr_tex_x*curr_tex_y;
}
}
}

string float_bits_string(const float f)
{
long unsigned int bit_mask = 1;
long unsigned int intval = *(long unsigned int*)&f;

string bits;

for(size_t i = 0; i < 32; i++, bit_mask <<= 1)
{
bits += '1';
else
bits += '0';
}

bits = string(bits.rbegin(), bits.rend());

return bits;
}


Edited by taby
0

##### Share on other sites

Problem doesn't occur on Intel GPU. Much obliged for the help.

0

## Create an account

Register a new account

Followers 0

• ### Similar Content

• By mapra99
Hello

I am working on a recent project and I have been learning how to code in C# using OpenGL libraries for some graphics. I have achieved some quite interesting things using TAO Framework writing in Console Applications, creating a GLUT Window. But my problem now is that I need to incorporate the Graphics in a Windows Form so I can relate the objects that I render with some .NET Controls.

To deal with this problem, I have seen in some forums that it's better to use OpenTK instead of TAO Framework, so I can use the glControl that OpenTK libraries offer. However, I haven't found complete articles, tutorials or source codes that help using the glControl or that may insert me into de OpenTK functions. Would somebody please share in this forum some links or files where I can find good documentation about this topic? Or may I use another library different of OpenTK?

Thanks!

• Hello, I have been working on SH Irradiance map rendering, and I have been using a GLSL pixel shader to render SH irradiance to 2D irradiance maps for my static objects. I already have it working with 9 3D textures so far for the first 9 SH functions.
In my GLSL shader, I have to send in 9 SH Coefficient 3D Texures that use RGBA8 as a pixel format. RGB being used for the coefficients for red, green, and blue, and the A for checking if the voxel is in use (for the 3D texture solidification shader to prevent bleeding).
My problem is, I want to knock this number of textures down to something like 4 or 5. Getting even lower would be a godsend. This is because I eventually plan on adding more SH Coefficient 3D Textures for other parts of the game map (such as inside rooms, as opposed to the outside), to circumvent irradiance probe bleeding between rooms separated by walls. I don't want to reach the 32 texture limit too soon. Also, I figure that it would be a LOT faster.
Is there a way I could, say, store 2 sets of SH Coefficients for 2 SH functions inside a texture with RGBA16 pixels? If so, how would I extract them from inside GLSL? Let me know if you have any suggestions ^^.
• By KarimIO
EDIT: I thought this was restricted to Attribute-Created GL contexts, but it isn't, so I rewrote the post.
Hey guys, whenever I call SwapBuffers(hDC), I get a crash, and I get a "Too many posts were made to a semaphore." from Windows as I call SwapBuffers. What could be the cause of this?
Update: No crash occurs if I don't draw, just clear and swap.
static PIXELFORMATDESCRIPTOR pfd = // pfd Tells Windows How We Want Things To Be { sizeof(PIXELFORMATDESCRIPTOR), // Size Of This Pixel Format Descriptor 1, // Version Number PFD_DRAW_TO_WINDOW | // Format Must Support Window PFD_SUPPORT_OPENGL | // Format Must Support OpenGL PFD_DOUBLEBUFFER, // Must Support Double Buffering PFD_TYPE_RGBA, // Request An RGBA Format 32, // Select Our Color Depth 0, 0, 0, 0, 0, 0, // Color Bits Ignored 0, // No Alpha Buffer 0, // Shift Bit Ignored 0, // No Accumulation Buffer 0, 0, 0, 0, // Accumulation Bits Ignored 24, // 24Bit Z-Buffer (Depth Buffer) 0, // No Stencil Buffer 0, // No Auxiliary Buffer PFD_MAIN_PLANE, // Main Drawing Layer 0, // Reserved 0, 0, 0 // Layer Masks Ignored }; if (!(hDC = GetDC(windowHandle))) return false; unsigned int PixelFormat; if (!(PixelFormat = ChoosePixelFormat(hDC, &pfd))) return false; if (!SetPixelFormat(hDC, PixelFormat, &pfd)) return false; hRC = wglCreateContext(hDC); if (!hRC) { std::cout << "wglCreateContext Failed!\n"; return false; } if (wglMakeCurrent(hDC, hRC) == NULL) { std::cout << "Make Context Current Second Failed!\n"; return false; } ... // OGL Buffer Initialization glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); glBindVertexArray(vao); glUseProgram(myprogram); glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_SHORT, (void *)indexStart); SwapBuffers(GetDC(window_handle));
• By Tchom
Hey devs!

I've been working on a OpenGL ES 2.0 android engine and I have begun implementing some simple (point) lighting. I had something fairly simple working, so I tried to get fancy and added color-tinting light. And it works great... with only one or two lights. Any more than that, the application drops about 15 frames per light added (my ideal is at least 4 or 5). I know implementing lighting is expensive, I just didn't think it was that expensive. I'm fairly new to the world of OpenGL and GLSL, so there is a good chance I've written some crappy shader code. If anyone had any feedback or tips on how I can optimize this code, please let me know.

uniform mat4 u_MVPMatrix; uniform mat4 u_MVMatrix; attribute vec4 a_Position; attribute vec3 a_Normal; attribute vec2 a_TexCoordinate; varying vec3 v_Position; varying vec3 v_Normal; varying vec2 v_TexCoordinate; void main() { v_Position = vec3(u_MVMatrix * a_Position); v_TexCoordinate = a_TexCoordinate; v_Normal = vec3(u_MVMatrix * vec4(a_Normal, 0.0)); gl_Position = u_MVPMatrix * a_Position; } Fragment Shader
precision mediump float; uniform vec4 u_LightPos["+numLights+"]; uniform vec4 u_LightColours["+numLights+"]; uniform float u_LightPower["+numLights+"]; uniform sampler2D u_Texture; varying vec3 v_Position; varying vec3 v_Normal; varying vec2 v_TexCoordinate; void main() { gl_FragColor = (texture2D(u_Texture, v_TexCoordinate)); float diffuse = 0.0; vec4 colourSum = vec4(1.0); for (int i = 0; i < "+numLights+"; i++) { vec3 toPointLight = vec3(u_LightPos[i]); float distance = length(toPointLight - v_Position); vec3 lightVector = normalize(toPointLight - v_Position); float diffuseDiff = 0.0; // The diffuse difference contributed from current light diffuseDiff = max(dot(v_Normal, lightVector), 0.0); diffuseDiff = diffuseDiff * (1.0 / (1.0 + ((1.0-u_LightPower[i])* distance * distance))); //Determine attenuatio diffuse += diffuseDiff; gl_FragColor.rgb *= vec3(1.0) / ((vec3(1.0) + ((vec3(1.0) - vec3(u_LightColours[i]))*diffuseDiff))); //The expensive part } diffuse += 0.1; //Add ambient light gl_FragColor.rgb *= diffuse; } Am I making any rookie mistakes? Or am I just being unrealistic about what I can do? Thanks in advance
• By yahiko00
Hi,
Not sure to post at the right place, if not, please forgive me...
For a game project I am working on, I would like to implement a 2D starfield as a background.
I do not want to deal with static tiles, since I plan to slowly animate the starfield. So, I am trying to figure out how to generate a random starfield for the entire map.
I feel that using a uniform distribution for the stars will not do the trick. Instead I would like something similar to the screenshot below, taken from the game Star Wars: Empire At War (all credits to Lucasfilm, Disney, and so on...).

Is there someone who could have an idea of a distribution which could result in such a starfield?
Any insight would be appreciated

• 10
• 12
• 21
• 11
• 28