Skip to content

Instantly share code, notes, and snippets.

@amoffat
Last active August 29, 2015 14:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save amoffat/2f3d836b2235106ec810 to your computer and use it in GitHub Desktop.
Save amoffat/2f3d836b2235106ec810 to your computer and use it in GitHub Desktop.
#include <iostream>
#include <cstdlib>
#include <cassert>
#include <sstream>
#include <fstream>
#include <stdexcept>
#include <vector>
#include <GL/glew.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glext.h>
#include <SDL/SDL.h>
int width = 1024;
int height = 768;
int fps = 10;
#define error_check()\
do {\
std::stringstream msg;\
GLenum error = glGetError();\
if (error != GL_NO_ERROR) {\
msg << "opengl error "\
<< __FILE__ << " " << __LINE__\
<< ": " << gluErrorString(error) << "\n";\
throw std::runtime_error(msg.str());\
}\
} while(0)
std::string readFile(const std::string &filename) {
std::ifstream shader_file(filename.c_str(), std::ios::in);
std::stringstream buffer;
buffer << shader_file.rdbuf();
std::string shaderBuffer(buffer.str());
shader_file.close();
return shaderBuffer;
}
void compileShader(int shader, const std::string &source) {
const char *c_str_buffer = source.c_str();
GLint size[1] = {GLint(source.length())};
glShaderSource(shader, 1, &c_str_buffer, size);
error_check();
glCompileShader(shader);
error_check();
int success;
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
error_check();
if (!success) {
GLint log_length;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length);
error_check();
std::string log(log_length, ' ');
glGetShaderInfoLog(shader, log_length, NULL, &log[0]);
error_check();
throw std::logic_error(log);
}
}
void linkProgram(int program) {
glLinkProgram(program);
error_check();
int success;
glGetProgramiv(program, GL_LINK_STATUS, &success);
error_check();
if (!success) {
GLint log_length;
glGetShaderiv(program, GL_INFO_LOG_LENGTH, &log_length);
error_check();
std::string log(log_length, ' ');
glGetShaderInfoLog(program, log_length, NULL, &log[0]);
error_check();
throw std::logic_error(log);
}
}
uint createProgram(const std::string &vert, const std::string &frag) {
int fragShader = glCreateShader(GL_FRAGMENT_SHADER);
error_check();
int vertShader = glCreateShader(GL_VERTEX_SHADER);
error_check();
std::string source;
source = readFile(frag);
compileShader(fragShader, source);
source = readFile(vert);
compileShader(vertShader, source);
uint program = glCreateProgram();
error_check();
glAttachShader(program, fragShader);
error_check();
glAttachShader(program, vertShader);
error_check();
linkProgram(program);
return program;
}
int writeNumberProgram;
int readNumberProgram;
uint vbo;
uint texId;
uint fboId;
bool bug = false;
void draw() {
/*
* the bug occurs if blending is enabled, regardless of whether or not
* we're blending with the destination pixel (GL_ZERO for glBlendFunc)
*/
if (bug) {
glEnable(GL_BLEND);
error_check();
glBlendEquation(GL_FUNC_ADD);
error_check();
glColorMask(true, true, true, true);
error_check();
glBlendFunc(GL_ONE, GL_ZERO);
error_check();
}
/*
* here we're binding our framebuffer, attaching our 32-bit float texture
* and writing a uint as a float with a specific bit pattern
*/
glBindFramebuffer(GL_FRAMEBUFFER, fboId);
error_check();
glDrawBuffer(GL_COLOR_ATTACHMENT0);
error_check();
GLfloat color[] = {0,0,0,0};
glClearBufferfv(GL_COLOR, 0, color);
error_check();
glUseProgram(writeNumberProgram);
error_check();
glBindBuffer(GL_ARRAY_BUFFER, vbo);
error_check();
glEnableVertexAttribArray(0);
error_check();
glVertexAttribPointer(0, 2, GL_INT, GL_FALSE, 0, 0);
error_check();
glDrawArrays(GL_QUADS, 0, 4);
error_check();
glDisableVertexAttribArray(0);
error_check();
glBindBuffer(GL_ARRAY_BUFFER, 0);
error_check();
glUseProgram(0);
error_check();
glBindFramebuffer(GL_FRAMEBUFFER, 0);
error_check();
glDisable(GL_BLEND);
error_check();
/*
* now we're going to read our uint written as a float from the drawbuffer
* texture. if blending was enabled in the previous pass, our screen
* will be red, indicating the bug. if it was disabled, our screen will
* be green
*/
glUseProgram(readNumberProgram);
error_check();
glActiveTexture(GL_TEXTURE0);
error_check();
glBindTexture(GL_TEXTURE_2D, texId);
error_check();
int loc = glGetUniformLocation(readNumberProgram, "tex");
error_check();
assert(loc != -1);
glProgramUniform1i(readNumberProgram, loc, 0);
error_check();
glBindBuffer(GL_ARRAY_BUFFER, vbo);
error_check();
glEnableVertexAttribArray(0);
error_check();
glVertexAttribPointer(0, 2, GL_INT, GL_FALSE, 0, 0);
error_check();
glDrawArrays(GL_QUADS, 0, 4);
error_check();
glDisableVertexAttribArray(0);
error_check();
glBindBuffer(GL_ARRAY_BUFFER, 0);
error_check();
glUseProgram(0);
error_check();
}
int main(int argc, char** argv) {
/*
* we can enable the bug by specifying any argument when running the program
*/
bug = argc > 1;
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) != 0) {
printf("Unable to initialize SDL: %s\n", SDL_GetError());
exit(1);
}
SDL_Surface *screen = SDL_SetVideoMode(width, height, 32, SDL_OPENGL);
if (screen == NULL) {
perror(SDL_GetError());
exit(1);
}
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
GLenum err = glewInit();
if (GLEW_OK != err) {
std::cerr << "Error: " << glewGetErrorString(err);
std::flush(std::cerr);
exit(1);
}
/*
* create a texture for our render buffer. this will hold the uint-as-float
* value that we'll be writing to it
*/
glGenTextures(1, &texId);
error_check();
glTextureImage2DEXT(texId, GL_TEXTURE_2D, 0, GL_RGBA32F, width, height, 0,
GL_RGBA, GL_FLOAT, nullptr);
error_check();
glTextureParameteriEXT(texId, GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
error_check();
glTextureParameteriEXT(texId, GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
error_check();
/*
* attach our texture to the FBO
*/
glGenFramebuffers(1, &fboId);
error_check();
glNamedFramebufferTextureEXT(fboId, GL_COLOR_ATTACHMENT0, texId, 0);
error_check();
GLenum status = glCheckNamedFramebufferStatus(fboId, GL_FRAMEBUFFER);
error_check();
assert(status == GL_FRAMEBUFFER_COMPLETE);
/*
* create a VBO to hold enough geometry to render a full-screen quad
*/
GLint vertices[8] = { 0, 0, 1, 0, 1, 1, 0, 1 };
glGenBuffers(1, &vbo);
error_check();
glBindBuffer(GL_ARRAY_BUFFER, vbo);
error_check();
glBufferData(GL_ARRAY_BUFFER, sizeof(GLint) * 8, vertices, GL_STATIC_DRAW);
error_check();
glBindBuffer(GL_ARRAY_BUFFER, 0);
error_check();
glViewport(0, 0, width, height);
error_check();
writeNumberProgram = createProgram("write.vert", "write.frag");
readNumberProgram = createProgram("read.vert", "read.frag");
SDL_Event event;
memset(&event, 0, sizeof(SDL_Event));
bool done = false;
int lastDraw = 0;
while (!done) {
int newtime = SDL_GetTicks();
if (!lastDraw) lastDraw = newtime - (1/(float)fps);
int elapsed = newtime - lastDraw;
if (elapsed < 1000/(float)fps) continue;
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_KEYDOWN:
case SDL_QUIT:
done = true;
break;
}
}
draw();
lastDraw = newtime;
SDL_GL_SwapBuffers();
}
return 0;
}
#version 450
in vec2 texcoord;
out vec4 texel;
uniform sampler2D tex;
void main(void) {
float f = texture(tex, texcoord).r;
uint u = floatBitsToUint(f);
if (u == 4292171093) {
texel = vec4(0,1,0,1);
}
else {
texel = vec4(1,0,0,1);
}
}
#version 450
layout(location=0) in vec4 position;
layout(location=0) out vec2 texcoord;
void main(void) {
texcoord = position.xy;
gl_Position = position * 2.0 - 1.0;
}
#version 450
out vec4 texel;
void main(void) {
float f = uintBitsToFloat(4292171093);
texel = vec4(f,0,0,1);
}
#version 450
layout(location = 0) in vec4 position;
void main(void) {
gl_Position = position * 2.0 - 1.0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment