OpenGL中的Bug?相同的浮点计算会导致不同的结果

OpenGL中的Bug?相同的浮点计算会导致不同的结果,opengl,glsl,Opengl,Glsl,不幸的是,“最小示例”相当长,因为如果我删除了太多不相关的代码,bug就会消失 这里是C++代码: #define _USE_MATH_DEFINES #include <iostream> #include <iomanip> #include <vector> #include <math.h> #include <time.h> #include <fstream> #include <sstream>

不幸的是,“最小示例”相当长,因为如果我删除了太多不相关的代码,bug就会消失

这里是C++代码:

#define _USE_MATH_DEFINES

#include <iostream>
#include <iomanip>
#include <vector>
#include <math.h>
#include <time.h>
#include <fstream>
#include <sstream>

//OpenGL includes
#include <GL/glew.h>                    //OpenGL Extension Wrangler
#include <GL/freeglut.h>                //Free OpenGL Utility Toolkit (includes gl.h and glu.h)

void fPressEnter(){
    std::cout << "Press enter to exit.";
    std::cin.get();
}

void fCompileShaderFromString(const std::string& Source, const GLuint ShaderId){
    const char* Code = Source.c_str();

    glShaderSource(ShaderId, 1, &Code, NULL);

    glCompileShader(ShaderId);

    int Status;
    glGetShaderiv(ShaderId, GL_COMPILE_STATUS, &Status);
    if(Status == GL_FALSE){
        int Length = 0;
        glGetShaderiv(ShaderId, GL_INFO_LOG_LENGTH, &Length);
        if(Length > 0){
            char* Log = new char[Length];
            int Written = 0;
            glGetShaderInfoLog(ShaderId, Length, &Written, Log);
            std::cout << Log << std::endl;
            delete[] Log;
        }
    }
}

void fCompileShaderFromFile(const char* FileName, const GLuint ShaderId){
    std::ifstream InFile(FileName, std::ios::in);
    std::ostringstream Code;

    while(InFile.good()){
        int TI = InFile.get();
        if(!InFile.eof()) Code << (char) TI;
    }
    InFile.close();

    fCompileShaderFromString(Code.str(), ShaderId);
}

void fLinkProgram(const GLuint ProgramId){
    glLinkProgram(ProgramId);

    int Status;
    glGetProgramiv(ProgramId, GL_LINK_STATUS, &Status);
    if(Status == GL_FALSE){
        int Length = 0;
        glGetProgramiv(ProgramId, GL_INFO_LOG_LENGTH, &Length);
        if(Length > 0){
            char* Log = new char[Length];
            int Written = 0;
            glGetProgramInfoLog(ProgramId, Length, &Written, Log);
            std::cout << Log << std::endl;
            delete[] Log;
        }
    }
}

int main(){
    //OpenGL setup

    //Glut
    int argc = 1;
    char* argv[1] = {(char*) ""};
    glutInit(&argc, argv);

    glutInitWindowPosition((glutGet(GLUT_SCREEN_WIDTH) - 256) / 2, (glutGet(GLUT_SCREEN_HEIGHT) - 256) / 2);
    glutInitWindowSize(256, 256);
    glutInitDisplayMode(GLUT_RGB);

    glutCreateWindow("Bug");
    glutHideWindow();

    //Glew
    GLenum glew_ok = glewInit();
    if(glew_ok != GLEW_OK){fprintf(stderr, "Glew error: '%s'\n", glewGetErrorString(glew_ok)); fPressEnter(); return(1);}

    //Main program

    //Auxiliary variables
    GLuint ShaderId, ProgramId, BufferId;
    float* BufferPointer;
    float* Data = new float[32 * 32 * 2];
    float dsup, esup;

    //Compile shader program and create buffer in graphics card memory
    ShaderId = glCreateShader(GL_COMPUTE_SHADER);
    fCompileShaderFromFile("Shader.txt", ShaderId);

    ProgramId = glCreateProgram();
    glAttachShader(ProgramId, ShaderId);
    fLinkProgram(ProgramId);
    glDetachShader(ProgramId, ShaderId);

    glDeleteShader(ShaderId);

    glUseProgram(ProgramId);

    glGenBuffers(1, &BufferId);

    glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, BufferId);
    glBufferData(GL_SHADER_STORAGE_BUFFER, 32 * 32 * 2 * sizeof(float), NULL, GL_STREAM_READ);
    glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);

    //Actual computation
    std::cout << "Starting computation" << std::endl << std::endl << std::flush;

    dsup = 0.f;
    esup = 0.f;

    glUniform1i(0, 0);
    glUniform1i(1, 0);

    glDispatchCompute(4, 4, 1);

    glMemoryBarrier(GL_ALL_BARRIER_BITS);
    glFinish();

    glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, BufferId);
    BufferPointer = (float*) glMapBuffer(GL_SHADER_STORAGE_BUFFER, GL_READ_ONLY);
    memcpy(Data, BufferPointer, 32 * 32 * 2 * sizeof(float));
    glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
    glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);

    for(int z2 = 0; z2 < 32; z2++) for(int z3 = 0; z3 < 32; z3++){
        if(Data[z2 * 32 * 2 + z3 * 2 + 0] > dsup) dsup = Data[z2 * 32 * 2 + z3 * 2 + 0];
        if(Data[z2 * 32 * 2 + z3 * 2 + 1] > esup) esup = Data[z2 * 32 * 2 + z3 * 2 + 1];
    }

    std::cout << std::setprecision(8) << dsup << ", " << std::setprecision(8) << esup << std::endl;

    //Cleanup
    delete[] Data;

    glDeleteBuffers(1, &BufferId);

    glDeleteProgram(ProgramId);

    fPressEnter();
    return(0);
}
我得到esup=85930.453,如果我使用

D_3_yg = D_3_gy;
(注意,这应该给出相同的结果)我得到esup=85928.164

还有,如果我换了线

zg = goffset + gl_GlobalInvocationID.x;
fg = float(zg) / fmg;

zb = boffset + gl_GlobalInvocationID.y;
fb = float(zb) / fmb;

(注意,goffset和boffset在C++中设置为0)我得到了另一个结果(当我分别使用第一行或第二行时,esup=85929.844和esup=85929.742)

另一个非常奇怪的方面是,只有当常量my设置为128时,“完整bug”才会出现。对于my的其他值,例如64、127、129或256,更改这两行不会更改esup的值,但删除goffset和boffset仍然会更改

此外,D_3_yg的值(由两条不同的线设置)实际上不应该对esup的计算起作用,而应该只对dsup起作用。但dsup保持不变,esup发生变化

我正在使用Nvidia Quadro M2000M和Visual Studio 2012进行编译。你知道问题出在哪里吗

请注意,程序运行大约需要20秒,在此期间屏幕冻结。在Windows中,您需要将TDR显示增加到60(秒)


因为Windows会终止默认情况下需要2秒以上时间的GPU计算。

您的着色器会执行大量浮点运算。任何一行都使用以前的结果进行操作。精度误差累积

此外,GPU可以进行更高精度的计算;但每次存储结果时,它都会被截断。有时编译器可以对其进行优化,有时则不能。 你应该读书


对于一些特殊的、条件恶劣的情况,问题会增加。一个简单的几何类比问题是确定两条几乎平行的线之间的交点。结果可能变化很大

你应该尽量简化那些冗长的计算。使用双精度类型(从OpenGL4.0开始提供)可能会有所帮助,但可能还不够

D_3_yg = D_3_gy;
zg = goffset + gl_GlobalInvocationID.x;
fg = float(zg) / fmg;

zb = boffset + gl_GlobalInvocationID.y;
fb = float(zb) / fmb;
zg = gl_GlobalInvocationID.x;
fg = float(zg) / fmg;

zb = gl_GlobalInvocationID.y;
fb = float(zb) / fmb;