Subject: [PATCH 06/21] wip: capture exact tiled allocation shadow

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

---
 src/backend/drm/DRM.cpp                 | 218 ++++++++++++
 src/backend/drm/TiledDRMIntegration.cpp | 427 +++++++++++++++++++++++-
 src/backend/drm/TiledDRMIntegration.hpp |  89 ++++-
 tests/TiledDRMIntegration.cpp           | 207 ++++++++++++
 4 files changed, 930 insertions(+), 11 deletions(-)

diff --git a/src/backend/drm/DRM.cpp b/src/backend/drm/DRM.cpp
index 8ca69fd0505688f6af08692aab29d5360e48dec1..3d0776703ca3ac96271c19025c78383b6109ed6d 100644
--- a/src/backend/drm/DRM.cpp
+++ b/src/backend/drm/DRM.cpp
@@ -17,6 +17,9 @@
 #include <cstring>
 #include <exception>
 #include <filesystem>
+#include <limits>
+#include <set>
+#include <span>
 #include <system_error>
 #include <sys/mman.h>
 #include <fcntl.h>
@@ -52,6 +55,8 @@ using namespace Hyprutils::Math;
 namespace {
     using Aquamarine::NDRM::NTiledDRMIntegration::eDetachReason;
     using Aquamarine::NDRM::NTiledDRMIntegration::eDetachResult;
+    using Aquamarine::NDRM::NTiledDRMIntegration::SAllocationCapture;
+    using Aquamarine::NDRM::NTiledDRMIntegration::eAllocationCaptureResult;
     using Aquamarine::NDRM::NTiledDRMIntegration::eScanCommitResult;
     using Aquamarine::NDRM::NTiledDRMIntegration::eScanStageResult;
     using Aquamarine::NDRM::NTiledScan::SConnectorView;
@@ -108,6 +113,207 @@ namespace {
         };
     }
 
+    std::optional<std::vector<uint32_t>> copyPlaneResourceIDs(const drmModePlaneRes* resources) {
+        if (!resources || (resources->count_planes > 0 && !resources->planes))
+            return std::nullopt;
+        if (resources->count_planes == 0)
+            return std::vector<uint32_t>{};
+        return std::vector<uint32_t>{resources->planes, resources->planes + resources->count_planes};
+    }
+
+    std::optional<uint64_t> rawConnectorProperty(const drmModeConnector* connector, uint32_t propertyId);
+
+    template <typename T>
+    bool exactResourceOwners(std::span<const uint32_t> ids, const std::vector<SP<T>>& owners) {
+        if (ids.size() != owners.size())
+            return false;
+        std::set<uint32_t> ownerIDs;
+        for (const auto& owner : owners) {
+            if (!owner || owner->id == 0 || !ownerIDs.insert(owner->id).second)
+                return false;
+        }
+        return std::ranges::all_of(ids, [&ownerIDs](uint32_t id) { return id != 0 && ownerIDs.contains(id); });
+    }
+
+    std::optional<uint32_t> possibleConnectorCRTCMask(int fd, const drmModeConnector* connector, std::span<const uint32_t> resourceEncoderIDs) {
+        if (!connector || connector->count_encoders < 0 || (connector->count_encoders > 0 && !connector->encoders))
+            return std::nullopt;
+
+        uint32_t result = 0;
+        for (int index = 0; index < connector->count_encoders; ++index) {
+            const auto ENCODER_ID = connector->encoders[index];
+            if (ENCODER_ID == 0 || !std::ranges::contains(resourceEncoderIDs, ENCODER_ID))
+                return std::nullopt;
+            auto* const ENCODER = drmModeGetEncoder(fd, ENCODER_ID);
+            if (!ENCODER)
+                return std::nullopt;
+            CScopeGuard encoderGuard([ENCODER] { drmModeFreeEncoder(ENCODER); });
+            if (ENCODER->encoder_id != ENCODER_ID)
+                return std::nullopt;
+            result |= ENCODER->possible_crtcs;
+        }
+        return result;
+    }
+
+    std::optional<uint32_t> currentConnectorCRTCID(int fd, const SDRMConnector& owner, const drmModeConnector* connector, std::span<const uint32_t> resourceEncoderIDs) {
+        if (!connector)
+            return std::nullopt;
+        if (owner.props.values.crtc_id != 0) {
+            const auto VALUE = rawConnectorProperty(connector, owner.props.values.crtc_id);
+            if (!VALUE || *VALUE > std::numeric_limits<uint32_t>::max())
+                return std::nullopt;
+            return static_cast<uint32_t>(*VALUE);
+        }
+        if (connector->encoder_id == 0)
+            return uint32_t{0};
+        if (!std::ranges::contains(resourceEncoderIDs, connector->encoder_id))
+            return std::nullopt;
+        auto* const ENCODER = drmModeGetEncoder(fd, connector->encoder_id);
+        if (!ENCODER)
+            return std::nullopt;
+        CScopeGuard encoderGuard([ENCODER] { drmModeFreeEncoder(ENCODER); });
+        if (ENCODER->encoder_id != connector->encoder_id)
+            return std::nullopt;
+        return ENCODER->crtc_id;
+    }
+
+    std::optional<SAllocationCapture> captureAllocationKMS(int fd, const std::vector<SP<SDRMConnector>>& connectors, const std::vector<SP<SDRMCRTC>>& crtcs,
+                                                           const std::vector<SP<SDRMPlane>>& planes) {
+        auto* const FIRST_RESOURCES = drmModeGetResources(fd);
+        if (!FIRST_RESOURCES)
+            return std::nullopt;
+        CScopeGuard firstResourcesGuard([FIRST_RESOURCES] { drmModeFreeResources(FIRST_RESOURCES); });
+        const auto  FIRST = copyResourcesSnapshot(FIRST_RESOURCES);
+        if (!FIRST || !exactResourceOwners(FIRST->connectors, connectors) || !exactResourceOwners(FIRST->crtcs, crtcs))
+            return std::nullopt;
+
+        auto* const FIRST_PLANE_RESOURCES = drmModeGetPlaneResources(fd);
+        if (!FIRST_PLANE_RESOURCES)
+            return std::nullopt;
+        CScopeGuard firstPlaneResourcesGuard([FIRST_PLANE_RESOURCES] { drmModeFreePlaneResources(FIRST_PLANE_RESOURCES); });
+        const auto  FIRST_PLANE_IDS = copyPlaneResourceIDs(FIRST_PLANE_RESOURCES);
+        if (!FIRST_PLANE_IDS || !exactResourceOwners(*FIRST_PLANE_IDS, planes))
+            return std::nullopt;
+
+        auto* const LESSEES = drmModeListLessees(fd);
+        if (!LESSEES)
+            return std::nullopt;
+        CScopeGuard lesseesGuard([LESSEES] { drmFree(LESSEES); });
+        if (LESSEES->count != 0)
+            return std::nullopt;
+
+        SAllocationCapture capture{
+            .resourceCRTCOrder = FIRST->crtcs,
+            .leasesKnownClear  = true,
+        };
+        capture.connectors.reserve(connectors.size());
+        capture.crtcs.reserve(crtcs.size());
+        capture.primaryPlanes.reserve(planes.size());
+
+        for (const auto CONNECTOR_ID : FIRST->connectors) {
+            const auto OWNER = std::ranges::find(connectors, CONNECTOR_ID, &SDRMConnector::id);
+            if (OWNER == connectors.end())
+                return std::nullopt;
+            auto* const KMS_CONNECTOR = drmModeGetConnector(fd, CONNECTOR_ID);
+            if (!KMS_CONNECTOR)
+                return std::nullopt;
+            CScopeGuard connectorGuard([KMS_CONNECTOR] { drmModeFreeConnector(KMS_CONNECTOR); });
+            if (KMS_CONNECTOR->connector_id != CONNECTOR_ID)
+                return std::nullopt;
+            const auto POSSIBLE = possibleConnectorCRTCMask(fd, KMS_CONNECTOR, FIRST->encoders);
+            const auto CURRENT  = currentConnectorCRTCID(fd, **OWNER, KMS_CONNECTOR, FIRST->encoders);
+            if (!POSSIBLE || !CURRENT)
+                return std::nullopt;
+            bool leased = false;
+            if ((*OWNER)->output) {
+                const auto LEASE = (*OWNER)->output->lease.lock();
+                leased           = LEASE && LEASE->active;
+            }
+            capture.connectors.push_back({
+                .connector        = *OWNER,
+                .possibleCRTCMask = *POSSIBLE,
+                .assignedCRTCID   = (*OWNER)->crtc ? (*OWNER)->crtc->id : 0,
+                .kernelCRTCID     = *CURRENT,
+                .enabled          = !(*OWNER)->output || (*OWNER)->output->enabledState,
+                .leased           = leased,
+            });
+        }
+
+        for (const auto CRTC_ID : FIRST->crtcs) {
+            const auto OWNER = std::ranges::find(crtcs, CRTC_ID, &SDRMCRTC::id);
+            if (OWNER == crtcs.end())
+                return std::nullopt;
+            auto* const KMS_CRTC = drmModeGetCrtc(fd, CRTC_ID);
+            if (!KMS_CRTC)
+                return std::nullopt;
+            CScopeGuard crtcGuard([KMS_CRTC] { drmModeFreeCrtc(KMS_CRTC); });
+            if (KMS_CRTC->crtc_id != CRTC_ID)
+                return std::nullopt;
+            bool active = KMS_CRTC->mode_valid || KMS_CRTC->buffer_id != 0;
+            if ((*OWNER)->props.values.active != 0) {
+                uint64_t value = 0;
+                if (!getDRMProp(fd, CRTC_ID, (*OWNER)->props.values.active, &value))
+                    return std::nullopt;
+                active = value != 0;
+            }
+            capture.crtcs.push_back({
+                .crtc                    = *OWNER,
+                .preferredPrimaryPlaneID = (*OWNER)->primary ? (*OWNER)->primary->id : 0,
+                .kernelActive            = active,
+                .pending                 = (*OWNER)->pendingFlip.id.has_value(),
+            });
+        }
+
+        for (const auto PLANE_ID : *FIRST_PLANE_IDS) {
+            const auto OWNER = std::ranges::find(planes, PLANE_ID, &SDRMPlane::id);
+            if (OWNER == planes.end() || (*OWNER)->props.values.type == 0)
+                return std::nullopt;
+            uint64_t type = 0;
+            if (!getDRMProp(fd, PLANE_ID, (*OWNER)->props.values.type, &type))
+                return std::nullopt;
+            if (type != DRM_PLANE_TYPE_PRIMARY)
+                continue;
+            auto* const KMS_PLANE = drmModeGetPlane(fd, PLANE_ID);
+            if (!KMS_PLANE)
+                return std::nullopt;
+            CScopeGuard planeGuard([KMS_PLANE] { drmModeFreePlane(KMS_PLANE); });
+            if (KMS_PLANE->plane_id != PLANE_ID || (KMS_PLANE->count_formats > 0 && !KMS_PLANE->formats))
+                return std::nullopt;
+            std::set<uint32_t> liveFormats;
+            for (uint32_t index = 0; index < KMS_PLANE->count_formats; ++index)
+                liveFormats.insert(KMS_PLANE->formats[index]);
+            std::set<uint32_t> capturedFormats;
+            for (const auto& format : (*OWNER)->formats)
+                capturedFormats.insert(format.drmFormat);
+            if (liveFormats != capturedFormats)
+                return std::nullopt;
+            capture.primaryPlanes.push_back({
+                .plane            = *OWNER,
+                .possibleCRTCMask = KMS_PLANE->possible_crtcs,
+                .kernelCRTCID     = KMS_PLANE->crtc_id,
+                .usable           = KMS_PLANE->possible_crtcs != 0 && !liveFormats.empty(),
+            });
+        }
+
+        auto* const SECOND_PLANE_RESOURCES = drmModeGetPlaneResources(fd);
+        if (!SECOND_PLANE_RESOURCES)
+            return std::nullopt;
+        CScopeGuard secondPlaneResourcesGuard([SECOND_PLANE_RESOURCES] { drmModeFreePlaneResources(SECOND_PLANE_RESOURCES); });
+        const auto  SECOND_PLANE_IDS = copyPlaneResourceIDs(SECOND_PLANE_RESOURCES);
+        if (!SECOND_PLANE_IDS || *SECOND_PLANE_IDS != *FIRST_PLANE_IDS)
+            return std::nullopt;
+
+        auto* const SECOND_RESOURCES = drmModeGetResources(fd);
+        if (!SECOND_RESOURCES)
+            return std::nullopt;
+        CScopeGuard secondResourcesGuard([SECOND_RESOURCES] { drmModeFreeResources(SECOND_RESOURCES); });
+        const auto  SECOND = copyResourcesSnapshot(SECOND_RESOURCES);
+        if (!SECOND || *SECOND != *FIRST)
+            return std::nullopt;
+
+        return capture;
+    }
+
     std::optional<uint64_t> rawConnectorProperty(const drmModeConnector* connector, uint32_t propertyId) {
         if (!connector || propertyId == 0 || connector->count_props < 0 || (connector->count_props > 0 && (!connector->props || !connector->prop_values)))
             return std::nullopt;
@@ -1197,6 +1403,18 @@ void Aquamarine::CDRMBackend::recheckOutputs() {
 
     if (!updateSecondaryRendererState())
         backend->log(AQ_LOG_ERROR, std::format("drm: Failed to update renderer state for {}", gpu->path));
+
+    // Read-only shadow evidence for the source-private allocator/bridge. It is
+    // captured only after ordinary routing has settled and never mutates it.
+    auto RESULT = eAllocationCaptureResult::INVALID_CAPTURE;
+    try {
+        auto capture = captureAllocationKMS(gpu->fd, connectors, crtcs, planes);
+        RESULT       = NDRM::NTiledDRMIntegration::captureAllocationShadow(SELF, capture ? std::move(*capture) : SAllocationCapture{});
+    } catch (...) { RESULT = NDRM::NTiledDRMIntegration::captureAllocationShadow(SELF, {}); }
+    try {
+        if (RESULT != eAllocationCaptureResult::INSTALLED && RESULT != eAllocationCaptureResult::SHADOW_DISABLED)
+            backend->log(AQ_LOG_DEBUG, std::format("drm: Tiled allocation shadow unavailable for {} ({})", gpu->path, static_cast<uint32_t>(RESULT)));
+    } catch (...) { return; }
 }
 
 void Aquamarine::CDRMBackend::scanConnectors() {
diff --git a/src/backend/drm/TiledDRMIntegration.cpp b/src/backend/drm/TiledDRMIntegration.cpp
index 1cd308d302bce764244f9fc7463c6cabd736ca4c..20cfec1da1181b6cd24acfcdd70cd341caaf8964 100644
--- a/src/backend/drm/TiledDRMIntegration.cpp
+++ b/src/backend/drm/TiledDRMIntegration.cpp
@@ -7,11 +7,15 @@
 #include <map>
 #include <mutex>
 #include <optional>
+#include <set>
 #include <utility>
 
+#include <hyprutils/utils/ScopeGuard.hpp>
+
 using namespace Aquamarine;
 using namespace Aquamarine::NDRM::NTiledDRMIntegration;
 using namespace Aquamarine::NDRM::NTiledIncarnations;
+using namespace Aquamarine::NDRM::NTiledCrtcAllocator;
 using namespace Aquamarine::NDRM::NTiledScan;
 
 namespace {
@@ -63,9 +67,12 @@ class CTiledDRMIntegrationRegistry::CImpl {
         PBackendLifetime    lifetime;
         PDeviceIncarnation  device;
         PSessionIncarnation session;
-        uint64_t            resourceEpoch = 0;
-        bool                detachBegun   = false;
-        eDetachReason       detachReason  = eDetachReason::BACKEND_SHUTDOWN;
+        uint64_t            resourceEpoch   = 0;
+        uint64_t            allocationEpoch = 0;
+        PAllocationShadow   allocationShadow;
+        bool                allocationGateActive = false;
+        bool                detachBegun          = false;
+        eDetachReason       detachReason         = eDetachReason::BACKEND_SHUTDOWN;
     };
 
     explicit CImpl(std::thread::id thread) : eventThread(thread), scanRegistry(thread) {}
@@ -128,6 +135,12 @@ SBeginConnectorScanResult::operator bool() const {
     return static_cast<bool>(scan);
 }
 
+CAllocationShadow::CAllocationShadow(CSharedPointer<const CPlan> plan) : m_plan(std::move(plan)) {}
+
+const CSharedPointer<const CPlan>& CAllocationShadow::plan() const {
+    return m_plan;
+}
+
 CTiledDRMIntegrationRegistry::CTiledDRMIntegrationRegistry(std::thread::id eventThread) : m_impl(Hyprutils::Memory::makeUnique<CImpl>(eventThread)) {}
 
 CTiledDRMIntegrationRegistry::~CTiledDRMIntegrationRegistry() noexcept {
@@ -144,6 +157,11 @@ SBeginConnectorScanResult CTiledDRMIntegrationRegistry::beginAttached(const void
     const auto ENTRY = m_impl->entries.find(key);
     if (ENTRY == m_impl->entries.end() || ENTRY->second.detachBegun)
         return {.result = eScanBeginResult::INVALID_STATE};
+    if (ENTRY->second.allocationGateActive)
+        return {.result = eScanBeginResult::BUSY};
+
+    setLastScan(key, false);
+    ENTRY->second.allocationShadow.reset();
     if (ENTRY->second.resourceEpoch == std::numeric_limits<uint64_t>::max())
         return {.result = eScanBeginResult::EXHAUSTED};
 
@@ -187,8 +205,6 @@ SBeginConnectorScanResult CTiledDRMIntegrationRegistry::beginConnectorScan(const
     if (!KEY)
         return {.result = eScanBeginResult::INVALID_STATE};
 
-    setLastScan(KEY, false);
-
     // This raw-key/thread-index gate must precede weak-owner construction,
     // locking, comparison, or any other Hypr owner operation.
     if (!m_impl->onEventThread()) {
@@ -209,6 +225,8 @@ SBeginConnectorScanResult CTiledDRMIntegrationRegistry::beginConnectorScan(const
         return beginAttached(KEY);
     }
 
+    setLastScan(KEY, false);
+
     auto shadowDisabled = [this, KEY]() -> SBeginConnectorScanResult {
         try {
             return {
@@ -274,7 +292,6 @@ SBeginConnectorScanResult CTiledDRMIntegrationRegistry::beginTestConnectorScan(c
     if (!KEY)
         return {.result = eScanBeginResult::INVALID_STATE};
 
-    setLastScan(KEY, false);
     if (!m_impl->onEventThread()) {
         if (m_impl->registeredThread(KEY))
             return {.result = eScanBeginResult::WRONG_THREAD};
@@ -296,6 +313,8 @@ SBeginConnectorScanResult CTiledDRMIntegrationRegistry::beginTestConnectorScan(c
         return beginAttached(KEY);
     }
 
+    setLastScan(KEY, false);
+
     auto shadowDisabled = [this, KEY]() -> SBeginConnectorScanResult {
         try {
             return {
@@ -496,6 +515,354 @@ void CTiledDRMIntegrationRegistry::abortConnectorScan(CConnectorScan& scan) noex
     scan.m_active = false;
 }
 
+eAllocationCaptureResult CTiledDRMIntegrationRegistry::captureAllocationKey(const void* key, SAllocationCapture capture) noexcept {
+    const auto THREAD = m_impl->registeredThread(key);
+    if (!THREAD)
+        return eAllocationCaptureResult::SHADOW_DISABLED;
+    if (*THREAD != std::this_thread::get_id())
+        return eAllocationCaptureResult::WRONG_THREAD;
+
+    const auto ENTRY = m_impl->entries.find(key);
+    if (ENTRY == m_impl->entries.end() || ENTRY->second.detachBegun)
+        return eAllocationCaptureResult::NO_CURRENT_SCAN;
+    if (ENTRY->second.allocationGateActive)
+        return eAllocationCaptureResult::BUSY;
+
+    // A failed replacement never leaves an older shadow looking current.
+    ENTRY->second.allocationShadow.reset();
+
+    auto* const STATE = m_impl->scanRegistry.find(ENTRY->second.lifetime);
+    if (!STATE)
+        return eAllocationCaptureResult::NO_CURRENT_SCAN;
+    const auto SCAN = STATE->currentComplete();
+    if (!SCAN)
+        return eAllocationCaptureResult::NO_CURRENT_SCAN;
+    if (ENTRY->second.allocationEpoch == std::numeric_limits<uint64_t>::max())
+        return eAllocationCaptureResult::EXHAUSTED;
+    if (!capture.leasesKnownClear)
+        return eAllocationCaptureResult::INVALID_CAPTURE;
+
+    try {
+        const auto ALLOCATION_EPOCH = ENTRY->second.allocationEpoch + 1;
+        if (capture.resourceCRTCOrder.size() > std::numeric_limits<uint32_t>::digits || capture.resourceCRTCOrder.size() != capture.crtcs.size())
+            return eAllocationCaptureResult::INVALID_CAPTURE;
+
+        std::set<uint32_t> resourceCRTCIDs(capture.resourceCRTCOrder.begin(), capture.resourceCRTCOrder.end());
+        if (resourceCRTCIDs.size() != capture.resourceCRTCOrder.size() || resourceCRTCIDs.contains(0))
+            return eAllocationCaptureResult::INVALID_CAPTURE;
+
+        struct SConnectorFact {
+            std::vector<uint32_t> possibleCRTCIDs;
+            uint32_t              assignedCRTCID = 0;
+            uint32_t              kernelCRTCID   = 0;
+            bool                  enabled        = true;
+            bool                  leased         = false;
+        };
+        struct SCRTCFact {
+            uint32_t preferredPrimaryPlaneID = 0;
+            bool     kernelActive            = false;
+            bool     pending                 = false;
+        };
+        struct SPlaneFact {
+            std::vector<uint32_t> possibleCRTCIDs;
+            uint32_t              kernelCRTCID = 0;
+            bool                  usable       = false;
+        };
+
+        std::map<uint32_t, SConnectorFact> connectorFacts;
+        std::map<uint32_t, SCRTCFact>      crtcFacts;
+        std::map<uint32_t, SPlaneFact>     planeFacts;
+        std::vector<SConnectorCapture>     connectorCaptures;
+        std::vector<SCRTCCapture>          crtcCaptures;
+        std::vector<SPrimaryPlaneCapture>  planeCaptures;
+        connectorCaptures.reserve(capture.connectors.size());
+        crtcCaptures.reserve(capture.crtcs.size());
+        planeCaptures.reserve(capture.primaryPlanes.size());
+
+        for (auto& connector : capture.connectors) {
+            if (!connector.connector || connector.connector->id == 0 || connector.leased)
+                return eAllocationCaptureResult::INVALID_CAPTURE;
+            auto possible = normalizeCRTCIDs(connector.possibleCRTCMask, capture.resourceCRTCOrder);
+            if (!possible || (connector.assignedCRTCID != 0 && !resourceCRTCIDs.contains(connector.assignedCRTCID)) ||
+                (connector.kernelCRTCID != 0 && !resourceCRTCIDs.contains(connector.kernelCRTCID)))
+                return eAllocationCaptureResult::INVALID_CAPTURE;
+            const auto ID = connector.connector->id;
+            if (!connectorFacts
+                     .emplace(ID,
+                              SConnectorFact{.possibleCRTCIDs = *possible,
+                                             .assignedCRTCID  = connector.assignedCRTCID,
+                                             .kernelCRTCID    = connector.kernelCRTCID,
+                                             .enabled         = connector.enabled,
+                                             .leased          = connector.leased})
+                     .second)
+                return eAllocationCaptureResult::INVALID_CAPTURE;
+            connectorCaptures.push_back({.connector = std::move(connector.connector), .possibleCRTCIDs = std::move(*possible)});
+        }
+
+        for (auto& crtc : capture.crtcs) {
+            if (!crtc.crtc || crtc.crtc->id == 0 || !resourceCRTCIDs.contains(crtc.crtc->id) ||
+                !crtcFacts.emplace(crtc.crtc->id, SCRTCFact{.preferredPrimaryPlaneID = crtc.preferredPrimaryPlaneID, .kernelActive = crtc.kernelActive, .pending = crtc.pending})
+                     .second)
+                return eAllocationCaptureResult::INVALID_CAPTURE;
+            crtcCaptures.push_back({.crtc = std::move(crtc.crtc)});
+        }
+        if (crtcFacts.size() != resourceCRTCIDs.size())
+            return eAllocationCaptureResult::INVALID_CAPTURE;
+
+        for (auto& plane : capture.primaryPlanes) {
+            if (!plane.plane || plane.plane->id == 0 || (plane.kernelCRTCID != 0 && !resourceCRTCIDs.contains(plane.kernelCRTCID)))
+                return eAllocationCaptureResult::INVALID_CAPTURE;
+            auto possible = normalizeCRTCIDs(plane.possibleCRTCMask, capture.resourceCRTCOrder);
+            if (!possible)
+                return eAllocationCaptureResult::INVALID_CAPTURE;
+            const auto ID = plane.plane->id;
+            if (!planeFacts.emplace(ID, SPlaneFact{.possibleCRTCIDs = *possible, .kernelCRTCID = plane.kernelCRTCID, .usable = plane.usable}).second)
+                return eAllocationCaptureResult::INVALID_CAPTURE;
+            planeCaptures.push_back({.plane = std::move(plane.plane), .possibleCRTCIDs = std::move(*possible), .usable = plane.usable});
+        }
+
+        const auto SNAPSHOT = CSnapshot::create(SCAN, ALLOCATION_EPOCH, std::move(connectorCaptures), std::move(crtcCaptures), std::move(planeCaptures));
+        if (!SNAPSHOT)
+            return eAllocationCaptureResult::INVALID_CAPTURE;
+
+        std::map<uint32_t, SConnectorHandle>    connectorHandles;
+        std::map<uint32_t, SCRTCHandle>         crtcHandles;
+        std::map<uint32_t, SPrimaryPlaneHandle> planeHandles;
+        for (const auto& connector : SNAPSHOT->connectors())
+            connectorHandles.emplace(connector.connector->id, SConnectorHandle{.id = connector.connector->id, .incarnation = connector.incarnation});
+        for (const auto& crtc : SNAPSHOT->crtcs())
+            crtcHandles.emplace(crtc.crtc->id, SCRTCHandle{.id = crtc.crtc->id, .incarnation = crtc.incarnation});
+        for (const auto& plane : SNAPSHOT->primaryPlanes())
+            planeHandles.emplace(plane.plane->id, SPrimaryPlaneHandle{.id = plane.plane->id, .incarnation = plane.incarnation});
+
+        std::vector<CSharedPointer<const CGroupDefinition>> currentDefinitions;
+        std::vector<SGroupRequest>                          groups;
+        std::set<uint32_t>                                  candidateConnectorIDs;
+        for (const auto& scanDefinition : SCAN->definitions()) {
+            if (!scanDefinition || scanDefinition->group().kind != NTiling::eTileGroupKind::MULTI_STREAM)
+                continue;
+            auto definition = CGroupDefinition::create(scanDefinition);
+            if (!definition)
+                return eAllocationCaptureResult::INVALID_CAPTURE;
+            SGroupRequest request{.definition = definition};
+            request.connectors.reserve(definition->group().members.size());
+            for (const auto& member : definition->group().members) {
+                const auto HANDLE = connectorHandles.find(member.connectorId);
+                if (HANDLE == connectorHandles.end() || !candidateConnectorIDs.insert(member.connectorId).second)
+                    return eAllocationCaptureResult::INVALID_CAPTURE;
+                request.connectors.push_back(HANDLE->second);
+            }
+            // Shadow capture has not installed grouped routing, so claiming
+            // retained grouped bindings here would be false authority.
+            request.retained.reset();
+            currentDefinitions.push_back(std::move(definition));
+            groups.push_back(std::move(request));
+        }
+
+        std::vector<SOrdinaryRequest> ordinary;
+        for (const auto& connector : SCAN->connectors()) {
+            if (!connector.view.connected || !connector.view.isMonitor || connector.view.nonDesktop ||
+                connector.projection.disposition != eConnectorDisposition::PUBLISH_ORDINARY || candidateConnectorIDs.contains(connector.view.connectorId))
+                continue;
+            const auto HANDLE = connectorHandles.find(connector.view.connectorId);
+            const auto FACT   = connectorFacts.find(connector.view.connectorId);
+            if (HANDLE == connectorHandles.end() || FACT == connectorFacts.end() || !candidateConnectorIDs.insert(connector.view.connectorId).second)
+                return eAllocationCaptureResult::INVALID_CAPTURE;
+            ordinary.push_back(
+                {.connector = HANDLE->second, .priority = FACT->second.enabled ? eOrdinaryPriority::ENABLED_UNMATCHED : eOrdinaryPriority::DETACHED_DISABLED_BACKUP});
+        }
+
+        const auto                   CONTAINS = [](const std::vector<uint32_t>& ids, uint32_t id) { return std::ranges::binary_search(ids, id); };
+        std::map<uint32_t, uint32_t> kernelConnectorByCRTC;
+        std::map<uint32_t, uint32_t> kernelPlaneByCRTC;
+        for (const auto& [connectorID, fact] : connectorFacts) {
+            if (fact.assignedCRTCID != 0 && !CONTAINS(fact.possibleCRTCIDs, fact.assignedCRTCID))
+                return eAllocationCaptureResult::INVALID_CAPTURE;
+            if (fact.kernelCRTCID == 0)
+                continue;
+            if (fact.assignedCRTCID != fact.kernelCRTCID || !CONTAINS(fact.possibleCRTCIDs, fact.kernelCRTCID) ||
+                !kernelConnectorByCRTC.emplace(fact.kernelCRTCID, connectorID).second)
+                return eAllocationCaptureResult::INVALID_CAPTURE;
+        }
+        for (const auto& [planeID, fact] : planeFacts) {
+            if (fact.kernelCRTCID == 0)
+                continue;
+            if (!fact.usable || !CONTAINS(fact.possibleCRTCIDs, fact.kernelCRTCID) || !kernelPlaneByCRTC.emplace(fact.kernelCRTCID, planeID).second)
+                return eAllocationCaptureResult::INVALID_CAPTURE;
+        }
+
+        std::vector<SHardHold>                hardHolds;
+        std::vector<SConnectorCRTCPreference> connectorPreferences;
+        std::vector<SCRTCPlanePreference>     planePreferences;
+        std::set<uint32_t>                    heldCRTCIDs;
+        for (const auto& [crtcID, fact] : crtcFacts) {
+            const auto KERNEL_CONNECTOR = kernelConnectorByCRTC.find(crtcID);
+            const auto KERNEL_PLANE     = kernelPlaneByCRTC.find(crtcID);
+            if (!fact.kernelActive) {
+                if (fact.pending || KERNEL_CONNECTOR != kernelConnectorByCRTC.end() || KERNEL_PLANE != kernelPlaneByCRTC.end())
+                    return eAllocationCaptureResult::INVALID_CAPTURE;
+                continue;
+            }
+            if (KERNEL_CONNECTOR == kernelConnectorByCRTC.end() || KERNEL_PLANE == kernelPlaneByCRTC.end())
+                return eAllocationCaptureResult::INVALID_CAPTURE;
+
+            const auto CONNECTOR_ID = KERNEL_CONNECTOR->second;
+            const auto PLANE_ID     = KERNEL_PLANE->second;
+            if (candidateConnectorIDs.contains(CONNECTOR_ID)) {
+                // This is only a transitionable shadow preference. A later
+                // mutation gate must safely replace and revalidate live KMS.
+                if (fact.pending)
+                    return eAllocationCaptureResult::INVALID_CAPTURE;
+                continue;
+            }
+
+            hardHolds.push_back({
+                .reason    = eHardHoldReason::ACTIVE_OR_PENDING,
+                .connector = connectorHandles.at(CONNECTOR_ID),
+                .crtc      = crtcHandles.at(crtcID),
+                .plane     = planeHandles.at(PLANE_ID),
+            });
+            heldCRTCIDs.insert(crtcID);
+        }
+
+        for (const auto CONNECTOR_ID : candidateConnectorIDs) {
+            const auto FACT = connectorFacts.find(CONNECTOR_ID);
+            if (FACT == connectorFacts.end() || FACT->second.leased)
+                return eAllocationCaptureResult::INVALID_CAPTURE;
+            if (FACT->second.assignedCRTCID != 0)
+                connectorPreferences.push_back({.connector = connectorHandles.at(CONNECTOR_ID), .crtc = crtcHandles.at(FACT->second.assignedCRTCID)});
+        }
+
+        for (const auto& [crtcID, fact] : crtcFacts) {
+            if (heldCRTCIDs.contains(crtcID))
+                continue;
+            uint32_t   preferredPlaneID = 0;
+            const auto LIVE_PLANE       = kernelPlaneByCRTC.find(crtcID);
+            if (LIVE_PLANE != kernelPlaneByCRTC.end() && candidateConnectorIDs.contains(kernelConnectorByCRTC.at(crtcID)))
+                preferredPlaneID = LIVE_PLANE->second;
+            else
+                preferredPlaneID = fact.preferredPrimaryPlaneID;
+            if (preferredPlaneID == 0)
+                continue;
+            const auto PLANE = planeFacts.find(preferredPlaneID);
+            if (PLANE == planeFacts.end() || !PLANE->second.usable || !CONTAINS(PLANE->second.possibleCRTCIDs, crtcID))
+                return eAllocationCaptureResult::INVALID_CAPTURE;
+            planePreferences.push_back({.crtc = crtcHandles.at(crtcID), .plane = planeHandles.at(preferredPlaneID)});
+        }
+
+        const SAllocationEpoch EPOCH = {
+            .session     = ENTRY->second.session->sessionEpoch(),
+            .allocation  = ALLOCATION_EPOCH,
+            .eventThread = ENTRY->second.session->eventThreadEpoch(),
+        };
+        const auto OBSERVATION = CAllocationObservation::create(SCAN, SNAPSHOT, std::move(currentDefinitions), std::move(hardHolds), std::move(groups), std::move(ordinary),
+                                                                std::move(connectorPreferences), std::move(planePreferences), EPOCH);
+        if (!OBSERVATION)
+            return eAllocationCaptureResult::INVALID_CAPTURE;
+        const auto PLAN = solve(OBSERVATION);
+        if (!PLAN || PLAN->status() != ePlanStatus::READY)
+            return eAllocationCaptureResult::NO_READY_PLAN;
+
+        SCurrentState current = {
+            .backendLifetime = ENTRY->second.lifetime,
+            .device          = ENTRY->second.device,
+            .session         = ENTRY->second.session,
+            .scan            = SCAN,
+            .scanObservation = SCAN->observation(),
+            .snapshot        = SNAPSHOT,
+            .observation     = OBSERVATION,
+            .epoch           = EPOCH,
+        };
+        if (!withCurrentPlan(PLAN, current, [](const CPlan&) {}))
+            return eAllocationCaptureResult::NO_READY_PLAN;
+        PAllocationShadow shadow{new CAllocationShadow{PLAN}};
+
+        ENTRY->second.allocationEpoch  = ALLOCATION_EPOCH;
+        ENTRY->second.allocationShadow = std::move(shadow);
+        return eAllocationCaptureResult::INSTALLED;
+    } catch (...) {
+        ENTRY->second.allocationShadow.reset();
+        return eAllocationCaptureResult::INVALID_CAPTURE;
+    }
+}
+
+eAllocationCaptureResult CTiledDRMIntegrationRegistry::captureAllocationShadow(const CSharedPointer<CDRMBackend>& backend, SAllocationCapture capture) noexcept {
+    return captureAllocationKey(backend.impl_, std::move(capture));
+}
+
+eAllocationCaptureResult CTiledDRMIntegrationRegistry::captureTestAllocationShadow(const PBackendLifetime& lifetime, SAllocationCapture capture) noexcept {
+    return captureAllocationKey(lifetime.impl_, std::move(capture));
+}
+
+PAllocationShadow CTiledDRMIntegrationRegistry::currentAllocationKey(const void* key) noexcept {
+    const auto THREAD = m_impl->registeredThread(key);
+    if (!THREAD || *THREAD != std::this_thread::get_id())
+        return {};
+    const auto ENTRY = m_impl->entries.find(key);
+    return ENTRY == m_impl->entries.end() || ENTRY->second.detachBegun ? PAllocationShadow{} : ENTRY->second.allocationShadow;
+}
+
+PAllocationShadow CTiledDRMIntegrationRegistry::currentAllocationShadow(const CWeakPointer<CDRMBackend>& backend) noexcept {
+    return currentAllocationKey(backend.impl_);
+}
+
+PAllocationShadow CTiledDRMIntegrationRegistry::testCurrentAllocationShadow(const PBackendLifetime& lifetime) noexcept {
+    return currentAllocationKey(lifetime.impl_);
+}
+
+bool CTiledDRMIntegrationRegistry::withCurrentAllocationKey(const void* key, const PAllocationShadow& expected,
+                                                            const std::function<void(const CPlan&, const SCurrentState&)>& callback) noexcept {
+    const auto THREAD = m_impl->registeredThread(key);
+    if (!THREAD || *THREAD != std::this_thread::get_id() || !expected || !callback)
+        return false;
+    const auto ENTRY = m_impl->entries.find(key);
+    if (ENTRY == m_impl->entries.end() || ENTRY->second.detachBegun || ENTRY->second.allocationGateActive || !ENTRY->second.allocationShadow ||
+        ENTRY->second.allocationShadow.impl_ != expected.impl_ || !expected->m_plan)
+        return false;
+    auto* const STATE = m_impl->scanRegistry.find(ENTRY->second.lifetime);
+    if (!STATE)
+        return false;
+    const auto SCAN        = STATE->currentComplete();
+    const auto SNAPSHOT    = expected->m_plan->snapshot();
+    const auto OBSERVATION = expected->m_plan->observation();
+    if (!SCAN || !SNAPSHOT || !OBSERVATION || SCAN.impl_ != SNAPSHOT->scan().impl_ || ENTRY->second.device.impl_ != SNAPSHOT->device().impl_ ||
+        ENTRY->second.session.impl_ != SNAPSHOT->session().impl_ || ENTRY->second.allocationEpoch != SNAPSHOT->allocationEpoch())
+        return false;
+
+    SCurrentState current = {
+        .backendLifetime = ENTRY->second.lifetime,
+        .device          = ENTRY->second.device,
+        .session         = ENTRY->second.session,
+        .scan            = SCAN,
+        .scanObservation = SCAN->observation(),
+        .snapshot        = SNAPSHOT,
+        .observation     = OBSERVATION,
+        .epoch           = OBSERVATION->epoch(),
+    };
+    ENTRY->second.allocationGateActive = true;
+    Hyprutils::Utils::CScopeGuard gateGuard([&] { ENTRY->second.allocationGateActive = false; });
+    bool                          invoked = false;
+    bool                          result  = false;
+    try {
+        const auto CURRENT = withCurrentPlan(expected->m_plan, current, [&](const CPlan& plan) {
+            callback(plan, current);
+            invoked = true;
+        });
+        result             = CURRENT && invoked;
+    } catch (...) { result = false; }
+    return result;
+}
+
+bool CTiledDRMIntegrationRegistry::withCurrentAllocationShadow(const CWeakPointer<CDRMBackend>& backend, const PAllocationShadow& expected,
+                                                               const std::function<void(const CPlan&, const SCurrentState&)>& callback) noexcept {
+    return withCurrentAllocationKey(backend.impl_, expected, callback);
+}
+
+bool CTiledDRMIntegrationRegistry::withCurrentTestAllocationShadow(const PBackendLifetime& lifetime, const PAllocationShadow& expected,
+                                                                   const std::function<void(const CPlan&, const SCurrentState&)>& callback) noexcept {
+    return withCurrentAllocationKey(lifetime.impl_, expected, callback);
+}
+
 eSessionResult CTiledDRMIntegrationRegistry::rotateSessionKey(const void* key) noexcept {
     const auto THREAD = m_impl->registeredThread(key);
     if (!THREAD)
@@ -506,6 +873,11 @@ eSessionResult CTiledDRMIntegrationRegistry::rotateSessionKey(const void* key) n
     const auto ENTRY = m_impl->entries.find(key);
     if (ENTRY == m_impl->entries.end() || ENTRY->second.detachBegun)
         return eSessionResult::INVALID_STATE;
+    if (ENTRY->second.allocationGateActive)
+        return eSessionResult::BUSY;
+
+    setLastScan(key, false);
+    ENTRY->second.allocationShadow.reset();
     auto* const STATE = m_impl->scanRegistry.find(ENTRY->second.lifetime);
     if (!STATE)
         return eSessionResult::INVALID_STATE;
@@ -527,7 +899,6 @@ eSessionResult CTiledDRMIntegrationRegistry::rotateSessionKey(const void* key) n
         return RESULT == eSessionRotationResult::WRONG_THREAD ? eSessionResult::WRONG_THREAD : eSessionResult::INVALID_STATE;
 
     ENTRY->second.session = std::move(session);
-    setLastScan(key, false);
     return eSessionResult::ROTATED;
 }
 
@@ -540,10 +911,11 @@ eSessionResult CTiledDRMIntegrationRegistry::rotateTestSession(const PBackendLif
 }
 
 eDetachResult CTiledDRMIntegrationRegistry::beginDetachKey(const void* key, eDetachReason reason) noexcept {
-    setLastScan(key, false);
     const auto THREAD = m_impl->registeredThread(key);
-    if (!THREAD)
+    if (!THREAD) {
+        setLastScan(key, false);
         return eDetachResult::NOT_ATTACHED;
+    }
     if (*THREAD != std::this_thread::get_id())
         return eDetachResult::WRONG_THREAD;
 
@@ -552,6 +924,11 @@ eDetachResult CTiledDRMIntegrationRegistry::beginDetachKey(const void* key, eDet
         return eDetachResult::INVALID_STATE;
     if (ENTRY->second.detachBegun)
         return eDetachResult::ALREADY_BEGUN;
+    if (ENTRY->second.allocationGateActive)
+        return eDetachResult::BUSY;
+
+    setLastScan(key, false);
+    ENTRY->second.allocationShadow.reset();
 
     const auto RESULT = m_impl->scanRegistry.beginTeardown(ENTRY->second.lifetime);
     if (RESULT == eRegistryResult::BUSY)
@@ -581,7 +958,11 @@ eDetachResult CTiledDRMIntegrationRegistry::eraseDetachedKey(const void* key) no
         return eDetachResult::WRONG_THREAD;
 
     const auto ENTRY = m_impl->entries.find(key);
-    if (ENTRY == m_impl->entries.end() || !ENTRY->second.detachBegun)
+    if (ENTRY == m_impl->entries.end())
+        return eDetachResult::INVALID_STATE;
+    if (ENTRY->second.allocationGateActive)
+        return eDetachResult::BUSY;
+    if (!ENTRY->second.detachBegun)
         return eDetachResult::INVALID_STATE;
     if (m_impl->scanRegistry.erase(ENTRY->second.lifetime) != eRegistryResult::OK)
         return eDetachResult::INVALID_STATE;
@@ -668,6 +1049,16 @@ void CTiledDRMIntegrationRegistry::failNextAttachmentForTest() noexcept {
     m_impl->failNextAttachment = true;
 }
 
+bool CTiledDRMIntegrationRegistry::exhaustTestResourceEpoch(const PBackendLifetime& lifetime) noexcept {
+    if (!m_impl->onEventThread() || !lifetime)
+        return false;
+    const auto ENTRY = m_impl->entries.find(lifetime.impl_);
+    if (ENTRY == m_impl->entries.end() || ENTRY->second.detachBegun || ENTRY->second.allocationGateActive || ENTRY->second.lifetime != lifetime)
+        return false;
+    ENTRY->second.resourceEpoch = std::numeric_limits<uint64_t>::max();
+    return true;
+}
+
 SBeginConnectorScanResult Aquamarine::NDRM::NTiledDRMIntegration::beginConnectorScan(const CSharedPointer<CDRMBackend>& backend) {
     if (!backend.impl_)
         return {.result = eScanBeginResult::INVALID_STATE};
@@ -692,6 +1083,22 @@ void Aquamarine::NDRM::NTiledDRMIntegration::abortConnectorScan(const PConnector
         REGISTRY->abortConnectorScan(scan);
 }
 
+eAllocationCaptureResult Aquamarine::NDRM::NTiledDRMIntegration::captureAllocationShadow(const CSharedPointer<CDRMBackend>& backend, SAllocationCapture capture) noexcept {
+    auto* const REGISTRY = productionRegistryIfCreated();
+    return REGISTRY ? REGISTRY->captureAllocationShadow(backend, std::move(capture)) : eAllocationCaptureResult::SHADOW_DISABLED;
+}
+
+PAllocationShadow Aquamarine::NDRM::NTiledDRMIntegration::currentAllocationShadow(const CWeakPointer<CDRMBackend>& backend) noexcept {
+    auto* const REGISTRY = productionRegistryIfCreated();
+    return REGISTRY ? REGISTRY->currentAllocationShadow(backend) : PAllocationShadow{};
+}
+
+bool Aquamarine::NDRM::NTiledDRMIntegration::withCurrentAllocationShadow(const CWeakPointer<CDRMBackend>& backend, const PAllocationShadow& expected,
+                                                                         const std::function<void(const CPlan&, const SCurrentState&)>& callback) noexcept {
+    auto* const REGISTRY = productionRegistryIfCreated();
+    return REGISTRY && REGISTRY->withCurrentAllocationShadow(backend, expected, callback);
+}
+
 eSessionResult Aquamarine::NDRM::NTiledDRMIntegration::rotateSession(const CWeakPointer<CDRMBackend>& backend) noexcept {
     auto* const REGISTRY = productionRegistryIfCreated();
     return REGISTRY ? REGISTRY->rotateSession(backend) : eSessionResult::NOT_ATTACHED;
diff --git a/src/backend/drm/TiledDRMIntegration.hpp b/src/backend/drm/TiledDRMIntegration.hpp
index 20b22b0ee3bcea388b26c533bfca38fcf076d7df..6ef88e29bcfac3fd38125f0c970097757e21cc13 100644
--- a/src/backend/drm/TiledDRMIntegration.hpp
+++ b/src/backend/drm/TiledDRMIntegration.hpp
@@ -1,10 +1,11 @@
 #pragma once
 
-#include "TiledBackendState.hpp"
+#include "TiledCrtcAllocator.hpp"
 
 #include <aquamarine/backend/DRM.hpp>
 
 #include <cstdint>
+#include <functional>
 #include <thread>
 #include <vector>
 
@@ -24,6 +25,8 @@ namespace Aquamarine::NDRM::NTiledDRMIntegration { // NOLINT(readability-identif
     using Hyprutils::Memory::CSharedPointer;
     using Hyprutils::Memory::CWeakPointer;
     using NTiledIncarnations::PBackendLifetime;
+    using NTiledCrtcAllocator::CPlan;
+    using NTiledCrtcAllocator::SCurrentState;
     using NTiledScan::PCommittedScan;
     using NTiledScan::PPreparedScan;
     using NTiledScan::PScanAttemptTicket;
@@ -83,6 +86,72 @@ namespace Aquamarine::NDRM::NTiledDRMIntegration { // NOLINT(readability-identif
         INVALID_STATE,
     };
 
+    enum class eAllocationCaptureResult : uint8_t {
+        INSTALLED = 0,
+        SHADOW_DISABLED,
+        WRONG_THREAD,
+        BUSY,
+        NO_CURRENT_SCAN,
+        EXHAUSTED,
+        INVALID_CAPTURE,
+        NO_READY_PLAN,
+    };
+
+    // Raw KMS masks are normalized only against resourceCRTCOrder. All owners
+    // are exact live SDRM owners from one coherently bracketed acquisition.
+    struct AQ_TILED_DRM_PRIVATE SAllocationConnectorCapture {
+        CSharedPointer<SDRMConnector> connector;
+        uint32_t                      possibleCRTCMask = 0;
+        uint32_t                      assignedCRTCID   = 0;
+        uint32_t                      kernelCRTCID     = 0;
+        bool                          enabled          = true;
+        bool                          leased           = false;
+    };
+
+    struct AQ_TILED_DRM_PRIVATE SAllocationCRTCCapture {
+        CSharedPointer<SDRMCRTC> crtc;
+        uint32_t                 preferredPrimaryPlaneID = 0;
+        bool                     kernelActive            = false;
+        bool                     pending                 = false;
+    };
+
+    struct AQ_TILED_DRM_PRIVATE SAllocationPrimaryPlaneCapture {
+        CSharedPointer<SDRMPlane> plane;
+        uint32_t                  possibleCRTCMask = 0;
+        uint32_t                  kernelCRTCID     = 0;
+        bool                      usable           = false;
+    };
+
+    struct AQ_TILED_DRM_PRIVATE SAllocationCapture {
+        std::vector<uint32_t>                       resourceCRTCOrder;
+        std::vector<SAllocationConnectorCapture>    connectors;
+        std::vector<SAllocationCRTCCapture>         crtcs;
+        std::vector<SAllocationPrimaryPlaneCapture> primaryPlanes;
+        // False includes acquisition failure and any known or foreign lessee.
+        bool leasesKnownClear = false;
+    };
+
+    // Immutable bridge handoff. This is shadow evidence only: it does not
+    // authorize applying the plan to already-live ordinary KMS routing.
+    class AQ_TILED_DRM_PRIVATE CAllocationShadow final {
+      public:
+        CAllocationShadow(const CAllocationShadow&)                                          = delete;
+        CAllocationShadow(CAllocationShadow&&)                                               = delete;
+        CAllocationShadow&                               operator=(const CAllocationShadow&) = delete;
+        CAllocationShadow&                               operator=(CAllocationShadow&&)      = delete;
+
+        [[nodiscard]] const CSharedPointer<const CPlan>& plan() const;
+
+      private:
+        explicit CAllocationShadow(CSharedPointer<const CPlan> plan);
+
+        CSharedPointer<const CPlan> m_plan;
+
+        friend class CTiledDRMIntegrationRegistry;
+    };
+
+    using PAllocationShadow = CSharedPointer<const CAllocationShadow>;
+
     class CTiledDRMIntegrationRegistry;
 
     class AQ_TILED_DRM_PRIVATE CConnectorScan final {
@@ -141,6 +210,11 @@ namespace Aquamarine::NDRM::NTiledDRMIntegration { // NOLINT(readability-identif
         [[nodiscard]] eScanCommitResult         commitConnectorScan(const PConnectorScan& scan) noexcept;
         void                                    abortConnectorScan(const PConnectorScan& scan) noexcept;
 
+        [[nodiscard]] eAllocationCaptureResult  captureAllocationShadow(const CSharedPointer<CDRMBackend>& backend, SAllocationCapture capture) noexcept;
+        [[nodiscard]] PAllocationShadow         currentAllocationShadow(const CWeakPointer<CDRMBackend>& backend) noexcept;
+        [[nodiscard]] bool                      withCurrentAllocationShadow(const CWeakPointer<CDRMBackend>& backend, const PAllocationShadow& expected,
+                                                                            const std::function<void(const CPlan&, const SCurrentState&)>& callback) noexcept;
+
         [[nodiscard]] eSessionResult            rotateSession(const CWeakPointer<CDRMBackend>& backend) noexcept;
         [[nodiscard]] eDetachResult             beginDetach(const CWeakPointer<CDRMBackend>& backend, eDetachReason reason) noexcept;
         [[nodiscard]] eDetachResult             eraseDetached(const CWeakPointer<CDRMBackend>& backend) noexcept;
@@ -162,6 +236,10 @@ namespace Aquamarine::NDRM::NTiledDRMIntegration { // NOLINT(readability-identif
         [[nodiscard]] bool                      attachedKey(const void* key) const noexcept;
         [[nodiscard]] PCommittedScan            currentCompleteKey(const void* key) noexcept;
         [[nodiscard]] SScanOutcome              outcomeKey(const void* key) noexcept;
+        [[nodiscard]] eAllocationCaptureResult  captureAllocationKey(const void* key, SAllocationCapture capture) noexcept;
+        [[nodiscard]] PAllocationShadow         currentAllocationKey(const void* key) noexcept;
+        [[nodiscard]] bool                      withCurrentAllocationKey(const void* key, const PAllocationShadow& expected,
+                                                                         const std::function<void(const CPlan&, const SCurrentState&)>& callback) noexcept;
         void                                    abortConnectorScan(CConnectorScan& scan) noexcept;
 
         [[nodiscard]] SBeginConnectorScanResult beginTestConnectorScan(const PBackendLifetime& lifetime);
@@ -172,6 +250,11 @@ namespace Aquamarine::NDRM::NTiledDRMIntegration { // NOLINT(readability-identif
         [[nodiscard]] bool                      testLastConnectorScanComplete(const PBackendLifetime& lifetime) const noexcept;
         [[nodiscard]] PCommittedScan            testCurrentComplete(const PBackendLifetime& lifetime) noexcept;
         [[nodiscard]] SScanOutcome              testOutcome(const PBackendLifetime& lifetime) noexcept;
+        [[nodiscard]] eAllocationCaptureResult  captureTestAllocationShadow(const PBackendLifetime& lifetime, SAllocationCapture capture) noexcept;
+        [[nodiscard]] PAllocationShadow         testCurrentAllocationShadow(const PBackendLifetime& lifetime) noexcept;
+        [[nodiscard]] bool                      withCurrentTestAllocationShadow(const PBackendLifetime& lifetime, const PAllocationShadow& expected,
+                                                                                const std::function<void(const CPlan&, const SCurrentState&)>& callback) noexcept;
+        [[nodiscard]] bool                      exhaustTestResourceEpoch(const PBackendLifetime& lifetime) noexcept;
 
         class CImpl;
         Hyprutils::Memory::CUniquePointer<CImpl> m_impl;
@@ -185,6 +268,10 @@ namespace Aquamarine::NDRM::NTiledDRMIntegration { // NOLINT(readability-identif
                                                                                     std::vector<SConnectorView> connectors) noexcept;
     [[nodiscard]] AQ_TILED_DRM_PRIVATE eScanCommitResult         commitConnectorScan(const PConnectorScan& scan) noexcept;
     AQ_TILED_DRM_PRIVATE void                                    abortConnectorScan(const PConnectorScan& scan) noexcept;
+    [[nodiscard]] AQ_TILED_DRM_PRIVATE eAllocationCaptureResult  captureAllocationShadow(const CSharedPointer<CDRMBackend>& backend, SAllocationCapture capture) noexcept;
+    [[nodiscard]] AQ_TILED_DRM_PRIVATE PAllocationShadow         currentAllocationShadow(const CWeakPointer<CDRMBackend>& backend) noexcept;
+    [[nodiscard]] AQ_TILED_DRM_PRIVATE bool                      withCurrentAllocationShadow(const CWeakPointer<CDRMBackend>& backend, const PAllocationShadow& expected,
+                                                                                             const std::function<void(const CPlan&, const SCurrentState&)>& callback) noexcept;
     [[nodiscard]] AQ_TILED_DRM_PRIVATE eSessionResult            rotateSession(const CWeakPointer<CDRMBackend>& backend) noexcept;
     [[nodiscard]] AQ_TILED_DRM_PRIVATE eDetachResult             beginDetach(const CWeakPointer<CDRMBackend>& backend, eDetachReason reason) noexcept;
     [[nodiscard]] AQ_TILED_DRM_PRIVATE eDetachResult             eraseDetached(const CWeakPointer<CDRMBackend>& backend) noexcept;
diff --git a/tests/TiledDRMIntegration.cpp b/tests/TiledDRMIntegration.cpp
index 970b236515f76a537cd3ba56750d559c117980bc..af54d041c8f02f566ea241d9a64bdb424294e2c6 100644
--- a/tests/TiledDRMIntegration.cpp
+++ b/tests/TiledDRMIntegration.cpp
@@ -1,8 +1,10 @@
 #include "backend/drm/TiledDRMIntegration.hpp"
 #include "shared.hpp"
 
+#include <algorithm>
 #include <atomic>
 #include <cstdint>
+#include <string_view>
 #include <thread>
 #include <utility>
 #include <vector>
@@ -11,6 +13,7 @@
 
 using namespace Aquamarine::NDRM::NTiledDRMIntegration;
 using namespace Aquamarine::NDRM::NTiledIncarnations;
+using namespace Aquamarine::NDRM::NTiledCrtcAllocator;
 using namespace Aquamarine::NDRM::NTiledScan;
 using namespace Hyprutils::Memory;
 
@@ -50,6 +53,23 @@ namespace Aquamarine::NDRM::NTiledDRMIntegrationTest {
         static SScanOutcome outcome(CTiledDRMIntegrationRegistry& registry, const PBackendLifetime& lifetime) {
             return registry.testOutcome(lifetime);
         }
+
+        static eAllocationCaptureResult capture(CTiledDRMIntegrationRegistry& registry, const PBackendLifetime& lifetime, SAllocationCapture input) {
+            return registry.captureTestAllocationShadow(lifetime, std::move(input));
+        }
+
+        static PAllocationShadow allocation(CTiledDRMIntegrationRegistry& registry, const PBackendLifetime& lifetime) {
+            return registry.testCurrentAllocationShadow(lifetime);
+        }
+
+        static bool withCurrent(CTiledDRMIntegrationRegistry& registry, const PBackendLifetime& lifetime, const PAllocationShadow& expected,
+                                const std::function<void(const CPlan&, const SCurrentState&)>& callback) {
+            return registry.withCurrentTestAllocationShadow(lifetime, expected, callback);
+        }
+
+        static bool exhaustResourceEpoch(CTiledDRMIntegrationRegistry& registry, const PBackendLifetime& lifetime) {
+            return registry.exhaustTestResourceEpoch(lifetime);
+        }
     };
 }
 
@@ -76,6 +96,10 @@ namespace {
         return static_cast<int>(result);
     }
 
+    int value(eAllocationCaptureResult result) {
+        return static_cast<int>(result);
+    }
+
     int value(eScanOutcome result) {
         return static_cast<int>(result);
     }
@@ -84,6 +108,33 @@ namespace {
         return {.connectorId = id};
     }
 
+    drmModeModeInfo mode(uint16_t width, uint16_t height, std::string_view name) {
+        drmModeModeInfo result = {
+            .clock       = 241500,
+            .hdisplay    = width,
+            .hsync_start = static_cast<uint16_t>(width + 8),
+            .hsync_end   = static_cast<uint16_t>(width + 40),
+            .htotal      = static_cast<uint16_t>(width + 80),
+            .vdisplay    = height,
+            .vsync_start = static_cast<uint16_t>(height + 1),
+            .vsync_end   = static_cast<uint16_t>(height + 9),
+            .vtotal      = static_cast<uint16_t>(height + 40),
+            .vrefresh    = 60,
+            .flags       = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC,
+            .type        = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
+        };
+        std::ranges::copy_n(name.begin(), std::min(name.size(), sizeof(result.name)), result.name);
+        return result;
+    }
+
+    SConnectorView tiledConnector(uint32_t id, std::string_view tile) {
+        return {
+            .connectorId  = id,
+            .tileProperty = std::string{tile},
+            .modes        = {mode(2560, 1440, id == 101 ? "left" : "right")},
+        };
+    }
+
     PCommittedScan commitComplete(CTiledDRMIntegrationRegistry& registry, const PBackendLifetime& lifetime, uint32_t connectorId, int& ret) {
         auto begun = CFixtureAuthority::begin(registry, lifetime);
         EXPECT(value(begun.result), value(eScanBeginResult::STARTED));
@@ -92,6 +143,51 @@ namespace {
         EXPECT(value(registry.commitConnectorScan(begun.scan)), value(eScanCommitResult::COMMITTED));
         return CFixtureAuthority::current(registry, lifetime);
     }
+
+    PCommittedScan commitComplete(CTiledDRMIntegrationRegistry& registry, const PBackendLifetime& lifetime, std::vector<SConnectorView> connectors, int& ret) {
+        auto begun = CFixtureAuthority::begin(registry, lifetime);
+        EXPECT(value(begun.result), value(eScanBeginResult::STARTED));
+        EXPECT(!!begun.scan, true);
+        EXPECT(value(registry.stageConnectorScan(begun.scan, eScanCompleteness::COMPLETE, std::move(connectors))), value(eScanStageResult::PREPARED));
+        EXPECT(value(registry.commitConnectorScan(begun.scan)), value(eScanCommitResult::COMMITTED));
+        return CFixtureAuthority::current(registry, lifetime);
+    }
+
+    SAllocationCapture allocationCapture() {
+        auto left   = makeShared<Aquamarine::SDRMConnector>();
+        auto right  = makeShared<Aquamarine::SDRMConnector>();
+        auto crtc0  = makeShared<Aquamarine::SDRMCRTC>();
+        auto crtc1  = makeShared<Aquamarine::SDRMCRTC>();
+        auto plane0 = makeShared<Aquamarine::SDRMPlane>();
+        auto plane1 = makeShared<Aquamarine::SDRMPlane>();
+        left->id    = 101;
+        right->id   = 202;
+        crtc0->id   = 10;
+        crtc1->id   = 11;
+        plane0->id  = 100;
+        plane1->id  = 101;
+        return {
+            .resourceCRTCOrder = {10, 11},
+            .connectors =
+                {
+                    {.connector = left, .possibleCRTCMask = 0b01, .assignedCRTCID = 10, .kernelCRTCID = 10, .enabled = true},
+                    {.connector = right, .possibleCRTCMask = 0b10, .assignedCRTCID = 11, .kernelCRTCID = 11, .enabled = true},
+                },
+            .crtcs =
+                {
+                    {.crtc = crtc0, .preferredPrimaryPlaneID = 100, .kernelActive = true},
+                    // Plane 101 remains a candidate even though legacy's greedy
+                    // CRTC cache did not select it as this CRTC's preference.
+                    {.crtc = crtc1, .kernelActive = true},
+                },
+            .primaryPlanes =
+                {
+                    {.plane = plane0, .possibleCRTCMask = 0b01, .kernelCRTCID = 10, .usable = true},
+                    {.plane = plane1, .possibleCRTCMask = 0b10, .kernelCRTCID = 11, .usable = true},
+                },
+            .leasesKnownClear = true,
+        };
+    }
 }
 
 int main() {
@@ -197,5 +293,116 @@ int main() {
         EXPECT(registry.readyForDestruction(), true);
     }
 
+    {
+        CTiledDRMIntegrationRegistry registry;
+        const auto                   LIFETIME = CBackendLifetime::createTestToken(61);
+        const auto                   SCAN     = commitComplete(registry, LIFETIME,
+                                                               {
+                                                                   tiledConnector(101, "7:1:2:1:0:0:2560:1440"),
+                                                                   tiledConnector(202, "7:1:2:1:1:0:2560:1440"),
+                                                               },
+                                                               ret);
+        EXPECT(!!SCAN, true);
+        EXPECT(SCAN ? SCAN->definitions().size() : 0U, 1U);
+
+        EXPECT(value(CFixtureAuthority::capture(registry, LIFETIME, allocationCapture())), value(eAllocationCaptureResult::INSTALLED));
+        const auto SHADOW = CFixtureAuthority::allocation(registry, LIFETIME);
+        EXPECT(!!SHADOW, true);
+        EXPECT(SHADOW && SHADOW->plan()->status() == ePlanStatus::READY, true);
+        EXPECT(SHADOW ? SHADOW->plan()->groups().size() : 0U, 1U);
+        EXPECT(SHADOW && SHADOW->plan()->groups().front().status() == eGroupStatus::ALLOCATED, true);
+        EXPECT(SHADOW ? SHADOW->plan()->groups().front().bindings().at(1).primaryPlane()->id : 0U, 101U);
+        size_t                    callbacks     = 0;
+        bool                      exactScan     = false;
+        bool                      nestedCurrent = true;
+        eAllocationCaptureResult  nestedCapture = eAllocationCaptureResult::INSTALLED;
+        SBeginConnectorScanResult nestedBegin;
+        eSessionResult            nestedRotate = eSessionResult::ROTATED;
+        eDetachResult             nestedDetach = eDetachResult::BEGUN;
+        eDetachResult             nestedErase  = eDetachResult::DETACHED;
+        EXPECT(CFixtureAuthority::withCurrent(registry, LIFETIME, SHADOW,
+                                              [&](const CPlan&, const SCurrentState& current) {
+                                                  ++callbacks;
+                                                  exactScan     = current.scan == SCAN;
+                                                  nestedCurrent = CFixtureAuthority::withCurrent(registry, LIFETIME, SHADOW, [](const CPlan&, const SCurrentState&) {});
+                                                  nestedCapture = CFixtureAuthority::capture(registry, LIFETIME, allocationCapture());
+                                                  nestedBegin   = CFixtureAuthority::begin(registry, LIFETIME);
+                                                  nestedRotate  = CFixtureAuthority::rotate(registry, LIFETIME);
+                                                  nestedDetach  = CFixtureAuthority::beginDetach(registry, LIFETIME, eDetachReason::BACKEND_SHUTDOWN);
+                                                  nestedErase   = CFixtureAuthority::erase(registry, LIFETIME);
+                                              }),
+               true);
+        EXPECT(callbacks, 1U);
+        EXPECT(exactScan, true);
+        EXPECT(nestedCurrent, false);
+        EXPECT(value(nestedCapture), value(eAllocationCaptureResult::BUSY));
+        EXPECT(value(nestedBegin.result), value(eScanBeginResult::BUSY));
+        EXPECT(!!nestedBegin.scan, false);
+        EXPECT(value(nestedRotate), value(eSessionResult::BUSY));
+        EXPECT(value(nestedDetach), value(eDetachResult::BUSY));
+        EXPECT(value(nestedErase), value(eDetachResult::BUSY));
+        EXPECT(CFixtureAuthority::allocation(registry, LIFETIME) == SHADOW, true);
+        EXPECT(CFixtureAuthority::lastComplete(registry, LIFETIME), true);
+
+        auto pending                = allocationCapture();
+        pending.crtcs.at(0).pending = true;
+        EXPECT(value(CFixtureAuthority::capture(registry, LIFETIME, std::move(pending))), value(eAllocationCaptureResult::INVALID_CAPTURE));
+        EXPECT(!!CFixtureAuthority::allocation(registry, LIFETIME), false);
+        EXPECT(CFixtureAuthority::withCurrent(registry, LIFETIME, SHADOW, [&](const CPlan&, const SCurrentState&) { ++callbacks; }), false);
+        EXPECT(callbacks, 1U);
+
+        auto ambiguous             = allocationCapture();
+        ambiguous.leasesKnownClear = false;
+        EXPECT(value(CFixtureAuthority::capture(registry, LIFETIME, std::move(ambiguous))), value(eAllocationCaptureResult::INVALID_CAPTURE));
+        EXPECT(!!CFixtureAuthority::allocation(registry, LIFETIME), false);
+
+        EXPECT(value(CFixtureAuthority::capture(registry, LIFETIME, allocationCapture())), value(eAllocationCaptureResult::INSTALLED));
+        const auto REPLACEMENT = CFixtureAuthority::allocation(registry, LIFETIME);
+        EXPECT(!!REPLACEMENT, true);
+        EXPECT(REPLACEMENT != SHADOW, true);
+        EXPECT(CFixtureAuthority::withCurrent(registry, LIFETIME, SHADOW, [&](const CPlan&, const SCurrentState&) { ++callbacks; }), false);
+        EXPECT(callbacks, 1U);
+        auto begun = CFixtureAuthority::begin(registry, LIFETIME);
+        EXPECT(value(begun.result), value(eScanBeginResult::STARTED));
+        EXPECT(!!CFixtureAuthority::allocation(registry, LIFETIME), false);
+        EXPECT(CFixtureAuthority::withCurrent(registry, LIFETIME, REPLACEMENT, [&](const CPlan&, const SCurrentState&) { ++callbacks; }), false);
+        EXPECT(callbacks, 1U);
+        registry.abortConnectorScan(begun.scan);
+
+        EXPECT(!!commitComplete(registry, LIFETIME,
+                                {
+                                    tiledConnector(101, "7:1:2:1:0:0:2560:1440"),
+                                    tiledConnector(202, "7:1:2:1:1:0:2560:1440"),
+                                },
+                                ret),
+               true);
+        EXPECT(value(CFixtureAuthority::capture(registry, LIFETIME, allocationCapture())), value(eAllocationCaptureResult::INSTALLED));
+        const auto BEFORE_ROTATE = CFixtureAuthority::allocation(registry, LIFETIME);
+        EXPECT(value(CFixtureAuthority::rotate(registry, LIFETIME)), value(eSessionResult::ROTATED));
+        EXPECT(CFixtureAuthority::withCurrent(registry, LIFETIME, BEFORE_ROTATE, [&](const CPlan&, const SCurrentState&) { ++callbacks; }), false);
+        EXPECT(callbacks, 1U);
+
+        EXPECT(!!commitComplete(registry, LIFETIME,
+                                {
+                                    tiledConnector(101, "7:1:2:1:0:0:2560:1440"),
+                                    tiledConnector(202, "7:1:2:1:1:0:2560:1440"),
+                                },
+                                ret),
+               true);
+        EXPECT(value(CFixtureAuthority::capture(registry, LIFETIME, allocationCapture())), value(eAllocationCaptureResult::INSTALLED));
+        const auto BEFORE_EXHAUSTION = CFixtureAuthority::allocation(registry, LIFETIME);
+        EXPECT(CFixtureAuthority::exhaustResourceEpoch(registry, LIFETIME), true);
+        const auto EXHAUSTED = CFixtureAuthority::begin(registry, LIFETIME);
+        EXPECT(value(EXHAUSTED.result), value(eScanBeginResult::EXHAUSTED));
+        EXPECT(!!EXHAUSTED.scan, false);
+        EXPECT(!!CFixtureAuthority::allocation(registry, LIFETIME), false);
+        EXPECT(CFixtureAuthority::lastComplete(registry, LIFETIME), false);
+        EXPECT(CFixtureAuthority::withCurrent(registry, LIFETIME, BEFORE_EXHAUSTION, [&](const CPlan&, const SCurrentState&) { ++callbacks; }), false);
+        EXPECT(callbacks, 1U);
+
+        EXPECT(value(CFixtureAuthority::beginDetach(registry, LIFETIME, eDetachReason::BACKEND_SHUTDOWN)), value(eDetachResult::BEGUN));
+        EXPECT(value(CFixtureAuthority::erase(registry, LIFETIME)), value(eDetachResult::DETACHED));
+    }
+
     return ret;
 }