mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-09 04:38:30 +09:00
[Fix] (MG_State, DirectGLES): read a shader-written storage buffer back before mapping it
Buffer contents live in a CPU shadow that every read - MapBuffer, MapBufferRange, GetBufferSubData, CopyBufferSubData - resolves against, and backend transfer ops only ever push the shadow outwards. Two paths already knew the GPU can write a buffer on its own and mirrored the result back by hand (ReadPixels into a pixel-pack buffer, the transform feedback capture at EndTransformFeedback); a shader storage buffer written by a draw or a dispatch had no such path at all, so the map handed the application the bytes from before the dispatch. Nothing exercised it until now because GL 3.3 has no compute stage. Every KHR-GL40.texture_gather case ends by dispatching a compute shader that writes its sampled texel into an SSBO and comparing the mapped result, and all 71 read back the zero-filled shadow. Adds the missing direction as a backend op: BufferObject::MarkGpuWritten flags a buffer the GPU may have moved ahead of the shadow, SyncGpuWrites pulls it back at every read point, and DirectGLES implements the readback with a plain read map of the ES buffer. The flag is raised where the storage-buffer points are bound for the upcoming draw or dispatch, which is the last moment the set of exposed buffers is known, and cleared by the readback - so a buffer nothing writes costs one bool test per map. Backends that cannot read their storage back leave the op null and keep today's behaviour; a GPU-resident (coherent persistent) buffer needs nothing, since its reads already resolve against the memory the shader wrote. Drops the texture_gather failures from 71/75 to 25/75 with no crashes left.
This commit is contained in:
@@ -174,6 +174,24 @@ namespace MobileGL::MG_State::GLState {
|
||||
++m_changeSerial;
|
||||
}
|
||||
|
||||
void BufferObject::MarkGpuWritten() {
|
||||
// A GPU-resident buffer has no separate shadow to refresh: reads already resolve
|
||||
// against the coherent map the shader wrote into.
|
||||
if (m_resource.IsGpuResident()) return;
|
||||
m_gpuWritePending = true;
|
||||
}
|
||||
|
||||
void BufferObject::SyncGpuWrites() {
|
||||
if (!m_gpuWritePending) return;
|
||||
// Cleared unconditionally: without a readback op the shadow can never catch up,
|
||||
// and retrying on every subsequent read would only repeat the same no-op.
|
||||
m_gpuWritePending = false;
|
||||
if (m_size == 0 || g_bufferBackendOps == nullptr || g_bufferBackendOps->ReadbackFromGpu == nullptr) {
|
||||
return;
|
||||
}
|
||||
g_bufferBackendOps->ReadbackFromGpu(*this);
|
||||
}
|
||||
|
||||
void BufferObject::UploadSubData(DataPtr data, SizeT atOffset) {
|
||||
MOBILEGL_ASSERT(!m_isMapped || (m_mappingAccess & BufferMappingAccessBit::Persistent),
|
||||
"Cannot upload sub data while buffer is non-persistently mapped.");
|
||||
@@ -204,11 +222,13 @@ namespace MobileGL::MG_State::GLState {
|
||||
"Destination buffer copy out of bounds: dstOffset (%zu) + size (%zu) > m_size (%zu)", dstOffset,
|
||||
size, m_size);
|
||||
|
||||
src->SyncGpuWrites();
|
||||
Memcpy(m_resource.Bytes() + dstOffset, src->m_resource.Bytes() + srcOffset, size);
|
||||
NotifyContentWrite(dstOffset, size);
|
||||
}
|
||||
|
||||
void* BufferObject::AcquireMemory(Bool markMapped, Bool read, Bool write) {
|
||||
SyncGpuWrites();
|
||||
if (markMapped) {
|
||||
m_isMapped = true;
|
||||
m_mappingAccess = (read ? BufferMappingAccessBit::Read : BufferMappingAccessBit::Null) |
|
||||
@@ -250,6 +270,10 @@ namespace MobileGL::MG_State::GLState {
|
||||
MOBILEGL_ASSERT(range.end <= m_size && range.start <= range.end,
|
||||
"AcquireMemoryRange out of bounds: range (%zu, %zu) exceeds m_size (%zu)", range.start,
|
||||
range.end, m_size);
|
||||
// The app is about to look at the bytes; a shader may have rewritten them since
|
||||
// the shadow was last authoritative. Also needed for a write map without an
|
||||
// invalidate bit, whose staging copy is seeded from the shadow.
|
||||
SyncGpuWrites();
|
||||
m_isMapped = true;
|
||||
m_mappingAccess = access;
|
||||
m_mappedRange = range;
|
||||
|
||||
Reference in New Issue
Block a user