mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-13 14:48:32 +09:00
[Perf] (MG_State): remember every dirty rect, not just their union
A Minecraft frame updates ~95 scattered 16x16 sprites in a 1024x512 atlas; MipmapStorage's single union dirty box turned ~95KB of changed texels into a ~2MB upload on every backend. The storage now keeps a bounded (96-slot) list of pairwise-disjoint dirty rects BEHIND the untouched union box: rects cascade-merge on touch or overlap, overflow folds the pair with minimum enlargement and re-cascades, whole-level dirties and respecifies just clear the list (empty list = "union box tells all"). GetDirtyRects hands the list out only when it has 2+ rects, fits the caller's capacity, and its summed area is under 75% of the union box - fewer driver calls beat equal bytes - so consumers can never stage more than the union box did. The list is maintained inside the same four mutation funnels every texel writer already goes through (MarkDirty, MarkDirtyRegion, AllocateLevel, TruncateToLevelCount - callers enumerated at the declaration), so list and union box cannot disagree. Backends OPT IN: the union-box API and its update order are byte-identical, and an unmodified backend keeps rendering exactly as before. 96 slots is measured, not guessed: on the bench's 95-sprite lattice a 16-slot list collapses to >93% of the union box, 96 slots reach 4.8% (~2MB -> ~95KB staged per frame). Verified by a 2859-check fuzz run against a reference dirty bitmap (union exactness, full coverage, disjointness, bounds, profitability). Unit tests 421/421.
This commit is contained in:
@@ -186,6 +186,20 @@ namespace MobileGL::MG_State::GLState {
|
||||
const IntVec3 size = GetMipmapTexelSize(uploadTarget, mipmapLevel);
|
||||
return {IntVec3{0, 0, 0}, IntVec3{size.x(), size.y(), std::max(size.z(), 1)}};
|
||||
}
|
||||
// Scatter detail behind GetStorageDirtyRegion: up to maxRects disjoint rects
|
||||
// that together cover every dirty texel, so ~100 sprite writes in a big atlas
|
||||
// need not be uploaded as one atlas-sized box. Returns how many rects were
|
||||
// written to outRects; 0 means "no list, upload the union box" and is always a
|
||||
// safe answer - this base fallback keeps whole-level semantics for storage
|
||||
// classes that do not track rects, and backends OPT IN by calling this.
|
||||
virtual SizeT GetStorageDirtyRects(TextureUploadTarget uploadTarget, Uint mipmapLevel,
|
||||
MipmapDirtyRegion* outRects, SizeT maxRects) const {
|
||||
(void)uploadTarget;
|
||||
(void)mipmapLevel;
|
||||
(void)outRects;
|
||||
(void)maxRects;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// The compressed image a glCompressedTexImage* call shadowed for this level, kept verbatim
|
||||
// next to the texel data rather than instead of it - see MipmapStorage. The texel shadow
|
||||
@@ -257,6 +271,8 @@ namespace MobileGL::MG_State::GLState {
|
||||
void MarkStorageDirtyRegion(TextureUploadTarget uploadTarget, Uint mipmapLevel, IntVec3 offset,
|
||||
IntVec3 size) override;
|
||||
MipmapDirtyRegion GetStorageDirtyRegion(TextureUploadTarget uploadTarget, Uint mipmapLevel) const override;
|
||||
SizeT GetStorageDirtyRects(TextureUploadTarget uploadTarget, Uint mipmapLevel, MipmapDirtyRegion* outRects,
|
||||
SizeT maxRects) const override;
|
||||
void SetMipmapCompressedImage(TextureUploadTarget uploadTarget, Uint mipmapLevel, GLenum internalFormat,
|
||||
const void* data, SizeT size) override;
|
||||
GLenum GetMipmapCompressedFormat(TextureUploadTarget uploadTarget, Uint mipmapLevel) const override;
|
||||
|
||||
Reference in New Issue
Block a user