Program Listing for File framebuffer.cpp¶
↰ Return to documentation for file (/home/runner/work/Legion-Engine/Legion-Engine/legion/engine/rendering/data/framebuffer.cpp
)
#include <rendering/data/framebuffer.hpp>
namespace legion::rendering
{
framebuffer::framebuffer(GLenum target)
: m_id([](app::gl_id& value) { // Assign logic for framebuffer deletion to managed resource.
if (!app::ContextHelper::initialized())
return;
#if defined(LEGION_DEBUG)
if (!app::ContextHelper::getCurrentContext())
{
log::error("No current context to delete framebuffer with.");
return;
}
#endif
if (value)
glDeleteFramebuffers(1, &value);
}, invalid_id),
m_target(target)
{
#if defined(LEGION_DEBUG)
if (!app::ContextHelper::getCurrentContext())
{
log::error("No current context to create framebuffer with.");
return;
}
#endif
glGenFramebuffers(1, &m_id); // Generate framebuffer
}
std::pair<bool, std::string> framebuffer::verify() const
{
#if defined(LEGION_DEBUG)
if (!app::ContextHelper::getCurrentContext())
{
log::error("No current context to work with.");
return { false, "No context made current." };
}
#endif
// Check if the framebuffer was ever even generated.
if (!m_id.value)
return { false, "Framebuffer has not been generated yet." };
glBindFramebuffer(m_target, m_id);
const auto verification = glCheckNamedFramebufferStatus(m_id, m_target); // Fetch the framebuffer status.
glBindFramebuffer(m_target, 0);
#if defined(LEGION_DEBUG)
// Initialize to success so no extra allocations need to happen when everything is good.
std::pair<bool, std::string> result(true, std::string("Framebuffer is complete."));
// Check the verification status.
switch (verification)
{
case GL_FRAMEBUFFER_COMPLETE: // Leave the preinitialized values be.
break;
case GL_FRAMEBUFFER_UNDEFINED:
result = { false, "Id: " + std::to_string(m_id.value) + " Framebuffer set to default but default does not exist." };
break;
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
result = { false, "Id: " + std::to_string(m_id.value) + " One or more attachments of the framebuffer are incomplete." };
break;
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
result = { false, "Id: " + std::to_string(m_id.value) + " Framebuffer does not have atleast one image attached to it." };
break;
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
result = { false, "Id: " + std::to_string(m_id.value) + " Value of GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is GL_NONE for any color attachment point(s) named by GL_DRAW_BUFFERi" };
break;
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
result = { false, "Id: " + std::to_string(m_id.value) + " 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_UNSUPPORTED:
result = { false, "Id: " + std::to_string(m_id.value) + " The combination of internal formats of the attached images violates an implementation-dependent set of restrictions" };
break;
case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:
result = { false, "Id: " + std::to_string(m_id.value) + " 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; 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; or, the value of GL_TEXTURE_FIXED_SAMPLE_LOCATIONS is not the same for all attached textures; or, if the attached images are a mix of renderbuffers and textures, the value of GL_TEXTURE_FIXED_SAMPLE_LOCATIONS is not GL_TRUE for all attached textures." };
break;
case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS:
result = { false, "Id: " + std::to_string(m_id.value) + " 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;
default:
result = { false, "Id: " + std::to_string(m_id.value) + " Unkown framebuffer error: " + std::to_string(verification) };
break;
}
if (!result.first) // If the framebuffer isn't complete we might want to know what the state of the framebuffer is, so we print the attachments.
{
glBindFramebuffer(m_target, m_id);
GLint value;
glGetFramebufferAttachmentParameteriv(m_target, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &value);
switch (value)
{
case GL_NONE:
log::error("Color attachment is of type GL_NONE");
break;
case GL_FRAMEBUFFER_DEFAULT:
log::error("Color attachment is of type GL_FRAMEBUFFER_DEFAULT");
break;
case GL_TEXTURE:
log::error("Color attachment is of type GL_TEXTURE");
break;
case GL_RENDERBUFFER:
log::error("Color attachment is of type GL_RENDERBUFFER");
break;
default:
log::error("Color attachment is of unknown type: {}", value);
break;
}
glGetFramebufferAttachmentParameteriv(m_target, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &value);
switch (value)
{
case GL_NONE:
log::error("Depth attachment is of type GL_NONE");
break;
case GL_FRAMEBUFFER_DEFAULT:
log::error("Depth attachment is of type GL_FRAMEBUFFER_DEFAULT");
break;
case GL_TEXTURE:
log::error("Depth attachment is of type GL_TEXTURE");
break;
case GL_RENDERBUFFER:
log::error("Depth attachment is of type GL_RENDERBUFFER");
break;
default:
log::error("Depth attachment is of unknown type: {}", value);
break;
}
glGetFramebufferAttachmentParameteriv(m_target, GL_STENCIL_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &value);
switch (value)
{
case GL_NONE:
log::error("Stencil attachment is of type GL_NONE");
break;
case GL_FRAMEBUFFER_DEFAULT:
log::error("Stencil attachment is of type GL_FRAMEBUFFER_DEFAULT");
break;
case GL_TEXTURE:
log::error("Stencil attachment is of type GL_TEXTURE");
break;
case GL_RENDERBUFFER:
log::error("Stencil attachment is of type GL_RENDERBUFFER");
break;
default:
log::error("Stencil attachment is of unknown type: {}", value);
break;
}
glBindFramebuffer(m_target, 0);
}
return result;
#else
// In release we skip all checks and only check if it's actually valid. For more debug information the engine should be ran in debug mode.
if (verification == GL_FRAMEBUFFER_COMPLETE)
return { true, std::string("Framebuffer is complete.") };
return { false, std::string("Framebuffer isn't complete.") };
#endif
}
GLenum framebuffer::target() const
{
return m_target;
}
app::gl_id framebuffer::id() const
{
return m_id;
}
void framebuffer::bind() const
{
#if defined(LEGION_DEBUG)
if (!app::ContextHelper::getCurrentContext())
{
log::error("No current context to work with.");
return;
}
#endif
glBindFramebuffer(m_target, m_id);
}
void framebuffer::attach(renderbuffer rbo, GLenum attachment)
{
OPTICK_EVENT();
#if defined(LEGION_DEBUG)
if (!app::ContextHelper::getCurrentContext())
{
log::error("No current context to work with.");
return;
}
#endif
if (m_id.value == 0)
{
log::error("Attempting to attach render targets to default framebuffer.");
return;
}
glBindFramebuffer(m_target, m_id);
glNamedFramebufferRenderbuffer(m_id, attachment, GL_RENDERBUFFER, rbo.id()); // Attach the renderbuffer.
glBindFramebuffer(m_target, 0);
m_attachments[attachment] = rbo; // Insert the renderbuffer into the map of attachments.
}
void framebuffer::attach(texture_handle texture, GLenum attachment)
{
OPTICK_EVENT();
#if defined(LEGION_DEBUG)
if (!app::ContextHelper::getCurrentContext())
{
log::error("No current context to work with.");
return;
}
#endif
if (m_id.value == 0)
{
log::error("Attempting to attach render targets to default framebuffer.");
return;
}
glBindFramebuffer(m_target, m_id);
glNamedFramebufferTexture(m_id, attachment, texture.get_texture().textureId, 0); // Attach the texture.
glBindFramebuffer(m_target, 0);
m_attachments[attachment] = texture; // Insert the texture into the map of attachments.
}
void framebuffer::detach(GLenum attachment)
{
if (m_id.value == 0)
{
log::error("Attempting to detach render targets to default framebuffer.");
return;
}
if (!m_attachments.count(attachment))
return;
auto att = m_attachments.at(attachment); // Otherwise return the active attachment.
if (std::holds_alternative<std::monostate>(att))
return;
glBindFramebuffer(m_target, m_id);
if (std::holds_alternative<texture_handle>(att))
glNamedFramebufferTexture(m_id, attachment, 0, 0); // Attach the texture.
else
glNamedFramebufferRenderbuffer(m_id, attachment, GL_RENDERBUFFER, 0); // Attach the renderbuffer.
glBindFramebuffer(m_target, 0);
m_attachments.erase(attachment);
}
void framebuffer::attach(attachment att, GLenum attachment)
{
OPTICK_EVENT();
#if defined(LEGION_DEBUG)
if (!app::ContextHelper::getCurrentContext())
{
log::error("No current context to work with.");
return;
}
if (m_id.value == 0)
{
log::error("Attempting to attach render targets to default framebuffer.");
return;
}
if(std::holds_alternative<std::monostate>(att))
{
log::error("Attempting to attach empty attachment to framebuffer.");
return;
}
#endif
glBindFramebuffer(m_target, m_id);
if (std::holds_alternative<texture_handle>(att))
glNamedFramebufferTexture(m_id, attachment, std::get<texture_handle>(att).get_texture().textureId, 0); // Attach the texture.
else
glNamedFramebufferRenderbuffer(m_id, attachment, GL_RENDERBUFFER, std::get<renderbuffer>(att).id()); // Attach the renderbuffer.
glBindFramebuffer(m_target, 0);
m_attachments[attachment] = att; // Insert the attachment into the map of attachments.
}
L_NODISCARD const attachment& framebuffer::getAttachment(GLenum attachment) const
{
OPTICK_EVENT();
if (m_id.value == 0)
{
log::error("Attempting to attach render targets to default framebuffer.");
return invalid_attachment;
}
if (!m_attachments.count(attachment)) // Return an empty std::any if the attachment wasn't active.
return invalid_attachment;
return m_attachments.at(attachment); // Otherwise return the active attachment.
}
void framebuffer::release() const
{
#if defined(LEGION_DEBUG)
if (!app::ContextHelper::getCurrentContext())
{
log::error("No current context to work with.");
return;
}
#endif
glBindFramebuffer(m_target, 0);
}
L_NODISCARD framebuffer::operator bool() const
{
return m_id.value;
}
}