diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.cpp b/MobileGL/MG_Backend/DirectGLES/Managers.cpp index 82fc9d18..fb90faf6 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.cpp +++ b/MobileGL/MG_Backend/DirectGLES/Managers.cpp @@ -899,8 +899,18 @@ namespace MobileGL::MG_Backend::DirectGLES { // shadow's to rewrite. Widening to page bounds looked free and was // not - the widened bytes clobbered GPU-written data (an SSBO // counter beside the app's SubData) with the stale shadow. + // + // A partial range-invalidating map must not overtake a ring copy into + // this store the GPU has not run yet. Adreno 830 answers such a map on + // a store with a copy still queued by losing the copy: the Embeddium + // section meshes create-instancing staged through the ring came out + // empty (whole terrain sections missing, SSIM 0.870), and routing the + // map ranges behind the copies - the ring tier, GPU-ordered after them - + // brings every one back. The whole-buffer orphan-map is exempt: it + // rewrites every byte from the shadow the queued copies were taken from. const Bool wholeBuffer = start == 0 && end == limit && limit == resource.storageSize; - if (mapUsable && (wholeBuffer || size >= kInvalidateRangeMinBytes)) { + const Bool ringCopyInFlight = resource.ringCopyRetireSerial > DirectGLES::CompletedFrameSerial(); + if (mapUsable && (wholeBuffer || (size >= kInvalidateRangeMinBytes && !ringCopyInFlight))) { BindBufferId(TempBufferTarget, resource.id); const GLbitfield access = GL_MAP_WRITE_BIT | @@ -921,6 +931,8 @@ namespace MobileGL::MG_Backend::DirectGLES { BindBufferId(GL_COPY_WRITE_BUFFER, resource.id); g_GLESFuncs.glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, (GLintptr)ringOffset, (GLintptr)start, (GLsizeiptr)size); + // Commands recorded now retire with the fence the next Present inserts. + resource.ringCopyRetireSerial = DirectGLES::CurrentFrameSerial() + 1; } else { UploadRangeNow(resource, bufferObject, start, end); } diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.h b/MobileGL/MG_Backend/DirectGLES/Managers.h index 8aa931e7..adeb06f1 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.h +++ b/MobileGL/MG_Backend/DirectGLES/Managers.h @@ -478,6 +478,12 @@ namespace MobileGL::MG_Backend::DirectGLES { // (the UBO binding walk) skip the probe while their pre-pass epoch read // matches, exactly like the per-VAO memo stamps. Uint64 drawCleanEpoch = 0; + // Frame serial whose completion retires the last glCopyBufferSubData the upload + // ring staged INTO this store (FlushPendingRangesNow's ring tier); 0 = none. Until + // it retires, FlushPendingRangesNow keeps every partial range off the + // range-invalidating map: on Adreno 830 that map, on a store with a queued copy, + // loses the copy (see FlushPendingRangesNow). Draw thread only, like the flush. + Uint64 ringCopyRetireSerial = 0; // Zero-copy coherent persistent map (EXT_buffer_storage): the GL store is // immutable, persistently+coherently mapped, and persistentPtr is what the app // (and the frontend PipeResource) write into directly. While set, draw-time