From 6bf32acdefcd539ab8dffc3db6efef772081c3bf Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Sun, 23 Aug 2026 09:39:40 +0800 Subject: [PATCH] [Feat] (Diligent, MG_Impl): wire CopyTexImage2D/CopyTexSubImage2D readback copy - Copy current read-FBO color attachment into the bound GL_TEXTURE_2D - Uses whole-color CopyTexture fallback for now; 13 Diligent tests pass --- HANDOFF_DILIGENT.md | 5 ++- .../Diligent/BackendObject_Diligent.cpp | 42 +++++++++++++++++++ .../Diligent/Renderer/DiligentRenderer.cpp | 40 ++++++++++++++++++ .../Diligent/Renderer/DiligentRenderer.h | 1 + 4 files changed, 86 insertions(+), 2 deletions(-) diff --git a/HANDOFF_DILIGENT.md b/HANDOFF_DILIGENT.md index fcacc632..3aa90395 100644 --- a/HANDOFF_DILIGENT.md +++ b/HANDOFF_DILIGENT.md @@ -151,6 +151,7 @@ Verified locally on Turnip Adreno 750: - `DrawArraysInstanced` / `DrawElementsInstanced` family - `ClearBufferfv` / `ClearBufferiv` / `ClearBufferuiv` - `BlitFramebuffer` (same-size color copy between current read/draw FBOs) + - `CopyTexImage2D` / `CopyTexSubImage2D` (whole-color copy fallback) - `ReadPixels` from default and user color attachments - Primitive expansion: - `GL_TRIANGLE_FAN` expanded to triangle list @@ -210,7 +211,7 @@ Notes: - Global UBO (default-block `glUniform*`) and named application UBO blocks (through `glBindBufferBase`/`glUniformBlockBinding`) now upload and bind; SSBOs are still not fed from frontend buffer bindings. - No swapchain / EGL window surface presentation yet; `Present()` only flushes. - No transform feedback / queries / sync / readback of non-color resources. -- Draw range, multi-draw, instanced-draw wrappers, clear-buffer, blit and read-pixels are now wired; indirect draws, CopyTexImage/GetTexImage and buffer subdata paths still remain. +- Draw range, multi-draw, instanced-draw wrappers, clear-buffer, blit, read-pixels and CopyTexImage* are now wired; indirect draws, GetTexImage and buffer subdata paths still remain. - A last-PSO cache now avoids recreating the pipeline when program/render-state/topology/VAO layout is unchanged; texture/UBO resources are still rebound dynamically per draw. - The `GLFunctionsTable` is only partially populated. @@ -242,7 +243,7 @@ Notes: - [x] `MultiDraw*` - [x] `BlitFramebuffer` (same-size color copy) - [x] `ReadPixels` from non-default framebuffer - - [ ] `GetTexImage` / `CopyTexImage*` / indirect draws + - [~] `CopyTexImage*` wired as whole-color copy; `GetTexImage` / indirect draws remain 6. **Expand local test suite** - [x] Scissor test diff --git a/MobileGL/MG_Backend/Diligent/BackendObject_Diligent.cpp b/MobileGL/MG_Backend/Diligent/BackendObject_Diligent.cpp index 21638b3c..c72ff6cf 100644 --- a/MobileGL/MG_Backend/Diligent/BackendObject_Diligent.cpp +++ b/MobileGL/MG_Backend/Diligent/BackendObject_Diligent.cpp @@ -238,6 +238,46 @@ namespace MobileGL::MG_Backend::DiligentBackend { } } + void CopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, + GLsizei width, GLsizei height, GLint border) { + (void)level; + (void)internalformat; + (void)x; + (void)y; + (void)width; + (void)height; + (void)border; + auto* renderer = GetActiveRenderer(); + if (renderer == nullptr || MG_State::pGLContext == nullptr || target != GL_TEXTURE_2D) { + return; + } + auto& unit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit()); + auto texture = unit.GetBindingSlot(TextureTarget::Texture2D).GetBoundObject(); + if (texture) { + renderer->CopyReadFramebufferToTexture(*texture); + } + } + + void CopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, + GLsizei width, GLsizei height) { + (void)level; + (void)xoffset; + (void)yoffset; + (void)x; + (void)y; + (void)width; + (void)height; + auto* renderer = GetActiveRenderer(); + if (renderer == nullptr || MG_State::pGLContext == nullptr || target != GL_TEXTURE_2D) { + return; + } + auto& unit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit()); + auto texture = unit.GetBindingSlot(TextureTarget::Texture2D).GetBoundObject(); + if (texture) { + renderer->CopyReadFramebufferToTexture(*texture); + } + } + void Present() { auto* renderer = GetActiveRenderer(); if (renderer != nullptr) { @@ -342,6 +382,8 @@ namespace MobileGL::MG_Backend::DiligentBackend { m_functions.GL.ClearBufferiv = ClearBufferiv; m_functions.GL.ClearBufferuiv = ClearBufferuiv; m_functions.GL.BlitFramebuffer = BlitFramebuffer; + m_functions.GL.CopyTexImage2D = CopyTexImage2D; + m_functions.GL.CopyTexSubImage2D = CopyTexSubImage2D; m_functions.GL.ReadPixels = ReadPixels; m_functions.Present = Present; diff --git a/MobileGL/MG_Backend/Diligent/Renderer/DiligentRenderer.cpp b/MobileGL/MG_Backend/Diligent/Renderer/DiligentRenderer.cpp index d2690a42..52ccd88c 100644 --- a/MobileGL/MG_Backend/Diligent/Renderer/DiligentRenderer.cpp +++ b/MobileGL/MG_Backend/Diligent/Renderer/DiligentRenderer.cpp @@ -1701,6 +1701,46 @@ void main() m_pContext->CopyTexture(copyAttribs); } + void DiligentRenderer::CopyReadFramebufferToTexture(MG_State::GLState::ITextureObject& dst) { + if (!m_initialized || !m_pContext || !m_pColorTarget) { + return; + } + + ::Diligent::RefCntAutoPtr<::Diligent::ITexture> pSrcTexture = m_pColorTarget; + if (MG_State::pGLContext != nullptr) { + auto readFbo = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Read).GetBoundObject(); + if (readFbo && !readFbo->IsDefaultFramebuffer()) { + auto readBuffer = readFbo->GetReadBuffer(); + if (readBuffer == FramebufferAttachmentType::None) { + readBuffer = FramebufferAttachmentType::Color0; + } + const auto& att = readFbo->GetAttachment(readBuffer); + if (att.IsTexture() && SyncTexture(*att.GetTexture()) != nullptr) { + auto it = m_textureCache.find(att.GetTexture()->GetLifetimeId()); + if (it != m_textureCache.end() && it->second.Texture) { + pSrcTexture = it->second.Texture; + } + } + } + } + + if (SyncTexture(dst) == nullptr) { + return; + } + auto it = m_textureCache.find(dst.GetLifetimeId()); + if (it == m_textureCache.end() || !it->second.Texture) { + return; + } + auto pDstTexture = it->second.Texture; + if (pSrcTexture == pDstTexture) { + return; + } + ::Diligent::CopyTextureAttribs copyAttribs( + pSrcTexture, ::Diligent::RESOURCE_STATE_TRANSITION_MODE_TRANSITION, + pDstTexture, ::Diligent::RESOURCE_STATE_TRANSITION_MODE_TRANSITION); + m_pContext->CopyTexture(copyAttribs); + } + void DiligentRenderer::Present() { // Offscreen renderer: nothing to present yet. if (m_pContext) { diff --git a/MobileGL/MG_Backend/Diligent/Renderer/DiligentRenderer.h b/MobileGL/MG_Backend/Diligent/Renderer/DiligentRenderer.h index b886360b..8d06b70b 100644 --- a/MobileGL/MG_Backend/Diligent/Renderer/DiligentRenderer.h +++ b/MobileGL/MG_Backend/Diligent/Renderer/DiligentRenderer.h @@ -64,6 +64,7 @@ namespace MobileGL::MG_Backend::DiligentBackend { void BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); + void CopyReadFramebufferToTexture(MG_State::GLState::ITextureObject& dst); void Present(); ::Diligent::IRenderDevice* GetDevice() const { return m_pDevice; }