[Fix] (MG_Backend/DirectGLES): rebase gl_InstanceID for native indirect draws on ANGLE

ES keeps gl_InstanceID zero-based and ignores the indirect command's
'reserved, must be zero' word, but ANGLE-on-Vulkan forwards the command
verbatim to vkCmdDraw*Indirect and compiles gl_InstanceID to SPIR-V
InstanceIndex, which includes firstInstance. Shaders computing
gl_BaseInstance + gl_InstanceID (Flywheel indirect) then add the base
twice, scrambling instance-to-mesh association.

Probe the actual driver semantics at capability-fill time with a tiny
indirect draw (an ES indirect draw needs a non-default VAO) and, on
leaking drivers, rewrite vertex shaders that use the native indirect
SSBO machinery so gl_InstanceID subtracts the command's baseInstance
word during native indirect draws.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-09 12:14:02 +00:00
co-authored by Claude Fable 5
parent a6a5edf573
commit 85b68a9969
4 changed files with 183 additions and 3 deletions
@@ -1078,8 +1078,11 @@ namespace MobileGL::MG_Backend::DirectGLES {
// shader emulation. Without GL_EXT_base_instance a non-zero baseInstance in the command is
// technically undefined in ES; mobile drivers ignore the reserved word, instanced-array
// fetches were never baseInstance-offset here anyway, and the CPU fallback cannot see
// GPU-written command fields at all - so native is never worse. Only client-memory
// commands take the CPU per-command loop.
// GPU-written command fields at all - so native is never worse. ANGLE-on-Vulkan instead
// leaks the word into gl_InstanceID (it becomes vkCmdDrawIndexedIndirect's firstInstance);
// the shader rewrite compensates by rebasing gl_InstanceID during these draws when
// IndirectDrawInstanceIdIncludesBaseInstance is set (PromoteDrawParameterGlobalsToUniforms).
// Only client-memory commands take the CPU per-command loop.
static void ExecuteIndexedIndirectCommands(GLenum mode, GLenum type, SizeT indexSize, const Uint8* commandBytes,
SizeT commandOffset,
const SharedPtr<MG_State::GLState::BufferObject>& drawIndirectBuffer,