// =====================================================================================================================
// glw, an open-source library that wraps OpenGL structures into classes.
// Copyright (C) 2024 Medusa Slockbower
//
// This program 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 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, see .
// =====================================================================================================================
#ifndef GLW_FRAMEBUFFER_H
#define GLW_FRAMEBUFFER_H
#include "texture.h"
#include "texture_array.h"
#include
namespace glw
{
enum framebuffer_usage : enum_t
{
fb_usage_read = GL_READ_FRAMEBUFFER
, fb_usage_draw = GL_DRAW_FRAMEBUFFER
, fb_usage_any = GL_FRAMEBUFFER
};
enum framebuffer_status : enum_t
{
fb_status_complete = GL_FRAMEBUFFER_COMPLETE
, fb_status_undefined = GL_FRAMEBUFFER_UNDEFINED
, fb_status_incomplete_attachment = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT
, fb_status_missing_attachment = GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT
, fb_status_incomplete_draw = GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER
, fb_status_incomplete_read = GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER
, fb_status_incomplete_multisample = GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE
, fb_status_incomplete_layers = GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS
, fb_status_unsupported = GL_FRAMEBUFFER_UNSUPPORTED
, fb_status_unknown = 0
};
static const char* translate_framebuffer_status(enum_t status)
{
switch(status)
{
case(fb_status_complete): return "Complete";
case(fb_status_undefined): return "Undefined";
case(fb_status_incomplete_attachment): return "Incomplete Attachment";
case(fb_status_missing_attachment): return "Missing Attachment";
case(fb_status_incomplete_draw): return "Incomplete Draw";
case(fb_status_incomplete_read): return "Incomplete Read";
case(fb_status_incomplete_multisample): return "Incomplete Multisample";
case(fb_status_incomplete_layers): return "Incomplete Layers";
case(fb_status_unsupported): return "Unsupported";
case(fb_status_unknown):
default:
return "Unknown";
}
}
enum attachment_bit
{
fb_depth_bit = GL_DEPTH_BUFFER_BIT
, fb_stencil_bit = GL_STENCIL_BUFFER_BIT
, fb_color_bit = GL_COLOR_BUFFER_BIT
};
enum attachment
{
no_attachment = GL_NONE
, depth_attachment = GL_DEPTH_ATTACHMENT
, stencil_attachment = GL_STENCIL_ATTACHMENT
, depth_stencil_attachment = GL_DEPTH_STENCIL_ATTACHMENT
, color_attachment0 = GL_COLOR_ATTACHMENT0
, color_attachment1 = GL_COLOR_ATTACHMENT1
, color_attachment2 = GL_COLOR_ATTACHMENT2
, color_attachment3 = GL_COLOR_ATTACHMENT3
, color_attachment4 = GL_COLOR_ATTACHMENT4
, color_attachment5 = GL_COLOR_ATTACHMENT5
, color_attachment6 = GL_COLOR_ATTACHMENT6
, color_attachment7 = GL_COLOR_ATTACHMENT7
, color_attachment8 = GL_COLOR_ATTACHMENT8
, color_attachment9 = GL_COLOR_ATTACHMENT9
, color_attachment10 = GL_COLOR_ATTACHMENT10
, color_attachment11 = GL_COLOR_ATTACHMENT11
, color_attachment12 = GL_COLOR_ATTACHMENT12
, color_attachment13 = GL_COLOR_ATTACHMENT13
, color_attachment14 = GL_COLOR_ATTACHMENT14
, color_attachment15 = GL_COLOR_ATTACHMENT15
, front_buffer = GL_FRONT
, back_buffer = GL_BACK
, front_and_back_buffer = GL_FRONT_AND_BACK
, left_buffer = GL_LEFT
, right_buffer = GL_RIGHT
, front_left_buffer = GL_FRONT_LEFT
, front_right_buffer = GL_FRONT_RIGHT
, back_left_buffer = GL_BACK_LEFT
, back_right_buffer = GL_BACK_RIGHT
};
inline constexpr handle_t default_framebuffer = 0;
template
class framebuffer
{
// Constants ===========================================================================================================
static_assert(!contains_enum::value);
static_assert(!contains_enum::value);
static_assert(!contains_enum::value);
static_assert(!contains_enum::value);
static_assert(!contains_enum::value);
static_assert(!contains_enum::value);
static_assert(!contains_enum::value);
static_assert(!contains_enum::value);
static_assert(!contains_enum::value);
static_assert(!contains_enum::value);
using array_type = texarray;
public:
inline static constexpr enum_t type = T_;
private:
// Functions ===========================================================================================================
// Helpers -------------------------------------------------------------------------------------------------------------
private:
template
void bind_textures_()
{
auto& tex = textures_.template get();
bool color = false;
switch(tex.get_format())
{;
case depth16:
case depth24:
case depth32_f:
glNamedFramebufferTexture(handle_, depth_attachment, tex.handle(), 0);
break;
case stencil1:
case stencil4:
case stencil8:
case stencil16:
glNamedFramebufferTexture(handle_, stencil_attachment, tex.handle(), 0);
break;
case depth24_stencil:
case depth32_f_stencil:
glNamedFramebufferTexture(handle_, depth_stencil_attachment, tex.handle(), 0);
break;
default:
glNamedFramebufferTexture(handle_, color_attachment0 + C, tex.handle(), 0);
colors_.push_back(color_attachment0 + C);
color = true;
break;
}
if constexpr(I + 1 < array_type::length)
{
if(color) bind_textures_();
else bind_textures_();
}
}
// Constructors --------------------------------------------------------------------------------------------------------
public:
framebuffer(size_t size)
: handle_(NULL), size_(size, 1, 1), samples_(1), textures_(size)
{ static_assert(contains_enum::value); glCreateFramebuffers(1, &handle_); }
framebuffer(glm::ivec2 size)
: handle_(NULL), size_(size, 1), samples_(1), textures_(size)
{ static_assert(contains_enum::value); glCreateFramebuffers(1, &handle_); }
framebuffer(glm::ivec2 size, size_t samples = 1) requires(type == texture2DMS)
: handle_(NULL), size_(size, 1), samples_(samples), textures_(size, samples)
{ glCreateFramebuffers(1, &handle_); }
framebuffer(glm::ivec3 size)
: handle_(NULL), size_(size), samples_(1), textures_(size)
{ static_assert(contains_enum::value); glCreateFramebuffers(1, &handle_); }
framebuffer(glm::ivec3 size, size_t samples = 1) requires(type == texture2DMSArray)
: handle_(NULL), size_(size), samples_(samples), textures_(size, samples)
{ glCreateFramebuffers(1, &handle_); }
~framebuffer() { glDeleteFramebuffers(1, &handle_); }
// Attachments ---------------------------------------------------------------------------------------------------------
void rebuild();
void build() { rebuild(); };
template
void blit(
enum_t src_att
, FB& dst, enum_t dst_att
, glm::ivec2 src_off, glm::ivec2 src_size
, glm::ivec2 dst_off, glm::ivec2 dst_size
, enum_t filter
) { blit(src_att, dst.handle_, dst_att, src_off, src_size, dst_off, dst_size, filter); }
void blit(
enum_t src_att
, handle_t dst, enum_t dst_att
, glm::ivec2 src_off, glm::ivec2 src_size
, glm::ivec2 dst_off, glm::ivec2 dst_size
, enum_t filter
);
template
auto& get() { return textures_.template get(); }
template
const auto& get() const { return textures_.template get(); }
// Status --------------------------------------------------------------------------------------------------------------
enum_t get_status() const { return glCheckNamedFramebufferStatus(handle_, GL_FRAMEBUFFER); }
bool is_complete() const { return get_status() == fb_status_complete; }
void bind(enum_t usage = fb_usage_any) { glBindFramebuffer(usage, handle_); }
// Capacity ------------------------------------------------------------------------------------------------------------
const glm::ivec3& size() const { return size_; }
void resize(size_t size);
void resize(const glm::ivec2& size);
void resize(const glm::ivec2& size, size_t samples);
void resize(const glm::ivec3& size);
void resize(const glm::ivec3& size, size_t samples);
size_t sample_count() const { return samples_; }
// Variables ===========================================================================================================
private:
handle_t handle_;
glm::ivec3 size_;
size_t samples_;
array_type textures_;
std::vector colors_;
};
template
void framebuffer::rebuild()
{
colors_.clear(); colors_.reserve(textures_.length);
bind_textures_();
glNamedFramebufferDrawBuffers(handle_, colors_.size(), colors_.data());
}
template
void framebuffer::blit(uint32_t src_att, handle_t dst, uint32_t dst_att, glm::ivec2 src_off,
glm::ivec2 src_size, glm::ivec2 dst_off, glm::ivec2 dst_size, enum_t filter)
{
flags_t mask;
bool src_color = (src_att >= color_attachment0 && src_att <= color_attachment15)
|| (src_att >= front_left_buffer && src_att <= front_and_back_buffer);
bool dst_color = (dst_att >= color_attachment0 && dst_att <= color_attachment15)
|| (dst_att >= front_left_buffer && dst_att <= front_and_back_buffer);
if(src_att != dst_att && src_color != dst_color) return;
switch(src_att)
{
case depth_attachment: mask = fb_depth_bit; break;
case stencil_attachment: mask = fb_stencil_bit; break;
case depth_stencil_attachment: mask = fb_depth_bit | fb_stencil_bit; break;
default:
mask = fb_color_bit;
glNamedFramebufferReadBuffer(handle_, src_att);
glNamedFramebufferDrawBuffer(dst, dst_att);
break;
}
glBlitNamedFramebuffer(
handle_, dst
, src_off.x, src_off.y, src_off.x + src_size.x, src_off.y + src_size.y
, dst_off.x, dst_off.y, dst_off.x + dst_size.x, dst_off.y + dst_size.y
, mask, filter
);
}
template
void framebuffer::resize(size_t size)
{
const auto& sample = textures_.template get<0>();
if(sample.size() == size) return;
glDeleteFramebuffers(1, &handle_);
glCreateFramebuffers(1, &handle_);
textures_.resize(size);
rebuild();
}
template
void framebuffer::resize(const glm::ivec2 &size)
{
const auto& sample = textures_.template get<0>();
if(sample.size() == size) return;
glDeleteFramebuffers(1, &handle_);
glCreateFramebuffers(1, &handle_);
textures_.resize(size);
rebuild();
}
template
void framebuffer::resize(const glm::ivec2 &size, size_t samples)
{
const auto& sample = textures_.template get<0>();
if(sample.size() == size && sample.samples() == samples) return;
glDeleteFramebuffers(1, &handle_);
glCreateFramebuffers(1, &handle_);
textures_.resize(size, samples);
rebuild();
}
template
void framebuffer::resize(const glm::ivec3 &size)
{
const auto& sample = textures_.template get<0>();
if(sample.size() == size) return;
glDeleteFramebuffers(1, &handle_);
glCreateFramebuffers(1, &handle_);
textures_.resize(size);
rebuild();
}
template
void framebuffer::resize(const glm::ivec3 &size, size_t samples)
{
const auto& sample = textures_.template get<0>();
if(sample.size() == size && sample.samples() == samples) return;
glDeleteFramebuffers(1, &handle_);
glCreateFramebuffers(1, &handle_);
textures_.resize(size, samples);
rebuild();
}
}
#endif // GLW_FRAMEBUFFER_H