mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-12 06:08:30 +09:00
[Perf] (MG_Backend): upload only the rects DirectGLES actually dirtied
Consume MipmapStorage's new dirty-rect list: when a level offers a profitable rect list, the sync path issues one glTexSubImage2D/3D per rect under a single UNPACK_ROW_LENGTH set/reset instead of one call covering the union box. Striding is the exact scheme the single-box path already uses (UNPACK_ALIGNMENT pinned to 1 by ScopedDefaultUnpackState, so every bpp is stride-exact); levels without a profitable list take the old path unchanged. On the atlas-streaming case this trades one ~2MB upload for ~95 small ones totalling ~95KB - roughly a wash in driver-call overhead on desktop NVIDIA GL (mc_tex_stream ~-3%), a clear byte-volume win for tiled/mobile GLES where the driver shadow-copies every upload. Unit tests 421/421.
This commit is contained in:
@@ -2440,10 +2440,45 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
static_cast<SizeT>(dirtyRegion.lo.z()) * levelSliceBytes +
|
static_cast<SizeT>(dirtyRegion.lo.z()) * levelSliceBytes +
|
||||||
static_cast<SizeT>(dirtyRegion.lo.y()) * levelRowBytes +
|
static_cast<SizeT>(dirtyRegion.lo.y()) * levelRowBytes +
|
||||||
static_cast<SizeT>(dirtyRegion.lo.x()) * bpp;
|
static_cast<SizeT>(dirtyRegion.lo.x()) * bpp;
|
||||||
|
// Scatter refinement behind the union box: ~100 sprite
|
||||||
|
// writes into an atlas leave a box that spans nearly the
|
||||||
|
// whole level while the touched texels are a few percent of
|
||||||
|
// it. The storage's bounded rect list recovers the true
|
||||||
|
// footprint; each rect is uploaded with the same
|
||||||
|
// ROW_LENGTH striding into the level shadow as the box
|
||||||
|
// path. The storage only hands the list out when its
|
||||||
|
// summed area is materially smaller than the box (0
|
||||||
|
// otherwise), so the extra calls always move fewer bytes.
|
||||||
|
MG_State::GLState::MipmapDirtyRegion
|
||||||
|
dirtyRects[MG_State::GLState::MipmapStorage::kMaxDirtyRects];
|
||||||
|
SizeT dirtyRectCount = 0;
|
||||||
|
if (subRectEligible) {
|
||||||
|
dirtyRectCount = textureMipmapObject->GetStorageDirtyRects(
|
||||||
|
uploadTarget, level, dirtyRects,
|
||||||
|
MG_State::GLState::MipmapStorage::kMaxDirtyRects);
|
||||||
|
}
|
||||||
|
const auto rectShadowPtr = [&](const MG_State::GLState::MipmapDirtyRegion& rect) {
|
||||||
|
return static_cast<const Uint8*>(uploadData) +
|
||||||
|
static_cast<SizeT>(rect.lo.z()) * levelSliceBytes +
|
||||||
|
static_cast<SizeT>(rect.lo.y()) * levelRowBytes +
|
||||||
|
static_cast<SizeT>(rect.lo.x()) * bpp;
|
||||||
|
};
|
||||||
switch (MapToBackendTextureTarget(stateTextureObject->GetTarget())) {
|
switch (MapToBackendTextureTarget(stateTextureObject->GetTarget())) {
|
||||||
case TextureTarget::Texture2D:
|
case TextureTarget::Texture2D:
|
||||||
case TextureTarget::TextureCubeMap:
|
case TextureTarget::TextureCubeMap:
|
||||||
if (subRectEligible) {
|
if (subRectEligible && dirtyRectCount >= 2) {
|
||||||
|
g_GLESFuncs.glPixelStorei(GL_UNPACK_ROW_LENGTH, texelSize.x());
|
||||||
|
for (SizeT r = 0; r < dirtyRectCount; ++r) {
|
||||||
|
const auto& rect = dirtyRects[r];
|
||||||
|
g_GLESFuncs.glTexSubImage2D(
|
||||||
|
glUploadTarget, static_cast<GLint>(level), rect.lo.x(),
|
||||||
|
rect.lo.y(), static_cast<GLsizei>(rect.hi.x() - rect.lo.x()),
|
||||||
|
static_cast<GLsizei>(rect.hi.y() - rect.lo.y()), glFormat,
|
||||||
|
glType, rectShadowPtr(rect));
|
||||||
|
}
|
||||||
|
// The surrounding ScopedDefaultUnpackState shadow says 0.
|
||||||
|
g_GLESFuncs.glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||||
|
} else if (subRectEligible) {
|
||||||
g_GLESFuncs.glPixelStorei(GL_UNPACK_ROW_LENGTH, texelSize.x());
|
g_GLESFuncs.glPixelStorei(GL_UNPACK_ROW_LENGTH, texelSize.x());
|
||||||
g_GLESFuncs.glTexSubImage2D(
|
g_GLESFuncs.glTexSubImage2D(
|
||||||
glUploadTarget, static_cast<GLint>(level), dirtyRegion.lo.x(),
|
glUploadTarget, static_cast<GLint>(level), dirtyRegion.lo.x(),
|
||||||
@@ -2463,7 +2498,22 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
// ES 3.2 has GL_TEXTURE_CUBE_MAP_ARRAY natively and it stores exactly
|
// ES 3.2 has GL_TEXTURE_CUBE_MAP_ARRAY natively and it stores exactly
|
||||||
// like a 2D array whose depth is 6 * the cube count.
|
// like a 2D array whose depth is 6 * the cube count.
|
||||||
case TextureTarget::TextureCubeMapArray:
|
case TextureTarget::TextureCubeMapArray:
|
||||||
if (subRectEligible) {
|
if (subRectEligible && dirtyRectCount >= 2) {
|
||||||
|
g_GLESFuncs.glPixelStorei(GL_UNPACK_ROW_LENGTH, texelSize.x());
|
||||||
|
g_GLESFuncs.glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, texelSize.y());
|
||||||
|
for (SizeT r = 0; r < dirtyRectCount; ++r) {
|
||||||
|
const auto& rect = dirtyRects[r];
|
||||||
|
g_GLESFuncs.glTexSubImage3D(
|
||||||
|
glUploadTarget, static_cast<GLint>(level), rect.lo.x(),
|
||||||
|
rect.lo.y(), rect.lo.z(),
|
||||||
|
static_cast<GLsizei>(rect.hi.x() - rect.lo.x()),
|
||||||
|
static_cast<GLsizei>(rect.hi.y() - rect.lo.y()),
|
||||||
|
static_cast<GLsizei>(rect.hi.z() - rect.lo.z()), glFormat,
|
||||||
|
glType, rectShadowPtr(rect));
|
||||||
|
}
|
||||||
|
g_GLESFuncs.glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||||
|
g_GLESFuncs.glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, 0);
|
||||||
|
} else if (subRectEligible) {
|
||||||
g_GLESFuncs.glPixelStorei(GL_UNPACK_ROW_LENGTH, texelSize.x());
|
g_GLESFuncs.glPixelStorei(GL_UNPACK_ROW_LENGTH, texelSize.x());
|
||||||
g_GLESFuncs.glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, texelSize.y());
|
g_GLESFuncs.glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, texelSize.y());
|
||||||
g_GLESFuncs.glTexSubImage3D(
|
g_GLESFuncs.glTexSubImage3D(
|
||||||
|
|||||||
Reference in New Issue
Block a user