Intent
What bad outcome is prevented? What is the safety policy when the kernel’s topology data is malformed or changes mid-scan?
Aquamarine · guided code review
A step-by-step explanation of why tiled connectors were hidden incorrectly, how commit c8dd371 makes that decision conservative, and where you should still push back before upstream submission.
Target: explain it without this page
01 · Review method
A raw diff makes you process syntax before you have a model. Use three passes instead. Each pass should answer a different question.
What bad outcome is prevented? What is the safety policy when the kernel’s topology data is malformed or changes mid-scan?
Follow one connector from its length-delimited TILE blob through pure validation into tilingRedundant.
Map every promised invariant to an assertion, then name the remaining integration and hardware gaps yourself.
The governing model
Hiding a connector is destructive from the compositor’s point of view. This remains a dimensions-based heuristic, but the patch only acts after five conservative gates agree.
Here, “open” means exposing connectors to the compositor. Malformed, incomplete, ambiguous, unreadable, or detected kernel-known disconnected state produces a safe false negative instead of suppression.
The old failure
The previous code stored the largest width and largest height independently. Those values could come from different advertised modes, yet the pair was treated as real.
The old maxMode became width 5120 and height 2880, even though no mode contained both.
The old condition passed and could hide a usable peer.
- const auto& ti = members.at(0)->tileInfo;- int fullWidth = ti.numHTile * ti.tileHSize;- int fullHeight = ti.numVTile * ti.tileVSize;- if (conn->maxMode.x >= fullWidth &&- conn->maxMode.y >= fullHeight)- fullResConn = conn;
// 1. First member represents every tile.
// 2. width × height is a real mode pair.
// 3. the first qualifying connector is unique.
All three can be false.
first tile width × column count only works for uniform grids. A valid 2×2 layout can have 1920- and 2560-pixel columns, plus 1080- and 1200-pixel rows. The patch sums distinct columns and rows instead.
Conceptual file order
Review them in dependency order, not alphabetical order. The pure model explains the policy; the DRM adapter explains where it takes effect.
src/backend/drm/Tiling.hppSmall hidden data model: parsed tile, concrete mode pair, connector, validated group.src/backend/drm/Tiling.cppLength-aware parser and hardware-independent topology validator.src/backend/drm/DRM.cppNon-probing libdrm state reads, grouping, and the operational suppression mutation: tilingRedundant.tests/DRMTiling.cpp48 assertions that act as the executable policy specification.CMakeLists.txtAdds one focused test target by compiling the pure helper directly.02 · Guided diff
The 410 added lines are mostly a testable policy boundary and its specification. Four chapters cover every substantive change.
The old sscanf call saw a pointer but not the blob length. The new parser receives a string_view over exactly those bytes, consumes exactly eight fields, and refuses partial conversions.
- char* blobData = (char*)getDRMPropBlob(...);- if (sscanf(blobData,- "%u:%d:%d:%d:%d:%d:%d:%d", ...) == 8)- tileInfo = ...;- free(blobData);
+ auto blobData = std::unique_ptr<char,+ decltype(&std::free)>{..., &std::free};+ const auto PARSED = parseTileInfo(+ std::string_view{blobData.get(), blobLen});+ if (PARSED)+ tileInfo = validated values;
from_chars consumptiongroupId > INT_MAX is rejected.The kernel’s tile-group ID is an int and its canonical blob serialization uses signed decimal. Positive kernel-produced IDs therefore fit INT_MAX, even though Aquamarine stores the value in uint32_t. A short source comment could make that provenance easier for the next reviewer to recover.
validateTileGroup is a pure function. It takes plain connector snapshots and either returns trustworthy aggregate geometry or no value. This is the architectural center of the patch.
Every tile in one column must agree on width. Every tile in one row must agree on height. Then each column and row contributes exactly once.
| Gate | What it rejects | Why it matters |
|---|---|---|
| Count | members ≠ columns × rows | No connector disappears merely because only part of a group was discovered. |
| Identity | zero/duplicate connector IDs, mixed group IDs | Keeps the anchor sentinel unambiguous and the group coherent. |
| Coordinates | duplicate or out-of-range locations | Every grid cell is represented exactly once. |
| Geometry | width disagreement within a column; height disagreement within a row | The aggregate can be derived without trusting the first tile. |
| Arithmetic | aggregate over uint32_t | Summation happens in uint64_t; impossible sizes fail closed to validation and open to outputs. |
for (const auto& MODE : CONNECTOR.modes) {
if (MODE.width == width && MODE.height == height) {
hasFullMode = true;
break; // duplicate modes on one connector are harmless
}
}
if (another connector also has it)
ambiguousFullMode = true;
return STileGroup{ .fullModeConnector =
ambiguousFullMode ? 0U : fullModeConnector };
A complete grid may return successfully with anchor 0 when no connector, or more than one connector, advertises the aggregate pair. The caller then suppresses nothing. Geometry truth and policy certainty remain separate.
The thin DRM adapter calls drmModeGetConnectorCurrent for every candidate member. This is a second, non-probing read of the connector state already known to the kernel. If the object is missing or reports disconnected, classification aborts instead of hiding anything from that snapshot.
parseTileInfo() starts from {}; tilingRedundant is cleared for every connector on every scan.
Disconnected or unparsed connectors never enter the candidate map; single-member groups take the existing fast path.
drmModeGetConnectorCurrent for every member.Copy actual width/height pairs. A missing object or one whose kernel-known state reports disconnected returns nullopt.
No libdrm object crosses the policy boundary, which keeps the core behavior deterministic and unit-testable.
Existing downstream code disconnects redundant outputs and excludes them from CRTC assignment.
auto drmConn = drmModeGetConnectorCurrent(fd, conn->id);
if (!drmConn || drmConn->connection != DRM_MODE_CONNECTED) {
if (drmConn)
drmModeFreeConnector(drmConn);
return std::nullopt;
}
for (int i = 0; i < drmConn->count_modes; ++i)
tile.modes.emplace_back(width, height);
if (GROUP->fullModeConnector == 0)
continue;
for (const auto& conn : members) {
if (conn->id == GROUP->fullModeConnector)
continue;
conn->tilingRedundant = true;
}
drmModeGetConnectorCurrent does not actively probe the hardware. It can observe kernel-known state changes, but a connector can still disappear after the read and before suppression. Later hotplug rechecks can repair state; there is no atomic topology transaction here.
The new drmTiling executable compiles Tiling.cpp directly. That lets the test call hidden, source-private helpers without exporting them from the shared library or linking the full libdrm backend.
Small enough to read linearly. Treat each test block as a policy decision, not just coverage.
The pure suite does not directly exercise empty validator input, zero first-member group/grid fields, validator-level zero sizes or out-of-range coordinates, row-height mismatch, or height overflow. Separately, state-read failure/disconnection, output/CRTC integration, and real hardware behavior are not unit-tested. Decide whether more cases or an injectable reader add enough confidence without overgrowing this patch.
03 · Contributor judgment
A seasoned review is not blanket approval. These are the decisions I would defend, preserve with caveats, or ask to revisit.
| Judgment | Design choice | Contributor reasoning |
|---|---|---|
| Defend | Fail open | A false negative exposes an extra connector. A false positive hides a usable one and can strand the session. |
| Defend | Exact width × height pair | A larger mode or independent maxima is not evidence of driver-managed tile aggregation. |
| Defend | Pure helper + thin adapter | The difficult policy becomes deterministic and testable without introducing a fake DRM stack. |
| Defend | Hidden source-private types | No installed header churn and no new dynamic symbols for an internal classification detail. |
| Defend | Retain existing maxMode | Classification no longer relies on it, but the member lives in an installed public struct. Removing it here would change layout/API and mix cleanup into a focused safety fix. |
| Defend explicitly | isSingleMonitor is consistency-only | A TILE group composes connectors into one logical screen; this bit describes whether they share one housing. Preserving all-false groups avoids a separate behavior change, while suppressibility still comes from the aggregate-mode heuristic. Make that rationale explicit upstream. |
| Preserve, caveat | Second connector state read | It conservatively catches a missing object or kernel-known disconnected state. It does not probe, is not atomic, and is not directly unit-tested. |
| Defend | groupId limited to INT_MAX | The kernel tile-group ID is signed int and canonical serialization uses signed decimal. The parser is validating the producer’s domain, not merely its local storage type. |
| Question | Resolution-only anchor identity | The patch intentionally preserves the old dimensions-only policy. Ask whether refresh/timing or preferred-mode identity is required, but do not expand scope casually. |
| Minor | Anchor diagnostics collapse to 0 | “No exact mode” and “more than one anchor” both suppress nothing. Separate debug logs could help field diagnosis but are not required for safety. |
It is an independently useful hardening patch for Aquamarine’s existing redundant-tile heuristic. The larger aggregation and CTM work remains a separate architectural series with different risks.
04 · Verification ledger
These results were reproduced against the clean branch on August 28, 2026. The commands deliberately remove a machine-specific library override.
clean build, all passed
clean build, all passed
no whitespace errors
installed interface files changed
The shell inherits an LD_LIBRARY_PATH pointing at a deployed experimental Aquamarine build. Old prototype test binaries then load that library before their own build output and fail with irrelevant missing symbols. Use a fresh build directory from this branch and env -u LD_LIBRARY_PATH.
cd "$HOME/Projects/tiled-display/aquamarine-upstream-v2-wt"
env -u LD_LIBRARY_PATH cmake -S . -B /tmp/aquamarine-c8dd371-debug -DCMAKE_BUILD_TYPE=Debug
env -u LD_LIBRARY_PATH cmake --build /tmp/aquamarine-c8dd371-debug -j4
env -u LD_LIBRARY_PATH ctest --test-dir /tmp/aquamarine-c8dd371-debug --output-on-failure
| Check | Result | How to interpret it |
|---|---|---|
| Debug and Release | Reproduced | All five configured branch tests pass: four pre-existing tests plus the new drmTiling target. |
| Patch replay | Verified in final audit | git am onto base 9d6fed9 produced the exact commit tree. |
| ASan + UBSan | 5 / 5 in final audit | The full sanitizer suite passed with leak detection disabled. Focused drmTiling also passed with leak detection enabled. A separate full leak-detection run reported one 128-byte allocation beneath a Mesa/GBM stack while running environment-dependent simpleWindow, outside the patch path. |
| Dynamic symbols | Unchanged in final audit | Release defined-symbol names matched the base; hidden NDRM helpers did not enter the dynamic interface. |
| Nix / upstream CI | Not local | Nix is unavailable on this machine. Treat CI as a required gate, not as assumed success. |
| Physical hotplug/hardware | Not proved by unit tests | The patch adds conservative validation gates, but real driver and hardware behavior remains an integration check. |
git -C "$HOME/Projects/tiled-display/aquamarine-upstream-v2-wt" diff --check 9d6fed9..c8dd371
sha256sum "$HOME/Projects/tiled-display/upstream-patches/aquamarine-9d6fed9-c8dd371/0001-drm-validate-tiled-groups-before-suppressing-connect.patch"
# expected: dc58953120adde209b93d8747bf66e9c21ba80b2b9da5087063bd86606dab89c
05 · Your review route
Do this with the code open beside the guide. The point is not to memorize every condition; it is to be able to justify every class of condition.
Tiling.hpp, then Tiling.cpp:11–60. Challenge every accepted and rejected input class.markRedundantTiles() into validatedTileGroup(), then forward into disconnection and CRTC assignment.git -C "$HOME/Projects/tiled-display/aquamarine-upstream-v2-wt" show --stat c8dd371
git -C "$HOME/Projects/tiled-display/aquamarine-upstream-v2-wt" diff 9d6fed9..c8dd371 -- src/backend/drm/Tiling.cpp
git -C "$HOME/Projects/tiled-display/aquamarine-upstream-v2-wt" diff 9d6fed9..c8dd371 -- src/backend/drm/DRM.cpp
git -C "$HOME/Projects/tiled-display/aquamarine-upstream-v2-wt" diff 9d6fed9..c8dd371 -- tests/DRMTiling.cpp
git -C "$HOME/Projects/tiled-display/aquamarine-upstream-v2-wt" diff 9d6fed9..c8dd371 -- CMakeLists.txt src/backend/drm/Tiling.hpp
Self-check · reveal answers only after responding
maxMode.x ≥ width && maxMode.y ≥ height not enough?The two maxima may come from different modes. Even one larger mode is not proof of the exact aggregate pair. The patch searches concrete width/height pairs and requires equality.
TILE permits nonuniform geometry. Every tile in a column must agree on that column’s width and every tile in a row on that row’s height; the aggregate is the sum of each distinct column and row.
An invalid group has untrustworthy topology and returns nullopt. A valid group may have trustworthy geometry but no unique exact aggregate-mode connector. It returns dimensions with fullModeConnector = 0. Both paths suppress nothing, but for different reasons.
It fails open when the connector object cannot be read or its kernel-known state reports disconnected. drmModeGetConnectorCurrent does not actively probe, and the read does not make the scan-to-suppression sequence atomic; a later disappearance is still possible and handled by subsequent hotplug rechecks.
They describe an internal DRM classification policy, not Aquamarine’s supported public API. Hidden visibility preserves the shared library interface, while direct compilation lets the pure logic be tested without exporting it.
groupId above INT_MAX when Aquamarine stores it in uint32_t?Because the producer’s domain matters too: Linux DRM models the tile-group ID as int and serializes its canonical text form with signed decimal. A positive canonical ID cannot exceed INT_MAX. The check is defensible even though the destination is unsigned.
Human gate
This guide is an educational map, not upstream prose. Before submitting, personally review every changed line, resolve the open judgments above, author the rationale in your own words, and follow Hyprland’s AI-assistance disclosure and contributor requirements.
groupId.