// ===================================================================================================================== // 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