FrameBuffer creation flags replace booleans

Instead of many creation options (with alpha, with multisampling, etc) use a single flag with boolean operators. Creation of the new mipmap flag for FrameBuffer, rendering the current FBO into multiple sub-resolutions.
This commit is contained in:
Bruno Herbelin
2022-05-22 15:14:10 +02:00
parent 7867aac55f
commit d695aa9f57
15 changed files with 151 additions and 77 deletions

View File

@@ -75,22 +75,24 @@ glm::ivec2 FrameBuffer::getParametersFromResolution(glm::vec3 res)
return p;
}
FrameBuffer::FrameBuffer(glm::vec3 resolution, bool useAlpha, bool multiSampling):
textureid_(0), intermediate_textureid_(0), framebufferid_(0), intermediate_framebufferid_(0),
use_alpha_(useAlpha), use_multi_sampling_(multiSampling)
FrameBuffer::FrameBuffer(glm::vec3 resolution, FrameBufferFlags flags): flags_(flags),
textureid_(0), multisampling_textureid_(0), framebufferid_(0), multisampling_framebufferid_(0)
{
attrib_.viewport = glm::ivec2(resolution);
setProjectionArea(glm::vec2(1.f, 1.f));
attrib_.clear_color = glm::vec4(0.f, 0.f, 0.f, 0.f);
for(int i=0; i<MIPMAP_LEVEL; ++i)
mipmap_framebufferid_[MIPMAP_LEVEL] = 0;
}
FrameBuffer::FrameBuffer(uint width, uint height, bool useAlpha, bool multiSampling):
textureid_(0), intermediate_textureid_(0), framebufferid_(0), intermediate_framebufferid_(0),
use_alpha_(useAlpha), use_multi_sampling_(multiSampling)
FrameBuffer::FrameBuffer(uint width, uint height, FrameBufferFlags flags): flags_(flags),
textureid_(0), multisampling_textureid_(0), framebufferid_(0), multisampling_framebufferid_(0)
{
attrib_.viewport = glm::ivec2(width, height);
setProjectionArea(glm::vec2(1.f, 1.f));
attrib_.clear_color = glm::vec4(0.f, 0.f, 0.f, 0.f);
for(int i=0; i<MIPMAP_LEVEL; ++i)
mipmap_framebufferid_[MIPMAP_LEVEL] = 0;
}
void FrameBuffer::init()
@@ -98,27 +100,40 @@ void FrameBuffer::init()
// generate texture
glGenTextures(1, &textureid_);
glBindTexture(GL_TEXTURE_2D, textureid_);
glTexStorage2D(GL_TEXTURE_2D, 1, use_alpha_ ? GL_RGBA8 : GL_RGB8, attrib_.viewport.x, attrib_.viewport.y);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
if (flags_ & FrameBuffer_mipmap) {
glTexStorage2D(GL_TEXTURE_2D, MIPMAP_LEVEL + 1, (flags_ & FrameBuffer_alpha) ? GL_RGBA8 : GL_RGB8, attrib_.viewport.x, attrib_.viewport.y);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
}
else {
glTexStorage2D(GL_TEXTURE_2D, 1, (flags_ & FrameBuffer_alpha) ? GL_RGBA8 : GL_RGB8, attrib_.viewport.x, attrib_.viewport.y);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glBindTexture(GL_TEXTURE_2D, 0);
// create a framebuffer object
glGenFramebuffers(1, &framebufferid_);
glBindFramebuffer(GL_FRAMEBUFFER, framebufferid_);
// take settings into account: no multisampling for level 0
use_multi_sampling_ &= Settings::application.render.multisampling > 0;
#ifdef FRAMEBUFFER_DEBUG
g_printerr("New FBO %d (%d x %d)\n", framebufferid_, attrib_.viewport.x, attrib_.viewport.y);
#endif
if (use_multi_sampling_){
// take settings into account: no multisampling if application multisampling is level 0
if ( Settings::application.render.multisampling < 1 )
flags_ &= ~FrameBuffer_multisampling;
if (flags_ & FrameBuffer_multisampling){
// create a multisample texture
glGenTextures(1, &intermediate_textureid_);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, intermediate_textureid_);
glGenTextures(1, &multisampling_textureid_);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, multisampling_textureid_);
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, Settings::application.render.multisampling,
use_alpha_ ? GL_RGBA8 : GL_RGB8, attrib_.viewport.x, attrib_.viewport.y, GL_TRUE);
(flags_ & FrameBuffer_alpha) ? GL_RGBA8 : GL_RGB8, attrib_.viewport.x, attrib_.viewport.y, GL_TRUE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
@@ -126,28 +141,34 @@ void FrameBuffer::init()
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0);
// attach the multisampled texture to FBO (framebufferid_ currently binded)
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, intermediate_textureid_, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, multisampling_textureid_, 0);
// create an intermediate FBO : this is the FBO to use for reading
glGenFramebuffers(1, &intermediate_framebufferid_);
glBindFramebuffer(GL_FRAMEBUFFER, intermediate_framebufferid_);
// attach the 2D texture to intermediate FBO (intermediate_framebufferid_)
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureid_, 0);
glGenFramebuffers(1, &multisampling_framebufferid_);
glBindFramebuffer(GL_FRAMEBUFFER, multisampling_framebufferid_);
#ifdef FRAMEBUFFER_DEBUG
g_printerr("New FBO %d Multi Sampling\n", framebufferid_);
g_printerr(" - with Multi Sampling (%d) \n", Settings::application.render.multisampling);
#endif
}
else {
// direct attach the 2D texture to FBO
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureid_, 0);
// attach the 2D texture to latest binded FBO
// (i.e. the multisampling_framebufferid_ FBO if enabled, default framebufferid_ otherwise)
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureid_, 0);
if (flags_ & FrameBuffer_mipmap) {
glGenFramebuffers(MIPMAP_LEVEL, mipmap_framebufferid_);
for(int i=0; i < MIPMAP_LEVEL; ++i) {
glBindFramebuffer(GL_FRAMEBUFFER, mipmap_framebufferid_[i]);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureid_, i + 1);
}
#ifdef FRAMEBUFFER_DEBUG
g_printerr("New FBO %d Single Sampling\n", framebufferid_);
g_printerr(" - with Mipmap (%d)\n", MIPMAP_LEVEL);
#endif
}
checkFramebufferStatus();
@@ -159,15 +180,17 @@ FrameBuffer::~FrameBuffer()
{
if (framebufferid_)
glDeleteFramebuffers(1, &framebufferid_);
if (intermediate_framebufferid_)
glDeleteFramebuffers(1, &intermediate_framebufferid_);
if (multisampling_framebufferid_)
glDeleteFramebuffers(1, &multisampling_framebufferid_);
if (mipmap_framebufferid_[0])
glDeleteFramebuffers(MIPMAP_LEVEL, mipmap_framebufferid_);
if (textureid_)
glDeleteTextures(1, &textureid_);
if (intermediate_textureid_)
glDeleteTextures(1, &intermediate_textureid_);
if (multisampling_textureid_)
glDeleteTextures(1, &multisampling_textureid_);
#ifdef FRAMEBUFFER_DEBUG
GLint framebufferMemoryInKB = ( width() * height() * (use_alpha_?4:3) ) / 1024;
GLint framebufferMemoryInKB = ( width() * height() * (flags_ & FrameBuffer_alpha?4:3) ) / 1024;
g_printerr("Framebuffer deleted %d x %d, ~%d kB freed\n", width(), height(), framebufferMemoryInKB);
#endif
}
@@ -213,15 +236,22 @@ void FrameBuffer::resize(int width, int height)
glDeleteFramebuffers(1, &framebufferid_);
framebufferid_ = 0;
if (intermediate_framebufferid_)
glDeleteFramebuffers(1, &intermediate_framebufferid_);
intermediate_framebufferid_ = 0;
if (multisampling_framebufferid_)
glDeleteFramebuffers(1, &multisampling_framebufferid_);
multisampling_framebufferid_ = 0;
if (mipmap_framebufferid_[0])
glDeleteFramebuffers(MIPMAP_LEVEL, mipmap_framebufferid_);
for(int i=0; i<MIPMAP_LEVEL; ++i)
mipmap_framebufferid_[MIPMAP_LEVEL] = 0;
if (textureid_)
glDeleteTextures(1, &textureid_);
textureid_ = 0;
if (intermediate_textureid_)
glDeleteTextures(1, &intermediate_textureid_);
intermediate_textureid_ = 0;
if (multisampling_textureid_)
glDeleteTextures(1, &multisampling_textureid_);
multisampling_textureid_ = 0;
// change resolution
attrib_.viewport = glm::ivec2(width, height);
@@ -245,15 +275,35 @@ void FrameBuffer::begin(bool clear)
void FrameBuffer::end()
{
// if multisampling frame buffer
if (use_multi_sampling_) {
if (flags_ & FrameBuffer_multisampling) {
// blit the multisample FBO into unisample FBO to generate 2D texture
// Doing this blit will automatically resolve the multisampled FBO.
glBindFramebuffer(GL_READ_FRAMEBUFFER, framebufferid_);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, intermediate_framebufferid_);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, multisampling_framebufferid_);
glBlitFramebuffer(0, 0, attrib_.viewport.x, attrib_.viewport.y,
0, 0, attrib_.viewport.x, attrib_.viewport.y, GL_COLOR_BUFFER_BIT, GL_NEAREST);
}
// if mipmapped texture, fill FBOs
if (flags_ & FrameBuffer_mipmap) {
// First read full-size framebuffer
glBindFramebuffer(GL_READ_FRAMEBUFFER, framebufferid_);
int width = attrib_.viewport.x;
int height = attrib_.viewport.y;
// iterate over all levels of mipmaps
for(int i=0; i < MIPMAP_LEVEL; ++i) {
// draw on Mipmap level i
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mipmap_framebufferid_[i]);
// blit into half-sized mipmap i (with linear nearest color)
glBlitFramebuffer(0, 0, width, height,
0, 0, width / 2, height / 2, GL_COLOR_BUFFER_BIT, GL_NEAREST);
// next iteration (i+1), read half-size mipmap level i
glBindFramebuffer(GL_READ_FRAMEBUFFER, mipmap_framebufferid_[i]);
width = MAX(1, (width / 2));
height = MAX(1, (height / 2));
}
}
FrameBuffer::release();
Rendering::manager().popAttrib();
@@ -273,23 +323,23 @@ void FrameBuffer::readPixels(uint8_t *target_data)
return;
}
if (use_multi_sampling_)
glBindFramebuffer(GL_READ_FRAMEBUFFER, intermediate_framebufferid_);
if (flags_ & FrameBuffer_multisampling)
glBindFramebuffer(GL_READ_FRAMEBUFFER, multisampling_framebufferid_);
else
glBindFramebuffer(GL_READ_FRAMEBUFFER, framebufferid_);
if (use_alpha())
if (flags_ & FrameBuffer_alpha)
glPixelStorei(GL_PACK_ALIGNMENT, 4);
else
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glReadPixels(0, 0, attrib_.viewport.x, attrib_.viewport.y, (use_alpha_? GL_RGBA : GL_RGB), GL_UNSIGNED_BYTE, target_data);
glReadPixels(0, 0, attrib_.viewport.x, attrib_.viewport.y, ((flags_ & FrameBuffer_alpha)? GL_RGBA : GL_RGB), GL_UNSIGNED_BYTE, target_data);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
bool FrameBuffer::blit(FrameBuffer *destination)
{
if (!framebufferid_ || !destination || (use_alpha_ != destination->use_alpha_) ){
if (!framebufferid_ || !destination || (flags_ & FrameBuffer_alpha) != (destination->flags_ & FrameBuffer_alpha) ){
#ifdef FRAMEBUFFER_DEBUG
g_printerr("FrameBuffer blit failed\n");
#endif
@@ -299,8 +349,8 @@ bool FrameBuffer::blit(FrameBuffer *destination)
if (!destination->framebufferid_)
destination->init();
if (use_multi_sampling_)
glBindFramebuffer(GL_READ_FRAMEBUFFER, intermediate_framebufferid_);
if (flags_ & FrameBuffer_multisampling)
glBindFramebuffer(GL_READ_FRAMEBUFFER, multisampling_framebufferid_);
else
glBindFramebuffer(GL_READ_FRAMEBUFFER, framebufferid_);
@@ -310,6 +360,8 @@ bool FrameBuffer::blit(FrameBuffer *destination)
0, 0, destination->width(), destination->height(), GL_COLOR_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// TODO : verify blit onto FBO with mipmapping ?
return true;
}
@@ -353,7 +405,7 @@ void FrameBuffer::checkFramebufferStatus()
{
// approximation of RAM needed for this FBO
GLint framebufferMemoryInKB = ( width() * height() * (use_alpha_?4:3) * (use_multi_sampling_?2:1) ) / 1024;
GLint framebufferMemoryInKB = ( width() * height() * (flags_ & FrameBuffer_alpha?4:3) * (flags_ & FrameBuffer_multisampling?2:1) ) / 1024;
// test available memory if created buffer is big (more than 8MB)
if ( framebufferMemoryInKB > 8000 ) {
@@ -463,7 +515,7 @@ FrameBufferImage *FrameBuffer::image(){
return img;
// only compatible for RGB FrameBuffers
if (use_alpha_ || use_multi_sampling_)
if (flags_ & FrameBuffer_alpha || flags_ & FrameBuffer_multisampling)
return img;
// allocate image
@@ -482,7 +534,7 @@ bool FrameBuffer::fill(FrameBufferImage *image)
init();
// only compatible for RGB FrameBuffers
if (use_alpha_ || use_multi_sampling_)
if (flags_ & FrameBuffer_alpha || flags_ & FrameBuffer_multisampling)
return false;
// invalid image