aboutsummaryrefslogtreecommitdiff
path: root/src/Platform/OpenGL
diff options
context:
space:
mode:
Diffstat (limited to 'src/Platform/OpenGL')
-rw-r--r--src/Platform/OpenGL/OpenGLFramebuffer.cpp238
-rw-r--r--src/Platform/OpenGL/OpenGLFramebuffer.h35
-rw-r--r--src/Platform/OpenGL/OpenGLRendererAPI.cpp6
-rw-r--r--src/Platform/OpenGL/OpenGLRendererAPI.h4
4 files changed, 283 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);
+ }
+};
diff --git a/src/Platform/OpenGL/OpenGLFramebuffer.h b/src/Platform/OpenGL/OpenGLFramebuffer.h
new file mode 100644
index 0000000..8cd376c
--- /dev/null
+++ b/src/Platform/OpenGL/OpenGLFramebuffer.h
@@ -0,0 +1,35 @@
+#pragma once
+
+#include "Rendering/Framebuffer.h"
+
+namespace Donut
+{
+ class OpenGLFramebuffer : public Framebuffer
+ {
+ public:
+ OpenGLFramebuffer(const FramebufferSpecification& spec);
+ virtual ~OpenGLFramebuffer();
+
+ void Invalidate();
+
+ virtual void Bind() override;
+ virtual void Unbind() override;
+
+ virtual void Resize(uint32_t width, uint32_t height) override;
+ virtual int ReadPixel(uint32_t attachmentIndex, int x, int y) override;
+
+ virtual void ClearAttachment(uint32_t attachmentIndex, int value) override;
+ virtual uint32_t GetColorAttachmentRendererID(uint32_t index = 0) const override { return m_ColorAttachments[index]; }
+
+ virtual const FramebufferSpecification& GetSpecification() const override { return m_Specification; }
+ private:
+ uint32_t m_RendererID = 0;
+ FramebufferSpecification m_Specification;
+
+ std::vector<FramebufferTextureSpecification> m_ColorAttachmentSpecifications;
+ FramebufferTextureSpecification m_DepthAttachmentSpecification = FramebufferTextureFormat::None;
+
+ std::vector<uint32_t> m_ColorAttachments;
+ uint32_t m_DepthAttachment = 0;
+ };
+};
diff --git a/src/Platform/OpenGL/OpenGLRendererAPI.cpp b/src/Platform/OpenGL/OpenGLRendererAPI.cpp
index cd54f82..230b19b 100644
--- a/src/Platform/OpenGL/OpenGLRendererAPI.cpp
+++ b/src/Platform/OpenGL/OpenGLRendererAPI.cpp
@@ -106,4 +106,10 @@ namespace Donut
glBindImageTexture(slot, textureID, 0, GL_FALSE, 0,
readOnly ? GL_READ_ONLY : GL_WRITE_ONLY, GL_RGBA8);
}
+
+ void OpenGLRendererAPI::ReadPixels(uint32_t x, uint32_t y, uint32_t width, uint32_t height,
+ uint32_t format, uint32_t type, void* pixels)
+ {
+ glReadPixels(x, y, width, height, format, type, pixels);
+ }
};
diff --git a/src/Platform/OpenGL/OpenGLRendererAPI.h b/src/Platform/OpenGL/OpenGLRendererAPI.h
index 0576db8..5a68622 100644
--- a/src/Platform/OpenGL/OpenGLRendererAPI.h
+++ b/src/Platform/OpenGL/OpenGLRendererAPI.h
@@ -36,5 +36,9 @@ namespace Donut
virtual void BindImageTexture(uint32_t textureID,
uint32_t slot = 0,
bool readOnly = false) override;
+ virtual void ReadPixels(uint32_t x, uint32_t y,
+ uint32_t width, uint32_t height,
+ uint32_t format, uint32_t type,
+ void* pixels) override;
};
};