mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-13 14:48:32 +09:00
[Refactor] (MG_State, MG_Backend): PipeResource storage layer + zero-copy coherent persistent maps
Introduce a Mesa pipe_resource-style PipeResource that owns a GL buffer's bytes and its backend GPU resource, abstracting WHERE the authoritative bytes live: - Shadow mode (non-persistent buffers): a CPU Vector; the backend keeps its own GPU copy in sync via BufferBackendOps, exactly as before. - Persistent mode (coherent GL_MAP_PERSISTENT maps): the backend's host-visible, COHERENT, persistently-mapped GPU memory is the single source of truth. The app writes into it directly, every reader resolves against it, and NO per-write backend transfer happens. The CPU shadow is released. BufferObject no longer owns a raw shadow Vector; it holds a PipeResource and exposes one accessor, MappedData(), that all readers go through. Every buffer-data consumer (UBO payload, PBO texture upload, indirect draws, resident/streamed uploads, both backends) was migrated from GetDataReadOnly()->data() to MappedData(), so a persistent buffer's readers see GPU memory - not a stale shadow. That stale-shadow inconsistency is what corrupted rendering (wrong UBOs -> misplaced/"lost" vertices) in the first zero-copy attempt (625c8a6, reverted in 896cafc); routing every consumer through one accessor makes it structurally impossible. Backends provide the map via BufferBackendOps::AcquirePersistentMap: - DirectVulkan: a HOST_VISIBLE|HOST_COHERENT (required, not just requested), persistently mapped resident VkBuffer carrying every usage, seeded from the shadow, never recreated; AcquireResidentSlice binds it directly. - DirectGLES: EXT_buffer_storage immutable persistent+coherent glMapBufferRange, falling back to the shadow when the extension is absent. Fixes the ~7GB GpuMemory OOM + 100%-CPU/ANR running modern Blaze3D Minecraft on both Magma and Espryt (per-draw whole-buffer re-upload of the coherent persistent ring buffer), without the coherency/stale-read hazards of the reverted attempt. BufferTest: zero-copy stress guard (15,360 draws -> 0 per-draw transfers, and every reader resolves to GPU memory) + a shadow-fallback test. Host suite: 203/203 pass. Device verification pending.
This commit is contained in:
@@ -49,6 +49,10 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
// Set when an immediate op could not be applied; forces a full re-upload
|
||||
// on the next AcquireResidentSlice.
|
||||
Bool pendingFullUpload = false;
|
||||
// Backs a zero-copy coherent persistent map (PipeResource GPU residency): the
|
||||
// buffer is HOST_VISIBLE+COHERENT, persistently mapped, carries every usage and is
|
||||
// never orphaned or recreated. Draw-time acquire binds it directly, no re-upload.
|
||||
Bool persistentMapped = false;
|
||||
|
||||
// Cached transient (streaming) slice for the current frame.
|
||||
BufferSlice transientSlice{};
|
||||
@@ -95,6 +99,12 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
Bool AcquireStreamedSlice(BufferKind kind, const SharedPtr<MG_State::GLState::BufferObject>& bufferObject,
|
||||
BufferSlice& outSlice);
|
||||
|
||||
// Zero-copy persistent map (PipeResource GPU residency): create (once) a
|
||||
// HOST_VISIBLE+COHERENT, persistently mapped resident buffer carrying every usage,
|
||||
// seed it from the shadow, and return its mapped base for the app to write into
|
||||
// directly. Idempotent. Returns nullptr on failure (frontend keeps its shadow).
|
||||
void* AcquirePersistentMap(MG_State::GLState::BufferObject& bufferObject);
|
||||
|
||||
// Immediate ops, dispatched from the frontend BufferBackendOps table.
|
||||
void OnRespecify(MG_State::GLState::BufferObject& bufferObject);
|
||||
void OnSubData(MG_State::GLState::BufferObject& bufferObject, SizeT offset, SizeT size);
|
||||
@@ -116,7 +126,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
static VkBufferUsageFlags GetVkBufferUsage(BufferKind kind);
|
||||
SharedPtr<VkBufferResource> GetOrCreateResource(const SharedPtr<MG_State::GLState::BufferObject>& bufferObject);
|
||||
static VkBufferResource* ResourceOf(MG_State::GLState::BufferObject& bufferObject);
|
||||
Bool CreateResidentStorage(VkBufferResource& resource, VkDeviceSize size, VkBufferUsageFlags usage);
|
||||
Bool CreateResidentStorage(VkBufferResource& resource, VkDeviceSize size, VkBufferUsageFlags usage,
|
||||
VkMemoryPropertyFlags requiredFlags = 0);
|
||||
// Swap storage (conditional orphan) and refill it from the shadow copy.
|
||||
Bool SwapStorageAndUploadAll(VkBufferResource& resource, MG_State::GLState::BufferObject& bufferObject);
|
||||
// Record a staging-slice copy into the resident storage, ordered against
|
||||
|
||||
Reference in New Issue
Block a user