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.
1347 lines
40 KiB
1347 lines
40 KiB
/* 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_GL_H_ |
|
#define _RENDERCHECK_GL_H_ |
|
|
|
#include <assert.h> |
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
#include <string.h> |
|
|
|
#include <map> |
|
#include <string> |
|
#include <vector> |
|
|
|
#if defined(__APPLE__) || defined(MACOSX) |
|
#include <GLUT/glut.h> |
|
#else |
|
#include <GL/freeglut.h> |
|
#endif |
|
|
|
#include <helper_image.h> |
|
#include <nvShaderUtils.h> |
|
|
|
using std::map; |
|
using std::string; |
|
using std::vector; |
|
|
|
#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_
|
|
|