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