diff --git a/MobileGL/MG_Backend/BackendObject.h b/MobileGL/MG_Backend/BackendObject.h index aa542cc1..7495424d 100644 --- a/MobileGL/MG_Backend/BackendObject.h +++ b/MobileGL/MG_Backend/BackendObject.h @@ -10,7 +10,7 @@ #include #include "MG_State/GLState/TextureState/TextureEnum.h" #include -#if MOBILEGL_BUILD_DISAGGREGATED +#if MOBILEGL_BUILD_RECORD_ARM #include #endif @@ -37,19 +37,19 @@ namespace MobileGL { struct CopyImageEndpoint { SharedPtr Texture; SharedPtr Renderbuffer; -#if MOBILEGL_BUILD_DISAGGREGATED +#if MOBILEGL_BUILD_RECORD_ARM MG_Pipe::MGPipeHandle TextureHandle = MG_Pipe::kMGPipeNullHandle; MG_Pipe::MGPipeHandle RenderbufferHandle = MG_Pipe::kMGPipeNullHandle; #endif Bool IsRenderbuffer() const { -#if MOBILEGL_BUILD_DISAGGREGATED +#if MOBILEGL_BUILD_RECORD_ARM if (!MG_Pipe::MGPipeHandleIsNull(RenderbufferHandle)) return true; #endif return Renderbuffer != nullptr; } Bool Exists() const { -#if MOBILEGL_BUILD_DISAGGREGATED +#if MOBILEGL_BUILD_RECORD_ARM if (!MG_Pipe::MGPipeHandleIsNull(TextureHandle)) return true; if (!MG_Pipe::MGPipeHandleIsNull(RenderbufferHandle)) return true; #endif diff --git a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp index ccea2e72..494daffa 100644 --- a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp @@ -1180,7 +1180,7 @@ namespace MobileGL::MG_Backend::DirectGLES { ZoneScopedC(TRACY_ZONECOLOR_BACKEND); #endif ProcessDeferredBufferReleases(); -#if MOBILEGL_BUILD_RECORD_ARM +#if MOBILEGL_BUILD_DISAGGREGATED ProcessDeferredT0Retires(); // P11 B2 #endif @@ -1426,7 +1426,7 @@ namespace MobileGL::MG_Backend::DirectGLES { ZoneScopedC(TRACY_ZONECOLOR_BACKEND); #endif ProcessDeferredBufferReleases(); -#if MOBILEGL_BUILD_RECORD_ARM +#if MOBILEGL_BUILD_DISAGGREGATED ProcessDeferredT0Retires(); // P11 B2 #endif // THE COMPUTE PATH IS THE ONE THAT NEEDS THE FRONTEND-INDEXED UNIFORM PASS, and it diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.cpp b/MobileGL/MG_Backend/DirectGLES/Managers.cpp index 77ccd23f..ec34ad02 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.cpp +++ b/MobileGL/MG_Backend/DirectGLES/Managers.cpp @@ -1254,7 +1254,7 @@ namespace MobileGL::MG_Backend::DirectGLES { return StorageMatchesSize(resource, bufferObject.GetSize()); } -#if MOBILEGL_BUILD_DISAGGREGATED +#if MOBILEGL_BUILD_RECORD_ARM // ------------------------------------------------------------------------------- // R-11 - THE SERVER'S OWN COPY OF THE STAGED BYTES. Package v1. // @@ -1302,7 +1302,9 @@ namespace MobileGL::MG_Backend::DirectGLES { ServerStaged().RequireCoverage(&resource, hostBase, start, end, site); } } +#endif // MOBILEGL_BUILD_RECORD_ARM +#if MOBILEGL_BUILD_DISAGGREGATED // ------------------------------------------------------------------------------- // P11 B2 (T0) - WHEN AN IMPORTED STORE'S AHardwareBuffer MAY BE LET GO. // @@ -1373,6 +1375,9 @@ namespace MobileGL::MG_Backend::DirectGLES { g_t0Retired.push_back(retired); SweepT0Retired(false); } +#else + // No transport, no imported store: nothing ever needs retiring (P13 W5). + inline void RetireT0Import(GLESBufferResource&) {} #endif // MOBILEGL_BUILD_DISAGGREGATED // The four hostBytes sites read the same in both builds. The non-split expansion is the @@ -3809,7 +3814,7 @@ namespace MobileGL::MG_Backend::DirectGLES { // generation, and a shadow that outlived its twin would be looked up by a RECYCLED // address on the next allocation - which is the quietest possible wrong answer. MGL_SERVER_STAGED_DROP_ALL(); -#if MOBILEGL_BUILD_DISAGGREGATED +#if MOBILEGL_BUILD_RECORD_ARM // tx's texture shadows die for the same reason, keyed by handle rather than address // but with the same recycled-identity failure mode: a new context's allocator may // hand out a {slot, gen} the old one's store still answers for. @@ -3817,8 +3822,10 @@ namespace MobileGL::MG_Backend::DirectGLES { // P11 B2: every retired T0 import's fence named work on the context that just ended, // so all of them let go now. LIVE imports keep their reference: the next ensure // imports them again on the new context (EnsureBufferResourceForHandle). +#if MOBILEGL_BUILD_DISAGGREGATED ProcessDeferredT0Retires(); SweepT0Retired(/*contextGone=*/true); +#endif #endif } @@ -3854,7 +3861,7 @@ namespace MobileGL::MG_Backend::DirectGLES { } } -#if MOBILEGL_BUILD_RECORD_ARM +#if MOBILEGL_BUILD_DISAGGREGATED // P11 B2: the T0 half of the deferred-release drain, run right after it at both sync // points (ProcessDeferredBufferReleases itself moves verbatim, G5). Retired imports whose // fence signalled let go of their AHardwareBuffer; a deferred T0 destroy is retired once diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.h b/MobileGL/MG_Backend/DirectGLES/Managers.h index acb5b8a6..c7cfd0b0 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.h +++ b/MobileGL/MG_Backend/DirectGLES/Managers.h @@ -476,7 +476,7 @@ namespace MobileGL::MG_Backend::DirectGLES { if (!EsprytSlotTablesEnabled()) return nullptr; if (MG_Pipe::MGPipeHandleIsNull(handle)) return nullptr; if (handle.Slot >= SlotTable::kMaxHandleSlot) { -#if MOBILEGL_BUILD_DISAGGREGATED +#if MOBILEGL_BUILD_RECORD_ARM MG_Pipe::MGPipeSessionFail( // @Ph-declined (ID-P7-1): PH-2 stays Fatal, CONTRACT-P7 §12 MG_Pipe::MGPipeFatalFamily::ProtocolCorruption, "MGPipe: Fatal{ProtocolCorruption, \"BackendSlotTable.HandleSlot\"} - " @@ -488,7 +488,7 @@ namespace MobileGL::MG_Backend::DirectGLES { } const Uint32 liveGen = m_slotTables.LiveGenAt(handle.Slot); if (liveGen != 0 && liveGen > handle.Gen) { -#if MOBILEGL_BUILD_DISAGGREGATED +#if MOBILEGL_BUILD_RECORD_ARM MG_Pipe::MGPipeSessionFail( // @Ph-declined (ID-P7-1): PH-2 stays Fatal, CONTRACT-P7 §12 MG_Pipe::MGPipeFatalFamily::ProtocolCorruption, "MGPipe: Fatal{ProtocolCorruption, \"BackendSlotTable.Generation\"} - " @@ -938,7 +938,7 @@ namespace MobileGL::MG_Backend::DirectGLES { // AcquirePersistentMap or (FLUSH_EXPLICIT) publish only via FlushMappedRange. Uint64 CurrentBufferMutationEpoch(); void BumpBufferMutationEpoch(); -#if MOBILEGL_BUILD_DISAGGREGATED +#if MOBILEGL_BUILD_RECORD_ARM // P5e (vi), CONTRACT-P5E §5.1's first pin: how many bumps came from a thread other than // the apply thread WHILE an apply thread was running, under a live transport. The // record arm's clean gate stamps this counter and skips its probes while the stamp @@ -1071,7 +1071,7 @@ namespace MobileGL::MG_Backend::DirectGLES { // the legacy arm did; this member exists for the drains that have no object, which // in P3a is the readback flush and the fp64 narrowing. const Uint8* hostBytes = nullptr; -#if MOBILEGL_BUILD_DISAGGREGATED +#if MOBILEGL_BUILD_RECORD_ARM // P11 B2 (T0): the client's AHardwareBuffer this store IS - imported with // glBufferStorageExternalEXT, so `id` is immutable, persistentMapped and // persistentPtr is this server's own coherent map of the client's pages. The @@ -1366,7 +1366,7 @@ namespace MobileGL::MG_Backend::DirectGLES { // Deletes GL buffers whose owning frontend objects died (possibly on a // thread without a current ES context). Called from draw-time sync. void ProcessDeferredBufferReleases(); -#if MOBILEGL_BUILD_RECORD_ARM +#if MOBILEGL_BUILD_DISAGGREGATED // P11 B2: the T0 half of that drain - run right after it (retired AHardwareBuffers). void ProcessDeferredT0Retires(); #endif @@ -3670,7 +3670,7 @@ namespace MobileGL::MG_Backend::DirectGLES { g_backendRenderbufferObjects; } // namespace RenderbufferImpl -#if MOBILEGL_BUILD_DISAGGREGATED +#if MOBILEGL_BUILD_RECORD_ARM // P5c (ct), CONTRACT-P5C.md §5.2: object_death's per-kind release, one entry point for all // seven kinds for the same reason the notice switch is one - the answer is the same for // all of them: every holder of the kind's twin table lets go of the twin at this handle. diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.h index 7cb7320d..63abf4eb 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.h @@ -215,7 +215,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { Uint32 cursor = 0; }; -#if MOBILEGL_BUILD_DISAGGREGATED +#if MOBILEGL_BUILD_RECORD_ARM // ResolveWireImageDescriptor emits no pNext chain. Key every view-create // value explicitly: hash collisions must never alias different windows, // formats or swizzles. The root handle and allocation epoch also prevent @@ -563,7 +563,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { VkBuffer uboBuffer = VK_NULL_HANDLE; VkDeviceSize uboRange = 0; VkDescriptorSet set = VK_NULL_HANDLE; -#if MOBILEGL_BUILD_DISAGGREGATED +#if MOBILEGL_BUILD_RECORD_ARM Uint64 wireStoreDestroyEpoch = 0; #endif }; diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferObject.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferObject.h index 9b6ce8c4..e8a6291f 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferObject.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferObject.h @@ -90,7 +90,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { VmaAllocation m_allocation = nullptr; void* m_mappedData = nullptr; VkDeviceSize m_size = 0; -#if MOBILEGL_BUILD_DISAGGREGATED +#if MOBILEGL_BUILD_RECORD_ARM VkDevice m_device = VK_NULL_HANDLE; VkDeviceMemory m_externalMemory = VK_NULL_HANDLE; void* m_externalAhb = nullptr; diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.h index 8b6c1779..a163a8a6 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.h @@ -604,7 +604,7 @@ public: TextureResource* SyncTextureAndGetDescriptor( MG_State::GLState::ITextureObject& texture); -#if MOBILEGL_BUILD_DISAGGREGATED +#if MOBILEGL_BUILD_RECORD_ARM // P5f (fm): THE HANDLE-KEYED ARM of SyncTextureAndGetDescriptor. Under an active transport // the apply thread may not name the client's ITextureObject (rule E), so the clear / blit / // readback / mipmap verbs resolve their attachment textures from the wire handle the @@ -856,7 +856,7 @@ private: void EraseTrackedTexture(const TextureIdentity& identity); void PruneStaleTextureAliases(MG_State::GLState::ITextureObject* texture); SizeT PruneDeadTextures(); -#if MOBILEGL_BUILD_DISAGGREGATED +#if MOBILEGL_BUILD_RECORD_ARM // The two halves of SyncTextureResourceByHandle: shape from the resource record's // descriptor (create / recreate / compatibility), then the pending-upload walk against the // server's staged store. UploadPendingWireLevels mutates the applier's record (consumed diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h index 94c3c125..0f3975d4 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h @@ -22,11 +22,13 @@ #include "VkSamplerManager.h" #include "VkTextureManager.h" #include "VkTimerQueryManager.h" -#if MOBILEGL_BUILD_DISAGGREGATED +#if MOBILEGL_BUILD_RECORD_ARM #include "GpuProgressMarkers.h" #include "WireRenderPassCompatibility.h" +#if MOBILEGL_BUILD_DISAGGREGATED #include #endif +#endif #include "MG_Util/Math/VectorTypes.h" #include #include @@ -148,7 +150,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { // of the swapchain image acquired for it (SwapchainObject::BufferAgeOf). Asking makes the // target keep its presented images' content from then on. Int32 CurrentDrawBufferAge(); -#if MOBILEGL_BUILD_DISAGGREGATED +#if MOBILEGL_BUILD_RECORD_ARM VkBufferManager& GetWireBufferManager() { return m_bufferManager; } Bool FlushWirePendingCommandsForTextureUpdate() { // A new texture upload must not race graphics work that samples @@ -629,7 +631,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { // See NoteDeviceLoss. Set once, never cleared: a lost VkDevice stays lost. Bool m_deviceLost = false; Bool m_deviceLossLatched = false; -#if MOBILEGL_BUILD_DISAGGREGATED +#if MOBILEGL_BUILD_RECORD_ARM // Brackets this device's frame submissions for the GPU hang watch (LatchIfGpuHung). GpuProgressMarkers m_progressMarkers; struct WireImage { @@ -777,7 +779,9 @@ namespace MobileGL::MG_Backend::DirectVulkan { Uint64 acquireSubmit = 0; }; Vector m_sharedImagesHeld; +#if MOBILEGL_BUILD_DISAGGREGATED MG_Remote::Server::SharedImages::ReadTracker m_sharedImageReads; +#endif Vector m_sharedImageUseScratch; // What one vkQueueSubmit carries beyond its own semaphores (AttachSharedImageSync). struct SharedImageSubmitSync { diff --git a/MobileGL/MG_Impl/Pipe/SlotAllocator.h b/MobileGL/MG_Impl/Pipe/SlotAllocator.h index d191c013..f4c82564 100644 --- a/MobileGL/MG_Impl/Pipe/SlotAllocator.h +++ b/MobileGL/MG_Impl/Pipe/SlotAllocator.h @@ -211,5 +211,19 @@ namespace MobileGL::MG_Pipe { MGPipeFrontendKeyedRegistryScope& operator=(const MGPipeFrontendKeyedRegistryScope&) = delete; }; +#else + // P13 W5: no transport, no apply thread - the role guards have nothing to refuse, and the two + // scopes nothing to mark (the record arm calls all of them in every build). + inline void MGPipeRefuseAllocatorFromApplyThread(const char*) {} + inline Bool MGPipeApplierIsUnbarrieredApply() { return false; } + inline void MGPipeRefuseFrontendKeyedRegistryFromApplyThread(const char*) {} + class MagmaP7AllocatorDebtScope { + public: + MagmaP7AllocatorDebtScope() = default; + }; + class MGPipeFrontendKeyedRegistryScope { + public: + MGPipeFrontendKeyedRegistryScope() = default; + }; #endif } // namespace MobileGL::MG_Pipe diff --git a/docs/Disaggregated/notes/p13/tools/s3-ops-1.txt b/docs/Disaggregated/notes/p13/tools/s3-ops-1.txt new file mode 100644 index 00000000..b218fd93 --- /dev/null +++ b/docs/Disaggregated/notes/p13/tools/s3-ops-1.txt @@ -0,0 +1,20 @@ +# P13 W5 S3, round 1: the record arm's declarations (headers). +# Magma renderer: progress markers and the wire render-pass table are the record arm's; the shared-image +# registry is the transport's. +nest | MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h | #include | #include +open | MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h | #include "GpuProgressMarkers.h" +open | MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h | VkBufferManager& GetWireBufferManager() +open | MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h | GpuProgressMarkers m_progressMarkers; +nest | MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h | MG_Remote::Server::SharedImages::ReadTracker m_sharedImageReads; | MG_Remote::Server::SharedImages::ReadTracker m_sharedImageReads; +open | MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.h | Bool ReadUnbackedWireLevel( +open | MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.h | Bool SyncWireTextureShape( +open | MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferObject.h | VkDeviceMemory m_externalMemory +open | MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.h | struct WireImageViewKey { +open | MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.h | Uint64 wireStoreDestroyEpoch = 0; +# Espryt managers: the record-keyed twin tables, the draw-input requests, the buffer twin's record fields, +# and the per-object death release (the monolith port drops its twins through it too). +open | MobileGL/MG_Backend/DirectGLES/Managers.h | "MGPipe: Fatal{ProtocolCorruption, \"BackendSlotTable.HandleSlot\"} - " +open | MobileGL/MG_Backend/DirectGLES/Managers.h | "MGPipe: Fatal{ProtocolCorruption, \"BackendSlotTable.Generation\"} - " +open | MobileGL/MG_Backend/DirectGLES/Managers.h | Uint64 BufferMutationEpochBumpsOffTheApplyThread(); +open | MobileGL/MG_Backend/DirectGLES/Managers.h | Bool serverGpuWritten = false; +open | MobileGL/MG_Backend/DirectGLES/Managers.h | Bool ReleaseTwinsForWireObjectDeath( diff --git a/docs/Disaggregated/notes/p13/tools/w5_regate.py b/docs/Disaggregated/notes/p13/tools/w5_regate.py new file mode 100644 index 00000000..b7badc58 --- /dev/null +++ b/docs/Disaggregated/notes/p13/tools/w5_regate.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python3 +"""P13 W5 S3: hand-directed guard edits, by content rather than line number. + + w5_regate.py + +Each non-comment line of the ops file is one operation (fields separated by ' | '): + open | | the `#if MOBILEGL_BUILD_DISAGGREGATED` group whose first body line + contains becomes `#if MOBILEGL_BUILD_RECORD_ARM` + (with its #else / #endif comments) + nest | | | wrap the lines from the one containing through the one + containing (searched after ) in + `#if MOBILEGL_BUILD_DISAGGREGATED` / `#endif` + line | | | replace the one line containing with verbatim +Every must match exactly one place (for `open`: exactly one group). +""" +import pathlib +import re +import sys + +OLD = 'MOBILEGL_BUILD_DISAGGREGATED' +NEW = 'MOBILEGL_BUILD_RECORD_ARM' +DIRECTIVE = re.compile(r'^\s*#\s*(if|ifdef|ifndef|elif|else|endif)\b(.*)$') + + +def load(path): + raw = pathlib.Path(path).read_bytes().decode('utf-8') + return '\r\n' in raw, raw.replace('\r\n', '\n').split('\n') + + +def save(path, crlf, lines): + text = '\n'.join(lines) + pathlib.Path(path).write_bytes((text.replace('\n', '\r\n') if crlf else text).encode('utf-8')) + + +def groups(lines): + stack, out = [], [] + for i, line in enumerate(lines): + m = DIRECTIVE.match(line) + if not m: + continue + kind = m.group(1) + if kind in ('if', 'ifdef', 'ifndef'): + stack.append({'open': i, 'branches': []}) + elif kind in ('elif', 'else'): + if stack: + stack[-1]['branches'].append(i) + elif stack: + g = stack.pop() + g['close'] = i + out.append(g) + return out + + +def op_open(lines, text): + hits = [] + for g in groups(lines): + if not re.match(r'^\s*#\s*if\s+' + OLD + r'\s*(//.*)?$', lines[g['open']]): + continue + end = g['branches'][0] if g['branches'] else g['close'] + if any(text in l for l in lines[g['open'] + 1:end]): + hits.append(g) + # The innermost group that contains the text: drop any group that contains another hit. + hits = [g for g in hits if not any(h is not g and g['open'] < h['open'] and h['close'] < g['close'] for h in hits)] + if len(hits) != 1: + raise SystemExit('open: %d groups contain %r' % (len(hits), text)) + g = hits[0] + for i in [g['open']] + g['branches'] + [g['close']]: + lines[i] = lines[i].replace(OLD, NEW) + + +def find_one(lines, text, start=0): + idx = [i for i in range(start, len(lines)) if text in lines[i]] + if not idx: + raise SystemExit('no line contains %r' % text) + if start == 0 and len(idx) != 1: + raise SystemExit('%d lines contain %r' % (len(idx), text)) + return idx[0] + + +def main(): + ops = [l for l in pathlib.Path(sys.argv[1]).read_text(encoding='utf-8').splitlines() + if l.strip() and not l.lstrip().startswith('#')] + files = {} + for op in ops: + parts = [p.strip() for p in op.split(' | ')] + kind, path = parts[0], parts[1] + if path not in files: + files[path] = load(path) + crlf, lines = files[path] + if kind == 'open': + op_open(lines, parts[2]) + elif kind == 'nest': + a = find_one(lines, parts[2]) + b = find_one(lines, parts[3], a) + lines[b + 1:b + 1] = ['#endif'] + lines[a:a] = ['#if ' + OLD] + elif kind == 'line': + a = find_one(lines, parts[2]) + lines[a] = parts[3] + else: + raise SystemExit('unknown op ' + kind) + for path, (crlf, lines) in files.items(): + save(path, crlf, lines) + print('applied', len(ops), 'op(s) to', len(files), 'file(s)') + + +if __name__ == '__main__': + main()