1
0
Fork 0
mirror of https://github.com/AquariaOSE/Aquaria.git synced 2024-11-25 17:53:47 +00:00
Aquaria/BBGE/FrameBuffer.cpp

312 lines
6.8 KiB
C++

/*
Copyright (C) 2007, 2010 - Bit-Blot
This file is part of Aquaria.
Aquaria is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "FrameBuffer.h"
#include "Core.h"
#include "RenderBase.h"
//WARNING: FrameBuffer objects have to have reloadDevice/unloadDevice called manually!
struct FBOStack
{
const FrameBuffer *fbo;
unsigned page;
};
static FBOStack s_fbostack[4]; // first entry is always unused
static size_t s_stackpos = 0;
static unsigned s_lastFBO = 0;
static bool isOnTop(const FrameBuffer *fbo)
{
return s_fbostack[s_stackpos].fbo == fbo;
}
static bool isInStack(const FrameBuffer *fbo, unsigned page)
{
for(size_t i = 1; i <= s_stackpos; ++i) // first entry is always NULL
if(s_fbostack[i].fbo == fbo && s_fbostack[i].page == page)
return true;
return false;
}
FrameBuffer::FrameBuffer()
{
inited = false;
texw = 0;
texh = 0;
viewportW = 0;
viewportH = 0;
_curpage = 0;
_w = _h = 0;
_numpages = 0;
_numfbos = 0;
for(size_t i = 0; i < Countof(_texs); ++i)
{
_fbos[i] = 0;
_texs[i] = 0;
}
}
FrameBuffer::~FrameBuffer()
{
unloadDevice();
}
float FrameBuffer::getWidthP() const
{
return (float)core->getWindowWidth()/(float)texw;
}
float FrameBuffer::getHeightP() const
{
return (float)core->getWindowHeight()/(float)texh;
}
bool FrameBuffer::getCurrentPage() const
{
assert(_curpage);
return _curpage - 1;
}
bool FrameBuffer::init(int width, int height, unsigned pages)
{
assert(pages && pages < Countof(_texs));
_w = width;
_h = height;
_numpages = pages;
if (width == -1)
width = core->width;
if (height == -1)
height = core->height;
viewportW = width;
viewportH = height;
sizePowerOf2Texture(width);
sizePowerOf2Texture(height);
if (width == 0 || height == 0)
return false;
texw = width;
texh = height;
std::ostringstream os;
os << "Loading EXT_framebuffer_object (" << texw << ", " << texh << ")";
debugLog(os.str());
if( !glIsFramebufferEXT || !glBindFramebufferEXT || !glDeleteFramebuffersEXT ||
!glGenFramebuffersEXT || !glCheckFramebufferStatusEXT || !glFramebufferTexture2DEXT)
{
debugLog("One or more EXT_framebuffer_object functions were not found");
return false;
}
unloadDevice();
// If glDrawBuffersARB() is present, we can attach multiple textures per FBO
// and switch between them as render targets. More efficient than switching FBOs.
_numfbos = glDrawBuffersARB ? 1 : pages;
glGenFramebuffersEXT(_numfbos, &_fbos[0]);
for(unsigned i = 0; i < _numfbos; ++i)
if(!_fbos[i])
goto out;
if(_numfbos == 1)
glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, _fbos[0] );
glGenTextures( pages, &_texs[0] );
for(unsigned i = 0; i < pages; ++i)
{
unsigned attach = GL_COLOR_ATTACHMENT0_EXT;
if(_numfbos > 1)
glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, _fbos[i] );
else
attach += i;
glBindTexture(GL_TEXTURE_2D, _texs[i]);
// GL_LINEAR
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA,
width, height,
0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, attach, GL_TEXTURE_2D, _texs[i], 0);
}
glBindTexture(GL_TEXTURE_2D, 0);
for(unsigned i = 0; i < _numfbos; ++i)
{
glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, _fbos[i] );
GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
switch( status )
{
case GL_FRAMEBUFFER_COMPLETE_EXT:
break;
case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
debugLog("GL_FRAMEBUFFER_UNSUPPORTED_EXT!");
default:
unloadDevice();
goto out;
}
}
debugLog("Done");
inited = true;
out:
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
return inited;
}
void FrameBuffer::unloadDevice()
{
debugLog("frameBuffer::unloadDevice");
inited = false;
if (glDeleteFramebuffersEXT == NULL)
{
s_lastFBO = 0;
debugLog("Already shut down the GL, don't delete framebuffers");
return;
}
for(size_t i = 0; i < Countof(_texs); ++i)
if (_fbos[i])
{
if(s_lastFBO == _fbos[i])
s_lastFBO = 0;
debugLog("frameBuffer handle present, deleting");
glDeleteFramebuffersEXT(1, &_fbos[i]);
_fbos[i] = 0;
}
for(size_t i = 0; i < Countof(_texs); ++i)
if (_texs[i])
{
debugLog("delete framebuffer texture");
glDeleteTextures(1, &_texs[i]);
_texs[i] = 0;
}
debugLog("done");
}
void FrameBuffer::reloadDevice()
{
if(!_numpages)
return;
s_lastFBO = 0;
debugLog("frameBuffer::reloadDevice");
init(_w, _h, _numpages);
}
void FrameBuffer::_bind(unsigned page) const
{
assert(page < _numpages);
_curpage = page + 1;
unsigned fbo = _numfbos == 1 ? _fbos[0] : _fbos[page];
assert(fbo);
if(fbo != s_lastFBO)
{
s_lastFBO = fbo;
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
}
if(glDrawBuffersARB)
{
GLenum buf = GL_COLOR_ATTACHMENT0_EXT + page;
glDrawBuffersARB(1, &buf);
}
glViewport(0,0,viewportW,viewportH);
}
void FrameBuffer::pushCapture(unsigned page) const
{
assert(inited);
_bind(page);
size_t idx = ++s_stackpos;
assert(idx < Countof(s_fbostack));
s_fbostack[idx].fbo = this;
s_fbostack[idx].page = page;
}
unsigned FrameBuffer::popCapture() const
{
assert(inited && s_stackpos && isOnTop(this));
const unsigned page = s_fbostack[s_stackpos].page;
FBOStack prev = s_fbostack[--s_stackpos];
if(prev.fbo)
prev.fbo->_bind(prev.page);
else
{
s_lastFBO = 0;
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
if(glDrawBuffersARB)
glDrawBuffer(GL_BACK);
glViewport(0, 0, core->width, core->height);
}
return page;
}
void FrameBuffer::replaceCapture(unsigned page) const
{
assert(inited && s_stackpos);
_bind(page);
s_fbostack[s_stackpos].fbo = this;
s_fbostack[s_stackpos].page = page;
}
unsigned FrameBuffer::getTextureID(unsigned page) const
{
assert(inited && page < _numpages);
return _texs[page];
}
void FrameBuffer::bindTexture(unsigned page) const
{
// Technically it's enough that this texture isn't the one currently rendered to,
// but because we don't know when the topmost FBO is popped (and subsequently
// writing to the texture we're now about to read from) let's be extra safe
// and generalize this to: Textures part of the FBO stack are forbidden to bind.
assert(!isInStack(this, page));
unsigned tex = getTextureID(page);
glBindTexture(GL_TEXTURE_2D, tex);
}