Commit b36b68cc authored by Christopher Tenter's avatar Christopher Tenter
Browse files

refactoring fbo, better resize

git-svn-id: http://www.openflipper.org/svnrepo/OpenFlipper/branches/Free@20049 383ad7c9-94d9-4d36-a494-682f7c89f535
parent df8f9c8c
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "FBO.hh" #include "FBO.hh"
#include "GLState.hh" #include "GLState.hh"
#include "GLError.hh" #include "GLError.hh"
#include "GLFormatInfo.hh"
//== NAMESPACES =============================================================== //== NAMESPACES ===============================================================
...@@ -19,6 +20,12 @@ namespace ACG ...@@ -19,6 +20,12 @@ namespace ACG
//== IMPLEMENTATION ========================================================== //== IMPLEMENTATION ==========================================================
FBO::RenderTexture::RenderTexture()
: id(0), target(0), internalFormat(0), format(0), gltype(0),
dim(0,0,0), wrapMode(0), minFilter(0), magFilter(0), owner(false)
{
}
FBO::FBO() FBO::FBO()
: fbo_(0), depthbuffer_(0), stencilbuffer_(0), width_(0), height_(0), samples_(0), fixedsamplelocation_(GL_TRUE), prevFbo_(0), prevDrawBuffer_(GL_NONE) : fbo_(0), depthbuffer_(0), stencilbuffer_(0), width_(0), height_(0), samples_(0), fixedsamplelocation_(GL_TRUE), prevFbo_(0), prevDrawBuffer_(GL_NONE)
...@@ -28,20 +35,7 @@ FBO::FBO() ...@@ -28,20 +35,7 @@ FBO::FBO()
FBO:: FBO::
~FBO() ~FBO()
{ {
// delete framebuffer object del();
if(fbo_)
glDeleteFramebuffersEXT( 1, &fbo_ );
// delete render buffer
if(depthbuffer_)
glDeleteRenderbuffersEXT(1, &depthbuffer_);
// delete stencil buffer
if(stencilbuffer_)
glDeleteRenderbuffersEXT(1, &stencilbuffer_);
for (size_t i = 0; i < internalTextures_.size(); ++i)
glDeleteTextures(1, &internalTextures_[i].id);
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
...@@ -64,6 +58,28 @@ init() ...@@ -64,6 +58,28 @@ init()
checkFramebufferStatus(); checkFramebufferStatus();
} }
//-----------------------------------------------------------------------------
void FBO::del()
{
// delete framebuffer object
if(fbo_)
glDeleteFramebuffersEXT( 1, &fbo_ );
// delete render buffer
if(depthbuffer_)
glDeleteRenderbuffersEXT(1, &depthbuffer_);
// delete stencil buffer
if(stencilbuffer_)
glDeleteRenderbuffersEXT(1, &stencilbuffer_);
for (AttachmentList::iterator it = attachments_.begin(); it != attachments_.end(); ++it)
if (it->second.id && it->second.owner)
glDeleteTextures(1, &it->second.id);
}
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void FBO::attachTexture( GLenum _attachment, GLuint _texture, GLuint _level ) void FBO::attachTexture( GLenum _attachment, GLuint _texture, GLuint _level )
...@@ -86,8 +102,13 @@ void FBO::attachTexture( GLenum _attachment, GLuint _texture, GLuint _level ) ...@@ -86,8 +102,13 @@ void FBO::attachTexture( GLenum _attachment, GLuint _texture, GLuint _level )
// unbind fbo // unbind fbo
unbind(); unbind();
// store texture id in internal array
RenderTexture intID;
intID.id = _texture;
// track texture id // track texture id
attachments_[_attachment] = std::pair<GLuint, GLenum>(_texture, GL_NONE); attachments_[_attachment] = intID;
#else #else
std::cerr << "error: FBO::attachTexture unsupported - update glew headers and rebuild" << std::endl; std::cerr << "error: FBO::attachTexture unsupported - update glew headers and rebuild" << std::endl;
#endif #endif
...@@ -113,8 +134,14 @@ attachTexture2D( GLenum _attachment, GLuint _texture, GLenum _target ) ...@@ -113,8 +134,14 @@ attachTexture2D( GLenum _attachment, GLuint _texture, GLenum _target )
// unbind fbo // unbind fbo
unbind(); unbind();
// store texture id in internal array
RenderTexture intID;
intID.id = _texture;
intID.target = _target;
// track texture id // track texture id
attachments_[_attachment] = std::pair<GLuint, GLenum>(_texture, _target); attachments_[_attachment] = intID;
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
...@@ -137,8 +164,13 @@ void FBO::attachTexture2D( GLenum _attachment, GLsizei _width, GLsizei _height, ...@@ -137,8 +164,13 @@ void FBO::attachTexture2D( GLenum _attachment, GLsizei _width, GLsizei _height,
intID.id = texID; intID.id = texID;
intID.internalFormat = _internalFmt; intID.internalFormat = _internalFmt;
intID.format = _format; intID.format = _format;
intID.gltype = GLFormatInfo(_internalFmt).type();
intID.target = target; intID.target = target;
internalTextures_.push_back(intID); intID.dim = ACG::Vec3i(_width, _height, 1);
intID.wrapMode = _wrapMode;
intID.minFilter = _minFilter;
intID.magFilter = _magFilter;
intID.owner = true;
// specify texture // specify texture
...@@ -152,7 +184,7 @@ void FBO::attachTexture2D( GLenum _attachment, GLsizei _width, GLsizei _height, ...@@ -152,7 +184,7 @@ void FBO::attachTexture2D( GLenum _attachment, GLsizei _width, GLsizei _height,
glTexParameteri(target, GL_TEXTURE_WRAP_T, _wrapMode); glTexParameteri(target, GL_TEXTURE_WRAP_T, _wrapMode);
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, _minFilter); glTexParameteri(target, GL_TEXTURE_MIN_FILTER, _minFilter);
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, _magFilter); glTexParameteri(target, GL_TEXTURE_MAG_FILTER, _magFilter);
glTexImage2D(target, 0, _internalFmt, _width, _height, 0, _format, GL_FLOAT, 0); glTexImage2D(target, 0, _internalFmt, _width, _height, 0, _format, intID.gltype, 0);
} }
else else
glTexImage2DMultisample(target, samples_, _internalFmt, _width, _height, fixedsamplelocation_); glTexImage2DMultisample(target, samples_, _internalFmt, _width, _height, fixedsamplelocation_);
...@@ -161,7 +193,7 @@ void FBO::attachTexture2D( GLenum _attachment, GLsizei _width, GLsizei _height, ...@@ -161,7 +193,7 @@ void FBO::attachTexture2D( GLenum _attachment, GLsizei _width, GLsizei _height,
glTexParameteri(target, GL_TEXTURE_WRAP_T, _wrapMode); glTexParameteri(target, GL_TEXTURE_WRAP_T, _wrapMode);
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, _minFilter); glTexParameteri(target, GL_TEXTURE_MIN_FILTER, _minFilter);
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, _magFilter); glTexParameteri(target, GL_TEXTURE_MAG_FILTER, _magFilter);
glTexImage2D(target, 0, _internalFmt, _width, _height, 0, _format, GL_FLOAT, 0); glTexImage2D(target, 0, _internalFmt, _width, _height, 0, _format, intID.gltype, 0);
#endif // GL_ARB_texture_multisample #endif // GL_ARB_texture_multisample
...@@ -174,6 +206,9 @@ void FBO::attachTexture2D( GLenum _attachment, GLsizei _width, GLsizei _height, ...@@ -174,6 +206,9 @@ void FBO::attachTexture2D( GLenum _attachment, GLsizei _width, GLsizei _height,
// attach // attach
attachTexture2D(_attachment, texID, target); attachTexture2D(_attachment, texID, target);
// track texture id
attachments_[_attachment] = intID;
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
...@@ -191,8 +226,13 @@ void FBO::attachTexture3D( GLenum _attachment, GLsizei _width, GLsizei _height, ...@@ -191,8 +226,13 @@ void FBO::attachTexture3D( GLenum _attachment, GLsizei _width, GLsizei _height,
intID.id = texID; intID.id = texID;
intID.internalFormat = _internalFmt; intID.internalFormat = _internalFmt;
intID.format = _format; intID.format = _format;
intID.gltype = GLFormatInfo(_internalFmt).type();
intID.target = target; intID.target = target;
internalTextures_.push_back(intID); intID.dim = ACG::Vec3i(_width, _height, _depth);
intID.wrapMode = _wrapMode;
intID.minFilter = _minFilter;
intID.magFilter = _magFilter;
intID.owner = true;
// specify texture // specify texture
...@@ -214,127 +254,23 @@ void FBO::attachTexture3D( GLenum _attachment, GLsizei _width, GLsizei _height, ...@@ -214,127 +254,23 @@ void FBO::attachTexture3D( GLenum _attachment, GLsizei _width, GLsizei _height,
// attach // attach
attachTexture(_attachment, texID, 0); attachTexture(_attachment, texID, 0);
// track texture id
attachments_[_attachment] = intID;
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void FBO::attachTexture2DDepth( GLsizei _width, GLsizei _height, GLuint _internalFmt /*= GL_DEPTH_COMPONENT32*/, GLenum _format /*= GL_DEPTH_COMPONENT */ ) void FBO::attachTexture2DDepth( GLsizei _width, GLsizei _height, GLuint _internalFmt /*= GL_DEPTH_COMPONENT32*/, GLenum _format /*= GL_DEPTH_COMPONENT */ )
{ {
// gen texture id attachTexture2D(GL_DEPTH_ATTACHMENT, _width, _height, _internalFmt, _format, GL_CLAMP_TO_EDGE, GL_NEAREST, GL_NEAREST);
GLuint texID;
glGenTextures(1, &texID);
#ifdef GL_ARB_texture_multisample
GLenum target = samples_ ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D;
#else
GLenum target = GL_TEXTURE_2D;
#endif // GL_ARB_texture_multisample
// store texture id in internal array
RenderTexture intID;
intID.id = texID;
intID.internalFormat = _internalFmt;
intID.format = _format;
intID.target = target;
internalTextures_.push_back(intID);
// specify texture
glBindTexture(target, texID);
#ifdef GL_ARB_texture_multisample
if (!samples_)
{
glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(target, 0, _internalFmt, _width, _height, 0, _format, _format == GL_DEPTH_STENCIL ? GL_UNSIGNED_INT_24_8 : GL_UNSIGNED_INT, 0);
}
else
glTexImage2DMultisample(target, samples_, _internalFmt, _width, _height, fixedsamplelocation_);
#else
glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(target, 0, _internalFmt, _width, _height, 0, _format, _format == GL_DEPTH_STENCIL ? GL_UNSIGNED_INT_24_8 : GL_FLOAT, 0);
#endif // GL_ARB_texture_multisample
checkGLError();
width_ = _width;
height_ = _height;
glBindTexture(target, 0);
// attach
attachTexture2D(GL_DEPTH_ATTACHMENT, texID, target);
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void FBO::attachTexture2DStencil( GLsizei _width, GLsizei _height ) void FBO::attachTexture2DStencil( GLsizei _width, GLsizei _height )
{ {
GLenum _internalFmt = GL_STENCIL_INDEX8; attachTexture2D(GL_STENCIL_ATTACHMENT_EXT, _width, _height, GL_STENCIL_INDEX8, GL_STENCIL_INDEX, GL_CLAMP_TO_EDGE, GL_NEAREST, GL_NEAREST);
GLenum _format = GL_STENCIL_INDEX;
// gen texture id
GLuint texID;
glGenTextures(1, &texID);
#ifdef GL_ARB_texture_multisample
GLenum target = samples_ ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D;
#else
GLenum target = GL_TEXTURE_2D;
#endif // GL_ARB_texture_multisample
// store texture id in internal array
RenderTexture intID;
intID.id = texID;
intID.internalFormat = _internalFmt;
intID.format = _format;
intID.target = target;
internalTextures_.push_back(intID);
// specify texture
glBindTexture(target, texID);
#ifdef GL_ARB_texture_multisample
if (!samples_)
{
glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(target, 0, _internalFmt, _width, _height, 0, _format, GL_UNSIGNED_BYTE, 0);
}
else
glTexImage2DMultisample(target, samples_, _internalFmt, _width, _height, fixedsamplelocation_);
#else
glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(target, 0, _internalFmt, _width, _height, 0, _format, GL_UNSIGNED_BYTE, 0);
#endif // GL_ARB_texture_multisample
checkGLError();
width_ = _width;
height_ = _height;
glBindTexture(target, 0);
// attach
attachTexture2D(GL_STENCIL_ATTACHMENT_EXT, texID, target);
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
...@@ -343,6 +279,9 @@ void ...@@ -343,6 +279,9 @@ void
FBO:: FBO::
addDepthBuffer( GLuint _width, GLuint _height ) addDepthBuffer( GLuint _width, GLuint _height )
{ {
if (depthbuffer_)
glDeleteRenderbuffersEXT(1, &depthbuffer_);
// create renderbuffer // create renderbuffer
glGenRenderbuffersEXT(1, &depthbuffer_); glGenRenderbuffersEXT(1, &depthbuffer_);
...@@ -373,6 +312,9 @@ void ...@@ -373,6 +312,9 @@ void
FBO:: FBO::
addStencilBuffer( GLuint _width, GLuint _height ) addStencilBuffer( GLuint _width, GLuint _height )
{ {
if (stencilbuffer_)
glDeleteRenderbuffersEXT(1, &stencilbuffer_);
// create renderbuffer // create renderbuffer
glGenRenderbuffersEXT(1, &stencilbuffer_); glGenRenderbuffersEXT(1, &stencilbuffer_);
...@@ -483,7 +425,7 @@ checkFramebufferStatus() ...@@ -483,7 +425,7 @@ checkFramebufferStatus()
GLuint FBO::getAttachment( GLenum _attachment ) GLuint FBO::getAttachment( GLenum _attachment )
{ {
return attachments_[_attachment].first; return attachments_[_attachment].id;
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
...@@ -492,168 +434,54 @@ void FBO::resize( GLsizei _width, GLsizei _height, bool _forceResize ) ...@@ -492,168 +434,54 @@ void FBO::resize( GLsizei _width, GLsizei _height, bool _forceResize )
{ {
if (_width != width_ ||_height != height_ || _forceResize) if (_width != width_ ||_height != height_ || _forceResize)
{ {
bool reattachTextures = false; // resizing already existing textures is highly driver dependent and does not always
bool detachedAlready = false; // work for all combinations of texture type (2d, 2dms, 3d) and format
// safest way to resize is to first delete the FBO and all its internal textures, and then rebuild
// resize every texture stored in internal array if (fbo_)
for (size_t i = 0; i < internalTextures_.size(); ++i) glDeleteFramebuffersEXT(1, &fbo_);
{ glGenFramebuffersEXT(1, &fbo_);
RenderTexture* rt = &internalTextures_[i];
#ifdef GL_ARB_texture_multisample
// find attachment
GLenum attachmentID = 0;
for (AttachmentList::iterator it = attachments_.begin(); it != attachments_.end(); ++it)
{
if (rt->id == it->second.first)
attachmentID = it->first;
}
// check if we have to convert to multisampling // "detach" all textures
if (rt->target == GL_TEXTURE_2D && samples_ > 0) AttachmentList temp;
{ temp.swap(attachments_);
rt->target = GL_TEXTURE_2D_MULTISAMPLE;
reattachTextures = true;
glDeleteTextures(1, &rt->id); // reattach all targets
glGenTextures(1, &rt->id); for (AttachmentList::iterator it = temp.begin(); it != temp.end(); ++it)
{
RenderTexture* rt = &it->second;
// update attachment map // only resize textures that are owned by the FBO
attachments_[attachmentID] = std::pair<GLuint, GLenum>(rt->id, rt->target); if (rt->owner)
}
else if (rt->target == GL_TEXTURE_2D_MULTISAMPLE && samples_ == 0)
{ {
rt->target = GL_TEXTURE_2D;
reattachTextures = true;
glDeleteTextures(1, &rt->id); glDeleteTextures(1, &rt->id);
glGenTextures(1, &rt->id);
// update attachment map switch (rt->target)
attachments_[attachmentID] = std::pair<GLuint, GLenum>(rt->id, rt->target);
}
#endif // GL_ARB_texture_multisample
if (reattachTextures && !detachedAlready)
{
// temporarily remove all attachments from the fbo
// bind();
//
// for (AttachmentList::iterator it = attachments_.begin(); it != attachments_.end(); ++it)
// glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, it->first, GL_TEXTURE_2D, 0, 0 );
//
// glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, 0);
// glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, 0);
//
// detachedAlready = true;
// .. too much trouble, highly dependent on driver implementation
// just delete the fbo and start from scratch
if (fbo_)
glDeleteFramebuffersEXT(1, &fbo_);
glGenFramebuffersEXT(1, &fbo_);
detachedAlready = true;
if (depthbuffer_)
{ {
glDeleteRenderbuffersEXT(1, &depthbuffer_); case GL_TEXTURE_2D:
glGenRenderbuffersEXT(1, &depthbuffer_);
}
}
glBindTexture(rt->target, rt->id);
#ifdef GL_ARB_texture_multisample
if (!samples_)
glTexImage2D(rt->target, 0, rt->internalFormat, _width, _height, 0, rt->format, rt->format == GL_DEPTH_STENCIL ? GL_UNSIGNED_INT_24_8 : GL_FLOAT, 0);
else
{
// Resizing directly by calling glTexImage2DMultisample leads to corrupted memory and weird runtime behaviour.
// Workaround: delete and alloc texture buffer manually and reattach to fbo.
// Maybe caused by unfinished render jobs or opengl driver bug
glDeleteTextures(1, &rt->id);
glGenTextures(1, &rt->id);
glBindTexture(rt->target, rt->id);
glTexImage2DMultisample(rt->target, samples_, rt->internalFormat, _width, _height, fixedsamplelocation_);
reattachTextures = true;
}
#else
glTexImage2D(rt->target, 0, rt->internalFormat, _width, _height, 0, rt->format, rt->format == GL_DEPTH_STENCIL ? GL_UNSIGNED_INT_24_8 : GL_FLOAT, 0);
#endif // GL_ARB_texture_multisample
}
// resize depth renderbuffer
if (depthbuffer_)
{
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthbuffer_);
#ifdef GL_ARB_texture_multisample #ifdef GL_ARB_texture_multisample
if (samples_) case GL_TEXTURE_2D_MULTISAMPLE:
glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, samples_, GL_DEPTH_COMPONENT, _width, _height);
else
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, _width, _height);
#else
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, _width, _height);
#endif #endif
} attachTexture2D(it->first, rt->dim[0], rt->dim[1], rt->internalFormat, rt->format, rt->wrapMode, rt->minFilter, rt->magFilter);
break;
// resize stencil renderbuffer case GL_TEXTURE_3D:
if (stencilbuffer_) attachTexture3D(it->first, rt->dim[0], rt->dim[1], rt->dim[2], rt->internalFormat, rt->format, rt->wrapMode, rt->minFilter, rt->magFilter);
{ break;
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, stencilbuffer_);
#ifdef GL_ARB_texture_multisample
glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, samples_, GL_STENCIL_INDEX, _width, _height);
#else
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_STENCIL_INDEX, _width, _height);
#endif
}
// store new size
width_ = _width;
height_ = _height;
default:
std::cout << "FBO::resize - unknown resize target " << rt->target << std::endl;
if (reattachTextures) }
{
// reattach render buffers
bind();
if (depthbuffer_)
{
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depthbuffer_);
}
checkFramebufferStatus();
if (stencilbuffer_)
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, stencilbuffer_);
checkFramebufferStatus();
unbind();
// reattach color targets
for (AttachmentList::iterator it = attachments_.begin(); it != attachments_.end(); ++it)
{
attachTexture2D( it->first, it->second.first, it->second.second );
} }
} }
// reattach render buffers
if(depthbuffer_)
addDepthBuffer(_width, _height);
glBindTexture(GL_TEXTURE_2D, 0);
if(stencilbuffer_)
addStencilBuffer(_width, _height);
} }
} }
GLuint FBO::getFboID() GLuint FBO::getFboID()
......
...@@ -53,6 +53,9 @@ public: ...@@ -53,6 +53,9 @@ public:
/// function to generate the framebuffer object /// function to generate the framebuffer object
void init(); void init();
/// delete fbo and all internally created render textures
void del();
/// enable/disable multisampling /// enable/disable multisampling
/// returns the MSAA sample count of the fbo, which might not be equal to the requested _samples count but you'll get something close /// returns the MSAA sample count of the fbo, which might not be equal to the requested _samples count but you'll get something close
GLsizei setMultisampling(GLsizei _samples, GLboolean _fixedsamplelocations = GL_TRUE); GLsizei setMultisampling(GLsizei _samples, GLboolean _fixedsamplelocations = GL_TRUE);
...@@ -112,6 +115,9 @@ public: ...@@ -112,6 +115,9 @@ public:
/// get height of fbo texture /// get height of fbo texture
GLsizei height() const {return height_;} GLsizei height() const {return height_;}