diff --git a/HANDOFF_DILIGENT.md b/HANDOFF_DILIGENT.md index 3aa90395..1f6879cf 100644 --- a/HANDOFF_DILIGENT.md +++ b/HANDOFF_DILIGENT.md @@ -152,6 +152,7 @@ Verified locally on Turnip Adreno 750: - `ClearBufferfv` / `ClearBufferiv` / `ClearBufferuiv` - `BlitFramebuffer` (same-size color copy between current read/draw FBOs) - `CopyTexImage2D` / `CopyTexSubImage2D` (whole-color copy fallback) + - `GetTexImage` / `GetTextureImage` (RGBA8 readback) - `ReadPixels` from default and user color attachments - Primitive expansion: - `GL_TRIANGLE_FAN` expanded to triangle list @@ -211,7 +212,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, read-pixels and CopyTexImage* are now wired; indirect draws, GetTexImage and buffer subdata paths still remain. +- Draw range, multi-draw, instanced-draw wrappers, clear-buffer, blit, read-pixels, CopyTexImage* and GetTexImage/GetTextureImage are now wired; indirect draws 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. @@ -243,7 +244,9 @@ Notes: - [x] `MultiDraw*` - [x] `BlitFramebuffer` (same-size color copy) - [x] `ReadPixels` from non-default framebuffer - - [~] `CopyTexImage*` wired as whole-color copy; `GetTexImage` / indirect draws remain + - [x] `CopyTexImage*` wired as whole-color copy + - [x] `GetTexImage` / `GetTextureImage` (RGBA8) + - [ ] Indirect draws 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 c72ff6cf..f7dfba92 100644 --- a/MobileGL/MG_Backend/Diligent/BackendObject_Diligent.cpp +++ b/MobileGL/MG_Backend/Diligent/BackendObject_Diligent.cpp @@ -278,6 +278,32 @@ namespace MobileGL::MG_Backend::DiligentBackend { } } + void GetTexImage(GLenum target, GLint level, GLenum format, GLenum type, GLvoid* pixels) { + auto* renderer = GetActiveRenderer(); + if (renderer == nullptr || MG_State::pGLContext == nullptr || target != GL_TEXTURE_2D || + format != GL_RGBA || type != GL_UNSIGNED_BYTE || pixels == nullptr) { + return; + } + auto& unit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit()); + auto texture = unit.GetBindingSlot(TextureTarget::Texture2D).GetBoundObject(); + if (texture) { + renderer->ReadTextureImage(*texture, static_cast(level), pixels); + } + } + + void GetTextureImage(const SharedPtr& texture, + TextureUploadTarget uploadTarget, GLint level, GLenum format, GLenum type, + GLsizei bufSize, GLvoid* pixels) { + (void)uploadTarget; + (void)bufSize; + auto* renderer = GetActiveRenderer(); + if (renderer == nullptr || !texture || format != GL_RGBA || type != GL_UNSIGNED_BYTE || + pixels == nullptr) { + return; + } + renderer->ReadTextureImage(*texture, static_cast(level), pixels); + } + void Present() { auto* renderer = GetActiveRenderer(); if (renderer != nullptr) { @@ -384,6 +410,8 @@ namespace MobileGL::MG_Backend::DiligentBackend { m_functions.GL.BlitFramebuffer = BlitFramebuffer; m_functions.GL.CopyTexImage2D = CopyTexImage2D; m_functions.GL.CopyTexSubImage2D = CopyTexSubImage2D; + m_functions.GL.GetTexImage = GetTexImage; + m_functions.GL.GetTextureImage = GetTextureImage; 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 52ccd88c..5d5a1edc 100644 --- a/MobileGL/MG_Backend/Diligent/Renderer/DiligentRenderer.cpp +++ b/MobileGL/MG_Backend/Diligent/Renderer/DiligentRenderer.cpp @@ -1741,6 +1741,68 @@ void main() m_pContext->CopyTexture(copyAttribs); } + Bool DiligentRenderer::ReadTextureImage(MG_State::GLState::ITextureObject& texture, Uint32 level, + void* pixels) { + if (!m_initialized || !m_pContext || pixels == nullptr) { + return false; + } + if (SyncTexture(texture) == nullptr) { + return false; + } + auto it = m_textureCache.find(texture.GetLifetimeId()); + if (it == m_textureCache.end() || !it->second.Texture) { + return false; + } + auto& resource = it->second; + const auto& srcDesc = resource.Texture->GetDesc(); + if (srcDesc.Format != ::Diligent::TEX_FORMAT_RGBA8_UNORM && + srcDesc.Format != ::Diligent::TEX_FORMAT_RGBA8_UNORM_SRGB) { + return false; + } + + ::Diligent::TextureDesc stagingDesc; + stagingDesc.Name = "MobileGL Diligent texture image staging"; + stagingDesc.Type = ::Diligent::RESOURCE_DIM_TEX_2D; + stagingDesc.Format = srcDesc.Format; + stagingDesc.Width = srcDesc.Width; + stagingDesc.Height = srcDesc.Height; + stagingDesc.MipLevels = 1; + stagingDesc.Usage = ::Diligent::USAGE_STAGING; + stagingDesc.CPUAccessFlags = ::Diligent::CPU_ACCESS_READ; + + ::Diligent::RefCntAutoPtr<::Diligent::ITexture> pStaging; + m_pDevice->CreateTexture(stagingDesc, nullptr, &pStaging); + if (!pStaging) { + return false; + } + + ::Diligent::CopyTextureAttribs copyAttribs( + resource.Texture, ::Diligent::RESOURCE_STATE_TRANSITION_MODE_TRANSITION, + pStaging, ::Diligent::RESOURCE_STATE_TRANSITION_MODE_TRANSITION); + copyAttribs.SrcMipLevel = level; + copyAttribs.DstMipLevel = 0; + m_pContext->CopyTexture(copyAttribs); + m_pContext->WaitForIdle(); + + ::Diligent::MappedTextureSubresource mapped; + m_pContext->MapTextureSubresource(pStaging, 0, 0, ::Diligent::MAP_READ, + ::Diligent::MAP_FLAG_DO_NOT_WAIT, nullptr, mapped); + if (mapped.pData == nullptr) { + return false; + } + + const Uint32 width = srcDesc.Width >> level; + const Uint32 height = srcDesc.Height >> level; + Uint8* dst = static_cast(pixels); + for (Uint32 row = 0; row < height; ++row) { + std::memcpy(dst + row * width * 4, + static_cast(mapped.pData) + row * mapped.Stride, + width * 4); + } + m_pContext->UnmapTextureSubresource(pStaging, 0, 0); + return true; + } + 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 8d06b70b..2036729e 100644 --- a/MobileGL/MG_Backend/Diligent/Renderer/DiligentRenderer.h +++ b/MobileGL/MG_Backend/Diligent/Renderer/DiligentRenderer.h @@ -65,6 +65,7 @@ namespace MobileGL::MG_Backend::DiligentBackend { GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); void CopyReadFramebufferToTexture(MG_State::GLState::ITextureObject& dst); + Bool ReadTextureImage(MG_State::GLState::ITextureObject& texture, Uint32 level, void* pixels); void Present(); ::Diligent::IRenderDevice* GetDevice() const { return m_pDevice; }