Subject: [PATCH 11/24] wip: execute tiled test modesets

From: Justin Youens <[email-masked]>
Date: Sat, 15 Aug 2026 09:42:52 -0500

---
 src/backend/drm/TiledAtomic.cpp | 290 +++++++++++++++++++++++++++-----
 src/backend/drm/TiledAtomic.hpp | 131 ++++++++++++---
 tests/TiledAtomic.cpp           | 145 ++++++++++++++--
 3 files changed, 489 insertions(+), 77 deletions(-)

diff --git a/src/backend/drm/TiledAtomic.cpp b/src/backend/drm/TiledAtomic.cpp
index 5c03a8ea4b458034cab8d50cc9ac3ececaefb1f7..531084848298c8fb4e66e689bac577f39dc0c321 100644
--- a/src/backend/drm/TiledAtomic.cpp
+++ b/src/backend/drm/TiledAtomic.cpp
@@ -2,6 +2,7 @@
 
 #include <algorithm>
 #include <array>
+#include <exception>
 #include <ranges>
 #include <tuple>
 #include <utility>
@@ -65,27 +66,36 @@ namespace {
         return result;
     }
 
-    void cleanupRequest(const CSharedPointer<ITiledAtomicRequestOperations>& operations, int drmFD, drmModeAtomicReqPtr& request, std::vector<uint32_t>& modeBlobs) noexcept {
+    void freeRequest(const CSharedPointer<ITiledAtomicFrameOperations>& operations, drmModeAtomicReqPtr& request) noexcept {
         if (request && operations)
             operations->freeRequest(request);
         request = nullptr;
-        if (operations && drmFD >= 0) {
-            for (auto BLOB = modeBlobs.rbegin(); BLOB != modeBlobs.rend(); ++BLOB) {
-                if (*BLOB != 0)
-                    static_cast<void>(operations->destroyModeBlob(drmFD, *BLOB));
-            }
-        }
-        modeBlobs.clear();
     }
 
-    struct SBuildResources {
-        CSharedPointer<ITiledAtomicRequestOperations> operations;
-        int                                           drmFD   = -1;
-        drmModeAtomicReqPtr                           request = nullptr;
-        std::vector<uint32_t>                         modeBlobs;
+    class CLibdrmTiledAtomicOperations final : public ITiledAtomicRequestOperations {
+      public:
+        drmModeAtomicReqPtr allocateRequest() noexcept override {
+            return drmModeAtomicAlloc();
+        }
 
-        ~SBuildResources() noexcept {
-            cleanupRequest(operations, drmFD, request, modeBlobs);
+        void freeRequest(drmModeAtomicReqPtr request) noexcept override {
+            drmModeAtomicFree(request);
+        }
+
+        int addProperty(drmModeAtomicReqPtr request, uint32_t objectId, uint32_t propertyId, uint64_t value) noexcept override {
+            return drmModeAtomicAddProperty(request, objectId, propertyId, value);
+        }
+
+        int submit(int drmFD, drmModeAtomicReqPtr request, uint32_t flags, void* userData) noexcept override {
+            return drmModeAtomicCommit(drmFD, request, flags, userData);
+        }
+
+        int createModeBlob(int drmFD, const drmModeModeInfo& mode, uint32_t* blobId) noexcept override {
+            return drmModeCreatePropertyBlob(drmFD, &mode, sizeof(mode), blobId);
+        }
+
+        int destroyModeBlob(int drmFD, uint32_t blobId) noexcept override {
+            return drmModeDestroyPropertyBlob(drmFD, blobId);
         }
     };
 
@@ -119,9 +129,7 @@ namespace {
     }
 
     void cleanupFrameRequest(const CSharedPointer<ITiledAtomicFrameOperations>& operations, drmModeAtomicReqPtr& request) noexcept {
-        if (request && operations)
-            operations->freeRequest(request);
-        request = nullptr;
+        freeRequest(operations, request);
     }
 
     struct SFrameBuildResources {
@@ -156,6 +164,50 @@ namespace {
 }
 
 namespace Aquamarine::NDRM::NTiledAtomic {
+    class CTiledModeBlobCleanupRecord final {
+      public:
+        CTiledModeBlobCleanupRecord(const CTiledModeBlobCleanupRecord&)            = delete;
+        CTiledModeBlobCleanupRecord(CTiledModeBlobCleanupRecord&&)                 = delete;
+        CTiledModeBlobCleanupRecord& operator=(const CTiledModeBlobCleanupRecord&) = delete;
+        CTiledModeBlobCleanupRecord& operator=(CTiledModeBlobCleanupRecord&&)      = delete;
+
+      private:
+        explicit CTiledModeBlobCleanupRecord(CSharedPointer<ITiledAtomicRequestOperations> operations) noexcept : m_operations(std::move(operations)) {}
+
+        CSharedPointer<ITiledAtomicRequestOperations> m_operations;
+        uint32_t                                      m_blobId = 0;
+
+        friend class CTiledModeBlobCleanupRegistry;
+    };
+
+    struct SBuildResources {
+        CSharedPointer<ITiledAtomicRequestOperations> operations;
+        PModeBlobCleanupRegistry                      cleanupRegistry;
+        drmModeAtomicReqPtr                           request = nullptr;
+        std::vector<uint32_t>                         modeBlobs;
+        std::vector<PModeBlobCleanupRecord>           modeBlobOwners;
+
+        [[nodiscard]] PModeBlobCleanupRecord          reserveModeBlob() noexcept {
+            return cleanupRegistry ? cleanupRegistry->reserve(operations) : PModeBlobCleanupRecord{};
+        }
+
+        [[nodiscard]] bool claimModeBlob(const PModeBlobCleanupRecord& owner, uint32_t blobId) noexcept {
+            return cleanupRegistry && cleanupRegistry->claim(owner, blobId);
+        }
+
+        [[nodiscard]] STiledModeBlobCleanupOutcome cleanup() noexcept {
+            freeRequest(operations, request);
+            const auto OUTCOME = cleanupRegistry ? cleanupRegistry->release(modeBlobOwners) : STiledModeBlobCleanupOutcome{};
+            modeBlobs.clear();
+            modeBlobOwners.clear();
+            return OUTCOME;
+        }
+
+        ~SBuildResources() noexcept {
+            static_cast<void>(cleanup());
+        }
+    };
+
     struct SRouteAssembler {
         static PResolvedTiledAtomicRoute create(POutputBridgeObservation generationOwner, PScanGroupDefinition groupDefinition,
                                                 CSharedPointer<const CGroupDefinition> allocationDefinition, POutputDefinitionIdentity definitionIdentity,
@@ -169,9 +221,10 @@ namespace Aquamarine::NDRM::NTiledAtomic {
     };
 
     struct SRequestAssembler {
-        static PPreparedTiledTestModeset create(PResolvedTiledAtomicRoute route, CSharedPointer<ITiledAtomicRequestOperations> operations, int drmFD, drmModeAtomicReqPtr request,
-                                                std::vector<uint32_t> modeBlobs) {
-            CSharedPointer<CPreparedTiledTestModeset> prepared{new CPreparedTiledTestModeset{std::move(route), std::move(operations), drmFD, request, std::move(modeBlobs)}};
+        static PPreparedTiledTestModeset create(PResolvedTiledAtomicRoute route, CSharedPointer<ITiledAtomicRequestOperations> operations, PModeBlobCleanupRegistry cleanupRegistry,
+                                                drmModeAtomicReqPtr request, std::vector<uint32_t> modeBlobs, std::vector<PModeBlobCleanupRecord> modeBlobOwners) {
+            CSharedPointer<CPreparedTiledTestModeset> prepared{
+                new CPreparedTiledTestModeset{std::move(route), std::move(operations), std::move(cleanupRegistry), request, std::move(modeBlobs), std::move(modeBlobOwners)}};
             return prepared;
         }
     };
@@ -184,6 +237,101 @@ namespace Aquamarine::NDRM::NTiledAtomic {
     };
 }
 
+CSharedPointer<ITiledAtomicRequestOperations> Aquamarine::NDRM::NTiledAtomic::createLibdrmTiledAtomicOperations() noexcept {
+    try {
+        CSharedPointer<CLibdrmTiledAtomicOperations> operations{new CLibdrmTiledAtomicOperations{}};
+        return operations;
+    } catch (...) { return {}; }
+}
+
+bool STiledModeBlobCleanupOutcome::complete() const noexcept {
+    return retained == 0;
+}
+
+CTiledModeBlobCleanupRegistry::CTiledModeBlobCleanupRegistry(int drmFD) noexcept : m_drmFD(drmFD) {}
+
+CTiledModeBlobCleanupRegistry::~CTiledModeBlobCleanupRegistry() noexcept {
+    if (m_abandoned)
+        return;
+    if (!retryPending().complete())
+        std::terminate();
+}
+
+PModeBlobCleanupRegistry CTiledModeBlobCleanupRegistry::create(int drmFD) noexcept {
+    if (drmFD < 0)
+        return {};
+    try {
+        return PModeBlobCleanupRegistry{new CTiledModeBlobCleanupRegistry{drmFD}};
+    } catch (...) { return {}; }
+}
+
+int CTiledModeBlobCleanupRegistry::drmFD() const noexcept {
+    return m_drmFD;
+}
+
+size_t CTiledModeBlobCleanupRegistry::pendingCount() const noexcept {
+    return std::ranges::count_if(m_records, [](const auto& record) { return record && record->m_blobId != 0; });
+}
+
+PModeBlobCleanupRecord CTiledModeBlobCleanupRegistry::reserve(const CSharedPointer<ITiledAtomicRequestOperations>& operations) noexcept {
+    if (!operations || m_drmFD < 0 || m_abandoned)
+        return {};
+    try {
+        std::erase_if(m_records, [](const auto& record) { return !record || record->m_blobId == 0; });
+        PModeBlobCleanupRecord record{new CTiledModeBlobCleanupRecord{operations}};
+        m_records.push_back(record);
+        return record;
+    } catch (...) { return {}; }
+}
+
+bool CTiledModeBlobCleanupRegistry::claim(const PModeBlobCleanupRecord& record, uint32_t blobId) noexcept {
+    if (!record || blobId == 0 || m_abandoned)
+        return false;
+    const auto RECORD = std::ranges::find_if(m_records, [&](const auto& candidate) { return sameOwner(candidate, record); });
+    if (RECORD == m_records.end())
+        std::terminate();
+    const auto DUPLICATE = std::ranges::find_if(m_records, [&](const auto& candidate) { return candidate && !sameOwner(candidate, record) && candidate->m_blobId == blobId; });
+    if (DUPLICATE != m_records.end())
+        return false;
+    record->m_blobId = blobId;
+    return true;
+}
+
+STiledModeBlobCleanupOutcome CTiledModeBlobCleanupRegistry::release(std::span<const PModeBlobCleanupRecord> records) noexcept {
+    STiledModeBlobCleanupOutcome outcome;
+    if (m_abandoned)
+        return outcome;
+
+    for (const auto& RECORD : std::views::reverse(records)) {
+        if (!RECORD || RECORD->m_blobId == 0)
+            continue;
+        ++outcome.attempted;
+        if (!RECORD->m_operations || RECORD->m_operations->destroyModeBlob(m_drmFD, RECORD->m_blobId) != 0) {
+            ++outcome.retained;
+            continue;
+        }
+        RECORD->m_blobId = 0;
+        ++outcome.destroyed;
+    }
+    return outcome;
+}
+
+STiledModeBlobCleanupOutcome CTiledModeBlobCleanupRegistry::retryPending() noexcept {
+    return release(m_records);
+}
+
+bool CTiledModeBlobCleanupRegistry::abandonPending(eTiledModeBlobAbandonProof proof) noexcept {
+    if (proof != eTiledModeBlobAbandonProof::DRM_FD_CLOSED)
+        return false;
+    m_abandoned = true;
+    for (const auto& RECORD : m_records) {
+        if (RECORD)
+            RECORD->m_blobId = 0;
+    }
+    m_records.clear();
+    return true;
+}
+
 CResolvedTiledAtomicRoute::CResolvedTiledAtomicRoute(POutputBridgeObservation generationOwner, PScanGroupDefinition groupDefinition,
                                                      CSharedPointer<const CGroupDefinition> allocationDefinition, POutputDefinitionIdentity definitionIdentity,
                                                      CSharedPointer<const CModeSet> modeSet, CSharedPointer<SOutputMode> outputMode, STiledAtomicFramebuffer framebuffer,
@@ -382,12 +530,14 @@ STiledAtomicResolveResult Aquamarine::NDRM::NTiledAtomic::resolveCapturedTiledAt
     } catch (...) { return {.error = eTiledAtomicError::ALLOCATION_FAILED}; }
 }
 
-CPreparedTiledTestModeset::CPreparedTiledTestModeset(PResolvedTiledAtomicRoute route, CSharedPointer<ITiledAtomicRequestOperations> operations, int drmFD,
-                                                     drmModeAtomicReqPtr request, std::vector<uint32_t> modeBlobs) noexcept :
-    m_route(std::move(route)), m_operations(std::move(operations)), m_drmFD(drmFD), m_request(request), m_modeBlobs(std::move(modeBlobs)) {}
+CPreparedTiledTestModeset::CPreparedTiledTestModeset(PResolvedTiledAtomicRoute route, CSharedPointer<ITiledAtomicRequestOperations> operations,
+                                                     PModeBlobCleanupRegistry cleanupRegistry, drmModeAtomicReqPtr request, std::vector<uint32_t> modeBlobs,
+                                                     std::vector<PModeBlobCleanupRecord> modeBlobOwners) noexcept :
+    m_route(std::move(route)), m_operations(std::move(operations)), m_cleanupRegistry(std::move(cleanupRegistry)), m_request(request), m_modeBlobs(std::move(modeBlobs)),
+    m_modeBlobOwners(std::move(modeBlobOwners)) {}
 
 CPreparedTiledTestModeset::~CPreparedTiledTestModeset() noexcept {
-    cleanupRequest(m_operations, m_drmFD, m_request, m_modeBlobs);
+    static_cast<void>(cleanup());
 }
 
 const PResolvedTiledAtomicRoute& CPreparedTiledTestModeset::route() const noexcept {
@@ -399,7 +549,7 @@ drmModeAtomicReqPtr CPreparedTiledTestModeset::request() const noexcept {
 }
 
 uint32_t CPreparedTiledTestModeset::flags() const noexcept {
-    return DRM_MODE_ATOMIC_ALLOW_MODESET | DRM_MODE_ATOMIC_TEST_ONLY;
+    return TILED_ATOMIC_TEST_MODESET_FLAGS;
 }
 
 uintptr_t CPreparedTiledTestModeset::userData() const noexcept {
@@ -410,50 +560,110 @@ std::span<const uint32_t> CPreparedTiledTestModeset::modeBlobs() const noexcept
     return m_modeBlobs;
 }
 
+STiledModeBlobCleanupOutcome CPreparedTiledTestModeset::cleanup() noexcept {
+    freeRequest(m_operations, m_request);
+    if (!m_cleanupRegistry && !m_modeBlobOwners.empty())
+        std::terminate();
+    const auto OUTCOME = m_cleanupRegistry ? m_cleanupRegistry->release(m_modeBlobOwners) : STiledModeBlobCleanupOutcome{};
+    m_modeBlobs.clear();
+    m_modeBlobOwners.clear();
+    return OUTCOME;
+}
+
 STiledAtomicBuildResult::operator bool() const noexcept {
     return error == eTiledAtomicError::NONE && prepared;
 }
 
 STiledAtomicBuildResult Aquamarine::NDRM::NTiledAtomic::buildTiledTestModeset(const PResolvedTiledAtomicRoute& route, int drmFD,
-                                                                              const CSharedPointer<ITiledAtomicRequestOperations>& operations) noexcept {
+                                                                              const CSharedPointer<ITiledAtomicRequestOperations>& operations,
+                                                                              const PModeBlobCleanupRegistry&                      cleanupRegistry) noexcept {
     if (!route)
         return {.error = eTiledAtomicError::INVALID_INPUT};
-    if (!operations || drmFD < 0)
+    if (!operations || !cleanupRegistry || drmFD < 0 || cleanupRegistry->drmFD() != drmFD)
         return {.error = eTiledAtomicError::INVALID_OPERATIONS};
 
+    SBuildResources resources{.operations = operations, .cleanupRegistry = cleanupRegistry};
+    const auto      fail = [&](eTiledAtomicError error) {
+        const auto CLEANUP = resources.cleanup();
+        return STiledAtomicBuildResult{
+            .error           = error,
+            .cleanup         = CLEANUP,
+            .retainedCleanup = CLEANUP.retained == 0 ? PModeBlobCleanupRegistry{} : cleanupRegistry,
+        };
+    };
+
     try {
-        SBuildResources resources{.operations = operations, .drmFD = drmFD, .request = operations->allocateRequest()};
+        resources.request = operations->allocateRequest();
         if (!resources.request)
-            return {.error = eTiledAtomicError::REQUEST_ALLOCATION_FAILED};
+            return fail(eTiledAtomicError::REQUEST_ALLOCATION_FAILED);
         resources.modeBlobs.reserve(route->members().size());
+        resources.modeBlobOwners.reserve(route->members().size());
 
         for (const auto& MEMBER : route->members()) {
+            const auto OWNER = resources.reserveModeBlob();
+            if (!OWNER)
+                return fail(eTiledAtomicError::ALLOCATION_FAILED);
+            resources.modeBlobOwners.push_back(OWNER);
+
             uint32_t  blobId = 0;
             const int result = operations->createModeBlob(drmFD, MEMBER.physicalMode, &blobId);
             if (blobId != 0) {
-                // One numeric blob ID has one cleanup owner. A broken provider
-                // returning the same nonzero ID twice is rejected without
-                // scheduling a second destroy for that same kernel object.
-                if (std::ranges::contains(resources.modeBlobs, blobId))
-                    return {.error = eTiledAtomicError::MODE_BLOB_FAILED};
+                // The registry record exists before the create call, so even a
+                // nonzero ID returned with failure already has a cleanup owner.
+                if (!resources.claimModeBlob(OWNER, blobId))
+                    return fail(eTiledAtomicError::MODE_BLOB_FAILED);
                 resources.modeBlobs.push_back(blobId);
             }
             if (result != 0 || blobId == 0)
-                return {.error = eTiledAtomicError::MODE_BLOB_FAILED};
+                return fail(eTiledAtomicError::MODE_BLOB_FAILED);
         }
 
         for (size_t index = 0; index < route->members().size(); ++index) {
             if (!addMember(operations, resources.request, route->members().subspan(index, 1).front(), route->framebuffer().id, resources.modeBlobs.at(index)))
-                return {.error = eTiledAtomicError::PROPERTY_ADD_FAILED};
+                return fail(eTiledAtomicError::PROPERTY_ADD_FAILED);
         }
 
-        auto prepared = SRequestAssembler::create(route, operations, drmFD, resources.request, resources.modeBlobs);
+        auto prepared = SRequestAssembler::create(route, operations, cleanupRegistry, resources.request, resources.modeBlobs, resources.modeBlobOwners);
         if (!prepared)
-            return {.error = eTiledAtomicError::ALLOCATION_FAILED};
+            return fail(eTiledAtomicError::ALLOCATION_FAILED);
         resources.request = nullptr;
         resources.modeBlobs.clear();
+        resources.modeBlobOwners.clear();
         return {.prepared = std::move(prepared), .error = eTiledAtomicError::NONE};
-    } catch (...) { return {.error = eTiledAtomicError::ALLOCATION_FAILED}; }
+    } catch (...) { return fail(eTiledAtomicError::ALLOCATION_FAILED); }
+}
+
+STiledTestModesetExecutionResult::operator bool() const noexcept {
+    return status == eTiledTestModesetStatus::SUCCEEDED;
+}
+
+STiledTestModesetExecutionResult Aquamarine::NDRM::NTiledAtomic::executeTiledTestModeset(const PResolvedTiledAtomicRoute& route, int drmFD,
+                                                                                         const CSharedPointer<ITiledAtomicRequestOperations>& operations,
+                                                                                         const PModeBlobCleanupRegistry&                      cleanupRegistry) noexcept {
+    STiledTestModesetExecutionResult execution;
+    const auto                       BUILT = buildTiledTestModeset(route, drmFD, operations, cleanupRegistry);
+    if (!BUILT) {
+        switch (BUILT.error) {
+            case eTiledAtomicError::INVALID_INPUT: execution.status = eTiledTestModesetStatus::INVALID_INPUT; break;
+            case eTiledAtomicError::INVALID_OPERATIONS: execution.status = eTiledTestModesetStatus::INVALID_OPERATIONS; break;
+            case eTiledAtomicError::REQUEST_ALLOCATION_FAILED: execution.status = eTiledTestModesetStatus::REQUEST_ALLOCATION_FAILED; break;
+            case eTiledAtomicError::MODE_BLOB_FAILED: execution.status = eTiledTestModesetStatus::MODE_BLOB_FAILED; break;
+            case eTiledAtomicError::PROPERTY_ADD_FAILED: execution.status = eTiledTestModesetStatus::PROPERTY_ADD_FAILED; break;
+            default: execution.status = eTiledTestModesetStatus::ALLOCATION_FAILED; break;
+        }
+        execution.cleanup         = BUILT.cleanup;
+        execution.retainedCleanup = BUILT.retainedCleanup;
+        return execution;
+    }
+
+    execution.submission = submitAtomicRequest(
+        drmFD, BUILT.prepared->request(), TILED_ATOMIC_TEST_MODESET_FLAGS, 0,
+        [&operations](int fd, drmModeAtomicReqPtr request, uint32_t flags, void* userData) noexcept { return operations->submit(fd, request, flags, userData); });
+    execution.cleanup = BUILT.prepared->cleanup();
+    if (execution.cleanup.retained != 0)
+        execution.retainedCleanup = cleanupRegistry;
+    execution.status = execution.submission && static_cast<bool>(*execution.submission) ? eTiledTestModesetStatus::SUCCEEDED : eTiledTestModesetStatus::SUBMISSION_FAILED;
+    return execution;
 }
 
 CPreparedTiledFrameRequest::CPreparedTiledFrameRequest(PResolvedTiledAtomicRoute route, CSharedPointer<ITiledAtomicFrameOperations> operations,
diff --git a/src/backend/drm/TiledAtomic.hpp b/src/backend/drm/TiledAtomic.hpp
index a113186cd2504be12cb8ece47b7536fe43e7efd1..decda3473adff7c58364856469204d5054aea3cc 100644
--- a/src/backend/drm/TiledAtomic.hpp
+++ b/src/backend/drm/TiledAtomic.hpp
@@ -204,21 +204,80 @@ namespace Aquamarine::NDRM::NTiledAtomic { // NOLINT(readability-identifier-nami
     // synchronously inside the combined registry/bridge current-owner gate.
     [[nodiscard]] AQ_TILED_ATOMIC_PRIVATE STiledAtomicResolveResult resolveCapturedTiledAtomic(const STiledAtomicResolveInput& input) noexcept;
 
-    // This deliberately lacks any submit operation. The first checkpoint can
-    // construct and inspect a TEST_MODESET request, but cannot issue an ioctl.
-    class AQ_TILED_ATOMIC_PRIVATE ITiledAtomicRequestOperations {
+    class AQ_TILED_ATOMIC_PRIVATE ITiledAtomicFrameOperations {
       public:
-        virtual ~ITiledAtomicRequestOperations() = default;
+        virtual ~ITiledAtomicFrameOperations() = default;
 
         virtual drmModeAtomicReqPtr allocateRequest() noexcept                                                                                = 0;
         virtual void                freeRequest(drmModeAtomicReqPtr request) noexcept                                                         = 0;
         virtual int                 addProperty(drmModeAtomicReqPtr request, uint32_t objectId, uint32_t propertyId, uint64_t value) noexcept = 0;
-        virtual int                 createModeBlob(int drmFD, const drmModeModeInfo& mode, uint32_t* blobId) noexcept                         = 0;
-        virtual int                 destroyModeBlob(int drmFD, uint32_t blobId) noexcept                                                      = 0;
+        virtual int                 submit(int drmFD, drmModeAtomicReqPtr request, uint32_t flags, void* userData) noexcept                   = 0;
+    };
+
+    // TEST_MODESET extends the normal-frame request boundary with temporary
+    // mode-blob ownership. One production implementation can serve both paths.
+    class AQ_TILED_ATOMIC_PRIVATE ITiledAtomicRequestOperations : public ITiledAtomicFrameOperations {
+      public:
+        ~ITiledAtomicRequestOperations() override = default;
+
+        virtual int createModeBlob(int drmFD, const drmModeModeInfo& mode, uint32_t* blobId) noexcept = 0;
+        virtual int destroyModeBlob(int drmFD, uint32_t blobId) noexcept                              = 0;
+    };
+
+    [[nodiscard]] AQ_TILED_ATOMIC_PRIVATE CSharedPointer<ITiledAtomicRequestOperations> createLibdrmTiledAtomicOperations() noexcept;
+
+    enum class eTiledModeBlobAbandonProof : uint8_t {
+        DRM_FD_CLOSED = 0,
+    };
+
+    struct AQ_TILED_ATOMIC_PRIVATE STiledModeBlobCleanupOutcome {
+        size_t             attempted = 0;
+        size_t             destroyed = 0;
+        size_t             retained  = 0;
+
+        [[nodiscard]] bool complete() const noexcept;
+    };
+
+    class CTiledModeBlobCleanupRecord;
+    using PModeBlobCleanupRecord = CSharedPointer<CTiledModeBlobCleanupRecord>;
+
+    class CTiledModeBlobCleanupRegistry;
+    using PModeBlobCleanupRegistry = CSharedPointer<CTiledModeBlobCleanupRegistry>;
+
+    // One registry is bound to one open DRM fd. A failed destroy remains a
+    // live retry record. Destroying a registry with a still-live record is a
+    // lifecycle error unless the caller explicitly proves that the fd is gone.
+    class AQ_TILED_ATOMIC_PRIVATE CTiledModeBlobCleanupRegistry final {
+      public:
+        CTiledModeBlobCleanupRegistry(const CTiledModeBlobCleanupRegistry&)            = delete;
+        CTiledModeBlobCleanupRegistry(CTiledModeBlobCleanupRegistry&&)                 = delete;
+        CTiledModeBlobCleanupRegistry& operator=(const CTiledModeBlobCleanupRegistry&) = delete;
+        CTiledModeBlobCleanupRegistry& operator=(CTiledModeBlobCleanupRegistry&&)      = delete;
+        ~CTiledModeBlobCleanupRegistry() noexcept;
+
+        [[nodiscard]] static PModeBlobCleanupRegistry create(int drmFD) noexcept;
+        [[nodiscard]] int                             drmFD() const noexcept;
+        [[nodiscard]] size_t                          pendingCount() const noexcept;
+        [[nodiscard]] STiledModeBlobCleanupOutcome    retryPending() noexcept;
+        [[nodiscard]] bool                            abandonPending(eTiledModeBlobAbandonProof proof) noexcept;
+
+      private:
+        explicit CTiledModeBlobCleanupRegistry(int drmFD) noexcept;
+
+        [[nodiscard]] PModeBlobCleanupRecord       reserve(const CSharedPointer<ITiledAtomicRequestOperations>& operations) noexcept;
+        [[nodiscard]] bool                         claim(const PModeBlobCleanupRecord& record, uint32_t blobId) noexcept;
+        [[nodiscard]] STiledModeBlobCleanupOutcome release(std::span<const PModeBlobCleanupRecord> records) noexcept;
+
+        int                                        m_drmFD     = -1;
+        bool                                       m_abandoned = false;
+        std::vector<PModeBlobCleanupRecord>        m_records;
+
+        friend class CPreparedTiledTestModeset;
+        friend struct SBuildResources;
     };
 
     class CPreparedTiledTestModeset;
-    using PPreparedTiledTestModeset = CSharedPointer<const CPreparedTiledTestModeset>;
+    using PPreparedTiledTestModeset = CSharedPointer<CPreparedTiledTestModeset>;
 
     class AQ_TILED_ATOMIC_PRIVATE CPreparedTiledTestModeset final {
       public:
@@ -233,45 +292,65 @@ namespace Aquamarine::NDRM::NTiledAtomic { // NOLINT(readability-identifier-nami
         [[nodiscard]] uint32_t                         flags() const noexcept;
         [[nodiscard]] uintptr_t                        userData() const noexcept;
         [[nodiscard]] std::span<const uint32_t>        modeBlobs() const noexcept;
+        [[nodiscard]] STiledModeBlobCleanupOutcome     cleanup() noexcept;
 
       private:
-        CPreparedTiledTestModeset(PResolvedTiledAtomicRoute route, CSharedPointer<ITiledAtomicRequestOperations> operations, int drmFD, drmModeAtomicReqPtr request,
-                                  std::vector<uint32_t> modeBlobs) noexcept;
+        CPreparedTiledTestModeset(PResolvedTiledAtomicRoute route, CSharedPointer<ITiledAtomicRequestOperations> operations, PModeBlobCleanupRegistry cleanupRegistry,
+                                  drmModeAtomicReqPtr request, std::vector<uint32_t> modeBlobs, std::vector<PModeBlobCleanupRecord> modeBlobOwners) noexcept;
 
         PResolvedTiledAtomicRoute                     m_route;
         CSharedPointer<ITiledAtomicRequestOperations> m_operations;
-        int                                           m_drmFD   = -1;
+        PModeBlobCleanupRegistry                      m_cleanupRegistry;
         drmModeAtomicReqPtr                           m_request = nullptr;
         std::vector<uint32_t>                         m_modeBlobs;
+        std::vector<PModeBlobCleanupRecord>           m_modeBlobOwners;
 
         friend struct SRequestAssembler;
     };
 
     struct AQ_TILED_ATOMIC_PRIVATE STiledAtomicBuildResult {
-        PPreparedTiledTestModeset prepared;
-        eTiledAtomicError         error = eTiledAtomicError::INVALID_INPUT;
+        PPreparedTiledTestModeset    prepared;
+        eTiledAtomicError            error = eTiledAtomicError::INVALID_INPUT;
+        STiledModeBlobCleanupOutcome cleanup;
+        PModeBlobCleanupRegistry     retainedCleanup;
 
-        explicit                  operator bool() const noexcept;
+        explicit                     operator bool() const noexcept;
     };
 
     [[nodiscard]] AQ_TILED_ATOMIC_PRIVATE STiledAtomicBuildResult buildTiledTestModeset(const PResolvedTiledAtomicRoute& route, int drmFD,
-                                                                                        const CSharedPointer<ITiledAtomicRequestOperations>& operations) noexcept;
+                                                                                        const CSharedPointer<ITiledAtomicRequestOperations>& operations,
+                                                                                        const PModeBlobCleanupRegistry&                      cleanupRegistry) noexcept;
 
-    inline constexpr uint32_t                                     TILED_ATOMIC_FRAME_FLAGS = DRM_MODE_ATOMIC_NONBLOCK | DRM_MODE_PAGE_FLIP_EVENT;
+    inline constexpr uint32_t                                     TILED_ATOMIC_TEST_MODESET_FLAGS = DRM_MODE_ATOMIC_ALLOW_MODESET | DRM_MODE_ATOMIC_TEST_ONLY;
 
-    // A separate injected boundary keeps normal-frame submit capability out of
-    // the TEST_MODESET-only operations contract above. There is deliberately no
-    // production implementation or call site in this checkpoint.
-    class AQ_TILED_ATOMIC_PRIVATE ITiledAtomicFrameOperations {
-      public:
-        virtual ~ITiledAtomicFrameOperations() = default;
-
-        virtual drmModeAtomicReqPtr allocateRequest() noexcept                                                                                = 0;
-        virtual void                freeRequest(drmModeAtomicReqPtr request) noexcept                                                         = 0;
-        virtual int                 addProperty(drmModeAtomicReqPtr request, uint32_t objectId, uint32_t propertyId, uint64_t value) noexcept = 0;
-        virtual int                 submit(int drmFD, drmModeAtomicReqPtr request, uint32_t flags, void* userData) noexcept                   = 0;
+    enum class eTiledTestModesetStatus : uint8_t {
+        SUCCEEDED = 0,
+        INVALID_INPUT,
+        INVALID_OPERATIONS,
+        REQUEST_ALLOCATION_FAILED,
+        MODE_BLOB_FAILED,
+        PROPERTY_ADD_FAILED,
+        SUBMISSION_FAILED,
+        ALLOCATION_FAILED,
     };
 
+    struct AQ_TILED_ATOMIC_PRIVATE STiledTestModesetExecutionResult {
+        eTiledTestModesetStatus            status = eTiledTestModesetStatus::INVALID_INPUT;
+        std::optional<SAtomicSubmitResult> submission;
+        STiledModeBlobCleanupOutcome       cleanup;
+        PModeBlobCleanupRegistry           retainedCleanup;
+
+        explicit                           operator bool() const noexcept;
+    };
+
+    // Executes only a synchronous TEST_ONLY modeset. Flags and the null event
+    // token are fixed here; runtime, scheduler, and page-flip state are absent.
+    [[nodiscard]] AQ_TILED_ATOMIC_PRIVATE STiledTestModesetExecutionResult executeTiledTestModeset(const PResolvedTiledAtomicRoute& route, int drmFD,
+                                                                                                   const CSharedPointer<ITiledAtomicRequestOperations>& operations,
+                                                                                                   const PModeBlobCleanupRegistry&                      cleanupRegistry) noexcept;
+
+    inline constexpr uint32_t                                              TILED_ATOMIC_FRAME_FLAGS = DRM_MODE_ATOMIC_NONBLOCK | DRM_MODE_PAGE_FLIP_EVENT;
+
     enum class eTiledFrameTransactionStatus : uint8_t {
         ACCEPTED = 0,
         ACCEPTED_INVARIANT_FAILURE,
diff --git a/tests/TiledAtomic.cpp b/tests/TiledAtomic.cpp
index 933c32ac548f2655f753d5c9d3eee384651dcf6e..a44eface76581ae512decbf72251ba803b22ba97 100644
--- a/tests/TiledAtomic.cpp
+++ b/tests/TiledAtomic.cpp
@@ -269,6 +269,16 @@ namespace {
             } catch (...) { return -1; }
         }
 
+        int submit(int fd, drmModeAtomicReqPtr request, uint32_t flags, void* userData) noexcept override {
+            ++submits;
+            submittedFD       = fd;
+            submittedRequest  = request;
+            submittedFlags    = flags;
+            submittedUserData = userData;
+            errno             = submitErrno;
+            return submitResult;
+        }
+
         int createModeBlob(int, const drmModeModeInfo& mode, uint32_t* blobId) noexcept override {
             if (!blobId)
                 return -1;
@@ -282,18 +292,32 @@ namespace {
 
         int destroyModeBlob(int, uint32_t blobId) noexcept override {
             try {
+                destructionAttempts.push_back(blobId);
+                if (destroyFailuresRemaining != 0) {
+                    --destroyFailuresRemaining;
+                    return -EBUSY;
+                }
                 destroyedBlobs.push_back(blobId);
                 return 0;
             } catch (...) { return -1; }
         }
 
-        size_t                       allocations    = 0;
-        size_t                       frees          = 0;
-        size_t                       failPropertyAt = static_cast<size_t>(-1);
-        bool                         duplicateBlob  = false;
-        uint32_t                     nextBlob       = 900;
+        size_t                       allocations              = 0;
+        size_t                       frees                    = 0;
+        size_t                       submits                  = 0;
+        size_t                       failPropertyAt           = static_cast<size_t>(-1);
+        size_t                       destroyFailuresRemaining = 0;
+        bool                         duplicateBlob            = false;
+        uint32_t                     nextBlob                 = 900;
+        int                          submitResult             = 0;
+        int                          submitErrno              = 0;
+        int                          submittedFD              = -1;
+        drmModeAtomicReqPtr          submittedRequest         = nullptr;
+        uint32_t                     submittedFlags           = 0;
+        void*                        submittedUserData        = nullptr;
         std::vector<SPropertyWrite>  properties;
         std::vector<uint32_t>        createdBlobs;
+        std::vector<uint32_t>        destructionAttempts;
         std::vector<uint32_t>        destroyedBlobs;
         std::vector<drmModeModeInfo> physicalModes;
     };
@@ -543,12 +567,14 @@ int main() {
     foreignIdentityInput.definitionIdentity                      = Hyprutils::Memory::reinterpretPointerCast<const COutputDefinitionIdentity>(identityCarrier);
     CHECK(resolveCapturedTiledAtomic(foreignIdentityInput).error == eTiledAtomicError::FOREIGN_DEFINITION_IDENTITY);
 
-    auto                                          recorder   = makeShared<SRecorder>();
-    CSharedPointer<ITiledAtomicRequestOperations> operations = recorder;
-    auto                                          built      = buildTiledTestModeset(resolved.route, 19, operations);
+    auto                                          recorder        = makeShared<SRecorder>();
+    auto                                          cleanupRegistry = CTiledModeBlobCleanupRegistry::create(19);
+    CSharedPointer<ITiledAtomicRequestOperations> operations      = recorder;
+    CHECK(static_cast<bool>(cleanupRegistry));
+    auto built = buildTiledTestModeset(resolved.route, 19, operations, cleanupRegistry);
     CHECK(static_cast<bool>(built));
     CHECK(built.prepared->request() != nullptr);
-    CHECK(built.prepared->flags() == (DRM_MODE_ATOMIC_ALLOW_MODESET | DRM_MODE_ATOMIC_TEST_ONLY));
+    CHECK(built.prepared->flags() == TILED_ATOMIC_TEST_MODESET_FLAGS);
     CHECK(built.prepared->userData() == 0);
     CHECK(std::ranges::equal(built.prepared->modeBlobs(), recorder->createdBlobs));
     CHECK(recorder->physicalModes.size() == resolved.route->members().size());
@@ -559,28 +585,125 @@ int main() {
     CHECK(recorder->properties == expected);
     CHECK(recorder->allocations == 1);
     CHECK(recorder->frees == 0);
+    const auto builtCleanup = built.prepared->cleanup();
+    CHECK(builtCleanup.attempted == 2);
+    CHECK(builtCleanup.destroyed == 2);
+    CHECK(builtCleanup.retained == 0);
+    CHECK(builtCleanup.complete());
     built.prepared.reset();
     CHECK(recorder->frees == 1);
     CHECK(recorder->destroyedBlobs == std::vector<uint32_t>({901, 900}));
+    CHECK(cleanupRegistry->pendingCount() == 0);
 
     auto failingRecorder                                            = makeShared<SRecorder>();
+    auto failingCleanup                                             = CTiledModeBlobCleanupRegistry::create(19);
     failingRecorder->failPropertyAt                                 = 3;
     CSharedPointer<ITiledAtomicRequestOperations> failingOperations = failingRecorder;
-    const auto                                    failedBuild       = buildTiledTestModeset(resolved.route, 19, failingOperations);
+    const auto                                    failedBuild       = buildTiledTestModeset(resolved.route, 19, failingOperations, failingCleanup);
     CHECK(!static_cast<bool>(failedBuild));
     CHECK(failedBuild.error == eTiledAtomicError::PROPERTY_ADD_FAILED);
+    CHECK(failedBuild.cleanup.attempted == 2);
+    CHECK(failedBuild.cleanup.destroyed == 2);
+    CHECK(failedBuild.cleanup.retained == 0);
     CHECK(failingRecorder->frees == 1);
     CHECK(failingRecorder->destroyedBlobs == std::vector<uint32_t>({901, 900}));
+    CHECK(failingCleanup->pendingCount() == 0);
 
     auto duplicateRecorder                                            = makeShared<SRecorder>();
+    auto duplicateCleanup                                             = CTiledModeBlobCleanupRegistry::create(19);
     duplicateRecorder->duplicateBlob                                  = true;
     CSharedPointer<ITiledAtomicRequestOperations> duplicateOperations = duplicateRecorder;
-    const auto                                    duplicateBuild      = buildTiledTestModeset(resolved.route, 19, duplicateOperations);
+    const auto                                    duplicateBuild      = buildTiledTestModeset(resolved.route, 19, duplicateOperations, duplicateCleanup);
     CHECK(!static_cast<bool>(duplicateBuild));
     CHECK(duplicateBuild.error == eTiledAtomicError::MODE_BLOB_FAILED);
     CHECK(duplicateRecorder->createdBlobs == std::vector<uint32_t>({900, 900}));
     CHECK(duplicateRecorder->frees == 1);
     CHECK(duplicateRecorder->destroyedBlobs == std::vector<uint32_t>({900}));
+    CHECK(duplicateBuild.cleanup.attempted == 1);
+    CHECK(duplicateBuild.cleanup.destroyed == 1);
+    CHECK(duplicateBuild.cleanup.retained == 0);
+    CHECK(duplicateCleanup->pendingCount() == 0);
+
+    static_assert(noexcept(executeTiledTestModeset(std::declval<const PResolvedTiledAtomicRoute&>(), 19, std::declval<const CSharedPointer<ITiledAtomicRequestOperations>&>(),
+                                                   std::declval<const PModeBlobCleanupRegistry&>())));
+
+    // A successful TEST_ONLY submit uses fixed flags and a null event token,
+    // then frees the request and destroys every temporary blob exactly once.
+    {
+        auto                                          successRecorder   = makeShared<SRecorder>();
+        auto                                          successCleanup    = CTiledModeBlobCleanupRegistry::create(19);
+        CSharedPointer<ITiledAtomicRequestOperations> successOperations = successRecorder;
+        const auto                                    success           = executeTiledTestModeset(resolved.route, 19, successOperations, successCleanup);
+        CHECK(static_cast<bool>(success));
+        CHECK(success.status == eTiledTestModesetStatus::SUCCEEDED);
+        CHECK(success.submission.has_value());
+        CHECK(success.submission.value_or(SAtomicSubmitResult{}).returnCode == 0);
+        CHECK(success.submission.value_or(SAtomicSubmitResult{}).error == 0);
+        CHECK(success.cleanup.attempted == 2);
+        CHECK(success.cleanup.destroyed == 2);
+        CHECK(success.cleanup.retained == 0);
+        CHECK(!success.retainedCleanup);
+        CHECK(successRecorder->submits == 1);
+        CHECK(successRecorder->submittedFD == 19);
+        CHECK(successRecorder->submittedFlags == TILED_ATOMIC_TEST_MODESET_FLAGS);
+        CHECK(successRecorder->submittedUserData == nullptr);
+        CHECK(successRecorder->frees == 1);
+        CHECK(successRecorder->destructionAttempts == std::vector<uint32_t>({901, 900}));
+        CHECK(successCleanup->pendingCount() == 0);
+    }
+
+    // A failed ioctl remains a normalized submission failure, but request and
+    // blob cleanup are identical and do not touch frame/runtime machinery.
+    {
+        auto submitFailureRecorder                                            = makeShared<SRecorder>();
+        auto submitFailureCleanup                                             = CTiledModeBlobCleanupRegistry::create(20);
+        submitFailureRecorder->submitResult                                   = -EBUSY;
+        CSharedPointer<ITiledAtomicRequestOperations> submitFailureOperations = submitFailureRecorder;
+        const auto                                    submitFailure           = executeTiledTestModeset(resolved.route, 20, submitFailureOperations, submitFailureCleanup);
+        CHECK(!static_cast<bool>(submitFailure));
+        CHECK(submitFailure.status == eTiledTestModesetStatus::SUBMISSION_FAILED);
+        CHECK(submitFailure.submission.has_value());
+        CHECK(submitFailure.submission.value_or(SAtomicSubmitResult{}).returnCode == -EBUSY);
+        CHECK(submitFailure.submission.value_or(SAtomicSubmitResult{}).error == EBUSY);
+        CHECK(submitFailure.cleanup.attempted == 2);
+        CHECK(submitFailure.cleanup.destroyed == 2);
+        CHECK(submitFailure.cleanup.retained == 0);
+        CHECK(!submitFailure.retainedCleanup);
+        CHECK(submitFailureRecorder->submits == 1);
+        CHECK(submitFailureRecorder->submittedFlags == TILED_ATOMIC_TEST_MODESET_FLAGS);
+        CHECK(submitFailureRecorder->submittedUserData == nullptr);
+        CHECK(submitFailureRecorder->frees == 1);
+        CHECK(submitFailureCleanup->pendingCount() == 0);
+    }
+
+    // Destroy failure transfers the exact blob into the fd-bound registry;
+    // retry later destroys it and leaves no terminal lifecycle debt.
+    {
+        auto retainedRecorder                                            = makeShared<SRecorder>();
+        auto retainedCleanup                                             = CTiledModeBlobCleanupRegistry::create(21);
+        retainedRecorder->destroyFailuresRemaining                       = 1;
+        CSharedPointer<ITiledAtomicRequestOperations> retainedOperations = retainedRecorder;
+        const auto                                    retained           = executeTiledTestModeset(resolved.route, 21, retainedOperations, retainedCleanup);
+        CHECK(static_cast<bool>(retained));
+        CHECK(retained.status == eTiledTestModesetStatus::SUCCEEDED);
+        CHECK(retained.cleanup.attempted == 2);
+        CHECK(retained.cleanup.destroyed == 1);
+        CHECK(retained.cleanup.retained == 1);
+        CHECK(sameOwner(retained.retainedCleanup, retainedCleanup));
+        CHECK(retainedRecorder->frees == 1);
+        CHECK(retainedRecorder->destructionAttempts == std::vector<uint32_t>({901, 900}));
+        CHECK(retainedRecorder->destroyedBlobs == std::vector<uint32_t>({900}));
+        CHECK(retainedCleanup->pendingCount() == 1);
+
+        const auto retried = retainedCleanup->retryPending();
+        CHECK(retried.attempted == 1);
+        CHECK(retried.destroyed == 1);
+        CHECK(retried.retained == 0);
+        CHECK(retried.complete());
+        CHECK(retainedRecorder->destructionAttempts == std::vector<uint32_t>({901, 900, 901}));
+        CHECK(retainedRecorder->destroyedBlobs == std::vector<uint32_t>({900, 901}));
+        CHECK(retainedCleanup->pendingCount() == 0);
+    }
 
     static_assert(noexcept(submitTiledFrameTransaction(std::declval<const PResolvedTiledAtomicRoute&>(), std::declval<SFrameRuntime&>(),
                                                        std::declval<CTiledFlipReservationRegistry&>(), std::declval<SFrameRuntime::SFrame>(), uintptr_t{1}, uint64_t{1}, 1,