From a5b7a49447b06d9ac0cbe1db5b334251c19e6ea7 Mon Sep 17 00:00:00 2001 From: Riyyi Date: Mon, 5 Aug 2024 19:12:39 +0200 Subject: [PATCH] Render: Make Framebuffer class more flexible --- src/inferno/application.cpp | 31 +++++++--- src/inferno/application.h | 5 +- src/inferno/render/framebuffer.cpp | 98 ++++++++++++++++-------------- src/inferno/render/framebuffer.h | 88 ++++++++++++++++++--------- 4 files changed, 137 insertions(+), 85 deletions(-) diff --git a/src/inferno/application.cpp b/src/inferno/application.cpp index 7571fa0..27040ef 100644 --- a/src/inferno/application.cpp +++ b/src/inferno/application.cpp @@ -53,10 +53,19 @@ Application::Application() Input::initialize(); RenderCommand::initialize(); - m_framebuffer = std::make_unique( - Framebuffer::Properties { .type = Framebuffer::Type::Color | Framebuffer::Type::Depth | Framebuffer::Type::Stencil, - .width = m_window->getWidth(), - .height = m_window->getHeight() }); + m_framebuffer = Framebuffer::create({ + .attachments = { Framebuffer::Type::Color, Framebuffer::Type::Depth }, + .width = m_window->getWidth(), + .height = m_window->getHeight(), + .clearColor = { 0.2f, 0.3f, 0.3f, 1.0f }, + .clearBit = GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, + }); + + m_screenFramebuffer = Framebuffer::create({ + .renderToScreen = true, + .clearColor = { 1.0f, 1.0f, 1.0f, 1.0f }, + .clearBit = GL_COLOR_BUFFER_BIT, + }); m_scene = std::make_shared(); m_scene->initialize(); @@ -175,10 +184,10 @@ int Application::run() m_framebuffer->bind(); - render(); + RenderCommand::clearColor(m_framebuffer->clearColor()); + RenderCommand::clearBit(m_framebuffer->clearBit()); - RenderCommand::clearColor({ 0.2f, 0.3f, 0.3f, 1.0f }); - RenderCommand::clearBit(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + render(); std::pair projectionView = m_scene->cameraProjectionView(); RendererCubemap::the().beginScene(projectionView.first, projectionView.second); // camera, lights, environment @@ -199,8 +208,10 @@ int Application::run() // --------------------------------- // Framebuffer - RenderCommand::clearColor({ 1.0f, 1.0f, 1.0f, 1.0f }); - RenderCommand::clearBit(GL_COLOR_BUFFER_BIT); + m_screenFramebuffer->bind(); + + RenderCommand::clearColor(m_screenFramebuffer->clearColor()); + RenderCommand::clearBit(m_screenFramebuffer->clearBit()); Renderer2D::the().setEnableDepthBuffer(false); Renderer2D::the().beginScene(matIdentity, matIdentity); @@ -208,6 +219,8 @@ int Application::run() Renderer2D::the().endScene(); Renderer2D::the().setEnableDepthBuffer(true); + m_screenFramebuffer->unbind(); + m_window->render(); } diff --git a/src/inferno/application.h b/src/inferno/application.h index 7ef5250..9804989 100644 --- a/src/inferno/application.h +++ b/src/inferno/application.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 Riyyi + * Copyright (C) 2022,2024 Riyyi * * SPDX-License-Identifier: MIT */ @@ -51,8 +51,9 @@ private: float m_lastFrameTime { 0.0f }; std::unique_ptr m_window; + std::shared_ptr m_framebuffer; + std::shared_ptr m_screenFramebuffer; std::shared_ptr m_scene; - std::unique_ptr m_framebuffer; // std::shared_ptr m_font; diff --git a/src/inferno/render/framebuffer.cpp b/src/inferno/render/framebuffer.cpp index 3a9d0b1..0bec36a 100644 --- a/src/inferno/render/framebuffer.cpp +++ b/src/inferno/render/framebuffer.cpp @@ -14,18 +14,13 @@ namespace Inferno { -Framebuffer::Framebuffer(const Properties& init) - : m_type(init.type) - , m_width(init.width) - , m_height(init.height) +std::shared_ptr Framebuffer::create(const Properties& properties) { - VERIFY(static_cast(init.type) != 0, - "malformed framebuffer type: {}", init.type); + auto result = std::shared_ptr(new Framebuffer(properties)); - m_id = UINT_MAX; - glGenFramebuffers(1, &m_id); + result->createTextures(); - createTextures(); + return result; } Framebuffer::~Framebuffer() @@ -35,13 +30,6 @@ Framebuffer::~Framebuffer() // ----------------------------------------- -bool Framebuffer::check() const -{ - VERIFY(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE, - "malformed framebuffer: {:#x}", glCheckFramebufferStatus(GL_FRAMEBUFFER)); - return true; -} - void Framebuffer::bind() const { glBindFramebuffer(GL_FRAMEBUFFER, m_id); @@ -52,6 +40,13 @@ void Framebuffer::unbind() const glBindFramebuffer(GL_FRAMEBUFFER, 0); } +bool Framebuffer::check() const +{ + VERIFY(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE, + "malformed framebuffer: {:#x}", glCheckFramebufferStatus(GL_FRAMEBUFFER)); + return true; +} + void Framebuffer::resize(uint32_t width, uint32_t height) { if (m_width == width && m_height == height) { @@ -68,40 +63,55 @@ void Framebuffer::resize(uint32_t width, uint32_t height) void Framebuffer::createTextures() { - bind(); - - m_textures[0] = AssetManager::the().remove(FRAMEBUFFER_TEXTURE_COLOR); - m_textures[1] = AssetManager::the().remove(FRAMEBUFFER_TEXTURE_DEPTH); - m_textures[2] = AssetManager::the().remove(FRAMEBUFFER_TEXTURE_STENCIL); - m_textures[3] = AssetManager::the().remove(FRAMEBUFFER_TEXTURE_DEPTH_STENCIL); - - if (m_type & Type::Color) { - // Set color attachment 0 out of 32 - m_textures[0] = AssetManager::the().load( - FRAMEBUFFER_TEXTURE_COLOR, m_width, m_height, GL_RGB, GL_RGB); - m_textures[0]->bind(0); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_textures[0]->id(), 0); - m_textures[0]->unbind(); + if (m_renderToScreen) { + return; } - // This combined texture is required for older GPUs - if (m_type & Type::Depth && m_type & Stencil) { - m_textures[3] = AssetManager::the().load( - FRAMEBUFFER_TEXTURE_DEPTH_STENCIL, m_width, m_height, GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, m_textures[3]->id(), 0); - } - else if (m_type & Type::Depth) { - m_textures[1] = AssetManager::the().load( - FRAMEBUFFER_TEXTURE_DEPTH, m_width, m_height, GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, m_textures[1]->id(), 0); + if (m_id) { + glDeleteFramebuffers(1, &m_id); + m_textures.clear(); } - else if (m_type & Type::Stencil) { - m_textures[2] = AssetManager::the().load( - FRAMEBUFFER_TEXTURE_STENCIL, m_width, m_height, GL_STENCIL_INDEX, GL_STENCIL_INDEX); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, m_textures[2]->id(), 0); + + m_id = UINT_MAX; + glGenFramebuffers(1, &m_id); + + bind(); + + auto it = m_attachments.begin(); + uint8_t color_attachment = 0; + + size_t size = m_attachments.size(); + m_textures.resize(size); + for (size_t i = 0; i < size; ++i) { + TypeProperties type = *(it + i); + + if (type.type == Type::Color) { + // Set color attachment 0 out of 32 + m_textures[i] = (TextureFramebuffer::create( + "", m_width, m_height, GL_RGB, GL_RGB)); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + color_attachment, GL_TEXTURE_2D, m_textures[i]->id(), 0); + } + + // This combined texture is required for older GPUs + if (type.type == Type::Depth24Stencil8) { + m_textures[i] = (TextureFramebuffer::create( + "", m_width, m_height, GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8)); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, m_textures[i]->id(), 0); + continue; + } + + if (type.type == Type::Depth32F) { + // FIXME: This isnt a 32-bit float texture + m_textures[i] = (TextureFramebuffer::create( + "", m_width, m_height, GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT)); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, m_textures[i]->id(), 0); + continue; + } } + VERIFY(color_attachment <= 32, "maximum color attachments was exceeded: {}/32", color_attachment); check(); + unbind(); } diff --git a/src/inferno/render/framebuffer.h b/src/inferno/render/framebuffer.h index a15623d..dc50505 100644 --- a/src/inferno/render/framebuffer.h +++ b/src/inferno/render/framebuffer.h @@ -6,67 +6,95 @@ #pragma once -#include // int8_t -#include // std::shared_ptr +#include //size_t +#include // uint8_t +#include +#include // std::shared_ptr -#include "inferno/asset/texture.h" -#include "ruc/meta/core.h" +#include "glm/ext/vector_float4.hpp" // glm::vec4 -// AssetManager paths to the global framebuffer textures -#define FRAMEBUFFER_TEXTURE_COLOR "@framebuffer-color" -#define FRAMEBUFFER_TEXTURE_DEPTH "@framebuffer-depth" -#define FRAMEBUFFER_TEXTURE_STENCIL "@framebuffer-stencil" -#define FRAMEBUFFER_TEXTURE_DEPTH_STENCIL "@framebuffer-depth-stencil" +#include "inferno/asset/texture.h" +#include "ruc/format/log.h" namespace Inferno { class Framebuffer final { // Frame Buffer Object, FBO public: - enum Type : int8_t { + enum Type : uint8_t { None = 0, - Color = BIT(0), - Depth = BIT(1), - Stencil = BIT(2), + + // Color + RGBA8 = 1, + + // Depth/stencil + Depth32F = 2, + Depth24Stencil8 = 3, + + // Defaults + Color = RGBA8, + Depth = Depth24Stencil8, + }; + + struct TypeProperties { + TypeProperties() = default; + TypeProperties(Type type) + : type(type) + { + } + ~TypeProperties() {}; + + Type type; }; struct Properties { - Type type { None }; + std::initializer_list attachments {}; + bool renderToScreen { false }; // (dummy framebuffer) + uint32_t width { 1280 }; uint32_t height { 720 }; + glm::vec4 clearColor { 1.0f, 0.0f, 1.0f, 1.0f }; // magenta + uint32_t clearBit { 0 }; }; - Framebuffer(const Properties& init); ~Framebuffer(); - bool check() const; + // Factory function + static std::shared_ptr create(const Properties& properties); void bind() const; void unbind() const; - + bool check() const; void resize(uint32_t width, uint32_t height); - Type type() const { return m_type; } + uint32_t id() const { return m_id; } uint32_t width() const { return m_width; } uint32_t height() const { return m_height; } - uint32_t id() const { return m_id; } - std::shared_ptr texture(uint8_t index) const { return m_textures[index]; } + uint32_t clearBit() const { return m_clearBit; } + glm::vec4 clearColor() const { return m_clearColor; } + const std::vector& attachments() const { return m_attachments; } + std::shared_ptr texture(size_t index) const { return m_textures[index]; } private: + Framebuffer(const Properties& properties) + : m_renderToScreen(properties.renderToScreen) + , m_width(properties.width) + , m_height(properties.height) + , m_clearBit(properties.clearBit) + , m_clearColor(properties.clearColor) + , m_attachments(properties.attachments) + { + } void createTextures(); private: - Type m_type { None }; + bool m_renderToScreen { false }; + uint32_t m_id { 0 }; uint32_t m_width { 0 }; uint32_t m_height { 0 }; - uint32_t m_id { 0 }; - std::shared_ptr m_textures[4] { nullptr, nullptr, nullptr, nullptr }; + uint32_t m_clearBit { 0 }; + glm::vec4 m_clearColor; + std::vector m_attachments; + std::vector> m_textures; }; -// Make bitwise OR '|' return the enum type instead of int -inline Framebuffer::Type operator|(Framebuffer::Type lhs, Framebuffer::Type rhs) -{ - return static_cast( - static_cast(lhs) | static_cast(rhs)); -} - } // namespace Inferno