mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-17 08:38:30 +09:00
[Merge] (dev): bring fff9d639 - the DirectVulkan resident-buffer ordering and coherent trace-copy fix for Minecraft 26.3-rc-3 - into feat/disaggregated
This commit is contained in:
@@ -67,6 +67,12 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
}
|
||||
}
|
||||
|
||||
void Ops_ResidentSubData(BufferObject& bufferObject, SizeT offset, DataPtr data) {
|
||||
if (g_activeBufferManager) {
|
||||
g_activeBufferManager->OnResidentSubData(bufferObject, offset, data);
|
||||
}
|
||||
}
|
||||
|
||||
void Ops_FlushMappedRange(BufferObject& bufferObject, Range1D range,
|
||||
Flags<BufferMappingAccessBit> appAccess) {
|
||||
if (g_activeBufferManager) {
|
||||
@@ -78,11 +84,12 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
// host-visible GPU storage (EnsureGpuResidentStorage adopts it when the buffer is
|
||||
// bound as a shader storage buffer), so nothing needs copying - but coherence only
|
||||
// says the writes are visible once they have happened, so the work has to retire
|
||||
// first.
|
||||
// first, including copies already submitted by a sync-point flush.
|
||||
void Ops_ReadbackFromGpu(BufferObject& bufferObject) {
|
||||
(void)bufferObject;
|
||||
if (pVulkanRenderer) {
|
||||
pVulkanRenderer->FinishPendingGpuWork();
|
||||
pVulkanRenderer->WaitForSubmitIndex(
|
||||
pVulkanRenderer->GetSyncPointSubmitIndex(), UINT64_MAX, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,6 +113,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
const BufferBackendOps g_vulkanBufferBackendOps = {
|
||||
.Respecify = Ops_Respecify,
|
||||
.SubData = Ops_SubData,
|
||||
.ResidentSubData = Ops_ResidentSubData,
|
||||
.FlushMappedRange = Ops_FlushMappedRange,
|
||||
.OnDestroy = Ops_OnDestroy,
|
||||
.AcquirePersistentMap = Ops_AcquirePersistentMap,
|
||||
@@ -378,13 +386,13 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
return true;
|
||||
}
|
||||
|
||||
Bool VkBufferManager::StagedRangeCopy(VkBufferResource& resource, MG_State::GLState::BufferObject& bufferObject,
|
||||
Bool VkBufferManager::StagedRangeCopy(VkBufferResource& resource, const void* data,
|
||||
SizeT offset, SizeT size) {
|
||||
if (!m_copyProvider) {
|
||||
return false;
|
||||
}
|
||||
BufferSlice staging{};
|
||||
if (!m_transientUploadArena.Upload(m_currentFrameIndex, bufferObject.MappedData() + offset,
|
||||
if (!m_transientUploadArena.Upload(m_currentFrameIndex, data,
|
||||
static_cast<VkDeviceSize>(size), 16, staging)) {
|
||||
return false;
|
||||
}
|
||||
@@ -499,11 +507,32 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
// Busy partial write: stage + GPU copy preserves GL ordering within the
|
||||
// frame and leaves bytes outside the range (possibly GPU-written, e.g.
|
||||
// SSBO) intact. Fall back to a storage swap if staging is unavailable.
|
||||
if (!StagedRangeCopy(*resource, bufferObject, offset, size)) {
|
||||
if (!StagedRangeCopy(*resource, bufferObject.MappedData() + offset, offset, size)) {
|
||||
SwapStorageAndUploadAll(*resource, bufferObject);
|
||||
}
|
||||
}
|
||||
|
||||
void VkBufferManager::OnResidentSubData(MG_State::GLState::BufferObject& bufferObject,
|
||||
SizeT offset, DataPtr data) {
|
||||
auto* resource = ResourceOf(bufferObject);
|
||||
MOBILEGL_ASSERT(resource && resource->persistentMapped && resource->buffer.IsValid(),
|
||||
"OnResidentSubData requires adopted Vulkan storage");
|
||||
// The mapping is also the GPU's storage. Copy the supplied bytes onto the
|
||||
// command timeline before touching it: earlier draws must keep seeing the
|
||||
// old contents, including draws recorded but not yet submitted. The buffer
|
||||
// cannot be orphaned because the application may hold its mapped pointer.
|
||||
if (StagedRangeCopy(*resource, data.data, offset, data.size)) {
|
||||
return;
|
||||
}
|
||||
// Allocation failure: a host write is safe only after all prior work retires.
|
||||
if (pVulkanRenderer && pVulkanRenderer->WaitForSubmitIndex(
|
||||
pVulkanRenderer->GetSyncPointSubmitIndex(), UINT64_MAX, true)) {
|
||||
resource->buffer.Upload(data.data, data.size, offset);
|
||||
} else {
|
||||
MGLOG_E_ONCE("VkBufferManager::OnResidentSubData: ordered upload failed");
|
||||
}
|
||||
}
|
||||
|
||||
void VkBufferManager::OnFlushMappedRange(MG_State::GLState::BufferObject& bufferObject, Range1D range,
|
||||
Flags<BufferMappingAccessBit> appAccess) {
|
||||
auto* resource = ResourceOf(bufferObject);
|
||||
@@ -536,7 +565,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!StagedRangeCopy(*resource, bufferObject, offset, size)) {
|
||||
if (!StagedRangeCopy(*resource, bufferObject.MappedData() + offset, offset, size)) {
|
||||
SwapStorageAndUploadAll(*resource, bufferObject);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,6 +160,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
// 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);
|
||||
void OnResidentSubData(MG_State::GLState::BufferObject& bufferObject, SizeT offset, DataPtr data);
|
||||
void OnFlushMappedRange(MG_State::GLState::BufferObject& bufferObject, Range1D range,
|
||||
Flags<BufferMappingAccessBit> appAccess);
|
||||
void OnResourceDestroyed(SharedPtr<MG_State::GLState::BackendBufferResource>&& resource);
|
||||
@@ -189,7 +190,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
Bool SwapStorageAndUploadAll(VkBufferResource& resource, MG_State::GLState::BufferObject& bufferObject);
|
||||
// Record a staging-slice copy into the resident storage, ordered against
|
||||
// in-flight and already-recorded GPU work.
|
||||
Bool StagedRangeCopy(VkBufferResource& resource, MG_State::GLState::BufferObject& bufferObject,
|
||||
Bool StagedRangeCopy(VkBufferResource& resource, const void* data,
|
||||
SizeT offset, SizeT size);
|
||||
void DeferRelease(VkBufferObject&& buffer);
|
||||
void CollectDeferredReleases(Uint32 frameIndex);
|
||||
|
||||
+35
@@ -123,6 +123,41 @@ static void mobilegl_capture_record_request(void) {
|
||||
if brace < 0:
|
||||
raise SystemExit("eglSwapBuffers wrapper has no function body")
|
||||
text = text[:brace + 1] + "\n mobilegl_capture_record_request();" + text[brace + 1:]
|
||||
|
||||
# A buffer copy consumes coherent mapped writes just like a draw. Upstream
|
||||
# only commits shadows at draws, so Minecraft's staging-buffer copies could
|
||||
# consume the previous frame's vertices before the current writes landed.
|
||||
for name in ("glCopyBufferSubData", "glCopyNamedBufferSubData", "glNamedCopyBufferSubDataEXT"):
|
||||
start = text.find(f"void APIENTRY {name}(")
|
||||
if start < 0:
|
||||
raise SystemExit(f"generated egltrace.cpp has no {name} wrapper to patch")
|
||||
brace = text.index("{", start)
|
||||
text = (text[:brace + 1] +
|
||||
"\n GLMemoryShadow::commitAllWrites(gltrace::getContext(), trace::fakeMemcpy);" +
|
||||
text[brace + 1:])
|
||||
|
||||
# Deleting a mapped buffer implicitly unmaps it. Retire its shadow before
|
||||
# the real storage is freed; a later draw must not commit to a deleted map,
|
||||
# and reuse of the GL name must not leave a dangling dirtyShadows entry.
|
||||
retire_shadows = r'''
|
||||
auto *_ctx = gltrace::getContext();
|
||||
for (GLsizei i = 0; i < n; ++i) {
|
||||
auto it = _ctx->sharedRes->bufferToShadowMemory.find(buffers[i]);
|
||||
if (it != _ctx->sharedRes->bufferToShadowMemory.end()) {
|
||||
if (it->second->getMapFlags() != 0) {
|
||||
it->second->unmap(trace::fakeMemcpy);
|
||||
}
|
||||
_ctx->sharedRes->bufferToShadowMemory.erase(it);
|
||||
}
|
||||
}
|
||||
'''
|
||||
for name in ("glDeleteBuffers", "glDeleteBuffersARB"):
|
||||
start = text.find(f"void APIENTRY {name}(")
|
||||
if start < 0:
|
||||
raise SystemExit(f"generated egltrace.cpp has no {name} wrapper to patch")
|
||||
brace = text.index("{", start)
|
||||
text = text[:brace + 1] + retire_shadows + text[brace + 1:]
|
||||
|
||||
generated.write_text(text, encoding="utf-8", newline="\n")
|
||||
return generated
|
||||
|
||||
|
||||
Reference in New Issue
Block a user