From 6c920ce5dc6a9229c5f97726b01aecebf8bcb249 Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Sun, 20 Jul 2025 02:30:53 +0800 Subject: [PATCH] [Feat|Fix] (MG_State/BufferState): Implement more functions. Implemented: BufferObject: ReleaseMemory, FlushMemoryRange, UploadSubData, CopyDataFrom, ClearDirty Fixed: BufferObject: AcquireMemory, AcquireMemoryRange Range1D: UnionUpdate --- .../GLState/BufferState/BufferObject.h | 18 ++- .../GLState/BufferState/BufferState.cpp | 114 ++++++++++++++++-- MobileGL/MG_Util/Types.h | 16 +-- 3 files changed, 121 insertions(+), 27 deletions(-) diff --git a/MobileGL/MG_State/GLState/BufferState/BufferObject.h b/MobileGL/MG_State/GLState/BufferState/BufferObject.h index 4d86ab44..4e366c9e 100644 --- a/MobileGL/MG_State/GLState/BufferState/BufferObject.h +++ b/MobileGL/MG_State/GLState/BufferState/BufferObject.h @@ -66,16 +66,24 @@ namespace MobileGL { class BufferObject { public: using TargetEnum = BufferTarget; + + BufferObject(); void Resize(SizeT size); void UploadData(DataPtr data, SizeT atOffset); void SetUsage(BufferUsage usage); + void* AcquireMemory(Bool markMapped, Bool read, Bool write); + void* AcquireMemoryRange(Range1D range, BufferMappingAccessBit access); + void ReleaseMemory(); + void FlushMemoryRange(SizeT offset, SizeT length); + void UploadSubData(SizeT offset, SizeT size, const void* data); + void CopyDataFrom(const SharedPtr& src, SizeT srcOffset, SizeT dstOffset, SizeT size); + void ClearDirty(); + + Bool IsMapped() const; SizeT GetSize() const; BufferUsage GetUsage() const; Range1D GetDirtyRange() const; - void* AcquireMemory(Bool markMapped, Bool read, Bool write); - void* AcquireMemoryRange(Range1D range, BufferMappingAccessBit access); - Bool IsMapped() const; private: Int m_id = 0; @@ -84,8 +92,10 @@ namespace MobileGL { Data m_data; Bool m_isMapped; BufferMappingAccessBit m_mappingAccess; - // UniquePtr m_dirtyRange; Range1D m_dirtyRange; + Range1D m_mappedRange; + std::vector m_stagingData; + bool m_ownsStagingData; }; } } diff --git a/MobileGL/MG_State/GLState/BufferState/BufferState.cpp b/MobileGL/MG_State/GLState/BufferState/BufferState.cpp index a98931cd..720423cf 100644 --- a/MobileGL/MG_State/GLState/BufferState/BufferState.cpp +++ b/MobileGL/MG_State/GLState/BufferState/BufferState.cpp @@ -4,40 +4,132 @@ namespace MobileGL { namespace MG_State { namespace GLState { // BufferObject + BufferObject::BufferObject() + : m_id(0), m_size(0), m_usage(BufferUsage::StaticDraw), + m_isMapped(false), m_mappingAccess(BufferMappingAccessBit::Null), + m_dirtyRange({ 0, 0 }), m_mappedRange({ 0, 0 }) { + } + void BufferObject::Resize(SizeT size) { m_size = size; - // Always reserve to power-of-2 size to amortize reallocation cost - m_data.reserve(std::bit_ceil(size)); + m_data.reserve(std::bit_ceil(size)); // power-of-2 reserve m_data.resize(size); - // no dirty range == discard all data m_dirtyRange.Update(0, 0); } void BufferObject::UploadData(DataPtr data, SizeT atOffset) { assert(atOffset + data.size <= m_size); - // m_size = data.size; + assert(!m_isMapped); memcpy(m_data.data() + atOffset, data.data, data.size); - // m_dirtyRange = MakeUnique(0, m_size); - m_dirtyRange.UnionUpdate(atOffset, atOffset + data.size); + m_dirtyRange.UnionUpdate(atOffset, atOffset + data.size); + } + + void BufferObject::SetUsage(BufferUsage usage) { + m_usage = usage; + } + + void BufferObject::ReleaseMemory() { + if (!m_isMapped) return; + + if (any(m_mappingAccess & BufferMappingAccessBit::Write)) { // if we wrote to the buffer + if (!any(m_mappingAccess & BufferMappingAccessBit::FlushExplicit)) { // if we didn't flush explicitly + memcpy(m_data.data() + m_mappedRange.start, + m_stagingData.data(), + m_mappedRange.end - m_mappedRange.start); + m_dirtyRange.UnionUpdate(m_mappedRange.start, m_mappedRange.end); + } + + m_stagingData.clear(); + m_stagingData.shrink_to_fit(); + } + + m_isMapped = false; + m_mappingAccess = BufferMappingAccessBit::Null; + m_mappedRange = { 0, 0 }; + m_ownsStagingData = false; + } + + void BufferObject::FlushMemoryRange(SizeT offset, SizeT length) { + assert(m_isMapped); + assert(any(m_mappingAccess & BufferMappingAccessBit::FlushExplicit)); + assert(any(m_mappingAccess & BufferMappingAccessBit::Write)); + + SizeT start = m_mappedRange.start + offset; + SizeT end = start + length; + assert(end <= m_mappedRange.end); + + memcpy(m_data.data() + start, m_stagingData.data() + offset, length); + m_dirtyRange.UnionUpdate(start, end); + } + + void BufferObject::UploadSubData(SizeT offset, SizeT size, const void* data) { + assert(!m_isMapped); + assert(offset + size <= m_size); + + memcpy(m_data.data() + offset, data, size); + m_dirtyRange.UnionUpdate(offset, offset + size); + } + + void BufferObject::CopyDataFrom(const SharedPtr& src, SizeT srcOffset, SizeT dstOffset, SizeT size) { + assert(!m_isMapped); + assert(!src->IsMapped()); + assert(srcOffset + size <= src->GetSize()); + assert(dstOffset + size <= m_size); + + const Uint8* srcData = src->m_data.data() + srcOffset; + memcpy(m_data.data() + dstOffset, srcData, size); + m_dirtyRange.UnionUpdate(dstOffset, dstOffset + size); } void* BufferObject::AcquireMemory(Bool markMapped, Bool read, Bool write) { if (markMapped) { m_isMapped = true; - m_mappingAccess = (read ? BufferMappingAccessBit::Read : BufferMappingAccessBit::Null) | + m_mappingAccess = + (read ? BufferMappingAccessBit::Read : BufferMappingAccessBit::Null) | (write ? BufferMappingAccessBit::Write : BufferMappingAccessBit::Null); + m_mappedRange = { 0, m_size }; + + if (any(m_mappingAccess & BufferMappingAccessBit::Write)) { + m_stagingData.resize(m_size); + m_ownsStagingData = true; + + if (!any(m_mappingAccess & (BufferMappingAccessBit::InvalidateRange | + BufferMappingAccessBit::InvalidateBuffer))) { + memcpy(m_stagingData.data(), m_data.data(), m_size); + } + + return m_stagingData.data(); + } } + return m_data.data(); } void* BufferObject::AcquireMemoryRange(Range1D range, BufferMappingAccessBit access) { + assert(range.end <= m_size && range.start <= range.end); m_isMapped = true; m_mappingAccess = access; - return m_data.data() + range.start; + m_mappedRange = range; + + if (any(access & BufferMappingAccessBit::Write)) { + m_stagingData.resize(range.end - range.start); + m_ownsStagingData = true; + + if (!any(access & (BufferMappingAccessBit::InvalidateRange | + BufferMappingAccessBit::InvalidateBuffer))) { + memcpy(m_stagingData.data(), m_data.data() + range.start, m_stagingData.size()); + } + + return m_stagingData.data(); + } + else { + m_ownsStagingData = false; + return m_data.data() + range.start; + } } - void BufferObject::SetUsage(BufferUsage usage) { - m_usage = usage; + void BufferObject::ClearDirty() { + m_dirtyRange = { 0, 0 }; } SizeT BufferObject::GetSize() const { @@ -78,7 +170,7 @@ namespace MobileGL { } BindingSlot& BufferState::GetBindingSlot(BufferTarget target) { - return m_bindingSlots[(SizeT)target]; + return m_bindingSlots[(SizeT)target]; } } } diff --git a/MobileGL/MG_Util/Types.h b/MobileGL/MG_Util/Types.h index 6edea2f3..a35da81c 100644 --- a/MobileGL/MG_Util/Types.h +++ b/MobileGL/MG_Util/Types.h @@ -73,18 +73,9 @@ namespace MobileGL { // represents a range of [start, end) struct Range1D { - // const SizeT maxEnd; - // const SizeT minStart; SizeT start = 0; SizeT end = 0; - // Range1D(SizeT minStart_, SizeT maxEnd_) - // : minStart(minStart_), maxEnd(maxEnd_), start(minStart_), end(minStart_) { - // if (minStart_ >= maxEnd_) { - // throw RuntimeError("Invalid range: minStart must be less than maxEnd"); - // } - // } - void Update(SizeT newStart, SizeT newEnd) { assert(newStart <= newEnd); start = newStart; @@ -92,10 +83,11 @@ namespace MobileGL { } void UnionUpdate(SizeT newStart, SizeT newEnd) { - // if (newStart < minStart || newEnd > maxEnd) { - // throw RuntimeError("Range exceeds bounds"); - // } assert(newStart <= newEnd); + if (start == end) { + Update(newStart, newEnd); + return; + } start = std::min(start, newStart); end = std::max(end, newEnd); }