mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-11 13:48:30 +09:00
[Perf] (MG_Backend/DirectGLES): dedup per-draw indexed UBO/SSBO binds with a shadow cache; BindCurrentProgramWithResources 5.1% -> 3.1%
This commit is contained in:
@@ -208,7 +208,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
auto& point = MG_State::pGLContext->GetBufferBindingPoint(target, i);
|
auto& point = MG_State::pGLContext->GetBufferBindingPoint(target, i);
|
||||||
auto& obj = point.GetBoundObject();
|
auto& obj = point.GetBoundObject();
|
||||||
if (!obj) {
|
if (!obj) {
|
||||||
g_GLESFuncs.glBindBufferBase(glTarget, static_cast<GLuint>(i), 0);
|
BindBufferBaseCached(glTarget, static_cast<GLuint>(i), 0);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,12 +222,12 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
const auto& range = point.GetRange();
|
const auto& range = point.GetRange();
|
||||||
auto backendBufferId = backendResource->id;
|
auto backendBufferId = backendResource->id;
|
||||||
if (range.start == 0 && range.end >= obj->GetSize()) {
|
if (range.start == 0 && range.end >= obj->GetSize()) {
|
||||||
g_GLESFuncs.glBindBufferBase(glTarget, static_cast<GLuint>(i), backendBufferId);
|
BindBufferBaseCached(glTarget, static_cast<GLuint>(i), backendBufferId);
|
||||||
} else {
|
} else {
|
||||||
const auto start = std::min(range.start, obj->GetSize());
|
const auto start = std::min(range.start, obj->GetSize());
|
||||||
const auto end = std::min(range.end, obj->GetSize());
|
const auto end = std::min(range.end, obj->GetSize());
|
||||||
g_GLESFuncs.glBindBufferRange(glTarget, static_cast<GLuint>(i), backendBufferId,
|
BindBufferRangeCached(glTarget, static_cast<GLuint>(i), backendBufferId,
|
||||||
static_cast<GLintptr>(start), static_cast<GLsizeiptr>(end - start));
|
static_cast<GLintptr>(start), static_cast<GLsizeiptr>(end - start));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1083,7 +1083,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
g_GLESFuncs.glBindBuffer(GL_UNIFORM_BUFFER, 0);
|
g_GLESFuncs.glBindBuffer(GL_UNIFORM_BUFFER, 0);
|
||||||
backendProgram.SetLastUploadedGlobalUboVersion(uboContentVersion);
|
backendProgram.SetLastUploadedGlobalUboVersion(uboContentVersion);
|
||||||
}
|
}
|
||||||
g_GLESFuncs.glBindBufferBase(GL_UNIFORM_BUFFER, 0, backendProgram.GetBackendGlobalUBOId());
|
BufferImpl::BindBufferBaseCached(GL_UNIFORM_BUFFER, 0, backendProgram.GetBackendGlobalUBOId());
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -1111,12 +1111,13 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
if (bufferObj) {
|
if (bufferObj) {
|
||||||
auto* backendResource = BufferImpl::EnsureBufferResource(bufferObj);
|
auto* backendResource = BufferImpl::EnsureBufferResource(bufferObj);
|
||||||
if (backendResource && backendResource->id != 0) {
|
if (backendResource && backendResource->id != 0) {
|
||||||
BufferImpl::BindBufferId(GL_UNIFORM_BUFFER, backendResource->id);
|
// glBindBufferBase/Range set the generic GL_UNIFORM_BUFFER binding
|
||||||
|
// as a side effect, so no separate BindBufferId is needed here.
|
||||||
if (range.end == 0) {
|
if (range.end == 0) {
|
||||||
g_GLESFuncs.glBindBufferBase(GL_UNIFORM_BUFFER, lastUBOBinding,
|
BufferImpl::BindBufferBaseCached(GL_UNIFORM_BUFFER, lastUBOBinding,
|
||||||
backendResource->id);
|
backendResource->id);
|
||||||
} else {
|
} else {
|
||||||
g_GLESFuncs.glBindBufferRange(
|
BufferImpl::BindBufferRangeCached(
|
||||||
GL_UNIFORM_BUFFER, lastUBOBinding, backendResource->id,
|
GL_UNIFORM_BUFFER, lastUBOBinding, backendResource->id,
|
||||||
(GLintptr)range.start, (GLintptr)(range.end - range.start));
|
(GLintptr)range.start, (GLintptr)(range.end - range.start));
|
||||||
}
|
}
|
||||||
@@ -1235,8 +1236,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
if (paramsBinding >= 0) {
|
if (paramsBinding >= 0) {
|
||||||
auto* resource = BufferImpl::EnsureBufferResource(drawIndirectBuffer);
|
auto* resource = BufferImpl::EnsureBufferResource(drawIndirectBuffer);
|
||||||
if (resource && resource->id != 0) {
|
if (resource && resource->id != 0) {
|
||||||
g_GLESFuncs.glBindBufferBase(GL_SHADER_STORAGE_BUFFER, static_cast<GLuint>(paramsBinding),
|
BufferImpl::BindBufferBaseCached(GL_SHADER_STORAGE_BUFFER, static_cast<GLuint>(paramsBinding),
|
||||||
resource->id);
|
resource->id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (GLsizei i = 0; i < drawcount; ++i) {
|
for (GLsizei i = 0; i < drawcount; ++i) {
|
||||||
@@ -1282,8 +1283,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
if (paramsBinding >= 0) {
|
if (paramsBinding >= 0) {
|
||||||
auto* resource = BufferImpl::EnsureBufferResource(drawIndirectBuffer);
|
auto* resource = BufferImpl::EnsureBufferResource(drawIndirectBuffer);
|
||||||
if (resource && resource->id != 0) {
|
if (resource && resource->id != 0) {
|
||||||
g_GLESFuncs.glBindBufferBase(GL_SHADER_STORAGE_BUFFER, static_cast<GLuint>(paramsBinding),
|
BufferImpl::BindBufferBaseCached(GL_SHADER_STORAGE_BUFFER, static_cast<GLuint>(paramsBinding),
|
||||||
resource->id);
|
resource->id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (GLsizei i = 0; i < drawcount; ++i) {
|
for (GLsizei i = 0; i < drawcount; ++i) {
|
||||||
@@ -3871,6 +3872,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
// Conservatively drop the redundant-glUseProgram guard: re-issuing one bind
|
// Conservatively drop the redundant-glUseProgram guard: re-issuing one bind
|
||||||
// after a MakeCurrent is cheaper than trusting a possibly-reset context.
|
// after a MakeCurrent is cheaper than trusting a possibly-reset context.
|
||||||
PrgramImpl::g_lastUsedBackendProgramId = 0;
|
PrgramImpl::g_lastUsedBackendProgramId = 0;
|
||||||
|
BufferImpl::InvalidateIndexedBufferBindingCache();
|
||||||
// eglSwapInterval requires a current context; a request made while none was
|
// eglSwapInterval requires a current context; a request made while none was
|
||||||
// current (and dropped by the driver) is retried here.
|
// current (and dropped by the driver) is retried here.
|
||||||
ApplyRequestedSwapInterval();
|
ApplyRequestedSwapInterval();
|
||||||
|
|||||||
@@ -749,6 +749,47 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
g_boundArrayBufferKnown = false;
|
g_boundArrayBufferKnown = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
// Shadow of the GL indexed buffer bindings so redundant glBindBufferBase/Range
|
||||||
|
// (same index + id + range) are skipped. isBase distinguishes a whole-buffer
|
||||||
|
// base bind from a sub-range bind. Fresh/reset context: every point is base(0)
|
||||||
|
// == unbound, which matches the GL default.
|
||||||
|
struct IndexedBufferBinding {
|
||||||
|
Uint id = 0;
|
||||||
|
GLintptr offset = 0;
|
||||||
|
GLsizeiptr size = 0;
|
||||||
|
Bool isBase = true;
|
||||||
|
};
|
||||||
|
constexpr SizeT kMaxIndexedBufferBindings = 64;
|
||||||
|
IndexedBufferBinding g_indexedUBOBindings[kMaxIndexedBufferBindings];
|
||||||
|
IndexedBufferBinding g_indexedSSBOBindings[kMaxIndexedBufferBindings];
|
||||||
|
IndexedBufferBinding* IndexedBindingShadow(GLenum glTarget, Uint index) {
|
||||||
|
if (index >= kMaxIndexedBufferBindings) return nullptr; // out of range: never cache
|
||||||
|
if (glTarget == GL_UNIFORM_BUFFER) return &g_indexedUBOBindings[index];
|
||||||
|
if (glTarget == GL_SHADER_STORAGE_BUFFER) return &g_indexedSSBOBindings[index];
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void BindBufferBaseCached(GLenum glTarget, Uint index, Uint id) {
|
||||||
|
auto* s = IndexedBindingShadow(glTarget, index);
|
||||||
|
if (s && s->isBase && s->id == id) return;
|
||||||
|
g_GLESFuncs.glBindBufferBase(glTarget, index, id);
|
||||||
|
if (s) *s = {id, 0, 0, true};
|
||||||
|
}
|
||||||
|
|
||||||
|
void BindBufferRangeCached(GLenum glTarget, Uint index, Uint id, GLintptr offset, GLsizeiptr size) {
|
||||||
|
auto* s = IndexedBindingShadow(glTarget, index);
|
||||||
|
if (s && !s->isBase && s->id == id && s->offset == offset && s->size == size) return;
|
||||||
|
g_GLESFuncs.glBindBufferRange(glTarget, index, id, offset, size);
|
||||||
|
if (s) *s = {id, offset, size, false};
|
||||||
|
}
|
||||||
|
|
||||||
|
void InvalidateIndexedBufferBindingCache() {
|
||||||
|
for (auto& b : g_indexedUBOBindings) b = {};
|
||||||
|
for (auto& b : g_indexedSSBOBindings) b = {};
|
||||||
|
}
|
||||||
|
|
||||||
void TrimBufferPool() {
|
void TrimBufferPool() {
|
||||||
const std::lock_guard<std::mutex> lock(g_poolMutex);
|
const std::lock_guard<std::mutex> lock(g_poolMutex);
|
||||||
if (g_pooledBytes <= kMaxPoolBytes) return;
|
if (g_pooledBytes <= kMaxPoolBytes) return;
|
||||||
|
|||||||
@@ -177,6 +177,13 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
// glBindBuffer with a redundant-bind cache for GL_ARRAY_BUFFER.
|
// glBindBuffer with a redundant-bind cache for GL_ARRAY_BUFFER.
|
||||||
void BindBufferId(GLenum target, Uint id);
|
void BindBufferId(GLenum target, Uint id);
|
||||||
void InvalidateArrayBufferBindingCache();
|
void InvalidateArrayBufferBindingCache();
|
||||||
|
// Redundant-bind cache for INDEXED buffer bindings (glBindBufferBase/Range on
|
||||||
|
// GL_UNIFORM_BUFFER / GL_SHADER_STORAGE_BUFFER): skips the GL call when the
|
||||||
|
// (id, range) already at that index matches, like the array-buffer/texture/
|
||||||
|
// sampler caches already do. Invalidated on MakeCurrent (context may reset).
|
||||||
|
void BindBufferBaseCached(GLenum glTarget, Uint index, Uint id);
|
||||||
|
void BindBufferRangeCached(GLenum glTarget, Uint index, Uint id, GLintptr offset, GLsizeiptr size);
|
||||||
|
void InvalidateIndexedBufferBindingCache();
|
||||||
// Buffer-storage pool maintenance. TrimBufferPool evicts over-budget entries
|
// Buffer-storage pool maintenance. TrimBufferPool evicts over-budget entries
|
||||||
// (called once per frame from Present); ClearBufferPool drops all pooled ids
|
// (called once per frame from Present); ClearBufferPool drops all pooled ids
|
||||||
// without glDeleteBuffers (called when the ES context is going away).
|
// without glDeleteBuffers (called when the ES context is going away).
|
||||||
|
|||||||
Reference in New Issue
Block a user