diff options
Diffstat (limited to 'src/Platform/OpenGL/OpenGLFramebuffer.cpp')
| -rw-r--r-- | src/Platform/OpenGL/OpenGLFramebuffer.cpp | 238 |
1 files changed, 238 insertions, 0 deletions
diff --git a/src/Platform/OpenGL/OpenGLFramebuffer.cpp b/src/Platform/OpenGL/OpenGLFramebuffer.cpp new file mode 100644 index 0000000..523d748 --- /dev/null +++ b/src/Platform/OpenGL/OpenGLFramebuffer.cpp @@ -0,0 +1,238 @@ +#include "OpenGLFramebuffer.h" +#include "Core/Log.h" + +#include <glad/glad.h> + +namespace Donut +{ + namespace Utils + { + static GLenum TextureTarget(bool multisampled) + { + return multisampled ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D; + } + + static void BindTexture(bool multisampled, uint32_t id) + { + glBindTexture(TextureTarget(multisampled), id); + } + + static void CreateTextures(bool multisampled, uint32_t* outID, uint32_t count) + { + glCreateTextures(TextureTarget(multisampled), count, outID); + } + + static void AttachColorTexture(uint32_t id, int samples, GLenum internalFormat, GLenum format, uint32_t width, uint32_t height, int index) + { + bool multisampled = samples > 1; + if (multisampled) + { + glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, internalFormat, width, height, GL_FALSE); + } + else + { + glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, GL_UNSIGNED_BYTE, nullptr); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } + + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + index, TextureTarget(multisampled), id, 0); + } + + static void AttachDepthTexture(uint32_t id, int samples, GLenum format, GLenum attachmentType, uint32_t width, uint32_t height) + { + bool multisampled = samples > 1; + if (multisampled) + { + glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, format, width, height, GL_FALSE); + } + else + { + glTexStorage2D(GL_TEXTURE_2D, 1, format, width, height); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } + + glFramebufferTexture2D(GL_FRAMEBUFFER, attachmentType, TextureTarget(multisampled), id, 0); + } + + static bool IsDepthFormat(FramebufferTextureFormat format) + { + switch (format) + { + case FramebufferTextureFormat::DEPTH24STENCIL8: return true; + } + return false; + } + + static GLenum DonutFBTextureFormatToGL(FramebufferTextureFormat format) + { + switch (format) + { + case FramebufferTextureFormat::RGBA8: return GL_RGBA8; + case FramebufferTextureFormat::RED_INTEGER: return GL_RED_INTEGER; + } + + return 0; + } + } + + OpenGLFramebuffer::OpenGLFramebuffer(const FramebufferSpecification& spec) + : m_Specification(spec) + { + for (auto spec : m_Specification.Attachments.Attachments) + { + if (!Utils::IsDepthFormat(spec.TextureFormat)) + m_ColorAttachmentSpecifications.emplace_back(spec); + else + m_DepthAttachmentSpecification = spec; + } + + Invalidate(); + } + + OpenGLFramebuffer::~OpenGLFramebuffer() + { + glDeleteFramebuffers(1, &m_RendererID); + glDeleteTextures(static_cast<GLsizei>(m_ColorAttachments.size()), m_ColorAttachments.data()); + glDeleteTextures(1, &m_DepthAttachment); + } + + void OpenGLFramebuffer::Invalidate() + { + if (m_RendererID) + { + glDeleteFramebuffers(1, &m_RendererID); + glDeleteTextures(static_cast<GLsizei>(m_ColorAttachments.size()), m_ColorAttachments.data()); + glDeleteTextures(1, &m_DepthAttachment); + + m_ColorAttachments.clear(); + m_DepthAttachment = 0; + } + + glCreateFramebuffers(1, &m_RendererID); + glBindFramebuffer(GL_FRAMEBUFFER, m_RendererID); + + bool multisample = m_Specification.Samples > 1; + + // Attachments + if (m_ColorAttachmentSpecifications.size()) + { + m_ColorAttachments.resize(m_ColorAttachmentSpecifications.size()); + Utils::CreateTextures(multisample, m_ColorAttachments.data(), static_cast<uint32_t>(m_ColorAttachments.size())); + + for (size_t i = 0; i < m_ColorAttachments.size(); i++) + { + Utils::BindTexture(multisample, m_ColorAttachments[i]); + switch (m_ColorAttachmentSpecifications[i].TextureFormat) + { + case FramebufferTextureFormat::RGBA8: + Utils::AttachColorTexture(m_ColorAttachments[i], m_Specification.Samples, GL_RGBA8, GL_RGBA, m_Specification.Width, m_Specification.Height, static_cast<int>(i)); + break; + case FramebufferTextureFormat::RED_INTEGER: + Utils::AttachColorTexture(m_ColorAttachments[i], m_Specification.Samples, GL_R32I, GL_RED_INTEGER, m_Specification.Width, m_Specification.Height, static_cast<int>(i)); + break; + } + } + } + + if (m_DepthAttachmentSpecification.TextureFormat != FramebufferTextureFormat::None) + { + Utils::CreateTextures(multisample, &m_DepthAttachment, 1); + Utils::BindTexture(multisample, m_DepthAttachment); + switch (m_DepthAttachmentSpecification.TextureFormat) + { + case FramebufferTextureFormat::DEPTH24STENCIL8: + Utils::AttachDepthTexture(m_DepthAttachment, m_Specification.Samples, GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL_ATTACHMENT, m_Specification.Width, m_Specification.Height); + break; + } + } + + if (m_ColorAttachments.size() > 1) + { + GLenum buffers[4] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3 }; + glDrawBuffers(static_cast<GLsizei>(m_ColorAttachments.size()), buffers); + } + else if (m_ColorAttachments.empty()) + glDrawBuffer(GL_NONE); + + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + { + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + switch (status) + { + case GL_FRAMEBUFFER_UNDEFINED: + DONUT_ERROR("Framebuffer is undefined"); + break; + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: + DONUT_ERROR("Framebuffer has incomplete attachment"); + break; + case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: + DONUT_ERROR("Framebuffer is missing attachment"); + break; + case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: + DONUT_ERROR("Framebuffer has incomplete draw buffer"); + break; + case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: + DONUT_ERROR("Framebuffer has incomplete read buffer"); + break; + case GL_FRAMEBUFFER_UNSUPPORTED: + DONUT_ERROR("Framebuffer format is unsupported"); + break; + case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: + DONUT_ERROR("Framebuffer has incomplete multisample"); + break; + case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS: + DONUT_ERROR("Framebuffer has incomplete layer targets"); + break; + default: + DONUT_ERROR("Framebuffer is incomplete (unknown error: {})", status); + break; + } + } + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + } + + void OpenGLFramebuffer::Bind() + { + glBindFramebuffer(GL_FRAMEBUFFER, m_RendererID); + glViewport(0, 0, m_Specification.Width, m_Specification.Height); + } + + void OpenGLFramebuffer::Unbind() + { + glBindFramebuffer(GL_FRAMEBUFFER, 0); + } + + void OpenGLFramebuffer::Resize(uint32_t width, uint32_t height) + { + m_Specification.Width = width; + m_Specification.Height = height; + + Invalidate(); + } + + int OpenGLFramebuffer::ReadPixel(uint32_t attachmentIndex, int x, int y) + { + glReadBuffer(GL_COLOR_ATTACHMENT0 + attachmentIndex); + int pixelData; + glReadPixels(x, y, 1, 1, GL_RED_INTEGER, GL_INT, &pixelData); + return pixelData; + } + + void OpenGLFramebuffer::ClearAttachment(uint32_t attachmentIndex, int value) + { + auto& spec = m_ColorAttachmentSpecifications[attachmentIndex]; + glClearTexImage(m_ColorAttachments[attachmentIndex], 0, + Utils::DonutFBTextureFormatToGL(spec.TextureFormat), GL_INT, &value); + } +}; |
