C++ AMD 6310 GLSL/FBO复制会损坏前几个最低阶位(但有时会损坏)

C++ AMD 6310 GLSL/FBO复制会损坏前几个最低阶位(但有时会损坏),c++,opengl,glsl,fbo,amd-processor,C++,Opengl,Glsl,Fbo,Amd Processor,我使用OpenGL 2.0和FBO将一些数据从RGBA纹理复制到RGB纹理,我遇到了一个问题,在复制过程中,它有时会“破坏”某些像素组件的前几个最低阶位 纹理副本分为几个步骤,我正在调整FBO的大小 起初,我认为这可能是一个与调整FBO大小的方式有关的问题,或者与纹理采样的方式有关,但问题并不总是发生,当它发生时,它不会出现在复制的每个像素上,也不会出现在每个有问题像素的所有组件上。换句话说,它看起来几乎是随机的,除了它确实是确定性的,如果在程序的每次运行期间使用相同的输入浮点值,则会发生相同的

我使用OpenGL 2.0和FBO将一些数据从RGBA纹理复制到RGB纹理,我遇到了一个问题,在复制过程中,它有时会“破坏”某些像素组件的前几个最低阶位

纹理副本分为几个步骤,我正在调整FBO的大小

起初,我认为这可能是一个与调整FBO大小的方式有关的问题,或者与纹理采样的方式有关,但问题并不总是发生,当它发生时,它不会出现在复制的每个像素上,也不会出现在每个有问题像素的所有组件上。换句话说,它看起来几乎是随机的,除了它确实是确定性的,如果在程序的每次运行期间使用相同的输入浮点值,则会发生相同的错误

此外,如果我总是使用1x1的FBO大小,那么问题永远不会发生(这有点误导,因为它让我认为这是一个采样问题,但是,同样,情况可能并非如此,因为并非每个有问题像素的每个组件都“损坏”)。不幸的是,在现实世界中,使用1x1的FBO大小是绝对没有用的,因为我将复制一个包含任何超过几个像素的纹理

问题发生在Windows 7和Ubuntu上,当我使用MSVC++或g++的std rand()或Mersenne Twister生成输入纹理值时,问题就发生了(我如何生成值并不重要,因为复制操作根据定义独立于要复制的数据是如何事先生成的)

我编写了一个测试程序(见下面的代码),其中除了输入纹理值之外,程序运行之间没有任何变化

有没有人有AMD6310(或者其他类型的硬件)可以运行这个测试程序?您必须运行它几次,因为它有时会产生错误,有时不会。我只是好奇它是否会在你的硬件上产生错误。我就是看不出这种模式,我天真的想法是,这要么一直有效,要么永远不会——不是偶尔有效

我也完全愿意接受,它最终可能与我使用OpenGL进行复制的方式有关。这实际上是在缓解压力,因为这意味着有一个简单可靠的解决方案。我希望是这样

我可能在那里的某个地方对glTexParameteri进行了一些无关的调用,但在编写这个测试程序时,我尝试了“比抱歉更安全”的方法

在任何情况下,该问题都会导致某些像素组件出现类似~1e-8的错误。是的,这是一个很小的错误,但这对我现在所做的是完全不可接受的

#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 <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);
bool initialize_fragment_shader(const string &fragment_shader_code, GLint &shader, string &error);
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)
{
    long long unsigned int bit_mask = 1;
    long long unsigned int intval = *(long unsigned int*)&f;

    string bits;

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

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

    return bits;
}


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;
    }

    GLint shader_handle = 0;
    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;

    if(false == initialize_fragment_shader(code, shader_handle, 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);

    glUseProgram(shader_handle);
    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);
        glLoadIdentity();
        glOrtho(0, 1, 0, 1, 0, 1);
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
        glViewport(0, 0, tex_size_x, tex_size_y);

        glBegin(GL_QUADS);
            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();

        // Read from GPU memory.
        glReadBuffer(GL_COLOR_ATTACHMENT0_EXT);
        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);
    glDeleteProgram(shader_handle);
    glutDestroyWindow(glut_window_handle);

    return 0;
}

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

bool initialize_fragment_shader(const string &fragment_shader_code, GLint &shader, string &error)
{
    error = "";

    // Compile shader.
    const char *cch = 0;
    GLint status = GL_FALSE;
    GLint frag = glCreateShader(GL_FRAGMENT_SHADER);

    glShaderSource(frag, 1, &(cch = fragment_shader_code.c_str()), 0);
    glCompileShader(frag);
    glGetShaderiv(frag, GL_COMPILE_STATUS, &status);

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

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

        error += '\n';

        return false;
    }

    // Link to get final shader.
    shader = glCreateProgram();
    glAttachShader(shader, frag);
    glLinkProgram(shader);
    glGetProgramiv(shader, GL_LINK_STATUS, &status);

    if(GL_FALSE == status)
    {
        error = "Program link error.\n";
        vector<GLchar> buf(4096, '\0');
        glGetShaderInfoLog(shader, 4095, 0, &buf[0]);

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

        error += '\n';

        glDetachShader(shader, frag);
        glDeleteShader(frag);
        return false;
    }

    // Cleanup.
    glDetachShader(shader, frag);
    glDeleteShader(frag);

    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;
        }
    }
}
#包括
使用std::cout;
使用std::endl;
#包括
使用std::ios_base;
使用std::setprecision;
使用std::fixed;
#包括
使用std::vector;
#包括
使用std::string;
#包括
使用std::pair;
#如果在glut.h之前未包含此项,则包含//MSVC++choke
#包括
#包括
#包括
//如果在MSVC上编译,则自动链接GLUT和GLEW库++
#ifdef硕士学位
#pragma注释(lib,“glew32”)
#pragma注释(lib,“glut32”)
#恩迪夫
浮动距离(浮动a、浮动b);
bool初始化片段着色器(常量字符串和片段着色器代码、闪烁和着色器、字符串和错误);
void get_chunk_大小(const size_t num_像素、向量和chunk_大小、const bool force_1x1_块、const bool force_square_块=false);
字符串浮点\u位\u字符串(常量浮点f)
{
长无符号整数位_掩码=1;
长-长无符号整数intval=*(长无符号整数*)&f;
字符串位;

对于(size_t i=0;i<32;i++,bit_mask,感谢您花时间阅读/回复。我尝试手动禁用GL_抖动和GL_ALPHA,但没有成功。我假设抖动选项没有效果,因为它们是浮动

在任何情况下,我发现这个问题并没有出现在英特尔HD系列GPU上,也许其他非AMD GPU上。这让我想知道这是我的6310 GPU的问题,一般来说是6310 GPU的问题,还是6310驱动程序的问题


所以答案是:这可能不是OpenGL使用方式的问题;这可能是AMD 6310 GPU或其驱动程序的问题。

听起来可能是输出抖动。如果调用
glDisable(GL\u抖动);
,是否会发生这种情况?