cuda-samples/Common/rendercheck_gles.h

1313 lines
39 KiB
C
Raw Normal View History

2022-01-13 14:05:24 +08:00
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of NVIDIA CORPORATION nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _RENDERCHECK_GLES_H_
#define _RENDERCHECK_GLES_H_
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <vector>
#include <map>
#include <string>
#include <GLES3/gl31.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_RGBA),
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, m_pboReadback);
glBufferData(GL_PIXEL_UNPACK_BUFFER, width * height * Bpp, NULL,
GL_STREAM_READ);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 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, 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, 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 = glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0,
width * height * m_Bpp, GL_READ_ONLY);
memcpy(m_pImageData, ioMem, width * height * m_Bpp);
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
// 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)
glBindFramebuffer(GL_FRAMEBUFFER, 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 = glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0,
width * height * m_Bpp, GL_MAP_READ_BIT);
memcpy(m_pImageData, ioMem, width * height * m_Bpp);
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
// release the FBO
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// release the PBO
unbindReadback();
} else {
printf("CheckBackBuffer::readback() FBO->m_pImageData\n");
// Reading direct to FBO using glReadPixels
glBindFramebuffer(GL_FRAMEBUFFER, 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));
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);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
} else {
printf("CheckBackBuffer::readback() PBO->m_pImageData\n");
// read from bufObject (PBO) to system memorys image
glBindBuffer(GL_PIXEL_PACK_BUFFER, bufObject); // Bind the PBO
// map - unmap simulates readback without the copy
void *ioMem = glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0,
width * height * m_Bpp, GL_MAP_READ_BIT);
// 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);
// read from bufObject (PBO) to system memory image
glBindBuffer(GL_PIXEL_PACK_BUFFER, 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) {
glGenFramebuffers(1, &m_fboData.fb);
m_fboData.colorTex =
createTexture(m_eGLTarget, width, height, GL_RGBA, GL_RGBA);
m_fboData.depthTex = createTexture(
m_eGLTarget, width, height, GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT);
attachTexture(m_eGLTarget, m_fboData.depthTex, GL_DEPTH_ATTACHMENT);
attachTexture(m_eGLTarget, m_fboData.colorTex, GL_COLOR_ATTACHMENT0);
bool ret = checkStatus(__FILE__, __LINE__, false);
}
void check_gl_error(const char *file, int line) {
GLenum err(glGetError());
while (err != GL_NO_ERROR) {
char error[64];
switch (err) {
case GL_INVALID_OPERATION:
strcpy(error, "INVALID_OPERATION");
break;
case GL_INVALID_ENUM:
strcpy(error, "INVALID_ENUM");
break;
case GL_INVALID_VALUE:
strcpy(error, "INVALID_VALUE");
break;
case GL_OUT_OF_MEMORY:
strcpy(error, "OUT_OF_MEMORY");
break;
case GL_INVALID_FRAMEBUFFER_OPERATION:
strcpy(error, "INVALID_FRAMEBUFFER_OPERATION");
break;
}
printf("GL_%s - %s : %d\n", error, file, line);
err = glGetError();
}
}
virtual ~CFrameBufferObject() { freeResources(); }
GLuint createTexture(GLenum target, int w, int h, GLint internalformat,
GLenum format) {
GLuint texid;
glGenTextures(1, &texid);
glBindTexture(target, texid);
if (format != GL_DEPTH_COMPONENT) {
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
} else {
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
}
glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
if (internalformat == GL_DEPTH_COMPONENT24) {
glTexImage2D(target, 0, internalformat, w, h, 0, format, GL_UNSIGNED_INT,
0);
} else {
glTexImage2D(target, 0, internalformat, w, h, 0, format, GL_UNSIGNED_BYTE,
0);
}
check_gl_error(__FILE__, __LINE__);
glBindTexture(target, 0);
return texid;
}
void attachTexture(GLenum texTarget, GLuint texId,
GLenum attachment = GL_COLOR_ATTACHMENT0, int mipLevel = 0,
int zSlice = 0) {
bindRenderPath();
check_gl_error(__FILE__, __LINE__);
glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, texTarget, texId,
mipLevel);
checkStatus(__FILE__, __LINE__, false);
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 : 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);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
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"));
glGenFramebuffers(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);
glGenerateMipmap(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
glBindFramebuffer(GL_FRAMEBUFFER, data.fb);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, 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);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, m_eGLTarget,
data.depthTex, 0);
ret &= checkStatus(__FILE__, __LINE__, false);
}
glBindFramebuffer(GL_FRAMEBUFFER, 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) {
glBindFramebuffer(GL_FRAMEBUFFER, data.resolveFB);
glGetIntegerv(GL_RED_BITS, &query);
if (query != config.redbits) {
ret = false;
}
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
ret &= checkStatus(__FILE__, __LINE__, true);
return ret;
}
virtual void freeResources() {
if (m_fboData.fb) {
glDeleteFramebuffers(1, &m_fboData.fb);
}
if (m_fboData.resolveFB) {
glDeleteFramebuffers(1, &m_fboData.resolveFB);
}
if (m_fboData.colorRB) {
glDeleteRenderbuffers(1, &m_fboData.colorRB);
}
if (m_fboData.depthRB) {
glDeleteRenderbuffers(1, &m_fboData.depthRB);
}
if (m_fboData.colorTex) {
glDeleteTextures(1, &m_fboData.colorTex);
}
if (m_fboData.depthTex) {
glDeleteTextures(1, &m_fboData.depthTex);
}
glDeleteProgram(m_textureProgram);
glDeleteProgram(m_overlayProgram);
}
virtual bool checkStatus(const char *zfile, int line, bool silent) {
GLenum status;
status = (GLenum)glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) {
printf("<%s : %d> - this one ", zfile, line);
}
switch (status) {
case GL_FRAMEBUFFER_COMPLETE:
break;
case GL_FRAMEBUFFER_UNSUPPORTED:
if (!silent) {
printf("Unsupported framebuffer format\n");
}
return false;
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
if (!silent) {
printf("Framebuffer incomplete, missing attachment\n");
}
return false;
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
if (!silent) {
printf("Framebuffer incomplete, duplicate attachment\n");
}
return false;
default:
assert(0);
return false;
}
return true;
}
// bind to the FrameBuffer Object
void bindRenderPath() { glBindFramebuffer(GL_FRAMEBUFFER, m_fboData.fb); }
// release current FrameBuffer Object
void unbindRenderPath() { glBindFramebuffer(GL_FRAMEBUFFER, 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) {}
void check_gl_error(const char *file, int line) {
GLenum err(glGetError());
while (err != GL_NO_ERROR) {
char error[64];
switch (err) {
case GL_INVALID_OPERATION:
strcpy(error, "INVALID_OPERATION");
break;
case GL_INVALID_ENUM:
strcpy(error, "INVALID_ENUM");
break;
case GL_INVALID_VALUE:
strcpy(error, "INVALID_VALUE");
break;
case GL_OUT_OF_MEMORY:
strcpy(error, "OUT_OF_MEMORY");
break;
case GL_INVALID_FRAMEBUFFER_OPERATION:
strcpy(error, "INVALID_FRAMEBUFFER_OPERATION");
break;
}
printf("GL_%s - %s : %d\n", error, file, line);
err = glGetError();
}
}
virtual ~CheckFBO() {}
virtual bool checkStatus(const char *zfile, int line, bool silent) {
GLenum status;
status = (GLenum)glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) {
printf("<%s : %d> - here ", zfile, line);
}
switch (status) {
case GL_FRAMEBUFFER_COMPLETE:
break;
case GL_FRAMEBUFFER_UNSUPPORTED:
if (!silent) {
printf("Unsupported framebuffer format\n");
}
return false;
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
if (!silent) {
printf("Framebuffer incomplete, missing attachment\n");
}
return false;
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
if (!silent) {
printf("Framebuffer incomplete, duplicate attachment\n");
}
return false;
case GL_FRAMEBUFFER_UNDEFINED:
if (!silent) {
printf("Framebuffer undefined\n");
}
return false;
default:
if (!silent) {
printf("Framebuffer incomplete, default state\n");
}
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)
glBindFramebuffer(GL_FRAMEBUFFER, m_pFrameBufferObject->getFbo());
ret = checkStatus(__FILE__, __LINE__, false);
if (!ret) {
printf("CheckFBO::readback() glBindFramebuffer checkStatus = %d\n",
ret);
}
// Now initiate the readback to PBO
glReadPixels(0, 0, width, height, getPixelFormat(), GL_UNSIGNED_BYTE,
BUFFER_OFFSET(0));
ret = checkStatus(__FILE__, __LINE__, false);
check_gl_error(__FILE__, __LINE__);
if (!ret) {
printf("CheckFBO::readback() FBO->PBO checkStatus = %d\n", ret);
}
int nBufferSize = 0;
glGetBufferParameteriv(GL_PIXEL_PACK_BUFFER, GL_BUFFER_SIZE,
&nBufferSize);
if (nBufferSize != width * height * m_Bpp) {
printf("Buffer size incorrect, exiting..\n");
exit(EXIT_FAILURE);
}
// map - unmap simulates readback without the copy
void *ioMem = glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0,
width * height * m_Bpp, GL_MAP_READ_BIT);
check_gl_error(__FILE__, __LINE__);
if (ioMem != NULL) {
memcpy(m_pImageData, ioMem, width * height * m_Bpp);
} else {
printf("\nError: Unable to map the PBO\n");
exit(EXIT_FAILURE);
}
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
// release the FBO
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// release the PBO
unbindReadback();
} else {
// Reading back from FBO using glReadPixels
glBindFramebuffer(GL_FRAMEBUFFER, 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));
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);
glBindFramebuffer(GL_FRAMEBUFFER, 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)
glBindFramebuffer(GL_FRAMEBUFFER, 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 = glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0,
width * height * m_Bpp, GL_MAP_READ_BIT);
memcpy(m_pImageData, ioMem, width * height * m_Bpp);
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
// release the FBO
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// release the PBO
unbindReadback();
} else {
printf("CheckFBO::readback() FBO->m_pImageData\n");
// Reading direct to FBO using glReadPixels
glBindFramebuffer(GL_FRAMEBUFFER, 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));
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);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
} else {
printf("CheckFBO::readback() PBO->m_pImageData\n");
// read from bufObject (PBO) to system memorys image
glBindBuffer(GL_PIXEL_PACK_BUFFER, bufObject); // Bind the PBO
// map - unmap simulates readback without the copy
void *ioMem = glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0,
width * height * m_Bpp, GL_MAP_READ_BIT);
memcpy(m_pImageData, ioMem, width * height * m_Bpp);
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
// read from bufObject (PBO) to system memory image
glBindBuffer(GL_PIXEL_PACK_BUFFER, 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_GLES_H_