mirror of
https://github.com/NVIDIA/cuda-samples.git
synced 2024-12-01 10:09:18 +08:00
1313 lines
39 KiB
C
1313 lines
39 KiB
C
|
/* 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_
|