mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-12 22:28:32 +09:00
[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
This commit is contained in:
+3
-2
@@ -149,6 +149,7 @@ Verified locally on Turnip Adreno 750:
|
|||||||
- `DrawRangeElements` / `DrawRangeElementsBaseVertex`
|
- `DrawRangeElements` / `DrawRangeElementsBaseVertex`
|
||||||
- `MultiDrawArrays` / `MultiDrawElements` / `MultiDrawElementsBaseVertex`
|
- `MultiDrawArrays` / `MultiDrawElements` / `MultiDrawElementsBaseVertex`
|
||||||
- `DrawArraysInstanced` / `DrawElementsInstanced` family
|
- `DrawArraysInstanced` / `DrawElementsInstanced` family
|
||||||
|
- Indirect draw CPU fallback: `DrawArraysIndirect`, `DrawElementsIndirect`, `MultiDraw*Indirect`, `*IndirectCount`
|
||||||
- `ClearBufferfv` / `ClearBufferiv` / `ClearBufferuiv`
|
- `ClearBufferfv` / `ClearBufferiv` / `ClearBufferuiv`
|
||||||
- `BlitFramebuffer` (same-size color copy between current read/draw FBOs)
|
- `BlitFramebuffer` (same-size color copy between current read/draw FBOs)
|
||||||
- `CopyTexImage2D` / `CopyTexSubImage2D` (whole-color copy fallback)
|
- `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.
|
- 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 swapchain / EGL window surface presentation yet; `Present()` only flushes.
|
||||||
- No transform feedback / queries / sync / readback of non-color resources.
|
- 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.
|
- 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.
|
- The `GLFunctionsTable` is only partially populated.
|
||||||
|
|
||||||
@@ -246,7 +247,7 @@ Notes:
|
|||||||
- [x] `ReadPixels` from non-default framebuffer
|
- [x] `ReadPixels` from non-default framebuffer
|
||||||
- [x] `CopyTexImage*` wired as whole-color copy
|
- [x] `CopyTexImage*` wired as whole-color copy
|
||||||
- [x] `GetTexImage` / `GetTextureImage` (RGBA8)
|
- [x] `GetTexImage` / `GetTextureImage` (RGBA8)
|
||||||
- [ ] Indirect draws
|
- [x] Indirect draws (CPU fallback)
|
||||||
|
|
||||||
6. **Expand local test suite**
|
6. **Expand local test suite**
|
||||||
- [x] Scissor test
|
- [x] Scissor test
|
||||||
|
|||||||
@@ -35,6 +35,39 @@ namespace MobileGL::MG_Backend::DiligentBackend {
|
|||||||
return backend != nullptr ? backend->GetRenderer() : nullptr;
|
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<SizeT>(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<const Uint8*>(indirect);
|
||||||
|
}
|
||||||
|
|
||||||
void Clear(GLbitfield mask) {
|
void Clear(GLbitfield mask) {
|
||||||
auto* renderer = GetActiveRenderer();
|
auto* renderer = GetActiveRenderer();
|
||||||
if (renderer == nullptr || MG_State::pGLContext == nullptr) {
|
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<GLint>(cmd.First), static_cast<GLsizei>(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<const void*>(static_cast<SizeT>(cmd.FirstIndex) * indexSize);
|
||||||
|
for (Uint32 i = 0; i < cmd.InstanceCount; ++i) {
|
||||||
|
renderer->DrawFromState(mode, 0, static_cast<GLsizei>(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<GLsizei>(sizeof(DrawArraysIndirectCommand)) : stride;
|
||||||
|
for (GLsizei i = 0; i < drawcount; ++i) {
|
||||||
|
const auto* bytes = ResolveIndirectCommandBytes(
|
||||||
|
static_cast<const Uint8*>(indirect) + static_cast<SizeT>(i) * static_cast<SizeT>(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<GLint>(cmd.First),
|
||||||
|
static_cast<GLsizei>(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<GLsizei>(sizeof(DrawElementsIndirectCommand)) : stride;
|
||||||
|
for (GLsizei i = 0; i < drawcount; ++i) {
|
||||||
|
const auto* bytes = ResolveIndirectCommandBytes(
|
||||||
|
static_cast<const Uint8*>(indirect) + static_cast<SizeT>(i) * static_cast<SizeT>(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<const void*>(static_cast<SizeT>(cmd.FirstIndex) * indexSize);
|
||||||
|
for (Uint32 instance = 0; instance < cmd.InstanceCount; ++instance) {
|
||||||
|
renderer->DrawFromState(mode, 0, static_cast<GLsizei>(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<SizeT>(drawcount), sizeof(actualDrawCount));
|
||||||
|
actualDrawCount = std::min<Uint32>(actualDrawCount, static_cast<Uint32>(maxdrawcount));
|
||||||
|
MultiDrawArraysIndirect(mode, indirect, static_cast<GLsizei>(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<SizeT>(drawcount), sizeof(actualDrawCount));
|
||||||
|
actualDrawCount = std::min<Uint32>(actualDrawCount, static_cast<Uint32>(maxdrawcount));
|
||||||
|
MultiDrawElementsIndirect(mode, type, indirect, static_cast<GLsizei>(actualDrawCount), stride);
|
||||||
|
}
|
||||||
|
|
||||||
void DrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei instancecount) {
|
void DrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei instancecount) {
|
||||||
auto* renderer = GetActiveRenderer();
|
auto* renderer = GetActiveRenderer();
|
||||||
if (renderer == nullptr || instancecount <= 0) {
|
if (renderer == nullptr || instancecount <= 0) {
|
||||||
@@ -404,6 +578,12 @@ namespace MobileGL::MG_Backend::DiligentBackend {
|
|||||||
m_functions.GL.DrawElementsInstancedBaseVertex = DrawElementsInstancedBaseVertex;
|
m_functions.GL.DrawElementsInstancedBaseVertex = DrawElementsInstancedBaseVertex;
|
||||||
m_functions.GL.DrawElementsInstancedBaseInstance = DrawElementsInstancedBaseInstance;
|
m_functions.GL.DrawElementsInstancedBaseInstance = DrawElementsInstancedBaseInstance;
|
||||||
m_functions.GL.DrawElementsInstancedBaseVertexBaseInstance = DrawElementsInstancedBaseVertexBaseInstance;
|
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.ClearBufferfv = ClearBufferfv;
|
||||||
m_functions.GL.ClearBufferiv = ClearBufferiv;
|
m_functions.GL.ClearBufferiv = ClearBufferiv;
|
||||||
m_functions.GL.ClearBufferuiv = ClearBufferuiv;
|
m_functions.GL.ClearBufferuiv = ClearBufferuiv;
|
||||||
|
|||||||
Reference in New Issue
Block a user