Intelligent Sigend Distance Fields
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1473 lines
48 KiB

/**
* Copyright 1993-2013 NVIDIA Corporation. All rights reserved.
*
* Please refer to the NVIDIA end user license agreement (EULA) associated
* with this source code for terms and conditions that govern your use of
* this software. Any use, reproduction, disclosure, or distribution of
* this software and related documentation outside the terms of the EULA
* is strictly prohibited.
*
*/
#ifndef _RENDERCHECK_GL_H_
#define _RENDERCHECK_GL_H_
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <vector>
#include <map>
#include <string>
#if defined(__APPLE__) || defined(MACOSX)
#include <GLUT/glut.h>
#else
#include <GL/freeglut.h>
#endif
#include <nvShaderUtils.h>
#include <helper_image.h>
using std::vector;
using std::map;
using std::string;
#define BUFFER_OFFSET(i) ((char *)NULL + (i))
#if _DEBUG
#define CHECK_FBO checkStatus(__FILE__, __LINE__, true)
#else
#define CHECK_FBO true
#endif
class CheckRender
{
public:
CheckRender(unsigned int width, unsigned int height, unsigned int Bpp,
bool bQAReadback, bool bUseFBO, bool bUsePBO) :
m_Width(width), m_Height(height), m_Bpp(Bpp), m_bQAReadback(bQAReadback),
m_bUseFBO(bUseFBO), m_bUsePBO(bUsePBO), m_PixelFormat(GL_BGRA), m_fThresholdCompare(0.0f)
{
allocateMemory(width, height, Bpp, bUseFBO, bUsePBO);
}
virtual ~CheckRender()
{
// Release PBO resources
if (m_bUsePBO)
{
glDeleteBuffers(1, &m_pboReadback);
m_pboReadback = 0;
}
free(m_pImageData);
}
virtual void allocateMemory(unsigned int width, unsigned int height, unsigned int Bpp,
bool bUseFBO, bool bUsePBO)
{
// Create the PBO for readbacks
if (bUsePBO)
{
glGenBuffers(1, &m_pboReadback);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, m_pboReadback);
glBufferData(GL_PIXEL_UNPACK_BUFFER_ARB, width*height*Bpp, NULL, GL_STREAM_READ);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, 0);
}
m_pImageData = (unsigned char *)malloc(width*height*Bpp); // This is the image data stored in system memory
}
virtual void setExecPath(char *path)
{
m_ExecPath = path;
}
virtual void EnableQAReadback(bool bStatus)
{
m_bQAReadback = bStatus;
}
virtual bool IsQAReadback()
{
return m_bQAReadback;
}
virtual bool IsFBO()
{
return m_bUseFBO;
}
virtual bool IsPBO()
{
return m_bUsePBO;
}
virtual void *imageData()
{
return m_pImageData;
}
// Interface to this class functions
virtual void setPixelFormat(GLenum format)
{
m_PixelFormat = format;
}
virtual int getPixelFormat()
{
return m_PixelFormat;
}
virtual bool checkStatus(const char *zfile, int line, bool silent) = 0;
virtual bool readback(GLuint width, GLuint height) = 0;
virtual bool readback(GLuint width, GLuint height, GLuint bufObject) = 0;
virtual bool readback(GLuint width, GLuint height, unsigned char *membuf) = 0;
virtual void bindReadback()
{
if (!m_bQAReadback)
{
printf("CheckRender::bindReadback() uninitialized!\n");
return;
}
if (m_bUsePBO)
{
glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, m_pboReadback); // Bind the PBO
}
}
virtual void unbindReadback()
{
if (!m_bQAReadback)
{
printf("CheckRender::unbindReadback() uninitialized!\n");
return;
}
if (m_bUsePBO)
{
glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, 0); // Release the bind on the PBO
}
}
virtual void savePGM(const char *zfilename, bool bInvert, void **ppReadBuf)
{
if (zfilename != NULL)
{
if (bInvert)
{
unsigned char *readBuf;
unsigned char *writeBuf= (unsigned char *)malloc(m_Width * m_Height);
for (unsigned int y=0; y < m_Height; y++)
{
if (ppReadBuf)
{
readBuf = *(unsigned char **)ppReadBuf;
}
else
{
readBuf = (unsigned char *)m_pImageData;
}
memcpy(&writeBuf[m_Width*m_Bpp*y], (readBuf+ m_Width*(m_Height-1-y)), m_Width);
}
// we copy the results back to original system buffer
if (ppReadBuf)
{
memcpy(*ppReadBuf, writeBuf, m_Width*m_Height);
}
else
{
memcpy(m_pImageData, writeBuf, m_Width*m_Height);
}
free(writeBuf);
}
printf("> Saving PGM: <%s>\n", zfilename);
if (ppReadBuf)
{
sdkSavePGM<unsigned char>(zfilename, *(unsigned char **)ppReadBuf, m_Width, m_Height);
}
else
{
sdkSavePGM<unsigned char>(zfilename, (unsigned char *)m_pImageData, m_Width, m_Height);
}
}
}
virtual void savePPM(const char *zfilename, bool bInvert, void **ppReadBuf)
{
if (zfilename != NULL)
{
if (bInvert)
{
unsigned char *readBuf;
unsigned char *writeBuf= (unsigned char *)malloc(m_Width * m_Height * m_Bpp);
for (unsigned int y=0; y < m_Height; y++)
{
if (ppReadBuf)
{
readBuf = *(unsigned char **)ppReadBuf;
}
else
{
readBuf = (unsigned char *)m_pImageData;
}
memcpy(&writeBuf[m_Width*m_Bpp*y], (readBuf+ m_Width*m_Bpp*(m_Height-1-y)), m_Width*m_Bpp);
}
// we copy the results back to original system buffer
if (ppReadBuf)
{
memcpy(*ppReadBuf, writeBuf, m_Width*m_Height*m_Bpp);
}
else
{
memcpy(m_pImageData, writeBuf, m_Width*m_Height*m_Bpp);
}
free(writeBuf);
}
printf("> Saving PPM: <%s>\n", zfilename);
if (ppReadBuf)
{
sdkSavePPM4ub(zfilename, *(unsigned char **)ppReadBuf, m_Width, m_Height);
}
else
{
sdkSavePPM4ub(zfilename, (unsigned char *)m_pImageData, m_Width, m_Height);
}
}
}
virtual bool PGMvsPGM(const char *src_file, const char *ref_file, const float epsilon, const float threshold = 0.0f)
{
unsigned char *src_data = NULL, *ref_data = NULL;
unsigned long error_count = 0;
unsigned int width, height;
char *ref_file_path = sdkFindFilePath(ref_file, m_ExecPath.c_str());
if (ref_file_path == NULL)
{
printf("CheckRender::PGMvsPGM unable to find <%s> in <%s> Aborting comparison!\n", ref_file, m_ExecPath.c_str());
printf(">>> Check info.xml and [project//data] folder <%s> <<<\n", ref_file);
printf("Aborting comparison!\n");
printf(" FAILED\n");
error_count++;
}
else
{
if (src_file == NULL || ref_file_path == NULL)
{
printf("PGMvsPGM: Aborting comparison\n");
return false;
}
printf(" src_file <%s>\n", src_file);
printf(" ref_file <%s>\n", ref_file_path);
if (sdkLoadPPMub(ref_file_path, &ref_data, &width, &height) != true)
{
printf("PGMvsPGM: unable to load ref image file: %s\n", ref_file_path);
return false;
}
if (sdkLoadPPMub(src_file, &src_data, &width, &height) != true)
{
printf("PGMvsPGM: unable to load src image file: %s\n", src_file);
return false;
}
printf("PGMvsPGM: comparing images size (%d,%d) epsilon(%2.4f), threshold(%4.2f%%)\n", m_Height, m_Width, epsilon, threshold*100);
if (compareDataAsFloatThreshold<unsigned char, float>(ref_data, src_data, m_Height*m_Width, epsilon, threshold) == false)
{
error_count = 1;
}
}
if (error_count == 0)
{
printf(" OK\n");
}
else
{
printf(" FAILURE: %d errors...\n", (unsigned int)error_count);
}
return (error_count == 0); // returns true if all pixels pass
}
virtual bool PPMvsPPM(const char *src_file, const char *ref_file, const float epsilon, const float threshold = 0.0f)
{
unsigned long error_count = 0;
char *ref_file_path = sdkFindFilePath(ref_file, m_ExecPath.c_str());
if (ref_file_path == NULL)
{
printf("CheckRender::PPMvsPPM unable to find <%s> in <%s> Aborting comparison!\n", ref_file, m_ExecPath.c_str());
printf(">>> Check info.xml and [project//data] folder <%s> <<<\n", ref_file);
printf("Aborting comparison!\n");
printf(" FAILED\n");
error_count++;
}
if (src_file == NULL || ref_file_path == NULL)
{
printf("PPMvsPPM: Aborting comparison\n");
return false;
}
printf(" src_file <%s>\n", src_file);
printf(" ref_file <%s>\n", ref_file_path);
return (sdkComparePPM(src_file, ref_file_path, epsilon, threshold, true) == true ? true : false);
}
void setThresholdCompare(float value)
{
m_fThresholdCompare = value;
}
virtual void dumpBin(void *data, unsigned int bytes, const char *filename)
{
FILE *fp;
printf("CheckRender::dumpBin: <%s>\n", filename);
FOPEN(fp, filename, "wb");
fwrite(data, bytes, 1, fp);
fflush(fp);
fclose(fp);
}
virtual bool compareBin2BinUint(const char *src_file, const char *ref_file, unsigned int nelements, const float epsilon, const float threshold)
{
unsigned int *src_buffer, *ref_buffer;
FILE *src_fp = NULL, *ref_fp = NULL;
unsigned long error_count = 0;
size_t fsize = 0;
FOPEN(src_fp, src_file, "rb");
if (src_fp == NULL)
{
printf("compareBin2Bin <unsigned int> unable to open src_file: %s\n", src_file);
error_count++;
}
char *ref_file_path = sdkFindFilePath(ref_file, m_ExecPath.c_str());
if (ref_file_path == NULL)
{
printf("compareBin2Bin <unsigned int> unable to find <%s> in <%s>\n", ref_file, m_ExecPath.c_str());
printf(">>> Check info.xml and [project//data] folder <%s> <<<\n", ref_file);
printf("Aborting comparison!\n");
printf(" FAILED\n");
error_count++;
if (src_fp)
{
fclose(src_fp);
}
if (ref_fp)
{
fclose(ref_fp);
}
}
else
{
FOPEN(ref_fp, ref_file_path, "rb");
if (ref_fp == NULL)
{
printf("compareBin2Bin <unsigned int> unable to open ref_file: %s\n", ref_file_path);
error_count++;
}
if (src_fp && ref_fp)
{
src_buffer = (unsigned int *)malloc(nelements*sizeof(unsigned int));
ref_buffer = (unsigned int *)malloc(nelements*sizeof(unsigned int));
fsize = fread(src_buffer, sizeof(unsigned int), nelements, src_fp);
if (fsize != nelements)
{
printf("compareBin2Bin <unsigned int> failed to read %u elements from %s\n", nelements, src_file);
error_count++;
}
fsize = fread(ref_buffer, sizeof(unsigned int), nelements, ref_fp);
if (fsize == 0)
{
printf("compareBin2Bin <unsigned int> failed to read %u elements from %s\n", nelements, ref_file_path);
error_count++;
}
printf("> compareBin2Bin <unsigned int> nelements=%d, epsilon=%4.2f, threshold=%4.2f\n", nelements, epsilon, threshold);
printf(" src_file <%s>\n", src_file);
printf(" ref_file <%s>\n", ref_file_path);
if (!compareData<unsigned int, float>(ref_buffer, src_buffer, nelements, epsilon, threshold))
{
error_count++;
}
fclose(src_fp);
fclose(ref_fp);
free(src_buffer);
free(ref_buffer);
}
else
{
if (src_fp)
{
fclose(src_fp);
}
if (ref_fp)
{
fclose(ref_fp);
}
}
}
if (error_count == 0)
{
printf(" OK\n");
}
else
{
printf(" FAILURE: %d errors...\n", (unsigned int)error_count);
}
return (error_count == 0); // returns true if all pixels pass
}
virtual bool compareBin2BinFloat(const char *src_file, const char *ref_file, unsigned int nelements, const float epsilon, const float threshold)
{
float *src_buffer, *ref_buffer;
FILE *src_fp = NULL, *ref_fp = NULL;
size_t fsize = 0;
unsigned long error_count = 0;
FOPEN(src_fp, src_file, "rb");
if (src_fp == NULL)
{
printf("compareBin2Bin <float> unable to open src_file: %s\n", src_file);
error_count = 1;
}
char *ref_file_path = sdkFindFilePath(ref_file, m_ExecPath.c_str());
if (ref_file_path == NULL)
{
printf("compareBin2Bin <float> unable to find <%s> in <%s>\n", ref_file, m_ExecPath.c_str());
printf(">>> Check info.xml and [project//data] folder <%s> <<<\n", m_ExecPath.c_str());
printf("Aborting comparison!\n");
printf(" FAILED\n");
error_count++;
if (src_fp)
{
fclose(src_fp);
}
if (ref_fp)
{
fclose(ref_fp);
}
}
else
{
FOPEN(ref_fp, ref_file_path, "rb");
if (ref_fp == NULL)
{
printf("compareBin2Bin <float> unable to open ref_file: %s\n", ref_file_path);
error_count = 1;
}
if (src_fp && ref_fp)
{
src_buffer = (float *)malloc(nelements*sizeof(float));
ref_buffer = (float *)malloc(nelements*sizeof(float));
fsize = fread(src_buffer, sizeof(float), nelements, src_fp);
if (fsize != nelements)
{
printf("compareBin2Bin <float> failed to read %u elements from %s\n", nelements, src_file);
error_count++;
}
fsize = fread(ref_buffer, sizeof(float), nelements, ref_fp);
if (fsize == 0)
{
printf("compareBin2Bin <float> failed to read %u elements from %s\n", nelements, ref_file_path);
error_count++;
}
printf("> compareBin2Bin <float> nelements=%d, epsilon=%4.2f, threshold=%4.2f\n", nelements, epsilon, threshold);
printf(" src_file <%s>\n", src_file);
printf(" ref_file <%s>\n", ref_file_path);
if (!compareDataAsFloatThreshold<float, float>(ref_buffer, src_buffer, nelements, epsilon, threshold))
{
error_count++;
}
fclose(src_fp);
fclose(ref_fp);
free(src_buffer);
free(ref_buffer);
}
else
{
if (src_fp)
{
fclose(src_fp);
}
if (ref_fp)
{
fclose(ref_fp);
}
}
}
if (error_count == 0)
{
printf(" OK\n");
}
else
{
printf(" FAILURE: %d errors...\n", (unsigned int)error_count);
}
return (error_count == 0); // returns true if all pixels pass
}
protected:
unsigned int m_Width, m_Height, m_Bpp;
unsigned char *m_pImageData; // This is the image data stored in system memory
bool m_bQAReadback, m_bUseFBO, m_bUsePBO;
GLuint m_pboReadback;
GLenum m_PixelFormat;
float m_fThresholdCompare;
string m_ExecPath;
};
class CheckBackBuffer : public CheckRender
{
public:
CheckBackBuffer(unsigned int width, unsigned int height, unsigned int Bpp, bool bUseOpenGL = true) :
CheckRender(width, height, Bpp, false, false, bUseOpenGL)
{
}
virtual ~CheckBackBuffer()
{
}
virtual bool checkStatus(const char *zfile, int line, bool silent)
{
GLenum nErrorCode = glGetError();
if (nErrorCode != GL_NO_ERROR)
{
if (!silent)
{
printf("Assertion failed(%s,%d): %s\n", zfile, line, gluErrorString(nErrorCode));
}
}
return true;
}
virtual bool readback(GLuint width, GLuint height)
{
bool ret = false;
if (m_bUsePBO)
{
// binds the PBO for readback
bindReadback();
// Initiate the readback BLT from BackBuffer->PBO->membuf
glReadPixels(0, 0, width, height, getPixelFormat(), GL_UNSIGNED_BYTE, BUFFER_OFFSET(0));
ret = checkStatus(__FILE__, __LINE__, true);
if (!ret)
{
printf("CheckBackBuffer::glReadPixels() checkStatus = %d\n", ret);
}
// map - unmap simulates readback without the copy
void *ioMem = glMapBuffer(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY);
memcpy(m_pImageData, ioMem, width*height*m_Bpp);
glUnmapBuffer(GL_PIXEL_PACK_BUFFER_ARB);
// release the PBO
unbindReadback();
}
else
{
// reading direct from the backbuffer
glReadBuffer(GL_FRONT);
glReadPixels(0, 0, width, height, getPixelFormat(), GL_UNSIGNED_BYTE, m_pImageData);
}
return ret;
}
virtual bool readback(GLuint width, GLuint height, GLuint bufObject)
{
bool ret = false;
if (m_bUseFBO)
{
if (m_bUsePBO)
{
printf("CheckBackBuffer::readback() FBO->PBO->m_pImageData\n");
// binds the PBO for readback
bindReadback();
// bind FBO buffer (we want to transfer FBO -> PBO)
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, bufObject);
// Now initiate the readback to PBO
glReadPixels(0, 0, width, height, getPixelFormat(), GL_UNSIGNED_BYTE, BUFFER_OFFSET(0));
ret = checkStatus(__FILE__, __LINE__, true);
if (!ret)
{
printf("CheckBackBuffer::readback() FBO->PBO checkStatus = %d\n", ret);
}
// map - unmap simulates readback without the copy
void *ioMem = glMapBuffer(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY);
memcpy(m_pImageData, ioMem, width*height*m_Bpp);
glUnmapBuffer(GL_PIXEL_PACK_BUFFER_ARB);
// release the FBO
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
// release the PBO
unbindReadback();
}
else
{
printf("CheckBackBuffer::readback() FBO->m_pImageData\n");
// Reading direct to FBO using glReadPixels
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, bufObject);
ret = checkStatus(__FILE__, __LINE__, true);
if (!ret)
{
printf("CheckBackBuffer::readback::glBindFramebufferEXT() fbo=%d checkStatus = %d\n", bufObject, ret);
}
glReadBuffer(static_cast<GLenum>(GL_COLOR_ATTACHMENT0_EXT));
ret &= checkStatus(__FILE__, __LINE__, true);
if (!ret)
{
printf("CheckBackBuffer::readback::glReadBuffer() fbo=%d checkStatus = %d\n", bufObject, ret);
}
glReadPixels(0, 0, width, height, getPixelFormat(), GL_UNSIGNED_BYTE, m_pImageData);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
}
}
else
{
printf("CheckBackBuffer::readback() PBO->m_pImageData\n");
// read from bufObject (PBO) to system memorys image
glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, bufObject); // Bind the PBO
// map - unmap simulates readback without the copy
void *ioMem = glMapBuffer(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY);
// allocate a buffer so we can flip the image
unsigned char *temp_buf = (unsigned char *)malloc(width*height*m_Bpp);
memcpy(temp_buf, ioMem, width*height*m_Bpp);
// let's flip the image as we copy
for (unsigned int y = 0; y < height; y++)
{
memcpy((void *)&(m_pImageData[(height-y)*width*m_Bpp]), (void *)&(temp_buf[y*width*m_Bpp]), width*m_Bpp);
}
free(temp_buf);
glUnmapBuffer(GL_PIXEL_PACK_BUFFER_ARB);
// read from bufObject (PBO) to system memory image
glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, 0); // unBind the PBO
}
return CHECK_FBO;
}
virtual bool readback(GLuint width, GLuint height, unsigned char *memBuf)
{
// let's flip the image as we copy
for (unsigned int y = 0; y < height; y++)
{
memcpy((void *)&(m_pImageData[(height-y)*width*m_Bpp]), (void *)&(memBuf[y*width*m_Bpp]), width*m_Bpp);
}
return true;
}
private:
virtual void bindFragmentProgram() {};
virtual void bindRenderPath() {};
virtual void unbindRenderPath() {};
// bind to the BackBuffer to Texture
virtual void bindTexture() {};
// release this bind
virtual void unbindTexture() {};
};
// structure defining the properties of a single buffer
struct bufferConfig
{
string name;
GLenum format;
int bits;
};
// structures defining properties of an FBO
struct fboConfig
{
string name;
GLenum colorFormat;
GLenum depthFormat;
int redbits;
int depthBits;
int depthSamples;
int coverageSamples;
};
struct fboData
{
GLuint colorTex; //color texture
GLuint depthTex; //depth texture
GLuint fb; // render framebuffer
GLuint resolveFB; //multisample resolve target
GLuint colorRB; //color render buffer
GLuint depthRB; // depth render buffer
};
class CFrameBufferObject
{
public:
CFrameBufferObject(unsigned int width, unsigned int height, unsigned int Bpp, bool bUseFloat, GLenum eTarget) :
m_Width(width),
m_Height(height),
m_bUseFloat(bUseFloat),
m_eGLTarget(eTarget)
{
glGenFramebuffersEXT(1, &m_fboData.fb);
m_fboData.colorTex = createTexture(m_eGLTarget, width, height,
(bUseFloat ? GL_RGBA32F_ARB : GL_RGBA8), GL_RGBA);
m_fboData.depthTex = createTexture(m_eGLTarget, width, height,
GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT);
attachTexture(m_eGLTarget, m_fboData.colorTex, GL_COLOR_ATTACHMENT0_EXT);
attachTexture(m_eGLTarget, m_fboData.depthTex, GL_DEPTH_ATTACHMENT_EXT);
// bool ret = checkStatus(__FILE__, __LINE__, true);
}
virtual ~CFrameBufferObject()
{
// freeResources();
}
GLuint createTexture(GLenum target, int w, int h, GLint internalformat, GLenum format)
{
GLuint texid;
glGenTextures(1, &texid);
glBindTexture(target, texid);
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(target, 0, internalformat, w, h, 0, format, GL_FLOAT, 0);
return texid;
}
void attachTexture(GLenum texTarget,
GLuint texId,
GLenum attachment = GL_COLOR_ATTACHMENT0_EXT,
int mipLevel = 0,
int zSlice = 0)
{
bindRenderPath();
switch (texTarget)
{
case GL_TEXTURE_1D:
glFramebufferTexture1DEXT(GL_FRAMEBUFFER_EXT, attachment,
GL_TEXTURE_1D, texId, mipLevel);
break;
case GL_TEXTURE_3D:
glFramebufferTexture3DEXT(GL_FRAMEBUFFER_EXT, attachment,
GL_TEXTURE_3D, texId, mipLevel, zSlice);
break;
default:
// Default is GL_TEXTURE_2D, GL_TEXTURE_RECTANGLE_ARB, or cube faces
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, attachment,
texTarget, texId, mipLevel);
break;
}
unbindRenderPath();
}
bool initialize(unsigned width, unsigned height, fboConfig &rConfigFBO, fboData &rActiveFBO)
{
//Framebuffer config options
vector<bufferConfig> colorConfigs;
vector<bufferConfig> depthConfigs;
bufferConfig temp;
//add default color configs
temp.name = (m_bUseFloat ? "RGBA32F" : "RGBA8");
temp.bits = (m_bUseFloat ? 32 : 8);
temp.format = (m_bUseFloat ? GL_RGBA32F_ARB : GL_RGBA8);
colorConfigs.push_back(temp);
//add default depth configs
temp.name = "D24";
temp.bits = 24;
temp.format = GL_DEPTH_COMPONENT24;
depthConfigs.push_back(temp);
// If the FBO can be created, add it to the list of available configs, and make a menu entry
string root = colorConfigs[0].name + " " + depthConfigs[0].name;
rConfigFBO.colorFormat = colorConfigs[0].format;
rConfigFBO.depthFormat = depthConfigs[0].format;
rConfigFBO.redbits = colorConfigs[0].bits;
rConfigFBO.depthBits = depthConfigs[0].bits;
//single sample
rConfigFBO.name = root;
rConfigFBO.coverageSamples = 0;
rConfigFBO.depthSamples = 0;
create(width, height, rConfigFBO, rActiveFBO);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
if (m_bUseFloat)
{
// load fragment programs
const char *strTextureProgram2D =
"!!ARBfp1.0\n"
"TEX result.color, fragment.texcoord[0], texture[0], 2D;\n"
"END\n";
m_textureProgram = nv::CompileASMShader(GL_FRAGMENT_PROGRAM_ARB, strTextureProgram2D);
const char *strOverlayProgram =
"!!ARBfp1.0\n"
"TEMP t;\n"
"TEX t, fragment.texcoord[0], texture[0], 2D;\n"
"MOV result.color, t;\n"
"END\n";
m_overlayProgram = nv::CompileASMShader(GL_FRAGMENT_PROGRAM_ARB, strOverlayProgram);
}
return CHECK_FBO;
}
bool create(GLuint width, GLuint height, fboConfig &config, fboData &data)
{
bool multisample = config.depthSamples > 0;
bool ret = true;
GLint query;
printf("\nCreating FBO <%s> (%dx%d) Float:%s\n", config.name.c_str(), (int)width, (int)height, (m_bUseFloat ? "Y":"N"));
glGenFramebuffersEXT(1, &data.fb);
glGenTextures(1, &data.colorTex);
// init texture
glBindTexture(m_eGLTarget, data.colorTex);
glTexImage2D(m_eGLTarget, 0, config.colorFormat,
width, height, 0, GL_RGBA,
(m_bUseFloat ? GL_FLOAT : GL_UNSIGNED_BYTE),
NULL);
glGenerateMipmapEXT(m_eGLTarget);
glTexParameterf(m_eGLTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(m_eGLTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameterf(m_eGLTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // GL_LINEAR_MIPMAP_LINEAR);
glTexParameterf(m_eGLTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // GL_LINEAR);
{
glGenTextures(1, &data.depthTex);
data.depthRB = 0;
data.colorRB = 0;
data.resolveFB = 0;
//non-multisample, so bind things directly to the FBO
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, data.fb);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, m_eGLTarget, data.colorTex, 0);
glBindTexture(m_eGLTarget, data.depthTex);
glTexImage2D(m_eGLTarget, 0, config.depthFormat,
width, height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
glTexParameterf(m_eGLTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // GL_LINEAR);
glTexParameterf(m_eGLTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // GL_LINEAR);
glTexParameterf(m_eGLTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(m_eGLTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameterf(m_eGLTarget, GL_DEPTH_TEXTURE_MODE, GL_LUMINANCE);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, m_eGLTarget, data.depthTex, 0);
ret &= checkStatus(__FILE__, __LINE__, true);
}
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, data.fb);
glGetIntegerv(GL_RED_BITS, &query);
if (query != config.redbits)
{
ret = false;
}
glGetIntegerv(GL_DEPTH_BITS, &query);
if (query != config.depthBits)
{
ret = false;
}
if (multisample)
{
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, data.resolveFB);
glGetIntegerv(GL_RED_BITS, &query);
if (query != config.redbits)
{
ret = false;
}
}
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
ret &= checkStatus(__FILE__, __LINE__, true);
return ret;
}
virtual void freeResources()
{
if (m_fboData.fb)
{
glDeleteFramebuffersEXT(1, &m_fboData.fb);
}
if (m_fboData.resolveFB)
{
glDeleteFramebuffersEXT(1, &m_fboData.resolveFB);
}
if (m_fboData.colorRB)
{
glDeleteRenderbuffersEXT(1, &m_fboData.colorRB);
}
if (m_fboData.depthRB)
{
glDeleteRenderbuffersEXT(1, &m_fboData.depthRB);
}
if (m_fboData.colorTex)
{
glDeleteTextures(1, &m_fboData.colorTex);
}
if (m_fboData.depthTex)
{
glDeleteTextures(1, &m_fboData.depthTex);
}
glDeleteProgramsARB(1, &m_textureProgram);
glDeleteProgramsARB(1, &m_overlayProgram);
}
virtual bool checkStatus(const char *zfile, int line, bool silent)
{
GLenum status;
status = (GLenum) glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
{
printf("<%s : %d> - ", zfile, line);
}
switch (status)
{
case GL_FRAMEBUFFER_COMPLETE_EXT:
break;
case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
if (!silent)
{
printf("Unsupported framebuffer format\n");
}
return false;
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:
if (!silent)
{
printf("Framebuffer incomplete, missing attachment\n");
}
return false;
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:
if (!silent)
{
printf("Framebuffer incomplete, duplicate attachment\n");
}
return false;
case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
if (!silent)
{
printf("Framebuffer incomplete, attached images must have same dimensions\n");
}
return false;
case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:
if (!silent)
{
printf("Framebuffer incomplete, attached images must have same format\n");
}
return false;
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:
if (!silent)
{
printf("Framebuffer incomplete, missing draw buffer\n");
}
return false;
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:
if (!silent)
{
printf("Framebuffer incomplete, missing read buffer\n");
}
return false;
default:
assert(0);
return false;
}
return true;
}
virtual void renderQuad(int width, int height, GLenum eTarget)
{
float width_norm = (float)width/(float)m_Width,
height_norm = (float)height/(float)m_Height;
// Bind the FBO texture for the display path
glBindTexture(eTarget, m_fboData.colorTex);
glGenerateMipmapEXT(GL_TEXTURE_2D);
glBindTexture(eTarget, 0);
// now render to the full screen using this texture
glClearColor(0.2f, 0.2f, 0.2f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, m_textureProgram);
glEnable(GL_FRAGMENT_PROGRAM_ARB);
glDisable(GL_DEPTH_TEST);
glBegin(GL_QUADS);
{
glVertex2f(0.0f , 0.0f);
glTexCoord2f(0.0f , 0.0f);
glVertex2f(0.0f , height_norm);
glTexCoord2f(width_norm, 0.0f);
glVertex2f(width_norm, height_norm);
glTexCoord2f(width_norm, height_norm);
glVertex2f(width_norm, 0.0f);
glTexCoord2f(0.0f , height_norm);
}
glEnd();
// Release the FBO texture (finished rendering)
glBindTexture(eTarget, 0);
}
// bind to the Fragment Program
void bindFragmentProgram()
{
glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, m_textureProgram);
glEnable(GL_FRAGMENT_PROGRAM_ARB);
}
// bind to the FrameBuffer Object
void bindRenderPath()
{
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_fboData.fb);
}
// release current FrameBuffer Object
void unbindRenderPath()
{
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
}
// bind to the FBO to Texture
void bindTexture()
{
glBindTexture(m_eGLTarget, m_fboData.colorTex);
}
// release this bind
void unbindTexture()
{
glBindTexture(m_eGLTarget, 0);
}
GLuint getFbo()
{
return m_fboData.fb;
}
GLuint getTex()
{
return m_fboData.colorTex;
}
GLuint getDepthTex()
{
return m_fboData.depthTex;
}
private:
GLuint m_Width, m_Height;
fboData m_fboData;
fboConfig m_fboConfig;
GLuint m_textureProgram;
GLuint m_overlayProgram;
bool m_bUseFloat;
GLenum m_eGLTarget;
};
// CheckFBO - render and verify contents of the FBO
class CheckFBO: public CheckRender
{
public:
CheckFBO(unsigned int width, unsigned int height, unsigned int Bpp) :
CheckRender(width, height, Bpp, false, false, true),
m_pFrameBufferObject(NULL)
{
}
CheckFBO(unsigned int width, unsigned int height, unsigned int Bpp, CFrameBufferObject *pFrameBufferObject) :
CheckRender(width, height, Bpp, false, true, true),
m_pFrameBufferObject(pFrameBufferObject)
{
}
virtual ~CheckFBO()
{
}
virtual bool checkStatus(const char *zfile, int line, bool silent)
{
GLenum status;
status = (GLenum) glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
{
printf("<%s : %d> - ", zfile, line);
}
switch (status)
{
case GL_FRAMEBUFFER_COMPLETE_EXT:
break;
case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
if (!silent)
{
printf("Unsupported framebuffer format\n");
}
return false;
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:
if (!silent)
{
printf("Framebuffer incomplete, missing attachment\n");
}
return false;
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:
if (!silent)
{
printf("Framebuffer incomplete, duplicate attachment\n");
}
return false;
case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
if (!silent)
{
printf("Framebuffer incomplete, attached images must have same dimensions\n");
}
return false;
case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:
if (!silent)
{
printf("Framebuffer incomplete, attached images must have same format\n");
}
return false;
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:
if (!silent)
{
printf("Framebuffer incomplete, missing draw buffer\n");
}
return false;
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:
if (!silent)
{
printf("Framebuffer incomplete, missing read buffer\n");
}
return false;
default:
assert(0);
return false;
}
return true;
}
virtual bool readback(GLuint width, GLuint height)
{
bool ret = false;
if (m_bUsePBO)
{
// binds the PBO for readback
bindReadback();
// bind FBO buffer (we want to transfer FBO -> PBO)
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_pFrameBufferObject->getFbo());
// Now initiate the readback to PBO
glReadPixels(0, 0, width, height, getPixelFormat(), GL_UNSIGNED_BYTE, BUFFER_OFFSET(0));
ret = checkStatus(__FILE__, __LINE__, true);
if (!ret)
{
printf("CheckFBO::readback() FBO->PBO checkStatus = %d\n", ret);
}
// map - unmap simulates readback without the copy
void *ioMem = glMapBuffer(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY);
memcpy(m_pImageData, ioMem, width*height*m_Bpp);
glUnmapBuffer(GL_PIXEL_PACK_BUFFER_ARB);
// release the FBO
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
// release the PBO
unbindReadback();
}
else
{
// Reading back from FBO using glReadPixels
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_pFrameBufferObject->getFbo());
ret = checkStatus(__FILE__, __LINE__, true);
if (!ret)
{
printf("CheckFBO::readback::glBindFramebufferEXT() checkStatus = %d\n", ret);
}
glReadBuffer(static_cast<GLenum>(GL_COLOR_ATTACHMENT0_EXT));
ret &= checkStatus(__FILE__, __LINE__, true);
if (!ret)
{
printf("CheckFBO::readback::glReadBuffer() checkStatus = %d\n", ret);
}
glReadPixels(0, 0, width, height, getPixelFormat(), GL_UNSIGNED_BYTE, m_pImageData);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
}
return CHECK_FBO;
}
virtual bool readback(GLuint width, GLuint height, GLuint bufObject)
{
bool ret = false;
if (m_bUseFBO)
{
if (m_bUsePBO)
{
printf("CheckFBO::readback() FBO->PBO->m_pImageData\n");
// binds the PBO for readback
bindReadback();
// bind FBO buffer (we want to transfer FBO -> PBO)
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, bufObject);
// Now initiate the readback to PBO
glReadPixels(0, 0, width, height, getPixelFormat(), GL_UNSIGNED_BYTE, BUFFER_OFFSET(0));
ret = checkStatus(__FILE__, __LINE__, true);
if (!ret)
{
printf("CheckFBO::readback() FBO->PBO checkStatus = %d\n", ret);
}
// map - unmap simulates readback without the copy
void *ioMem = glMapBuffer(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY);
memcpy(m_pImageData, ioMem, width*height*m_Bpp);
glUnmapBuffer(GL_PIXEL_PACK_BUFFER_ARB);
// release the FBO
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
// release the PBO
unbindReadback();
}
else
{
printf("CheckFBO::readback() FBO->m_pImageData\n");
// Reading direct to FBO using glReadPixels
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, bufObject);
ret = checkStatus(__FILE__, __LINE__, true);
if (!ret)
{
printf("CheckFBO::readback::glBindFramebufferEXT() fbo=%d checkStatus = %d\n", (int)bufObject, (int)ret);
}
glReadBuffer(static_cast<GLenum>(GL_COLOR_ATTACHMENT0_EXT));
ret &= checkStatus(__FILE__, __LINE__, true);
if (!ret)
{
printf("CheckFBO::readback::glReadBuffer() fbo=%d checkStatus = %d\n", (int)bufObject, (int)ret);
}
glReadPixels(0, 0, width, height, getPixelFormat(), GL_UNSIGNED_BYTE, m_pImageData);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
}
}
else
{
printf("CheckFBO::readback() PBO->m_pImageData\n");
// read from bufObject (PBO) to system memorys image
glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, bufObject); // Bind the PBO
// map - unmap simulates readback without the copy
void *ioMem = glMapBuffer(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY);
memcpy(m_pImageData, ioMem, width*height*m_Bpp);
glUnmapBuffer(GL_PIXEL_PACK_BUFFER_ARB);
// read from bufObject (PBO) to system memory image
glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, 0); // unBind the PBO
}
return CHECK_FBO;
}
virtual bool readback(GLuint width, GLuint height, unsigned char *memBuf)
{
// let's flip the image as we copy
for (unsigned int y = 0; y < height; y++)
{
memcpy((void *)&(m_pImageData[(height-y)*width*m_Bpp]), (void *)&(memBuf[y*width*m_Bpp]), width*m_Bpp);
}
return true;
}
private:
CFrameBufferObject *m_pFrameBufferObject;
};
#endif // _RENDERCHECK_GL_H_