From eb2f5dfc6132d3d04fac010bbd8bed7fc97b4759 Mon Sep 17 00:00:00 2001 From: Kailash Date: Wed, 16 Sep 2026 21:01:54 +0530 Subject: [PATCH] GLES: Pass known index range to glDrawRangeElements DrawEngineGLES already knows that every index it generates is below the decoded/transformed vertex count, but only passed the count to glDrawElements. Drivers that need the vertex range before vertex shading, like Mesa's Panfrost, then scan the index data on the CPU for every draw, and their min/max caches can't help since the index push buffer is rewritten every frame. Only used for single-instance draws on desktop GL or GLES3, and only when the caller provides the range, so other DrawIndexed callers are unchanged. Co-Authored-By: Claude Opus 5 --- Common/GPU/OpenGL/GLQueueRunner.cpp | 4 +++- Common/GPU/OpenGL/GLQueueRunner.h | 1 + Common/GPU/OpenGL/GLRenderManager.h | 5 ++++- GPU/GLES/DrawEngineGLES.cpp | 4 ++-- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/Common/GPU/OpenGL/GLQueueRunner.cpp b/Common/GPU/OpenGL/GLQueueRunner.cpp index c302f3c9ec..4c27693f75 100644 --- a/Common/GPU/OpenGL/GLQueueRunner.cpp +++ b/Common/GPU/OpenGL/GLQueueRunner.cpp @@ -1266,7 +1266,9 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buf); curElemArrayBuffer = buf; } - if (c.draw.instances == 1) { + if (c.draw.instances == 1 && c.draw.maxIndex >= 0 && (!gl_extensions.IsGLES || gl_extensions.GLES3)) { + glDrawRangeElements(c.draw.mode, 0, c.draw.maxIndex, c.draw.count, c.draw.indexType, (void *)(intptr_t)c.draw.indexOffset); + } else if (c.draw.instances == 1) { glDrawElements(c.draw.mode, c.draw.count, c.draw.indexType, (void *)(intptr_t)c.draw.indexOffset); } else { glDrawElementsInstanced(c.draw.mode, c.draw.count, c.draw.indexType, (void *)(intptr_t)c.draw.indexOffset, c.draw.instances); diff --git a/Common/GPU/OpenGL/GLQueueRunner.h b/Common/GPU/OpenGL/GLQueueRunner.h index e7934ecfde..2bc4b890b2 100644 --- a/Common/GPU/OpenGL/GLQueueRunner.h +++ b/Common/GPU/OpenGL/GLQueueRunner.h @@ -116,6 +116,7 @@ struct GLRRenderData { GLint count; GLint indexType; GLint instances; + GLint maxIndex; // -1 if unknown. Otherwise, all indices are known to be <= maxIndex. } draw; struct { const char *name; // if null, use loc diff --git a/Common/GPU/OpenGL/GLRenderManager.h b/Common/GPU/OpenGL/GLRenderManager.h index 2e03897320..687307c67d 100644 --- a/Common/GPU/OpenGL/GLRenderManager.h +++ b/Common/GPU/OpenGL/GLRenderManager.h @@ -794,7 +794,9 @@ public: } // Would really love to have a basevertex parameter, but impossible in unextended GLES, without glDrawElementsBaseVertex, unfortunately. - void DrawIndexed(GLRInputLayout *inputLayout, GLRBuffer *vertexBuffer, uint32_t vertexOffset, GLRBuffer *indexBuffer, uint32_t indexOffset, GLenum mode, int count, GLenum indexType, int instances = 1) { + // If maxIndex is known (>= 0), it's passed on to the driver through glDrawRangeElements, which saves drivers that need the index + // range (like Panfrost) from scanning the index data on the CPU for every draw. + void DrawIndexed(GLRInputLayout *inputLayout, GLRBuffer *vertexBuffer, uint32_t vertexOffset, GLRBuffer *indexBuffer, uint32_t indexOffset, GLenum mode, int count, GLenum indexType, int instances = 1, int maxIndex = -1) { _dbg_assert_(vertexBuffer && indexBuffer && curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); GLRRenderData &data = curRenderStep_->commands.push_uninitialized(); data.cmd = GLRRenderCommand::DRAW; @@ -807,6 +809,7 @@ public: data.draw.count = count; data.draw.indexType = indexType; data.draw.instances = instances; + data.draw.maxIndex = maxIndex; } enum { MAX_INFLIGHT_FRAMES = 3 }; diff --git a/GPU/GLES/DrawEngineGLES.cpp b/GPU/GLES/DrawEngineGLES.cpp index f778b8d451..a86ff17852 100644 --- a/GPU/GLES/DrawEngineGLES.cpp +++ b/GPU/GLES/DrawEngineGLES.cpp @@ -332,7 +332,7 @@ void DrawEngineGLES::Flush() { render_->DrawIndexed(inputLayout, vertexBuffer, vertexBufferOffset, indexBuffer, indexBufferOffset, - glprim[prim], vertexCount, GL_UNSIGNED_SHORT); + glprim[prim], vertexCount, GL_UNSIGNED_SHORT, 1, maxIndex - 1); } else { render_->Draw( inputLayout, vertexBuffer, vertexBufferOffset, @@ -419,7 +419,7 @@ void DrawEngineGLES::Flush() { indexBufferOffset = (uint32_t)frameData.pushIndex->Push(inds, sizeof(uint16_t) * result.drawIndexCount, 2, &indexBuffer); render_->DrawIndexed( softwareInputLayout_, vertexBuffer, vertexBufferOffset, indexBuffer, indexBufferOffset, - glprim[prim], result.drawIndexCount, GL_UNSIGNED_SHORT); + glprim[prim], result.drawIndexCount, GL_UNSIGNED_SHORT, 1, result.drawVertexCount - 1); gpuStats.perFrame.numVertsDrawn += result.drawIndexCount; } else if (action == SW_CLEAR) { u32 clearColor = result.color;