OpenGL中的Bug?相同的浮点计算会导致不同的结果
不幸的是,“最小示例”相当长,因为如果我删除了太多不相关的代码,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>
这里是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;