From e3f44e8da1bc5fd9f63938bb9042a34c41118932 Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Sun, 23 Aug 2026 09:45:34 +0800 Subject: [PATCH] [Feat] (Diligent, MG_Impl): add indirect draw CPU fallbacks - Wire DrawArraysIndirect/DrawElementsIndirect - Wire MultiDraw*Indirect and *IndirectCount using client memory or GL_DRAW_INDIRECT_BUFFER/GL_PARAMETER_BUFFER CPU reads - Update handoff; 13 Diligent tests pass --- HANDOFF_DILIGENT.md | 5 +- .../Diligent/BackendObject_Diligent.cpp | 180 ++++++++++++++++++ 2 files changed, 183 insertions(+), 2 deletions(-) diff --git a/HANDOFF_DILIGENT.md b/HANDOFF_DILIGENT.md index 1f6879cf..b5e96ea1 100644 --- a/HANDOFF_DILIGENT.md +++ b/HANDOFF_DILIGENT.md @@ -149,6 +149,7 @@ Verified locally on Turnip Adreno 750: - `DrawRangeElements` / `DrawRangeElementsBaseVertex` - `MultiDrawArrays` / `MultiDrawElements` / `MultiDrawElementsBaseVertex` - `DrawArraysInstanced` / `DrawElementsInstanced` family + - Indirect draw CPU fallback: `DrawArraysIndirect`, `DrawElementsIndirect`, `MultiDraw*Indirect`, `*IndirectCount` - `ClearBufferfv` / `ClearBufferiv` / `ClearBufferuiv` - `BlitFramebuffer` (same-size color copy between current read/draw FBOs) - `CopyTexImage2D` / `CopyTexSubImage2D` (whole-color copy fallback) @@ -212,7 +213,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, CopyTexImage* and GetTexImage/GetTextureImage are now wired; indirect draws and buffer subdata paths still remain. +- Draw range, multi-draw, instanced-draw wrappers, clear-buffer, blit, read-pixels, CopyTexImage*, GetTexImage/GetTextureImage and indirect draws are now wired; 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. @@ -246,7 +247,7 @@ Notes: - [x] `ReadPixels` from non-default framebuffer - [x] `CopyTexImage*` wired as whole-color copy - [x] `GetTexImage` / `GetTextureImage` (RGBA8) - - [ ] Indirect draws + - [x] Indirect draws (CPU fallback) 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 f7dfba92..d5fa8b0f 100644 --- a/MobileGL/MG_Backend/Diligent/BackendObject_Diligent.cpp +++ b/MobileGL/MG_Backend/Diligent/BackendObject_Diligent.cpp @@ -35,6 +35,39 @@ namespace MobileGL::MG_Backend::DiligentBackend { return backend != nullptr ? backend->GetRenderer() : nullptr; } + struct DrawArraysIndirectCommand { + Uint32 Count = 0; + Uint32 InstanceCount = 0; + Uint32 First = 0; + Uint32 BaseInstance = 0; + }; + + struct DrawElementsIndirectCommand { + Uint32 Count = 0; + Uint32 InstanceCount = 0; + Uint32 FirstIndex = 0; + Int32 BaseVertex = 0; + Uint32 BaseInstance = 0; + }; + + const Uint8* ResolveIndirectCommandBytes(const void* indirect, SizeT requiredBytes, const char* label) { + auto drawBuffer = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::DrawIndirect).GetBoundObject(); + if (drawBuffer) { + drawBuffer->SyncPersistentMappedRange(); + const SizeT commandOffset = reinterpret_cast(indirect); + if (drawBuffer->MappedData() == nullptr || commandOffset + requiredBytes > drawBuffer->GetSize()) { + MGLOG_E_ONCE("%s skipped: invalid GL_DRAW_INDIRECT_BUFFER binding or range", label); + return nullptr; + } + return drawBuffer->MappedData() + commandOffset; + } + if (indirect == nullptr) { + MGLOG_E_ONCE("%s skipped: indirect pointer is null", label); + return nullptr; + } + return reinterpret_cast(indirect); + } + void Clear(GLbitfield mask) { auto* renderer = GetActiveRenderer(); if (renderer == nullptr || MG_State::pGLContext == nullptr) { @@ -129,6 +162,147 @@ namespace MobileGL::MG_Backend::DiligentBackend { } } + void DrawArraysIndirect(GLenum mode, const void* indirect) { + auto* renderer = GetActiveRenderer(); + if (renderer == nullptr || MG_State::pGLContext == nullptr) { + return; + } + const auto* bytes = ResolveIndirectCommandBytes(indirect, sizeof(DrawArraysIndirectCommand), + "DrawArraysIndirect"); + if (bytes == nullptr) { + return; + } + DrawArraysIndirectCommand cmd{}; + std::memcpy(&cmd, bytes, sizeof(cmd)); + if (cmd.Count == 0 || cmd.InstanceCount == 0) { + return; + } + for (Uint32 i = 0; i < cmd.InstanceCount; ++i) { + renderer->DrawFromState(mode, static_cast(cmd.First), static_cast(cmd.Count), + 0, nullptr); + } + } + + void DrawElementsIndirect(GLenum mode, GLenum type, const void* indirect) { + auto* renderer = GetActiveRenderer(); + if (renderer == nullptr || MG_State::pGLContext == nullptr) { + return; + } + const SizeT indexSize = MG_Util::GetGLTypeSize(type); + if (indexSize == 0) { + return; + } + const auto* bytes = ResolveIndirectCommandBytes(indirect, sizeof(DrawElementsIndirectCommand), + "DrawElementsIndirect"); + if (bytes == nullptr) { + return; + } + DrawElementsIndirectCommand cmd{}; + std::memcpy(&cmd, bytes, sizeof(cmd)); + if (cmd.Count == 0 || cmd.InstanceCount == 0) { + return; + } + const void* indices = reinterpret_cast(static_cast(cmd.FirstIndex) * indexSize); + for (Uint32 i = 0; i < cmd.InstanceCount; ++i) { + renderer->DrawFromState(mode, 0, static_cast(cmd.Count), type, indices); + } + } + + void MultiDrawArraysIndirect(GLenum mode, const void* indirect, GLsizei drawcount, GLsizei stride) { + auto* renderer = GetActiveRenderer(); + if (renderer == nullptr || MG_State::pGLContext == nullptr || drawcount <= 0) { + return; + } + const GLsizei realStride = stride == 0 ? static_cast(sizeof(DrawArraysIndirectCommand)) : stride; + for (GLsizei i = 0; i < drawcount; ++i) { + const auto* bytes = ResolveIndirectCommandBytes( + static_cast(indirect) + static_cast(i) * static_cast(realStride), + sizeof(DrawArraysIndirectCommand), "MultiDrawArraysIndirect"); + if (bytes == nullptr) { + continue; + } + DrawArraysIndirectCommand cmd{}; + std::memcpy(&cmd, bytes, sizeof(cmd)); + if (cmd.Count == 0 || cmd.InstanceCount == 0) { + continue; + } + for (Uint32 instance = 0; instance < cmd.InstanceCount; ++instance) { + renderer->DrawFromState(mode, static_cast(cmd.First), + static_cast(cmd.Count), 0, nullptr); + } + } + } + + void MultiDrawElementsIndirect(GLenum mode, GLenum type, const void* indirect, GLsizei drawcount, + GLsizei stride) { + auto* renderer = GetActiveRenderer(); + if (renderer == nullptr || MG_State::pGLContext == nullptr || drawcount <= 0) { + return; + } + const SizeT indexSize = MG_Util::GetGLTypeSize(type); + if (indexSize == 0) { + return; + } + const GLsizei realStride = stride == 0 ? static_cast(sizeof(DrawElementsIndirectCommand)) : stride; + for (GLsizei i = 0; i < drawcount; ++i) { + const auto* bytes = ResolveIndirectCommandBytes( + static_cast(indirect) + static_cast(i) * static_cast(realStride), + sizeof(DrawElementsIndirectCommand), "MultiDrawElementsIndirect"); + if (bytes == nullptr) { + continue; + } + DrawElementsIndirectCommand cmd{}; + std::memcpy(&cmd, bytes, sizeof(cmd)); + if (cmd.Count == 0 || cmd.InstanceCount == 0) { + continue; + } + const void* indices = reinterpret_cast(static_cast(cmd.FirstIndex) * indexSize); + for (Uint32 instance = 0; instance < cmd.InstanceCount; ++instance) { + renderer->DrawFromState(mode, 0, static_cast(cmd.Count), type, indices); + } + } + } + + void MultiDrawArraysIndirectCount(GLenum mode, const void* indirect, GLintptr drawcount, + GLsizei maxdrawcount, GLsizei stride) { + if (MG_State::pGLContext == nullptr) { + return; + } + auto paramBuffer = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::Parameter).GetBoundObject(); + if (!paramBuffer) { + return; + } + paramBuffer->SyncPersistentMappedRange(); + const Uint8* paramData = paramBuffer->MappedData(); + if (paramData == nullptr) { + return; + } + Uint32 actualDrawCount = 0; + std::memcpy(&actualDrawCount, paramData + static_cast(drawcount), sizeof(actualDrawCount)); + actualDrawCount = std::min(actualDrawCount, static_cast(maxdrawcount)); + MultiDrawArraysIndirect(mode, indirect, static_cast(actualDrawCount), stride); + } + + void MultiDrawElementsIndirectCount(GLenum mode, GLenum type, const void* indirect, + GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride) { + if (MG_State::pGLContext == nullptr) { + return; + } + auto paramBuffer = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::Parameter).GetBoundObject(); + if (!paramBuffer) { + return; + } + paramBuffer->SyncPersistentMappedRange(); + const Uint8* paramData = paramBuffer->MappedData(); + if (paramData == nullptr) { + return; + } + Uint32 actualDrawCount = 0; + std::memcpy(&actualDrawCount, paramData + static_cast(drawcount), sizeof(actualDrawCount)); + actualDrawCount = std::min(actualDrawCount, static_cast(maxdrawcount)); + MultiDrawElementsIndirect(mode, type, indirect, static_cast(actualDrawCount), stride); + } + void DrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei instancecount) { auto* renderer = GetActiveRenderer(); if (renderer == nullptr || instancecount <= 0) { @@ -404,6 +578,12 @@ namespace MobileGL::MG_Backend::DiligentBackend { m_functions.GL.DrawElementsInstancedBaseVertex = DrawElementsInstancedBaseVertex; m_functions.GL.DrawElementsInstancedBaseInstance = DrawElementsInstancedBaseInstance; m_functions.GL.DrawElementsInstancedBaseVertexBaseInstance = DrawElementsInstancedBaseVertexBaseInstance; + m_functions.GL.DrawArraysIndirect = DrawArraysIndirect; + m_functions.GL.DrawElementsIndirect = DrawElementsIndirect; + m_functions.GL.MultiDrawArraysIndirect = MultiDrawArraysIndirect; + m_functions.GL.MultiDrawElementsIndirect = MultiDrawElementsIndirect; + m_functions.GL.MultiDrawArraysIndirectCount = MultiDrawArraysIndirectCount; + m_functions.GL.MultiDrawElementsIndirectCount = MultiDrawElementsIndirectCount; m_functions.GL.ClearBufferfv = ClearBufferfv; m_functions.GL.ClearBufferiv = ClearBufferiv; m_functions.GL.ClearBufferuiv = ClearBufferuiv;