[MG_Backend, CI] (Disaggregated): reclaim abandoned wire objects only after texture uploads are idle

This commit is contained in:
2026-09-20 05:24:25 -04:00
parent 509c056b85
commit 194382c96a
4 changed files with 49 additions and 2 deletions
@@ -3525,6 +3525,16 @@ namespace MobileGL::MG_Backend::DirectVulkan {
return current->mapped + alignedCursor;
}
#if MOBILEGL_BUILD_DISAGGREGATED
Bool VkTextureManager::WireUploadsAreIdle() {
// An open batch still owns references even before it has a fence. Poll
// submitted batches without waiting; reclamation is deferred if any lives.
if (m_uploadBatchOpen) return false;
ReclaimCompletedUploads();
return m_pendingUploadReclaims.empty();
}
#endif
void VkTextureManager::FlushPendingUploads() {
if (!m_uploadBatchOpen) {
return;
@@ -480,6 +480,12 @@ public:
// preserve-on-recreate copy are the existing callers. No-op when the
// batch is empty.
void FlushPendingUploads();
#if MOBILEGL_BUILD_DISAGGREGATED
// Non-blocking idle proof for wire-object retirement. Independent texture
// submissions are not represented by VulkanRenderer's submit counter.
Bool WireUploadsAreIdle();
#endif
// Drains every frame slot's deferred image/view releases. Only valid when
// the caller has proven every queue submission complete; used by the
// present-less frame-boundary drain.
@@ -13089,6 +13089,18 @@ void main() {
return false;
}
#if MOBILEGL_BUILD_DISAGGREGATED
if (MG_Config::Transport != MG_Config::TransportMode::Monolith) {
// Texture uploads submit on this queue with their own fences. The
// renderer watermark alone cannot prove images/views are idle.
if (m_textureManager && !m_textureManager->WireUploadsAreIdle()) return false;
// No preparation, recording or GPU work remains. A minimized Present
// may have abandoned a recording tagged for a submission that will
// never occur, so reclaim those future-tagged objects as well.
CollectWireObjects(m_completedSubmitCounter, true);
}
#endif
// Every submission is complete and nothing recorded references the
// per-frame transients. Pure-reclaim work runs on every drain: it only
// releases memory that is provably dead, never invalidates anything a
+21 -2
View File
@@ -34,6 +34,23 @@ NEGATIVE = f"{PREFIX}1.MagmaRunAheadScenario.QueuedProgramRebindsKeepEachUniform
CLEAR_ASSERTION = "the clear waited for apply instead of returning while its server batch was held"
ERROR = re.compile(r"VUID-[A-Za-z0-9_-]+|SYNC-HAZARD-[A-Z_]+|Validation Error|\bERROR\b", re.I)
LAYER = re.compile(r'Insert instance layer\s+["\']?VK_LAYER_KHRONOS_validation', re.I)
# MobileGL's own diagnostics are not Vulkan-layer messages. Keep them visible
# separately; an embedded validation/VUID/hazard message is never classified away.
APPLICATION_ERROR = re.compile(r"^\[\d{2}:\d{2}:\d{2}(?:\.\d+)?\] \[(?:Linux|Android|Windows|macOS)[^\]]*/ERROR\]:")
VALIDATION_MARKER = re.compile(r"VUID-|SYNC-HAZARD|\bvalidation\b", re.I)
def split_diagnostics(stdout, private):
validation, application = [], []
for line in (stdout + "\n" + private).splitlines():
if not ERROR.search(line):
continue
if APPLICATION_ERROR.search(line) and not VALIDATION_MARKER.search(line):
application.append(line)
else:
validation.append(line)
return validation, application
def write_json(path, data):
@@ -183,9 +200,11 @@ def validation(tests, build, out):
copied = copy.deepcopy(case)
copied.set("name", name) # Raw gtest.xml remains unchanged beside this aggregate.
junit.append(copied)
lines = [line for line in (stdout + "\n" + private).splitlines() if ERROR.search(line)]
lines, application_lines = split_diagnostics(stdout, private)
(directory / "validation-errors.txt").write_text("\n".join(lines), encoding="utf-8")
result.update(validation_error_lines=len(lines), layer_inserted=bool(LAYER.search(stdout + private)))
(directory / "application-errors.txt").write_text("\n".join(application_lines), encoding="utf-8")
result.update(validation_error_lines=len(lines), application_error_lines=len(application_lines),
layer_inserted=bool(LAYER.search(stdout + private)))
if result["returncode"] or failed or not result["private_log_present"]:
raise RuntimeError("GPU case did not complete with a passing result and actual private log")
if not result["layer_inserted"]: