[Feat|Perf] (*/BufferState): Improve dirty mark for Buffer.

This commit is contained in:
BZLZHH
2026-02-02 02:25:41 +08:00
parent 91650e29c5
commit 0cf9d9223d
41 changed files with 908 additions and 678 deletions
@@ -14,14 +14,18 @@ namespace MobileGL {
namespace GLState {
BufferObject::BufferObject(Uint externalIndex)
: m_externalIndex(externalIndex), m_size(0), m_usage(BufferUsage::StaticDraw), m_isMapped(false),
m_mappingAccess(BufferMappingAccessBit::Null), m_dirtyRange({0, 0}), m_mappedRange({0, 0}),
m_dataPtr(MakeShared<Data>()) {}
m_mappingAccess(BufferMappingAccessBit::Null),
m_change(BufferChangeBits::DirtyBit | BufferChangeBits::PreferReallocationBit), m_mappedRange({0, 0}),
m_dataPtr(MakeShared<Data>()) {
m_change.DirtyRanges.reserve(BufferChange::DEFAULT_RESERVED_DIRTY_RANGES_COUNT);
}
void BufferObject::Resize(SizeT size) {
m_size = size;
m_dataPtr->reserve(std::bit_ceil(size)); // power-of-2 reserve
m_dataPtr->resize(size);
m_dirtyRange = {0, 0};
m_change.Bits |= BufferChangeBits::DirtyBit;
m_change.Bits |= BufferChangeBits::PreferReallocationBit;
}
void BufferObject::UploadData(DataPtr data, SizeT atOffset) {
@@ -30,7 +34,14 @@ namespace MobileGL {
data.size, m_size);
MOBILEGL_ASSERT(!m_isMapped, "Cannot upload data while buffer is mapped.");
Memcpy(m_dataPtr->data() + atOffset, data.data, data.size);
m_dirtyRange.UnionUpdate(atOffset, atOffset + data.size);
m_change.DirtyRanges.Add({atOffset, atOffset + data.size});
m_change.Bits |= BufferChangeBits::DirtyBit;
m_change.Bits |= BufferChangeBits::ForbidInvalidationBit;
m_change.Bits |= BufferChangeBits::ForbidUnsynchronizationBit;
// This function may be called by `glBufferData`, but we still set the forbid bits above,
// because when `PreferReallocationBit` is set, those bits are ignored anyway.
// The bits can fit the `glBufferSubData` semantics
// (though `glBufferSubData` calls `UploadSubData` instead).
}
void BufferObject::SetUsage(BufferUsage usage) {
@@ -44,7 +55,8 @@ namespace MobileGL {
if (!(m_mappingAccess & BufferMappingAccessBit::FlushExplicit)) { // if we didn't flush explicitly
Memcpy(m_dataPtr->data() + m_mappedRange.start, m_stagingData.data(),
m_mappedRange.end - m_mappedRange.start);
m_dirtyRange.UnionUpdate(m_mappedRange.start, m_mappedRange.end);
m_change.DirtyRanges.Add({m_mappedRange.start, m_mappedRange.end});
m_change.Bits |= BufferChangeBits::DirtyBit;
}
m_stagingData.clear();
@@ -69,7 +81,8 @@ namespace MobileGL {
"Flush range out of bounds: mappedRange.end (%zu) < end (%zu)", m_mappedRange.end, end);
Memcpy(m_dataPtr->data() + start, m_stagingData.data() + offset, length);
m_dirtyRange.UnionUpdate(start, end);
m_change.DirtyRanges.Add({start, end});
m_change.Bits |= BufferChangeBits::DirtyBit;
}
void BufferObject::UploadSubData(DataPtr data, SizeT atOffset) {
@@ -79,7 +92,10 @@ namespace MobileGL {
atOffset, data.size, m_size);
Memcpy(m_dataPtr->data() + atOffset, data.data, data.size);
m_dirtyRange.UnionUpdate(atOffset, atOffset + data.size);
m_change.DirtyRanges.Add({atOffset, atOffset + data.size});
m_change.Bits |= BufferChangeBits::DirtyBit;
m_change.Bits |= BufferChangeBits::ForbidInvalidationBit;
m_change.Bits |= BufferChangeBits::ForbidUnsynchronizationBit;
}
void BufferObject::CopyDataFrom(const SharedPtr<BufferObject>& src, SizeT srcOffset, SizeT dstOffset,
@@ -95,7 +111,8 @@ namespace MobileGL {
const Uint8* srcData = src->m_dataPtr->data() + srcOffset;
Memcpy(m_dataPtr->data() + dstOffset, srcData, size);
m_dirtyRange.UnionUpdate(dstOffset, dstOffset + size);
m_change.DirtyRanges.Add({dstOffset, dstOffset + size});
m_change.Bits |= BufferChangeBits::DirtyBit;
}
void* BufferObject::AcquireMemory(Bool markMapped, Bool read, Bool write) {
@@ -144,6 +161,14 @@ namespace MobileGL {
m_ownsStagingData = false;
return m_dataPtr->data() + range.start;
}
m_change.Bits |= !(access & BufferMappingAccessBit::InvalidateBuffer ||
access & BufferMappingAccessBit::InvalidateRange)
? BufferChangeBits::ForbidInvalidationBit
: BufferChangeBits::None;
m_change.Bits |= !(access & BufferMappingAccessBit::Unsynchronized)
? BufferChangeBits::ForbidUnsynchronizationBit
: BufferChangeBits::None;
}
const SharedPtr<Data> BufferObject::GetDataReadOnly() const {
@@ -151,7 +176,8 @@ namespace MobileGL {
}
void BufferObject::ClearDirty() {
m_dirtyRange = {0, 0};
m_change.DirtyRanges.clear();
m_change.Bits = BufferChangeBits::None;
}
SizeT BufferObject::GetSize() const {
@@ -162,8 +188,12 @@ namespace MobileGL {
return m_usage;
}
Range1D BufferObject::GetDirtyRange() const {
return m_dirtyRange;
const VecRange1D& BufferObject::GetDirtyRanges() const {
return m_change.DirtyRanges;
}
Flags<BufferChangeBits> BufferObject::GetChangeBits() const {
return m_change.Bits;
}
Bool BufferObject::IsMapped() const {
@@ -9,6 +9,7 @@
#pragma once
#include "MG_Util/Types.h"
#include <Includes.h>
#include <MG_Util/Math/VectorTypes.h>
namespace MobileGL {
enum class BufferTarget {
@@ -55,6 +56,23 @@ namespace MobileGL {
Coherent = 0x80
};
enum class BufferChangeBits : Uint8 {
None = 0,
DirtyBit = 1 << 0, // When not set, bits below are ignored and nothing should be synced to backend
PreferReallocationBit =
1 << 1, // <=> `glBufferData`; When set, ForbidInvalidationBit and ForbidUnsynchronizationBit are ignored
ForbidInvalidationBit = 1 << 2, // Indidate that invalidation flags were not used during mapping, else we're
// allowed to act as `GL_MAP_INVALIDATE_*` in backend
ForbidUnsynchronizationBit = 1 << 3, // (the same description as above, but for unsynchronization)
};
struct BufferChange {
static constexpr int DEFAULT_RESERVED_DIRTY_RANGES_COUNT = 50;
Flags<BufferChangeBits> Bits = BufferChangeBits::None;
VecRange1D DirtyRanges;
};
namespace MG_State {
namespace GLState {
class BufferObject {
@@ -77,11 +95,12 @@ namespace MobileGL {
Bool IsMapped() const;
SizeT GetSize() const;
BufferUsage GetUsage() const;
Range1D GetDirtyRange() const;
Range1D GetMappedRange() const;
const SharedPtr<Data> GetDataReadOnly() const;
Flags<BufferMappingAccessBit> GetMappingAccess() const;
Uint GetExternalIndex() const;
const VecRange1D& GetDirtyRanges() const;
Flags<BufferChangeBits> GetChangeBits() const;
private:
const Uint m_externalIndex = 0;
@@ -90,7 +109,7 @@ namespace MobileGL {
SharedPtr<Data> m_dataPtr;
Bool m_isMapped;
Flags<BufferMappingAccessBit> m_mappingAccess;
Range1D m_dirtyRange;
BufferChange m_change;
Range1D m_mappedRange;
Vector<Uint8> m_stagingData;
Bool m_ownsStagingData;