mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-11 13:48:30 +09:00
[Merge] (DirectGLES, ShaderTranspiler): land GL43 wave4 with the interface-block rename inside the L2 boundary
This commit is contained in:
@@ -163,7 +163,7 @@ namespace MobileGL::MG_State::GLState {
|
||||
if (!m_resource.IsGpuResident() &&
|
||||
!(m_mappingAccess & BufferMappingAccessBit::FlushExplicit)) { // if we didn't flush explicitly
|
||||
if (!(m_mappingAccess & BufferMappingAccessBit::Persistent)) {
|
||||
Memcpy(m_resource.Bytes() + m_mappedRange.start, m_stagingData.data(),
|
||||
Memcpy(m_resource.Bytes() + m_mappedRange.start, m_stagingData.data() + m_stagingBias,
|
||||
m_mappedRange.end - m_mappedRange.start);
|
||||
}
|
||||
NotifyFlushMappedRange(m_mappedRange, m_mappingAccess);
|
||||
@@ -175,6 +175,7 @@ namespace MobileGL::MG_State::GLState {
|
||||
m_isMapped = false;
|
||||
m_mappingAccess = BufferMappingAccessBit::Null;
|
||||
m_mappedRange = {0, 0};
|
||||
m_stagingBias = 0;
|
||||
m_ownsStagingData = false;
|
||||
}
|
||||
|
||||
@@ -193,7 +194,7 @@ namespace MobileGL::MG_State::GLState {
|
||||
// FLUSH_EXPLICIT maps are never GPU-resident (only coherent maps are adopted), so
|
||||
// the staged bytes must be copied into the shadow before the backend reads them.
|
||||
if (!(m_mappingAccess & BufferMappingAccessBit::Persistent)) {
|
||||
Memcpy(m_resource.Bytes() + start, m_stagingData.data() + offset, length);
|
||||
Memcpy(m_resource.Bytes() + start, m_stagingData.data() + m_stagingBias + offset, length);
|
||||
}
|
||||
NotifyFlushMappedRange({start, end}, m_mappingAccess);
|
||||
}
|
||||
@@ -311,6 +312,9 @@ namespace MobileGL::MG_State::GLState {
|
||||
m_mappedRange = {0, m_size};
|
||||
|
||||
if (m_mappingAccess & BufferMappingAccessBit::Write) {
|
||||
// glMapBuffer maps from offset 0, so no bias: the allocation's own
|
||||
// GL_MIN_MAP_BUFFER_ALIGNMENT-aligned base is what the application must get.
|
||||
m_stagingBias = 0;
|
||||
m_stagingData.resize(m_size);
|
||||
m_ownsStagingData = true;
|
||||
|
||||
@@ -372,14 +376,21 @@ namespace MobileGL::MG_State::GLState {
|
||||
}
|
||||
|
||||
if (access & BufferMappingAccessBit::Write) {
|
||||
m_stagingData.resize(range.end - range.start);
|
||||
// ARB_map_buffer_alignment constrains (returned pointer - offset), not the pointer:
|
||||
// a map at offset 63 must hand back a pointer 63 bytes past the alignment grid, which
|
||||
// is exactly what the read path below gets for free from shadowBase + offset. The
|
||||
// staging store has to be biased by the same phase to match, so it over-allocates by
|
||||
// it and the mapped bytes start at data() + m_stagingBias.
|
||||
m_stagingBias = range.start % MIN_MAP_BUFFER_ALIGNMENT;
|
||||
const SizeT mappedLength = range.end - range.start;
|
||||
m_stagingData.resize(m_stagingBias + mappedLength);
|
||||
m_ownsStagingData = true;
|
||||
|
||||
if (!(access & (BufferMappingAccessBit::InvalidateRange | BufferMappingAccessBit::InvalidateBuffer))) {
|
||||
Memcpy(m_stagingData.data(), m_resource.Bytes() + range.start, m_stagingData.size());
|
||||
Memcpy(m_stagingData.data() + m_stagingBias, m_resource.Bytes() + range.start, mappedLength);
|
||||
}
|
||||
|
||||
return m_stagingData.data();
|
||||
return m_stagingData.data() + m_stagingBias;
|
||||
} else {
|
||||
m_ownsStagingData = false;
|
||||
return m_resource.Bytes() + range.start;
|
||||
@@ -438,7 +449,7 @@ namespace MobileGL::MG_State::GLState {
|
||||
return const_cast<Uint8*>(m_resource.Bytes()) + m_mappedRange.start;
|
||||
}
|
||||
if (m_ownsStagingData) {
|
||||
return const_cast<Uint8*>(m_stagingData.data());
|
||||
return const_cast<Uint8*>(m_stagingData.data()) + m_stagingBias;
|
||||
}
|
||||
return const_cast<Uint8*>(m_resource.Bytes()) + m_mappedRange.start;
|
||||
}
|
||||
|
||||
@@ -239,7 +239,14 @@ namespace MobileGL {
|
||||
// Set by MarkGpuWritten, cleared by SyncGpuWrites once the shadow is refreshed.
|
||||
Bool m_gpuWritePending = false;
|
||||
Range1D m_mappedRange;
|
||||
Vector<Uint8> m_stagingData;
|
||||
// The write-map staging store. MapAlignedData because the application is handed a
|
||||
// pointer into it, and biased by m_stagingBias because ARB_map_buffer_alignment
|
||||
// requires (returned pointer - offset) to be aligned, not the pointer itself: a range
|
||||
// map at offset 63 must hand back a pointer sitting 63 bytes past the alignment grid.
|
||||
// The bias is the offset's phase, so the mapped bytes still start at
|
||||
// m_stagingData.data() + m_stagingBias and the allocation is that much longer.
|
||||
MapAlignedData m_stagingData;
|
||||
SizeT m_stagingBias = 0;
|
||||
Bool m_ownsStagingData;
|
||||
};
|
||||
} // namespace MG_State::GLState
|
||||
|
||||
@@ -10,8 +10,56 @@
|
||||
#include <Includes.h>
|
||||
#include <MG_Util/Types.h>
|
||||
#include <bit>
|
||||
#include <new>
|
||||
#include <vector>
|
||||
|
||||
namespace MobileGL::MG_State::GLState {
|
||||
// GL_MIN_MAP_BUFFER_ALIGNMENT. GL 4.2 / ARB_map_buffer_alignment fix the minimum at 64 and
|
||||
// MobileGL advertises exactly that (MG_Impl/GLImpl/Getter/GL_Getter.cpp reads this constant),
|
||||
// so under-reporting is not available - the implementation has to be brought up to the number
|
||||
// instead. The promise is about POINTERS, not just the query: glMapBuffer must return a
|
||||
// 64-byte-aligned pointer, and glMapBufferRange must return one whose base - the returned
|
||||
// pointer minus the offset the caller asked for - is. Every pointer the frontend hands out
|
||||
// comes from the shadow below or from BufferObject's staging buffer, and std::vector only
|
||||
// promises alignof(std::max_align_t) (16 on aarch64), so both allocations carry the alignment
|
||||
// themselves. One constant for the getter and the allocator, because the two may never
|
||||
// disagree - the same reason the atomic-counter limits are shared through
|
||||
// MG_Util/ShaderTranspiler/Types.h.
|
||||
inline constexpr SizeT MIN_MAP_BUFFER_ALIGNMENT = 64;
|
||||
|
||||
// Allocator that gives every allocation MIN_MAP_BUFFER_ALIGNMENT. Deliberately minimal: the
|
||||
// vectors it backs hold raw bytes and are only ever sized, so allocate/deallocate plus the
|
||||
// rebinding and equality boilerplate std::vector requires is the whole interface.
|
||||
template <typename T>
|
||||
struct MapAlignedAllocator {
|
||||
using value_type = T;
|
||||
|
||||
MapAlignedAllocator() noexcept = default;
|
||||
template <typename U>
|
||||
MapAlignedAllocator(const MapAlignedAllocator<U>&) noexcept {}
|
||||
|
||||
T* allocate(SizeT count) {
|
||||
if (count == 0) return nullptr;
|
||||
return static_cast<T*>(
|
||||
::operator new(count * sizeof(T), std::align_val_t{MIN_MAP_BUFFER_ALIGNMENT}));
|
||||
}
|
||||
void deallocate(T* pointer, SizeT) noexcept {
|
||||
::operator delete(pointer, std::align_val_t{MIN_MAP_BUFFER_ALIGNMENT});
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
Bool operator==(const MapAlignedAllocator<U>&) const noexcept {
|
||||
return true;
|
||||
}
|
||||
template <typename U>
|
||||
Bool operator!=(const MapAlignedAllocator<U>&) const noexcept {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// Byte store for anything the application may end up holding a mapped pointer into.
|
||||
using MapAlignedData = std::vector<Uint8, MapAlignedAllocator<Uint8>>;
|
||||
|
||||
// Opaque, refcounted handle to the backend's GPU storage for one buffer
|
||||
// (the driver-side resource). The active backend derives from it and attaches
|
||||
// its own payload (VkBufferResource / GLESBufferResource). Held by PipeResource.
|
||||
@@ -57,8 +105,8 @@ namespace MobileGL::MG_State::GLState {
|
||||
}
|
||||
// Direct shadow access, used only by the backend's upload-from-shadow path,
|
||||
// which never runs for a GPU-resident (persistent) buffer.
|
||||
Data& Shadow() { return *m_shadow; }
|
||||
const Data& Shadow() const { return *m_shadow; }
|
||||
MapAlignedData& Shadow() { return *m_shadow; }
|
||||
const MapAlignedData& Shadow() const { return *m_shadow; }
|
||||
|
||||
// Transition to persistent GPU residency: adopt the backend's coherent
|
||||
// mapped base as the source of truth and drop the CPU shadow. The caller
|
||||
@@ -85,7 +133,10 @@ namespace MobileGL::MG_State::GLState {
|
||||
SharedPtr<BackendBufferResource> ReleaseBackend() { return std::move(m_backend); }
|
||||
|
||||
private:
|
||||
SharedPtr<Data> m_shadow = MakeShared<Data>();
|
||||
// MapAlignedData, not Data: a read-only glMapBuffer hands the application this very
|
||||
// pointer, and a range map hands it base + offset, so the base has to be on the
|
||||
// GL_MIN_MAP_BUFFER_ALIGNMENT grid for either to satisfy ARB_map_buffer_alignment.
|
||||
SharedPtr<MapAlignedData> m_shadow = MakeShared<MapAlignedData>();
|
||||
void* m_gpuMapped = nullptr;
|
||||
SharedPtr<BackendBufferResource> m_backend;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user