From 30beaf6eb9f3d4a3bdd62fda56784a7d35ef2213 Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Sun, 4 May 2025 01:17:09 +0800 Subject: [PATCH] [Feat] (...): Add FramebufferState while implementing these gl functions. --- CMakeLists.txt | 2 + MG/Constants.h | 15 ++ MG/Global.h | 2 +- MG/Includes.h | 3 + .../GL/Framebuffer/GL_Framebuffer.cpp | 58 ++++++- .../GL/Framebuffer/GL_Framebuffer.h | 4 + .../GLFuncsDefinitions/GLFuncsDefinitions.cpp | 8 +- MG/MG_GL/State/Core/GLState.cpp | 30 ++++ MG/MG_GL/State/Core/GLState.h | 10 ++ .../State/Framebuffer/FramebufferState.cpp | 163 ++++++++++++++++++ MG/MG_GL/State/Framebuffer/FramebufferState.h | 48 ++++++ 11 files changed, 337 insertions(+), 6 deletions(-) create mode 100644 MG/MG_GL/State/Framebuffer/FramebufferState.cpp create mode 100644 MG/MG_GL/State/Framebuffer/FramebufferState.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 29be66e1..accb47a3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -66,6 +66,8 @@ add_library(${CMAKE_PROJECT_NAME} SHARED MG/MG_GL/State/Program/ShaderObject.cpp MG/MG_GL/State/Program/ProgramState.cpp + + MG/MG_GL/State/Framebuffer/FramebufferState.cpp # MG_GL/BackendLoader MG/MG_GL/BackendLoader/GLES/Loader.cpp diff --git a/MG/Constants.h b/MG/Constants.h index d7caed3f..49ec46e2 100644 --- a/MG/Constants.h +++ b/MG/Constants.h @@ -23,6 +23,21 @@ namespace MG_Constants { inline const int LOG_LEVEL_FATAL = 0x0014; } + namespace Framebuffer { + static const std::unordered_set VALID_ATTACHMENTS = { + GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT, + GL_STENCIL_ATTACHMENT, GL_DEPTH_STENCIL_ATTACHMENT + }; // OpenGL 3 + + static const std::unordered_set VALID_TARGETS = { + GL_FRAMEBUFFER, GL_DRAW_FRAMEBUFFER, GL_READ_FRAMEBUFFER + }; // OpenGL 3 + + static const std::unordered_set VALID_ACCESS = { + GL_READ_ONLY, GL_WRITE_ONLY, GL_READ_WRITE + }; // OpenGL 3 + } + namespace Buffer { static const std::unordered_set VALID_PARAM_NAMES = { GL_BUFFER_ACCESS, GL_BUFFER_MAPPED, GL_BUFFER_SIZE, GL_BUFFER_USAGE diff --git a/MG/Global.h b/MG/Global.h index 61adf95d..2fb584b4 100644 --- a/MG/Global.h +++ b/MG/Global.h @@ -27,7 +27,7 @@ namespace MG_Global { } namespace Common { - inline const int LogLevel = MG_Constants::Common::LOG_LEVEL_INFO; + inline const int LogLevel = MG_Constants::Common::LOG_LEVEL_DEBUG; #ifdef __ANDROID__ inline const char* LOG_FILE_PATH = "/sdcard/MG/latest.log"; diff --git a/MG/Includes.h b/MG/Includes.h index 5ba62c99..a353ec19 100644 --- a/MG/Includes.h +++ b/MG/Includes.h @@ -53,6 +53,9 @@ #ifndef MOBILEGL_PROGRAMSTATE_H #include "MG_GL/State/Program/ProgramState.h" #endif +#ifndef MOBILEGL_FRAMEBUFFERSTATE_H +#include "MG_GL/State/Framebuffer/FramebufferState.h" +#endif #ifndef MOBILEGL_LOOKUP_H #include "MG_GL/Implementations/GLX/LookUp/LookUp.h" #endif diff --git a/MG/MG_GL/Implementations/GL/Framebuffer/GL_Framebuffer.cpp b/MG/MG_GL/Implementations/GL/Framebuffer/GL_Framebuffer.cpp index 7db80b64..7a4945bc 100644 --- a/MG/MG_GL/Implementations/GL/Framebuffer/GL_Framebuffer.cpp +++ b/MG/MG_GL/Implementations/GL/Framebuffer/GL_Framebuffer.cpp @@ -5,7 +5,63 @@ #include "GL_Framebuffer.h" namespace MG_GL::GL { + void GenFramebuffers(GLsizei n, GLuint* framebuffers) { + MG_Util::Debug::LogD("glGenFramebuffers, n: %d, framebuffers: %p", n, framebuffers); + GLenum result = MG_State::CreateFramebuffers(n, framebuffers); + if (result == GL_NO_ERROR) return; + MG_State::SetError(result); + MG_Util::Debug::LogE("Framebuffer generation failed: %s", MG_Util::Debug::GLEnumToString(result)); + } + + void DeleteFramebuffers(GLsizei n, const GLuint* framebuffers) { + MG_Util::Debug::LogD("glDeleteFramebuffers, n: %d, framebuffers: %p", n, framebuffers); + for (GLsizei i = 0; i < n; ++i) { + GLenum result = MG_State::DeleteFramebuffer(framebuffers[i]); + if (result != GL_NO_ERROR) { + MG_State::SetError(result); + MG_Util::Debug::LogE("Failed to delete framebuffer %u: %s", + framebuffers[i], MG_Util::Debug::GLEnumToString(result)); + } + } + } + + void BindFramebuffer(GLenum target, GLuint framebuffer) { + MG_Util::Debug::LogD("glBindFramebuffer, target: %s, fb: %u", + MG_Util::Debug::GLEnumToString(target), framebuffer); + GLenum result = MG_State::BindFramebuffer(target, framebuffer); + if (result == GL_NO_ERROR) return; + MG_State::SetError(result); + MG_Util::Debug::LogE("Framebuffer bind error: %s", MG_Util::Debug::GLEnumToString(result)); + } + + void FramebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget, + GLuint texture, GLint level) { + MG_Util::Debug::LogD("glFramebufferTexture2D, target: %s, attach: %s, textarget: %s, tex: %u, level: %d", + MG_Util::Debug::GLEnumToString(target), + MG_Util::Debug::GLEnumToString(attachment), + MG_Util::Debug::GLEnumToString(textarget), + texture, level); + + GLenum result = MG_State::AttachTexture2DToFramebuffer( + target, attachment, textarget, texture, level + ); + if (result == GL_NO_ERROR) return; + MG_State::SetError(result); + MG_Util::Debug::LogE("Texture attachment failed: %s", MG_Util::Debug::GLEnumToString(result)); + } + GLenum CheckFramebufferStatus(GLenum target) { - return GL_FRAMEBUFFER_COMPLETE; + MG_Util::Debug::LogD("glCheckFramebufferStatus, target: %s", + MG_Util::Debug::GLEnumToString(target)); + GLenum result = MG_State::ValidateFramebufferCompleteness(target); + if (result >= GL_FRAMEBUFFER_COMPLETE) { + MG_Util::Debug::LogD("Framebuffer status: %s", + MG_Util::Debug::GLEnumToString(result)); + return result; + } + MG_State::SetError(result); + MG_Util::Debug::LogE("Framebuffer incomplete: %s", + MG_Util::Debug::GLEnumToString(result)); + return result; } } \ No newline at end of file diff --git a/MG/MG_GL/Implementations/GL/Framebuffer/GL_Framebuffer.h b/MG/MG_GL/Implementations/GL/Framebuffer/GL_Framebuffer.h index 36946301..712b3289 100644 --- a/MG/MG_GL/Implementations/GL/Framebuffer/GL_Framebuffer.h +++ b/MG/MG_GL/Implementations/GL/Framebuffer/GL_Framebuffer.h @@ -8,6 +8,10 @@ #include "../../../../Includes.h" namespace MG_GL::GL { + void GenFramebuffers(GLsizei n, GLuint* framebuffers); + void DeleteFramebuffers(GLsizei n, const GLuint* framebuffers); + void BindFramebuffer(GLenum target, GLuint framebuffer); + void FramebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); GLenum CheckFramebufferStatus(GLenum target); } diff --git a/MG/MG_GL/Implementations/GL/GLFuncsDefinitions/GLFuncsDefinitions.cpp b/MG/MG_GL/Implementations/GL/GLFuncsDefinitions/GLFuncsDefinitions.cpp index 3c9c5c1a..1bd623b4 100644 --- a/MG/MG_GL/Implementations/GL/GLFuncsDefinitions/GLFuncsDefinitions.cpp +++ b/MG/MG_GL/Implementations/GL/GLFuncsDefinitions/GLFuncsDefinitions.cpp @@ -37,7 +37,7 @@ DECLARE_GL_FUNCTION_HEAD(void, ActiveTexture, GLenum texture) DECLARE_GL_FUNCTIO DECLARE_GL_FUNCTION_HEAD(void, AttachShader, GLuint program, GLuint shader) DECLARE_GL_FUNCTION_END_NO_RETURN(void, AttachShader, program,shader) DECLARE_GL_FUNCTION_HEAD(void, BindAttribLocation, GLuint program, GLuint index, const GLchar *name) DECLARE_GL_FUNCTION_END_NO_RETURN(void, BindAttribLocation, program,index,name) DECLARE_GL_FUNCTION_HEAD(void, BindBuffer, GLenum target, GLuint buffer) DECLARE_GL_FUNCTION_END_NO_RETURN(void, BindBuffer, target,buffer) -DECLARE_GL_FUNCTION_STUB_HEAD(void, BindFramebuffer, GLenum target, GLuint framebuffer) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BindFramebuffer, target,framebuffer) +DECLARE_GL_FUNCTION_HEAD(void, BindFramebuffer, GLenum target, GLuint framebuffer) DECLARE_GL_FUNCTION_END_NO_RETURN(void, BindFramebuffer, target,framebuffer) DECLARE_GL_FUNCTION_STUB_HEAD(void, BindRenderbuffer, GLenum target, GLuint renderbuffer) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BindRenderbuffer, target,renderbuffer) DECLARE_GL_FUNCTION_HEAD(void, BindTexture, GLenum target, GLuint texture) DECLARE_GL_FUNCTION_END_NO_RETURN(void, BindTexture, target,texture) DECLARE_GL_FUNCTION_STUB_HEAD(void, BlendColor, GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BlendColor, red,green,blue,alpha) @@ -62,7 +62,7 @@ DECLARE_GL_FUNCTION_HEAD(GLuint, CreateProgram) DECLARE_GL_FUNCTION_END(GLuint, DECLARE_GL_FUNCTION_HEAD(GLuint, CreateShader, GLenum type) DECLARE_GL_FUNCTION_END(GLuint, CreateShader, type) DECLARE_GL_FUNCTION_STUB_HEAD(void, CullFace, GLenum mode) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CullFace, mode) DECLARE_GL_FUNCTION_STUB_HEAD(void, DeleteBuffers, GLsizei n, const GLuint *buffers) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, DeleteBuffers, n,buffers) -DECLARE_GL_FUNCTION_STUB_HEAD(void, DeleteFramebuffers, GLsizei n, const GLuint *framebuffers) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, DeleteFramebuffers, n,framebuffers) +DECLARE_GL_FUNCTION_HEAD(void, DeleteFramebuffers, GLsizei n, const GLuint *framebuffers) DECLARE_GL_FUNCTION_END_NO_RETURN(void, DeleteFramebuffers, n,framebuffers) DECLARE_GL_FUNCTION_HEAD(void, DeleteProgram, GLuint program) DECLARE_GL_FUNCTION_END_NO_RETURN(void, DeleteProgram, program) DECLARE_GL_FUNCTION_STUB_HEAD(void, DeleteRenderbuffers, GLsizei n, const GLuint *renderbuffers) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, DeleteRenderbuffers, n,renderbuffers) DECLARE_GL_FUNCTION_HEAD(void, DeleteShader, GLuint shader) DECLARE_GL_FUNCTION_END_NO_RETURN(void, DeleteShader, shader) @@ -80,11 +80,11 @@ DECLARE_GL_FUNCTION_HEAD(void, EnableVertexAttribArray, GLuint index) DECLARE_GL DECLARE_GL_FUNCTION_STUB_HEAD(void, Finish) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, Finish) DECLARE_GL_FUNCTION_STUB_HEAD(void, Flush) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, Flush) DECLARE_GL_FUNCTION_STUB_HEAD(void, FramebufferRenderbuffer, GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, FramebufferRenderbuffer, target,attachment,renderbuffertarget,renderbuffer) -DECLARE_GL_FUNCTION_STUB_HEAD(void, FramebufferTexture2D, GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, FramebufferTexture2D, target,attachment,textarget,texture,level) +DECLARE_GL_FUNCTION_HEAD(void, FramebufferTexture2D, GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) DECLARE_GL_FUNCTION_END_NO_RETURN(void, FramebufferTexture2D, target,attachment,textarget,texture,level) DECLARE_GL_FUNCTION_STUB_HEAD(void, FrontFace, GLenum mode) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, FrontFace, mode) DECLARE_GL_FUNCTION_HEAD(void, GenBuffers, GLsizei n, GLuint *buffers) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GenBuffers, n,buffers) DECLARE_GL_FUNCTION_STUB_HEAD(void, GenerateMipmap, GLenum target) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GenerateMipmap, target) -DECLARE_GL_FUNCTION_STUB_HEAD(void, GenFramebuffers, GLsizei n, GLuint *framebuffers) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GenFramebuffers, n,framebuffers) +DECLARE_GL_FUNCTION_HEAD(void, GenFramebuffers, GLsizei n, GLuint *framebuffers) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GenFramebuffers, n,framebuffers) DECLARE_GL_FUNCTION_STUB_HEAD(void, GenRenderbuffers, GLsizei n, GLuint *renderbuffers) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GenRenderbuffers, n,renderbuffers) DECLARE_GL_FUNCTION_HEAD(void, GenTextures, GLsizei n, GLuint *textures) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GenTextures, n,textures) DECLARE_GL_FUNCTION_STUB_HEAD(void, GetActiveAttrib, GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetActiveAttrib, program,index,bufSize,length,size,type,name) diff --git a/MG/MG_GL/State/Core/GLState.cpp b/MG/MG_GL/State/Core/GLState.cpp index dff2f3dd..862f039e 100644 --- a/MG/MG_GL/State/Core/GLState.cpp +++ b/MG/MG_GL/State/Core/GLState.cpp @@ -10,6 +10,7 @@ CommonState* MG_State_T::commonState = nullptr; BufferState* MG_State_T::bufferState = nullptr; VertexArrayState* MG_State_T::vertexArrayState = nullptr; ProgramState* MG_State_T::programState = nullptr; +FramebufferState* MG_State_T::framebufferState = nullptr; namespace MG_State { void Init() { @@ -18,6 +19,7 @@ namespace MG_State { MG_State_T::bufferState = new BufferState(); MG_State_T::vertexArrayState = new VertexArrayState(); MG_State_T::programState = new ProgramState(); + MG_State_T::framebufferState = new FramebufferState(); } void Destroy() { @@ -26,6 +28,7 @@ namespace MG_State { delete MG_State_T::bufferState; delete MG_State_T::vertexArrayState; delete MG_State_T::programState; + delete MG_State_T::framebufferState; } void SetError(GLenum error) { @@ -300,4 +303,31 @@ void UpdateProgramUniformMatrix##suffix##Vector(GLint location, GLsizei count, G bool SetShaderStatus(GLuint shader, GLboolean status) { return MG_State_T::programState->SetShaderStatus(shader, status); } + + // Framebuffer + GLenum MG_State::CreateFramebuffer(GLuint* framebuffer) { + return MG_State_T::framebufferState->Create(framebuffer); + } + + GLenum MG_State::CreateFramebuffers(GLsizei n, GLuint* framebuffers) { + return MG_State_T::framebufferState->CreateN(n, framebuffers); + } + + GLenum MG_State::DeleteFramebuffer(GLuint framebuffer) { + return MG_State_T::framebufferState->Delete(framebuffer); + } + + GLenum MG_State::BindFramebuffer(GLenum target, GLuint framebuffer) { + return MG_State_T::framebufferState->Bind(target, framebuffer); + } + + GLenum MG_State::AttachTexture2DToFramebuffer(GLenum target, GLenum attachment, GLenum textarget, + GLuint texture, GLint level) { + return MG_State_T::framebufferState->glAttachTexture2D(target, attachment, textarget, + texture, level); + } + + GLenum MG_State::ValidateFramebufferCompleteness(GLenum target) { + return MG_State_T::framebufferState->glValidateCompleteness(target); + } } \ No newline at end of file diff --git a/MG/MG_GL/State/Core/GLState.h b/MG/MG_GL/State/Core/GLState.h index 818b2b61..2691f275 100644 --- a/MG/MG_GL/State/Core/GLState.h +++ b/MG/MG_GL/State/Core/GLState.h @@ -14,6 +14,7 @@ struct MG_State_T { static BufferState* bufferState; static VertexArrayState* vertexArrayState; static ProgramState* programState; + static FramebufferState* framebufferState; }; namespace MG_State { @@ -117,6 +118,15 @@ namespace MG_State { GLuint GetCurrentProgram(); bool SetProgramStatus(GLuint program, GLboolean status); bool SetShaderStatus(GLuint shader, GLboolean status); + + // Framebuffer + GLenum CreateFramebuffer(GLuint* framebuffer); + GLenum CreateFramebuffers(GLsizei n, GLuint* framebuffers); + GLenum DeleteFramebuffer(GLuint framebuffer); + GLenum BindFramebuffer(GLenum target, GLuint framebuffer); + GLenum AttachTexture2DToFramebuffer(GLenum target, GLenum attachment, GLenum textarget, + GLuint texture, GLint level); + GLenum ValidateFramebufferCompleteness(GLenum target); } diff --git a/MG/MG_GL/State/Framebuffer/FramebufferState.cpp b/MG/MG_GL/State/Framebuffer/FramebufferState.cpp new file mode 100644 index 00000000..2c899445 --- /dev/null +++ b/MG/MG_GL/State/Framebuffer/FramebufferState.cpp @@ -0,0 +1,163 @@ +// +// Created by BZLZHH on 2025/5/3. +// + +// TODO: Add more gl error check for framebuffer state manager. +// TODO: Check if the state machine really meets up to OpenGL 3 standard. + +#include "FramebufferState.h" + +// Re-Include Includes.h, cuz of the absence of GLState.h +#undef MOBILEGL_GLSTATE_H +#include "../../../Includes.h" + +GLenum FramebufferState::Create(GLuint* framebuffer) { + if (!framebuffer) return GL_INVALID_VALUE; + GLuint id = freeIds_.empty() ? ++lastId_ : *freeIds_.begin(); + if (!freeIds_.empty()) freeIds_.erase(freeIds_.begin()); + *framebuffer = id; + framebuffers_[id].generated = true; + return GL_NO_ERROR; +} + +GLenum FramebufferState::CreateN(GLsizei n, GLuint* framebuffers) { + if (n < 0) return GL_INVALID_VALUE; + for (GLsizei i = 0; i < n; ++i) { + if (GLenum err = Create(&framebuffers[i])) return err; + } + return GL_NO_ERROR; +} + +GLenum FramebufferState::Delete(GLuint framebuffer) { + if (framebuffer == 0) return GL_INVALID_VALUE; + + if (framebuffers_.erase(framebuffer)) { + freeIds_.insert(framebuffer); + for (auto& [target, id] : currentBindings_) { + if (id == framebuffer) id = 0; + } + } + return GL_NO_ERROR; +} + +GLenum FramebufferState::Bind(GLenum target, GLuint framebuffer) { + if (MG_Constants::Framebuffer::VALID_TARGETS.find(target) == MG_Constants::Framebuffer::VALID_TARGETS.end()) + return GL_INVALID_ENUM; + if (framebuffer != 0 && !ValidateHandle(framebuffer)) + return GL_INVALID_OPERATION; + if (target == GL_FRAMEBUFFER) { + currentBindings_[GL_READ_FRAMEBUFFER] = framebuffer; + currentBindings_[GL_DRAW_FRAMEBUFFER] = framebuffer; + currentBindings_[GL_FRAMEBUFFER] = framebuffer; + } else { + currentBindings_[target] = framebuffer; + } + return GL_NO_ERROR; +} + +GLenum FramebufferState::glAttachTexture2D(GLenum target, GLenum attachment, + GLenum textarget, GLuint texture, GLint level) { + if (MG_Constants::Framebuffer::VALID_ATTACHMENTS.find(attachment) == MG_Constants::Framebuffer::VALID_ATTACHMENTS.end() || + MG_Constants::Texture::VALID_TARGETS.find(textarget) == MG_Constants::Texture::VALID_TARGETS.end()) + return GL_INVALID_ENUM; + FramebufferObject* fbo = GetCurrentFBO(target); + if (!fbo) return GL_INVALID_OPERATION; + if (texture != 0 && !MG_State_T::textureState->IsTexture(texture)) + return GL_INVALID_VALUE; + FramebufferAttachment att; + att.type = GL_TEXTURE_2D; + att.handle = texture; + att.mipLevel = level; + fbo->attachments[attachment] = att; + + if (texture != 0) { + GLint params = 0; + MG_State_T::textureState->QueryLevelPropertyIntVector( + textarget, level, GL_TEXTURE_WIDTH, ¶ms); + fbo->width = params; + MG_State_T::textureState->QueryLevelPropertyIntVector( + textarget, level, GL_TEXTURE_HEIGHT, ¶ms); + fbo->height = params; + } + UpdateCompleteness(*fbo); + return GL_NO_ERROR; +} + +GLenum FramebufferState::glValidateCompleteness(GLenum target) { + FramebufferObject* fbo = GetCurrentFBO(target); + if (!fbo) return GL_INVALID_OPERATION; + return fbo->status; +} + +void FramebufferState::UpdateCompleteness(FramebufferObject& fbo) { + if (fbo.attachments.empty()) { + fbo.status = GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT; + return; + } + bool hasColor = false; + bool hasDepth = false; + bool hasStencil = false; + GLsizei width = 0, height = 0; + for (const auto& [attach, att] : fbo.attachments) { + GLsizei attWidth = 0, attHeight = 0; + if (att.handle != 0) { + GLint params = 0; + MG_State_T::textureState->QueryLevelPropertyIntVector( + GL_TEXTURE_2D, att.mipLevel, GL_TEXTURE_WIDTH, ¶ms); + attWidth = params; + MG_State_T::textureState->QueryLevelPropertyIntVector( + GL_TEXTURE_2D, att.mipLevel, GL_TEXTURE_HEIGHT, ¶ms); + attHeight = params; + } + if (width == 0) { + width = attWidth; + height = attHeight; + } else if (attWidth != width || attHeight != height) { + fbo.status = GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS; + return; + } + + if (attach == GL_DEPTH_ATTACHMENT) hasDepth = true; + if (attach == GL_STENCIL_ATTACHMENT) hasStencil = true; + if (attach >= GL_COLOR_ATTACHMENT0 && + attach <= GL_COLOR_ATTACHMENT31) hasColor = true; + } + + if (!ValidateAttachmentCombination(fbo)) { + fbo.status = GL_FRAMEBUFFER_UNSUPPORTED; + return; + } + fbo.status = GL_FRAMEBUFFER_COMPLETE; +} + +bool FramebufferState::ValidateAttachmentCombination(const FramebufferObject& fbo) { + bool depthStencilConflict = false; + for (const auto& [attach, att] : fbo.attachments) { + if (attach == GL_DEPTH_STENCIL_ATTACHMENT) { + GLint internalFormat = 0; + MG_State_T::textureState->QueryLevelPropertyIntVector( + GL_TEXTURE_2D, att.mipLevel, GL_TEXTURE_INTERNAL_FORMAT, &internalFormat); + if (internalFormat != GL_DEPTH24_STENCIL8 && + internalFormat != GL_DEPTH32F_STENCIL8) { + return false; + } + } + } + return true; +} + +FramebufferObject* FramebufferState::GetCurrentFBO(GLenum target) { + auto it = currentBindings_.find(target); + if (it == currentBindings_.end()) return nullptr; + + GLuint id = it->second; + if (id == 0) return nullptr; + + auto fboIt = framebuffers_.find(id); + return (fboIt != framebuffers_.end()) ? &fboIt->second : nullptr; +} + +bool FramebufferState::ValidateHandle(GLuint framebuffer) { + return framebuffers_.count(framebuffer) && + framebuffers_[framebuffer].generated; +} \ No newline at end of file diff --git a/MG/MG_GL/State/Framebuffer/FramebufferState.h b/MG/MG_GL/State/Framebuffer/FramebufferState.h new file mode 100644 index 00000000..8cf7b3df --- /dev/null +++ b/MG/MG_GL/State/Framebuffer/FramebufferState.h @@ -0,0 +1,48 @@ +// +// Created by BZLZHH on 2025/5/3. +// + +#ifndef MOBILEGL_FRAMEBUFFERSTATE_H +#define MOBILEGL_FRAMEBUFFERSTATE_H +#define MOBILEGL_GLSTATE_H + +#include "../../../Includes.h" + +struct FramebufferAttachment { + GLenum type = GL_NONE; + GLuint handle = 0; + GLint mipLevel = 0; + GLint zoffset = 0; +}; + +struct FramebufferObject { + bool generated = false; + std::unordered_map attachments; + GLenum status = GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT; + GLsizei width = 0; + GLsizei height = 0; +}; + +class FramebufferState { +public: + // Return: the validity of the operation + GLenum Create(GLuint* framebuffer); + GLenum CreateN(GLsizei n, GLuint* framebuffers); + GLenum Delete(GLuint framebuffer); + GLenum Bind(GLenum target, GLuint framebuffer); + GLenum glAttachTexture2D(GLenum target, GLenum attachment, + GLenum textarget, GLuint texture, GLint level); + GLenum glValidateCompleteness(GLenum target); + FramebufferObject* GetCurrentFBO(GLenum target); + +private: + std::unordered_map framebuffers_; + std::unordered_map currentBindings_; // READ/DRAW/FRAMEBUFFER + std::set freeIds_; + GLuint lastId_ = 0; + bool ValidateHandle(GLuint framebuffer); + static void UpdateCompleteness(FramebufferObject& fbo); + static bool ValidateAttachmentCombination(const FramebufferObject& fbo); +}; + +#endif //MOBILEGL_FRAMEBUFFERSTATE_H