376 lines
13 KiB
C++
Executable File
376 lines
13 KiB
C++
Executable File
// =====================================================================================================================
|
|
// 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 <https://www.gnu.org/licenses/>.
|
|
// =====================================================================================================================
|
|
|
|
#ifndef GLW_FRAMEBUFFER_H
|
|
#define GLW_FRAMEBUFFER_H
|
|
|
|
#include "texture.h"
|
|
#include "texture_array.h"
|
|
|
|
#include <glm/vec2.hpp>
|
|
|
|
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<enum_t T_, enum_t F_, enum_t...Fs_>
|
|
class framebuffer
|
|
{
|
|
// Constants ===========================================================================================================
|
|
|
|
static_assert(!contains_enum<depth16, Fs_...>::value);
|
|
static_assert(!contains_enum<depth24, Fs_...>::value);
|
|
static_assert(!contains_enum<depth32, Fs_...>::value);
|
|
static_assert(!contains_enum<depth32_f, Fs_...>::value);
|
|
|
|
static_assert(!contains_enum<stencil1, Fs_...>::value);
|
|
static_assert(!contains_enum<stencil4, Fs_...>::value);
|
|
static_assert(!contains_enum<stencil8, Fs_...>::value);
|
|
static_assert(!contains_enum<stencil16, Fs_...>::value);
|
|
|
|
static_assert(!contains_enum<depth24_stencil, Fs_...>::value);
|
|
static_assert(!contains_enum<depth32_f_stencil, Fs_...>::value);
|
|
|
|
using array_type = texarray<T_, F_, Fs_...>;
|
|
|
|
public:
|
|
|
|
inline static constexpr enum_t type = T_;
|
|
|
|
private:
|
|
|
|
|
|
// Functions ===========================================================================================================
|
|
|
|
// Helpers -------------------------------------------------------------------------------------------------------------
|
|
|
|
private:
|
|
template<int I = 0, int C = 0>
|
|
void bind_textures_()
|
|
{
|
|
auto& tex = textures_.template get<I>();
|
|
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_<I + 1, C + 1>();
|
|
else bind_textures_<I + 1, C>();
|
|
}
|
|
}
|
|
|
|
|
|
// Constructors --------------------------------------------------------------------------------------------------------
|
|
|
|
public:
|
|
framebuffer(size_t size)
|
|
: handle_(NULL), size_(size, 1, 1), samples_(1), textures_(size)
|
|
{ static_assert(contains_enum<type, texture1D>::value); glCreateFramebuffers(1, &handle_); }
|
|
|
|
framebuffer(glm::ivec2 size)
|
|
: handle_(NULL), size_(size, 1), samples_(1), textures_(size)
|
|
{ static_assert(contains_enum<type, texture1DArray, texture2D, textureCubemap>::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<type, texture2DArray, textureCubemapArray, texture3D>::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<class FB>
|
|
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<int I>
|
|
auto& get() { return textures_.template get<I>(); }
|
|
|
|
template<int I>
|
|
const auto& get() const { return textures_.template get<I>(); }
|
|
|
|
|
|
// 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<enum_t> colors_;
|
|
};
|
|
|
|
template<enum_t T_, enum_t F_, enum_t... Fs_>
|
|
void framebuffer<T_, F_, Fs_...>::rebuild()
|
|
{
|
|
colors_.clear(); colors_.reserve(textures_.length);
|
|
bind_textures_();
|
|
|
|
glNamedFramebufferDrawBuffers(handle_, colors_.size(), colors_.data());
|
|
}
|
|
|
|
template<enum_t T_, enum_t F_, enum_t... Fs_>
|
|
void framebuffer<T_, F_, Fs_...>::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<enum_t T_, enum_t F_, enum_t... Fs_>
|
|
void framebuffer<T_, F_, Fs_...>::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<enum_t T_, enum_t F_, enum_t... Fs_>
|
|
void framebuffer<T_, F_, Fs_...>::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<enum_t T_, enum_t F_, enum_t... Fs_>
|
|
void framebuffer<T_, F_, Fs_...>::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<enum_t T_, enum_t F_, enum_t... Fs_>
|
|
void framebuffer<T_, F_, Fs_...>::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<enum_t T_, enum_t F_, enum_t... Fs_>
|
|
void framebuffer<T_, F_, Fs_...>::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
|