ResInsight/Fwk/VizFwk/LibRender/cvfFramebufferObject.cpp

622 lines
22 KiB
C++
Raw Normal View History

//##################################################################################################
//
// Custom Visualization Core library
// Copyright (C) 2011-2013 Ceetron AS
//
// This library may be used under the terms of either the GNU General Public License or
// the GNU Lesser General Public License as follows:
//
// GNU General Public License Usage
// This library 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 3 of the License, or
// (at your option) any later version.
//
// This library 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 at <<http://www.gnu.org/licenses/gpl.html>>
// for more details.
//
// GNU Lesser General Public License Usage
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library 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 Lesser General Public License at <<http://www.gnu.org/licenses/lgpl-2.1.html>>
// for more details.
//
//##################################################################################################
#include "cvfBase.h"
#include "cvfFramebufferObject.h"
#include "cvfRenderbufferObject.h"
#include "cvfTexture.h"
#include "cvfOpenGL.h"
#include "cvfString.h"
#include "cvfOglRc.h"
#include "cvfOpenGLResourceManager.h"
#include "cvfOpenGLCapabilities.h"
namespace cvf {
//==================================================================================================
///
/// \class cvf::FramebufferObject
/// \ingroup Render
///
/// Encapsulates FBOs which are used to redirect OpenGL (and thus shader) output from the default
/// Window Framebuffer to a number of custom buffers (either textures or renderbuffers)
///
//==================================================================================================
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
FramebufferObject::FramebufferObject()
: m_depthAttachmentVersionTick(0)
{
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
FramebufferObject::~FramebufferObject()
{
// Just release our reference
CVF_ASSERT(OglRc::isSafeToRelease(m_oglRcBuffer.p()));
m_oglRcBuffer = NULL;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void FramebufferObject::attachColorRenderbuffer(uint index, RenderbufferObject* renderbuffer)
{
// Not legal to have both a texture and a render buffer attachment
CVF_ASSERT(m_colorTextures.size() <= index || m_colorTextures[index].isNull());
if (index >= m_colorRenderBuffers.size())
{
m_colorRenderBuffers.resize(index + 1);
}
m_colorRenderBuffers[index] = renderbuffer;
setColorBufferVersionTick(index, 0);
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void FramebufferObject::attachColorTexture2d(uint index, Texture* texture)
{
// Not legal to have both a texture and a render buffer attachment
CVF_ASSERT(m_colorRenderBuffers.size() <= index || m_colorRenderBuffers[index].isNull());
if (index >= m_colorTextures.size())
{
m_colorTextures.resize(index + 1);
}
m_colorTextures[index] = texture;
setColorBufferVersionTick(index, 0);
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void FramebufferObject::attachColorTexture2dCubeMap(uint index, Texture::CubeMapFace face, Texture* texture)
{
// Not legal to have both a texture and a render buffer attachment
CVF_ASSERT(m_colorRenderBuffers.size() <= index || m_colorRenderBuffers[index].isNull());
if (index >= m_colorTextures.size())
{
m_colorTextures.resize(index + 1);
}
m_colorTextures[index] = texture;
m_colorCubeMapFaces[index] = face;
setColorBufferVersionTick(index, 0);
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void FramebufferObject::attachDepthRenderbuffer(RenderbufferObject* renderbuffer)
{
m_depthRenderBuffer = renderbuffer;
m_depthTexture2d = NULL;
m_depthStencilTexture2d = NULL;
m_depthStencilRenderBuffer = NULL;
m_depthAttachmentVersionTick = 0;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void FramebufferObject::attachDepthTexture2d(Texture* texture)
{
m_depthTexture2d = texture;
m_depthRenderBuffer = NULL;
m_depthStencilRenderBuffer = NULL;
m_depthStencilTexture2d = NULL;
m_depthAttachmentVersionTick = 0;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void FramebufferObject::attachDepthStencilRenderbuffer(RenderbufferObject* renderbuffer)
{
m_depthStencilRenderBuffer = renderbuffer;
m_depthStencilTexture2d = NULL;
m_depthTexture2d = NULL;
m_depthRenderBuffer = NULL;
m_depthAttachmentVersionTick = 0;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void FramebufferObject::attachDepthStencilTexture2d(Texture* texture)
{
m_depthStencilTexture2d = texture;
m_depthStencilRenderBuffer = NULL;
m_depthTexture2d = NULL;
m_depthRenderBuffer = NULL;
m_depthAttachmentVersionTick = 0;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void FramebufferObject::resizeAttachedBuffers(uint width, uint height)
{
size_t i;
for (i = 0; i < m_colorRenderBuffers.size(); i++)
{
RenderbufferObject* buffer = m_colorRenderBuffers[i].p();
if (buffer)
{
buffer->setSize(width, height);
}
}
for (i = 0; i < m_colorTextures.size(); i++)
{
Texture* texture = m_colorTextures[i].p();
if (texture)
{
texture->setSize(width, height);
}
}
if (m_depthRenderBuffer.notNull())
{
m_depthRenderBuffer->setSize(width, height);
}
if (m_depthTexture2d.notNull())
{
m_depthTexture2d->setSize(width, height);
}
if (m_depthStencilTexture2d.notNull())
{
m_depthStencilTexture2d->setSize(width, height);
}
if (m_depthStencilRenderBuffer.notNull())
{
m_depthStencilRenderBuffer->setSize(width, height);
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void FramebufferObject::applyOpenGL(OpenGLContext* oglContext)
{
CVF_CALLSITE_OPENGL(oglContext);
CVF_ASSERT(FramebufferObject::supportedOpenGL(oglContext));
bool createdNewFrameBuffer = false;
if (OglRc::safeOglId(m_oglRcBuffer.p()) == 0)
{
m_oglRcBuffer = oglContext->resourceManager()->createOglRcFramebuffer(oglContext);
createdNewFrameBuffer = true;
}
bind(oglContext);
bool attachmentsModified = createdNewFrameBuffer;
uint i;
for (i = 0; i < m_colorRenderBuffers.size(); i++)
{
RenderbufferObject* renderBuffer = m_colorRenderBuffers[i].p();
if (renderBuffer)
{
if (renderBuffer->versionTick() != m_colorAttachmentVersionTicks[i])
{
if (renderBuffer->renderbufferOglId() == 0)
{
if (!renderBuffer->create(oglContext))
{
return;
}
}
glFramebufferRenderbuffer(GL_FRAMEBUFFER, static_cast<GLenum>(GL_COLOR_ATTACHMENT0 + i), GL_RENDERBUFFER, renderBuffer->renderbufferOglId());
setColorBufferVersionTick(i, renderBuffer->versionTick());
attachmentsModified = true;
}
}
}
for (i = 0; i < m_colorTextures.size(); i++)
{
Texture* texture = m_colorTextures[i].p();
if (texture)
{
if (texture->versionTick() != m_colorAttachmentVersionTicks[i])
{
if (texture->textureOglId() == 0)
{
if (!texture->setupTexture(oglContext))
{
return;
}
}
// Assume we'll change the texture so flag mipmaps as invalid
texture->removeMipmap();
std::map<uint, Texture::CubeMapFace>::iterator faceIt = m_colorCubeMapFaces.find(i);
if (faceIt != m_colorCubeMapFaces.end())
{
glFramebufferTexture2D(GL_FRAMEBUFFER, static_cast<GLenum>(GL_COLOR_ATTACHMENT0 + i), static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + faceIt->second), texture->textureOglId(), 0);
}
else
{
if (texture->textureType() == Texture::TEXTURE_RECTANGLE)
{
glFramebufferTexture2D(GL_FRAMEBUFFER, static_cast<GLenum>(GL_COLOR_ATTACHMENT0 + i), GL_TEXTURE_RECTANGLE, texture->textureOglId(), 0);
}
else
{
glFramebufferTexture2D(GL_FRAMEBUFFER, static_cast<GLenum>(GL_COLOR_ATTACHMENT0 + i), GL_TEXTURE_2D, texture->textureOglId(), 0);
}
}
setColorBufferVersionTick(i, texture->versionTick());
attachmentsModified = true;
}
}
}
// Setup draw buffers. Drawbuffer settings are stored in the FBO, so we only need to do this whenever the FBO attachments are changed.
if (attachmentsModified)
{
size_t maxAttachmentIndex = CVF_MAX(m_colorRenderBuffers.size(), m_colorTextures.size());
if (maxAttachmentIndex > 0)
{
GLenum* drawBuffers = new GLenum[maxAttachmentIndex];
size_t i;
for (i = 0; i < maxAttachmentIndex; i++)
{
bool attachmentInUse = false;
if (m_colorRenderBuffers.size() > i && m_colorRenderBuffers[i].notNull())
{
attachmentInUse = true;
}
if (m_colorTextures.size() > i && m_colorTextures[i].notNull())
{
attachmentInUse = true;
}
if (attachmentInUse)
{
drawBuffers[i] = static_cast<GLenum>(GL_COLOR_ATTACHMENT0 + i);
}
else
{
drawBuffers[i] = GL_NONE;
}
}
glDrawBuffers(static_cast<GLsizei>(maxAttachmentIndex), drawBuffers);
delete[] drawBuffers;
}
else
{
glDrawBuffer(GL_NONE);
}
}
// Depth attachment, can only have one
CVF_ASSERT(!(m_depthTexture2d.notNull() && m_depthRenderBuffer.notNull()));
if (m_depthRenderBuffer.notNull())
{
if (m_depthRenderBuffer->versionTick() != m_depthAttachmentVersionTick)
{
if (m_depthRenderBuffer->renderbufferOglId() == 0)
{
if (!m_depthRenderBuffer->create(oglContext))
{
return;
}
}
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_depthRenderBuffer->renderbufferOglId());
m_depthAttachmentVersionTick = m_depthRenderBuffer->versionTick();
}
}
if (m_depthTexture2d.notNull())
{
if (m_depthTexture2d->versionTick() != m_depthAttachmentVersionTick)
{
if (m_depthTexture2d->textureOglId() == 0)
{
if (!m_depthTexture2d->setupTexture(oglContext))
{
return;
}
}
if (m_depthTexture2d->textureType() == Texture::TEXTURE_2D)
{
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, m_depthTexture2d->textureOglId(), 0);
}
else if (m_depthTexture2d->textureType() == Texture::TEXTURE_RECTANGLE)
{
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_RECTANGLE, m_depthTexture2d->textureOglId(), 0);
}
else
{
CVF_FAIL_MSG("Not implemented");
}
m_depthAttachmentVersionTick = m_depthTexture2d->versionTick();
}
}
if (m_depthStencilRenderBuffer.notNull())
{
if (m_depthStencilRenderBuffer->versionTick() != m_depthAttachmentVersionTick)
{
if (m_depthStencilRenderBuffer->renderbufferOglId() == 0)
{
if (!m_depthStencilRenderBuffer->create(oglContext))
{
return;
}
}
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_depthStencilRenderBuffer->renderbufferOglId());
m_depthAttachmentVersionTick = m_depthStencilRenderBuffer->versionTick();
}
}
if (m_depthStencilTexture2d.notNull())
{
if (m_depthStencilTexture2d->versionTick() != m_depthAttachmentVersionTick)
{
if (m_depthStencilTexture2d->textureOglId() == 0)
{
if (!m_depthStencilTexture2d->setupTexture(oglContext))
{
return;
}
}
if (m_depthStencilTexture2d->textureType() == Texture::TEXTURE_2D)
{
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, m_depthStencilTexture2d->textureOglId(), 0);
}
else if (m_depthStencilTexture2d->textureType() == Texture::TEXTURE_RECTANGLE)
{
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_RECTANGLE, m_depthStencilTexture2d->textureOglId(), 0);
}
else
{
CVF_FAIL_MSG("Not implemented");
}
m_depthAttachmentVersionTick = m_depthStencilTexture2d->versionTick();
}
}
CVF_CHECK_OGL(oglContext);
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void FramebufferObject::bind(OpenGLContext* oglContext) const
{
CVF_CALLSITE_OPENGL(oglContext);
CVF_ASSERT(OglRc::safeOglId(m_oglRcBuffer.p()) != 0);
glBindFramebuffer(GL_FRAMEBUFFER, m_oglRcBuffer->oglId());
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void FramebufferObject::useDefaultWindowFramebuffer(OpenGLContext* oglContext)
{
CVF_CALLSITE_OPENGL(oglContext);
2024-01-23 01:48:05 -06:00
const OglId defaultFBO = oglContext->defaultFramebufferObject();
glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO);
if (defaultFBO == 0)
{
glDrawBuffer(GL_BACK);
}
CVF_CHECK_OGL(oglContext);
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void FramebufferObject::deleteFramebuffer(OpenGLContext* oglContext)
{
CVF_ASSERT(oglContext);
if (m_oglRcBuffer.notNull())
{
m_oglRcBuffer->deleteResource(oglContext);
CVF_CHECK_OGL(oglContext);
CVF_ASSERT(OglRc::isSafeToRelease(m_oglRcBuffer.p()));
m_oglRcBuffer = NULL;
m_colorAttachmentVersionTicks.clear();
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void FramebufferObject::deleteOrReleaseOpenGLResources(OpenGLContext* oglContext)
{
deleteFramebuffer(oglContext);
size_t i;
for (i = 0; i < m_colorRenderBuffers.size(); i++)
{
RenderbufferObject* buffer = m_colorRenderBuffers[i].p();
if (buffer)
{
buffer->deleteRenderbuffer(oglContext);
}
}
for (i = 0; i < m_colorTextures.size(); i++)
{
Texture* texture = m_colorTextures[i].p();
if (texture)
{
texture->deleteTexture(oglContext);
}
}
if (m_depthRenderBuffer.notNull())
{
m_depthRenderBuffer->deleteRenderbuffer(oglContext);
}
if (m_depthTexture2d.notNull())
{
m_depthTexture2d->deleteTexture(oglContext);
}
if (m_depthStencilTexture2d.notNull())
{
m_depthStencilTexture2d->deleteTexture(oglContext);
}
if (m_depthStencilRenderBuffer.notNull())
{
m_depthStencilRenderBuffer->deleteRenderbuffer(oglContext);
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool FramebufferObject::supportedOpenGL(OpenGLContext* oglContext)
{
return oglContext->capabilities()->hasCapability(OpenGLCapabilities::FRAMEBUFFER_OBJECT);
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool FramebufferObject::isFramebufferComplete(OpenGLContext* oglContext, String* failReason) const
{
CVF_CALLSITE_OPENGL(oglContext);
bind(oglContext);
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE)
{
switch (status)
{
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: *failReason = "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT is returned if any of the framebuffer attachment points are framebuffer incomplete."; break;
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: *failReason = "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT is returned if the framebuffer does not have at least one image attached to it."; break;
case GL_FRAMEBUFFER_UNSUPPORTED: *failReason = "GL_FRAMEBUFFER_UNSUPPORTED is returned if the combination of internal formats of the attached images violates an implementation-dependent set of restrictions."; break;
case GL_FRAMEBUFFER_UNDEFINED: *failReason = "GL_FRAMEBUFFER_UNDEFINED is returned if target is the default framebuffer, but the default framebuffer does not exist."; break;
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: *failReason = "GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER is returned if the value of GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is GL_NONE for any color attachment point(s) named by GL_DRAWBUFFERi."; break;
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: *failReason = "GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER is returned if GL_READ_BUFFER is not GL_NONE and the value of GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is GL_NONE for the color attachment point named by GL_READ_BUFFER."; break;
case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: *failReason = "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE is returned if the value of GL_RENDERBUFFER_SAMPLES is not the same for all attached renderbuffers; if the value of GL_TEXTURE_SAMPLES is the not same for all attached textures; or, if the attached images are a mix of renderbuffers and textures, the value of GL_RENDERBUFFER_SAMPLES does not match the value of GL_TEXTURE_SAMPLES."; break;
case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS: *failReason = "GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS is returned if any framebuffer attachment is layered, and any populated attachment is not layered, or if all populated color attachments are not from textures of the same target."; break;
}
}
return status == GL_FRAMEBUFFER_COMPLETE;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void FramebufferObject::setColorBufferVersionTick(uint index, uint versionTick)
{
if (index >= m_colorAttachmentVersionTicks.size())
{
m_colorAttachmentVersionTicks.resize(index + 1, 0);
}
m_colorAttachmentVersionTicks[index] = versionTick;
}
} // namespace cvf