#include "TGLFBO.h"
#include <TMath.h>
#include <TString.h>
#include <TError.h>
#include <GL/glew.h>
#include <stdexcept>
ClassImp(TGLFBO);
Bool_t TGLFBO::fgRescaleToPow2 = kTRUE;
Bool_t TGLFBO::fgMultiSampleNAWarned = kFALSE;
TGLFBO::TGLFBO() :
fFrameBuffer (0),
fColorTexture (0),
fDepthBuffer (0),
fMSFrameBuffer(0),
fMSColorBuffer(0),
fW (-1),
fH (-1),
fReqW (-1),
fReqH (-1),
fMSSamples (0),
fMSCoverageSamples (0),
fWScale (1),
fHScale (1),
fIsRescaled (kFALSE)
{
}
TGLFBO::~TGLFBO()
{
Release();
}
void TGLFBO::Init(int w, int h, int ms_samples)
{
static const std::string eh("TGLFBO::Init ");
if (!GLEW_EXT_framebuffer_object)
{
throw std::runtime_error(eh + "GL_EXT_framebuffer_object extension required for FBO.");
}
fReqW = w; fReqH = h;
fIsRescaled = kFALSE;
if (fgRescaleToPow2)
{
Int_t nw = 1 << TMath::CeilNint(TMath::Log2(w));
Int_t nh = 1 << TMath::CeilNint(TMath::Log2(h));
if (nw != w || nh != h)
{
fWScale = ((Float_t)w) / nw;
fHScale = ((Float_t)h) / nh;
w = nw; h = nh;
fIsRescaled = kTRUE;
}
}
if (ms_samples > 0 && ! GLEW_EXT_framebuffer_multisample)
{
if (!fgMultiSampleNAWarned)
{
Info(eh.c_str(), "GL implementation does not support multi-sampling for FBOs.");
fgMultiSampleNAWarned = kTRUE;
}
ms_samples = 0;
}
if (fFrameBuffer != 0)
{
if (fW == w && fH == h && fMSSamples == ms_samples)
return;
Release();
}
Int_t maxSize;
glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE_EXT, &maxSize);
if (w > maxSize || h > maxSize)
{
throw std::runtime_error(eh + Form("maximum size supported by GL implementation is %d.", maxSize));
}
fW = w; fH = h; fMSSamples = ms_samples;
if (fMSSamples > 0)
{
if (GLEW_NV_framebuffer_multisample_coverage)
{
GLint n_modes;
glGetIntegerv(GL_MAX_MULTISAMPLE_COVERAGE_MODES_NV, &n_modes);
GLint *modes = new GLint[2*n_modes];
glGetIntegerv(GL_MULTISAMPLE_COVERAGE_MODES_NV, modes);
for (int i = 0; i < n_modes; ++i)
{
if (modes[i*2+1] == fMSSamples && modes[i*2] > fMSCoverageSamples)
fMSCoverageSamples = modes[i*2];
}
delete [] modes;
}
if (gDebug > 0) {
Info(eh.c_str(), "InitMultiSample coverage_samples=%d, color_samples=%d.", fMSCoverageSamples, fMSSamples);
}
InitMultiSample();
}
else
{
if (gDebug > 0) {
Info(eh.c_str(), "InitStandard (no multi-sampling).");
}
InitStandard();
}
GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0);
glBindTexture (GL_TEXTURE_2D, 0);
switch (status)
{
case GL_FRAMEBUFFER_COMPLETE_EXT:
if (gDebug > 0)
printf("%sConstructed TGLFBO ... all fine.\n", eh.c_str());
break;
case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
Release();
throw std::runtime_error(eh + "Constructed TGLFBO not supported, choose different formats.");
break;
default:
Release();
throw std::runtime_error(eh + "Constructed TGLFBO is not complete, unexpected error.");
break;
}
}
void TGLFBO::Release()
{
glDeleteFramebuffersEXT (1, &fFrameBuffer);
glDeleteRenderbuffersEXT(1, &fDepthBuffer);
if (fMSFrameBuffer) glDeleteFramebuffersEXT (1, &fMSFrameBuffer);
if (fMSColorBuffer) glDeleteRenderbuffersEXT(1, &fMSColorBuffer);
if (fColorTexture) glDeleteTextures (1, &fColorTexture);
fW = fH = -1; fMSSamples = fMSCoverageSamples = 0;
fFrameBuffer = fColorTexture = fDepthBuffer = fMSFrameBuffer = fMSColorBuffer = 0;
}
void TGLFBO::Bind()
{
if (fMSSamples > 0) {
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fMSFrameBuffer);
} else {
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fFrameBuffer);
}
}
void TGLFBO::Unbind()
{
if (fMSSamples > 0)
{
glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, fMSFrameBuffer);
glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, fFrameBuffer);
glBlitFramebufferEXT(0, 0, fW, fH, 0, 0, fW, fH, GL_COLOR_BUFFER_BIT, GL_NEAREST);
}
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
}
void TGLFBO::BindTexture()
{
glPushAttrib(GL_TEXTURE_BIT);
glBindTexture(GL_TEXTURE_2D, fColorTexture);
glEnable(GL_TEXTURE_2D);
if (fIsRescaled)
{
glMatrixMode(GL_TEXTURE);
glPushMatrix();
glScalef(fWScale, fHScale, 1);
glMatrixMode(GL_MODELVIEW);
}
}
void TGLFBO::UnbindTexture()
{
if (fIsRescaled)
{
glMatrixMode(GL_TEXTURE);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
}
glPopAttrib();
}
void TGLFBO::SetAsReadBuffer()
{
glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, fFrameBuffer);
}
void TGLFBO::InitStandard()
{
glGenFramebuffersEXT(1, &fFrameBuffer);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fFrameBuffer);
fDepthBuffer = CreateAndAttachRenderBuffer(GL_DEPTH_COMPONENT24, GL_DEPTH_ATTACHMENT);
fColorTexture = CreateAndAttachColorTexture();
}
void TGLFBO::InitMultiSample()
{
glGenFramebuffersEXT(1, &fMSFrameBuffer);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fMSFrameBuffer);
fMSColorBuffer = CreateAndAttachRenderBuffer(GL_RGBA8, GL_COLOR_ATTACHMENT0);
fDepthBuffer = CreateAndAttachRenderBuffer(GL_DEPTH_COMPONENT24, GL_DEPTH_ATTACHMENT);
glGenFramebuffersEXT(1, &fFrameBuffer);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fFrameBuffer);
fColorTexture = CreateAndAttachColorTexture();
}
UInt_t TGLFBO::CreateAndAttachRenderBuffer(Int_t format, Int_t type)
{
UInt_t id = 0;
glGenRenderbuffersEXT(1, &id);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, id);
if (fMSSamples > 0)
{
if (fMSCoverageSamples > 0)
glRenderbufferStorageMultisampleCoverageNV(GL_RENDERBUFFER_EXT, fMSCoverageSamples, fMSSamples, format, fW, fH);
else
glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, fMSSamples, format, fW, fH);
}
else
{
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, format, fW, fH);
}
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, type, GL_RENDERBUFFER_EXT, id);
return id;
}
UInt_t TGLFBO::CreateAndAttachColorTexture()
{
UInt_t id = 0;
glGenTextures(1, &id);
glBindTexture(GL_TEXTURE_2D, id);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, fW, fH, 0, GL_RGBA,
GL_UNSIGNED_BYTE, NULL);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
GL_TEXTURE_2D, id, 0);
return id;
}
Bool_t TGLFBO::GetRescaleToPow2()
{
return fgRescaleToPow2;
}
void TGLFBO::SetRescaleToPow2(Bool_t r)
{
fgRescaleToPow2 = r;
}