From e93dff52dc2c00e6cf608ed4acfa27398991fc20 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Sat, 15 Nov 2025 00:39:07 +0800 Subject: [PATCH] [Fix] (MG_Impl/Framebuffer, MG_State/Framebuffer, MG_Backend/DirectGLES): redone fbo attachment management. remap GL -> ES attachment names --- MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp | 52 ++++-- MobileGL/MG_Backend/DirectGLES/Managers.cpp | 148 +++++++++++++----- MobileGL/MG_Backend/DirectGLES/Managers.h | 22 ++- .../FramebufferState/FramebufferObject.cpp | 7 +- .../FramebufferState/FramebufferObject.h | 4 +- .../MGToGL/FramebufferEnumConverter.cpp | 2 +- 6 files changed, 171 insertions(+), 64 deletions(-) diff --git a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp index 58c2b858..d0fc2dd6 100644 --- a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp @@ -137,26 +137,44 @@ namespace MobileGL::MG_Backend::DirectGLES { namespace FramebufferImpl { void SyncCurrentFBO() { - auto currentFBO = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject(); - if (!currentFBO) { - MGLOG_E("No FBO is currently bound, cannot sync current FBO."); - return; - } + const FramebufferTarget fboTargets[] = {FramebufferTarget::Draw, FramebufferTarget::Read}; - if (currentFBO == MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo->defaultFBO) { - // Default FBO, nothing to sync - return; - } + MG_State::GLState::FramebufferObject* lastUpdatedFBO = nullptr; - const auto& backendFBOIt = g_backendFramebufferObjects.find(currentFBO); - SharedPtr backendFBOObject; - if (backendFBOIt == g_backendFramebufferObjects.end()) { - backendFBOObject = MakeShared(); - g_backendFramebufferObjects[currentFBO] = backendFBOObject; - } else { - backendFBOObject = backendFBOIt->second; + for (auto target: fboTargets) { + auto currentFBO = + MG_State::pGLContext->GetFramebufferBindingSlot(target).GetBoundObject(); + + if (!currentFBO) { + MGLOG_E("No FBO is currently bound, cannot sync current FBO."); + continue; + } + + if (currentFBO == + MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo->defaultFBO) { + // Default FBO, nothing to sync + continue; + } + + const auto &backendFBOIt = g_backendFramebufferObjects.find(currentFBO); + SharedPtr backendFBOObject; + if (backendFBOIt == g_backendFramebufferObjects.end()) { + backendFBOObject = MakeShared(); + g_backendFramebufferObjects[currentFBO] = backendFBOObject; + } else { + backendFBOObject = backendFBOIt->second; + } + + if (currentFBO.get() == lastUpdatedFBO) { + MGLOG_I("Draw FBO and read FBO are the same, skipping sync."); + } else { + backendFBOObject->SyncToBackend(currentFBO, target); + } + + backendFBOObject->Bind(target); + + lastUpdatedFBO = currentFBO.get(); } - backendFBOObject->SyncToBackend(currentFBO); } } // namespace FramebufferImpl diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.cpp b/MobileGL/MG_Backend/DirectGLES/Managers.cpp index 658e9d55..3920ac5d 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.cpp +++ b/MobileGL/MG_Backend/DirectGLES/Managers.cpp @@ -4,6 +4,7 @@ #include "MG_Util/Types.h" #include "Utils.h" #include "DirectGLES.h" +#include "MG_Util/Converters/GLToMG/FramebufferEnumConverter.h" #include #include #include @@ -383,62 +384,127 @@ namespace MobileGL::MG_Backend::DirectGLES { MG_External::GLES::glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_backendFBOId); } - void BackendFramebufferObject::SyncToBackend(SharedPtr& stateFBOObject) { + void BackendFramebufferObject::SyncToBackend(SharedPtr& stateFBOObject, FramebufferTarget asTarget) { if (!stateFBOObject) { MGLOG_E("State FBO object is null, cannot sync to backend."); return; } - MGLOG_D("Syncing FBO object with ID: %u to backend for state: %p", m_backendFBOId, stateFBOObject.get()); + MGLOG_D("Syncing FBO object with ID: %u to backend for state: %p, as %s FBO", m_backendFBOId, stateFBOObject.get(), + (asTarget == FramebufferTarget::Draw ? "DRAW" : "READ")); - BackendFramebufferBindingProtector backendFBOBindingProtector(GL_FRAMEBUFFER); - // TODO: do i really need to bind here? - Bind(FramebufferTarget::Read); - Bind(FramebufferTarget::Draw); + GLenum glFBOTarget = MG_Util::ConvertFramebufferTargetToGLEnum(asTarget); + BackendFramebufferBindingProtector backendFBOBindingProtector(glFBOTarget); - // TODO: add dirty check - // Sync all attachments - const auto& attachments = stateFBOObject->GetAllAttachments(); - for (SizeT index = 0; index < attachments.size(); ++index) { - FramebufferAttachmentType attachmentType = static_cast(index); - const auto& attachment = attachments[index]; - if (!attachment.IsComplete()) { - continue; + Bind(asTarget); + + if (asTarget == FramebufferTarget::Draw) { + int nBuffers = 0; + // Attach attachment to FBO, realize `glDrawBuffers` + if (stateFBOObject->DrawBuffersIsDirty()) { + std::fill(m_frontendBuffers, + m_frontendBuffers + MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS, + GL_NONE); + std::fill(m_backendBuffers, + m_backendBuffers + MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS, + GL_NONE); + auto &stateDrawBuffers = stateFBOObject->GetDrawBuffers(); + for (GLint i = 0; i < stateDrawBuffers.size(); ++i) { + m_frontendBuffers[i] = + MG_Util::ConvertFramebufferAttachmentTypeToGLEnum(stateDrawBuffers[i]); + } + + MGLOG_D("%s: mapping draw buffers gl -> es:", __func__); + for (int i = 0; + i < MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS; ++i) { + if (m_frontendBuffers[i] == GL_NONE) { + MGLOG_D("(m_frontendBuffers[%d] = GL_NONE), skipped", i); + continue; + } + m_backendBuffers[nBuffers] = GL_COLOR_ATTACHMENT0 + nBuffers; + m_compactedFrontendBuffers[nBuffers] = m_frontendBuffers[i]; + MGLOG_D("(m_frontendBuffers[%d] = %s) -> %s", + i, MG_Util::ConvertGLEnumToString(m_frontendBuffers[i]).c_str(), + MG_Util::ConvertGLEnumToString(m_backendBuffers[nBuffers]).c_str()); + nBuffers++; + } + + MG_External::GLES::glDrawBuffers(nBuffers, m_backendBuffers); + + stateFBOObject->ClearDrawBuffersDirtyState(); } - if (attachment.IsTexture()) { - const auto& textureObject = attachment.GetTexture(); - const auto& backendTextureIt = TextureImpl::g_backendTextureObjects.find(textureObject); - if (backendTextureIt == TextureImpl::g_backendTextureObjects.end()) { - MGLOG_E("No backend texture found for FBO attachment, cannot bind texture."); + // Sync all attachments + const auto& attachments = stateFBOObject->GetAllAttachments(); + for (SizeT i = 0; i < nBuffers; ++i) { + FramebufferAttachmentType frontendAttachmentType = + MG_Util::ConvertGLEnumToFramebufferAttachmentType(m_compactedFrontendBuffers[i]); + GLenum glBackendAttachmentType = m_backendBuffers[i]; + + const auto& attachment = attachments[(SizeT)frontendAttachmentType]; + if (!attachment.IsComplete()) { continue; } - const auto& backendTextureObject = backendTextureIt->second; - backendTextureObject->Bind(MG_Util::ConvertTextureTargetToGLEnum(textureObject->GetTarget())); - MG_External::GLES::glFramebufferTexture2D( - GL_FRAMEBUFFER, MG_Util::ConvertFramebufferAttachmentTypeToGLEnum(attachmentType), - MG_Util::ConvertTextureTargetToGLEnum(textureObject->GetTarget()), - backendTextureObject->GetBackendTextureId(), static_cast(attachment.GetTextureLevel())); - } else if (attachment.IsRenderbuffer()) { - // TODO - } - } - if (stateFBOObject->DrawBuffersIsDirty()) { - static GLenum drawbufs[MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS]; - std::fill(drawbufs, drawbufs + MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS, GL_NONE); - auto& stateDrawBuffers = stateFBOObject->GetDrawBuffers(); - GLint i = 0; - for (; i < stateDrawBuffers.size(); ++i) { - if (stateDrawBuffers[i] == FramebufferAttachmentType::None) { - break; + if (attachment.IsTexture()) { + const auto& textureObject = attachment.GetTexture(); + const auto& backendTextureIt = TextureImpl::g_backendTextureObjects.find(textureObject); + if (backendTextureIt == TextureImpl::g_backendTextureObjects.end()) { + MGLOG_E("No backend texture found for FBO attachment, cannot bind texture."); + continue; + } + const auto& backendTextureObject = backendTextureIt->second; + auto glTextureTarget = MG_Util::ConvertTextureTargetToGLEnum(textureObject->GetTarget()); + backendTextureObject->Bind(glTextureTarget); + MG_External::GLES::glFramebufferTexture2D( + glFBOTarget, glBackendAttachmentType, glTextureTarget, + backendTextureObject->GetBackendTextureId(), static_cast(attachment.GetTextureLevel())); + } else if (attachment.IsRenderbuffer()) { + // TODO } - drawbufs[i] = MG_Util::ConvertFramebufferAttachmentTypeToGLEnum(stateDrawBuffers[i]); } + } else { + // Attach attachment to FBO, realize `glReadBuffer` + // TODO: do we actually need "virtualization" here? I assume not? + GLenum frontendAtt = MG_Util::ConvertFramebufferAttachmentTypeToGLEnum(stateFBOObject->GetReadBuffer()); + GLenum backendAtt = GL_NONE; + // Attach attachment to FBO, fast path + for (SizeT i = 0; i < MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS; ++i) { + if (m_compactedFrontendBuffers[i] == frontendAtt) + backendAtt = m_backendBuffers[i]; + } + if (backendAtt != GL_NONE) { + MG_External::GLES::glReadBuffer(backendAtt); + } else { + // Maybe we should properly do `glFramebufferTexture2D` here + // don't need remap / virtualization + MG_External::GLES::glReadBuffer(frontendAtt); + const auto &attachments = stateFBOObject->GetAllAttachments(); + const auto &attachment = attachments[(SizeT) frontendAtt]; + if (!attachment.IsComplete()) { + return; + } - MG_External::GLES::glDrawBuffers(i, drawbufs); - - stateFBOObject->ClearDrawBuffersDirtyState(); + if (attachment.IsTexture()) { + const auto &textureObject = attachment.GetTexture(); + const auto &backendTextureIt = + TextureImpl::g_backendTextureObjects.find(textureObject); + if (backendTextureIt == TextureImpl::g_backendTextureObjects.end()) { + MGLOG_E("No backend texture found for FBO attachment, cannot bind texture."); + return; + } + const auto &backendTextureObject = backendTextureIt->second; + auto glTextureTarget = MG_Util::ConvertTextureTargetToGLEnum( + textureObject->GetTarget()); + backendTextureObject->Bind(glTextureTarget); + MG_External::GLES::glFramebufferTexture2D( + glFBOTarget, frontendAtt, glTextureTarget, + backendTextureObject->GetBackendTextureId(), + static_cast(attachment.GetTextureLevel())); + } else if (attachment.IsRenderbuffer()) { + // TODO + } + } } } diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.h b/MobileGL/MG_Backend/DirectGLES/Managers.h index 036ea31e..52e6e806 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.h +++ b/MobileGL/MG_Backend/DirectGLES/Managers.h @@ -83,12 +83,32 @@ namespace MobileGL::MG_Backend::DirectGLES { class BackendFramebufferObject { public: BackendFramebufferObject(); - void SyncToBackend(SharedPtr& stateFBOObject); + void SyncToBackend(SharedPtr& stateFBOObject, FramebufferTarget asTarget); Uint GetBackendFramebufferId() { return m_backendFBOId; } void Bind(FramebufferTarget target); private: Uint m_backendFBOId = 0; + + /* this will save buffers in its original form, + reversion, absence or not consecutive are all allowed, as long as GL spec allows it + i.e. it could be like [COLOR_ATTACHMENT0, COLOR_ATTACHMENT5, NONE, COLOR_ATTACHMENT4] + Probably useful to re-link shader output according to this. + aka. realizing `glBindFragDataLocation` + */ + GLenum m_frontendBuffers[MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS] = {GL_NONE}; + /* this will save buffers in its compacted GL form, + not consecutive is not allowed + i.e. it could be like [COLOR_ATTACHMENT0, COLOR_ATTACHMENT5, COLOR_ATTACHMENT4] + (no GL_NONE among those) + */ + GLenum m_compactedFrontendBuffers[MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS] = {GL_NONE}; + /* this will save buffers in stricter ES rules + reversion, absence or not consecutive are not allowed, according to ES spec + i.e. it could be like [COLOR_ATTACHMENT0, COLOR_ATTACHMENT1, NONE, NONE, ...] + this array could be provided as data directly to ES `glDrawBuffers` function + */ + GLenum m_backendBuffers[MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS] = {GL_NONE}; }; extern UnorderedMap, SharedPtr> diff --git a/MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.cpp b/MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.cpp index 3ac66fbd..43a8c7e2 100644 --- a/MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.cpp +++ b/MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.cpp @@ -67,23 +67,24 @@ namespace MobileGL { m_externalIndex(externalIndex) { m_attachments.fill(FramebufferAttachment(false)); m_drawBuffers.fill(FramebufferAttachmentType::None); + m_drawBuffers[0] = FramebufferAttachmentType::Color0; } void FramebufferObject::AttachTexture(FramebufferAttachmentType type, SharedPtr texture, int level) { m_attachments[static_cast(type)] = FramebufferAttachment(std::move(texture), level); -// m_drawBuffersDirty = true; + m_drawBuffersDirty = true; } void FramebufferObject::AttachRenderbuffer(FramebufferAttachmentType type, std::shared_ptr renderbuffer) { m_attachments[static_cast(type)] = FramebufferAttachment(renderbuffer); -// m_drawBuffersDirty = true; + m_drawBuffersDirty = true; } void FramebufferObject::Detach(FramebufferAttachmentType type) { m_attachments[static_cast(type)] = FramebufferAttachment(false); -// m_drawBuffersDirty = true; + m_drawBuffersDirty = true; } const FramebufferAttachment& FramebufferObject::GetAttachment(FramebufferAttachmentType type) const { diff --git a/MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.h b/MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.h index 84c53d04..bd93f106 100644 --- a/MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.h +++ b/MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.h @@ -103,11 +103,12 @@ namespace MobileGL { static_cast(FramebufferAttachmentType::FramebufferAttachmentTypeCount)>& GetAllAttachments() const; Bool CheckCompleteness() const; -// void SetDrawBuffers(const Vector& buffers); + // aka. `buffer` as in glDrawBuffers/glReadBuffers void SetDrawBuffer(Uint index, FramebufferAttachmentType buffer); bool DrawBuffersIsDirty() const { return m_drawBuffersDirty; } void ClearDrawBuffersDirtyState() { m_drawBuffersDirty = false; } const Array& GetDrawBuffers() const; + FramebufferAttachmentType GetReadBuffer() const { return m_readBuffer; } Uint GetExternalIndex() const; private: @@ -117,6 +118,7 @@ namespace MobileGL { m_attachments; Bool m_drawBuffersDirty = false; Array m_drawBuffers; + FramebufferAttachmentType m_readBuffer = FramebufferAttachmentType::None; }; } // namespace GLState diff --git a/MobileGL/MG_Util/Converters/MGToGL/FramebufferEnumConverter.cpp b/MobileGL/MG_Util/Converters/MGToGL/FramebufferEnumConverter.cpp index cabca613..92a7ea23 100644 --- a/MobileGL/MG_Util/Converters/MGToGL/FramebufferEnumConverter.cpp +++ b/MobileGL/MG_Util/Converters/MGToGL/FramebufferEnumConverter.cpp @@ -26,7 +26,7 @@ namespace MobileGL { case FramebufferAttachmentType::Stencil: return GL_STENCIL_ATTACHMENT; default: - return GL_COLOR_ATTACHMENT0; + return GL_NONE; } }