mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 01:53:24 +09:00
[MG_Pipe, MG_Test, docs] (P7): the reverse channel is nine callbacks, and the prose says what the tree does
P3b/P4b espryt D1 slice 3, landing under the P7 program (notes/p7/PLAN-PH-P34B-P7.md
§1.2 row "XFB scatter" item 3; CONTRACT-P7.md §139 already records the deletion).
Plan B section 7.1 named ten reverse-channel callbacks. The tenth, OnXfbScatterReady,
belonged to a design the tree does not have: plan B put the XFB scatter on the CLIENT - the
server would hand back the packed scratch and the client would run the patch loop over its
own shadow - so something had to tell the client the layout. P5c/P5f went the other way,
and the declaration was left behind with zero producers, zero consumers and no EventKind of
its own, while the static_assert beside kMGPipeCallbackCount made it look load-bearing.
Deleted; kMGPipeCallbackCount 10 -> 9.
Three pieces of prose that still described the old design go with it. MGPipeCallbacks.h's
own header said "ten callbacks", and now says nine and why the tenth went.
MGPipeTypes.h's MGPXfbAccounting comment said the scatter "is a read-modify-write of the
client's shadow and lives there"; it lives on the server's staged shadow, and nothing about
the scatter crosses in that record. ARCHITECTURE.md §8.5, titled "XFB scatter 搬到 client",
is rewritten to what the tree does: the pre-capture bytes are the server's own staged
shadow (rule C, since MappedData() on the apply thread is Fatal{RoleViolation,
"buffer-legacy-arm"}), the reconciled whole range comes back as one OnBufferWriteback
sliced against SEG_EVENT's capacity (slice 2), and an orphaned target is a legal one (slice
1). Its row in §8.1's callback table goes too - leaving it would be a new staleness rather
than an inherited one.
PipeCatalogue.ReverseChannelHasTenCallbacks now asserts 9. The CASE NAME IS DELIBERATELY
NOT RENAMED: G2/G14 say the ctest name set only ever grows, so renaming would delete a name
the gate watches. The count it asserts is what has to be right and the comment carries the
number.
Red-once (R-16): with the member and the count restored the case fails with
"kMGPipeCallbackCount Which is: 10 / 9u Which is: 9", which is what makes the struct's
shrink a measured fact - the static_assert beside the constant only proves the two agree
with each other.
G1 matters for this slice in particular, because MGPipeCallbacks.h is included from
MG_Backend and the struct is NOT behind MOBILEGL_BUILD_DISAGGREGATED, so
sizeof(gMGPipeCallbacks) really does shrink by 8 bytes in the pull build. The shrink is in
.bss and nothing indexes the table by count: .text is still 0xa52203 and both nm lists are
byte-identical to ~/w7/p7-before.
Gates: integration-split 202/202, integration-spawn 118/118, integration-tcp 121/121,
ctest -L unit 2408/2408, spawn_lane_parity / fatal_census / link_ratchet (186, unchanged)
green.
This commit is contained in:
@@ -17,9 +17,20 @@
|
||||
// Today this traffic is 95 call sites across 17 methods poked directly into frontend
|
||||
// objects. gallium has no vocabulary for shadow writeback, GPU-write notification, texture
|
||||
// re-send requests or default-framebuffer geometry, because in Mesa the state tracker and
|
||||
// the driver share an address space. Naming them as ten callbacks plus one forward
|
||||
// the driver share an address space. Naming them as nine callbacks plus one forward
|
||||
// terminator (MGPipeContext::ResourceSubDataComplete) is the deliberate deviation (D8).
|
||||
//
|
||||
// NINE, NOT THE TEN PLAN B WROTE. The tenth was OnXfbScatterReady, and it went with the
|
||||
// design it belonged to: plan B put the XFB scatter on the CLIENT (the server would hand
|
||||
// back the packed scratch and the client would run the patch loop over its own shadow), so
|
||||
// there had to be a callback that told the client the layout. P5c/P5f went the other way -
|
||||
// the server reads its OWN staged shadow, scatters there and returns the reconciled range
|
||||
// through OnBufferWriteback, which needs no second callback - and the declaration was left
|
||||
// behind with zero producers, zero consumers and no EventKind of its own. Deleted here
|
||||
// (P3b/P4b espryt D1 slice 3) rather than carried: an entry that cannot fire is one every
|
||||
// reader of this file has to rule out by hand, and the size assertion below made it look
|
||||
// load-bearing.
|
||||
//
|
||||
// Installed at context creation. In a monolith these are direct calls; under split they are
|
||||
// records on the reverse channel, and their ORDER is a correctness requirement rather than
|
||||
// an optimization (section 7.4).
|
||||
@@ -46,14 +57,11 @@ namespace MobileGL::MG_Pipe {
|
||||
void (*OnCapsInvalidated)();
|
||||
// <= WARN is lossy, >= ERROR is lossless and rate limited.
|
||||
void (*OnLog)(Uint8 level, const char* text);
|
||||
// The XFB scatter is a read-modify-write of the CLIENT's shadow, so the server
|
||||
// hands back the packed scratch and the client scatters (section 7.2.1).
|
||||
void (*OnXfbScatterReady)(MGPipeHandle scratch, Uint64 packedStride, Uint64 vertices);
|
||||
};
|
||||
|
||||
// Ten, and the count is asserted so an eleventh cannot be added without touching the
|
||||
// Nine, and the count is asserted so a tenth cannot be added without touching the
|
||||
// transport's reverse-channel record table.
|
||||
inline constexpr SizeT kMGPipeCallbackCount = 10;
|
||||
inline constexpr SizeT kMGPipeCallbackCount = 9;
|
||||
static_assert(sizeof(MGPipeCallbacks) == kMGPipeCallbackCount * sizeof(void (*)()),
|
||||
"MGPipeCallbacks gained or lost a callback");
|
||||
|
||||
|
||||
@@ -1672,8 +1672,11 @@ namespace MobileGL::MG_Pipe {
|
||||
};
|
||||
MGP_ASSERT_POD(MGPStreamOutputBegin, 120);
|
||||
|
||||
// end_stream_output carries the accounting the client owns; the scatter itself is a
|
||||
// read-modify-write of the client's shadow and lives there (section 7.2.1).
|
||||
// end_stream_output carries the accounting the client owns. THE SCATTER ITSELF IS THE
|
||||
// SERVER'S, and this sentence used to say the opposite: plan B section 7.2.1 put the
|
||||
// read-modify-write on the client's shadow, P5c/P5f put it on the server's own staged
|
||||
// shadow, and the reconciled range comes back as an ordinary OnBufferWriteback. Nothing
|
||||
// about the scatter crosses in this record.
|
||||
struct MGPXfbAccounting {
|
||||
Uint64 CapturedVertices;
|
||||
Uint64 PrimitivesWritten;
|
||||
|
||||
@@ -1098,9 +1098,17 @@ TEST(PipeCatalogue, PipelineSubsetMembersArePinned) {
|
||||
"ScissorTestEnabledMask");
|
||||
}
|
||||
|
||||
// The reverse channel is exactly ten callbacks (section 7.1).
|
||||
// The reverse channel is exactly NINE callbacks: plan B's section 7.1 wrote ten, and the tenth
|
||||
// (OnXfbScatterReady) went with the design it belonged to when P5c/P5f moved the XFB scatter to
|
||||
// the server's own staged shadow and its OnBufferWriteback return path. P3b/P4b espryt D1 slice 3
|
||||
// deleted the declaration; this case is what makes the struct's shrink a measured fact rather
|
||||
// than a claim, since the static_assert beside kMGPipeCallbackCount only proves the two agree.
|
||||
//
|
||||
// THE CASE NAME STAYS "…HasTenCallbacks" DELIBERATELY. G2/G14 say the ctest name set only ever
|
||||
// grows, so renaming this would delete a name the gate is watching; the count it asserts is what
|
||||
// has to be right, and the comment is where the number lives.
|
||||
TEST(PipeCatalogue, ReverseChannelHasTenCallbacks) {
|
||||
EXPECT_EQ(kMGPipeCallbackCount, 10u);
|
||||
EXPECT_EQ(kMGPipeCallbackCount, 9u);
|
||||
EXPECT_EQ(sizeof(MGPipeCallbacks), kMGPipeCallbackCount * sizeof(void (*)()));
|
||||
}
|
||||
|
||||
|
||||
@@ -260,7 +260,6 @@ GL 是每 unit 每 target 各一个绑定;shader 看见哪一个取决于 samp
|
||||
| `OnSurfaceChanged(info)` | `SwapchainObject` 写 `pDefaultFramebufferInfo` 的分层倒置;client 自己合成 default-FB 对象 |
|
||||
| `OnCapsInvalidated()` | 2 处 `InvalidateCompileEnv` |
|
||||
| `OnLog(level, text)` | ≤WARN 有损,≥ERROR 无损 + 速率限制 |
|
||||
| `OnXfbScatterReady(scratch, packedStride, vertices)` | §8.5 |
|
||||
|
||||
95 个写回点的其余归属:`MarkStorageDirty` 大多是 server 本地记账;后端凭空造的前端对象(Magma 占位纹理、swapchain default-FB 占位)→ server 原生;`SetBackendStateMemo` 直接删除;`SetBackendHashMemo/AuxMemo` → server 侧 per-slot 字段。**D-K(P3a)**:`PipeResource::m_backend` / `SetBackendResource` / `ReleaseBackend` / `BackendBufferResource` 在 push 下只是不再被写,真删会移动 pull 构建里 `sizeof(BufferObject)`(G1 破坏),随 pull 路径在 P13 退役。
|
||||
|
||||
@@ -278,9 +277,16 @@ GL 是每 unit 每 target 各一个绑定;shader 看见哪一个取决于 samp
|
||||
|
||||
server 不保留纹素,三个原因会要求重发已发过的 level:`RequireImageBindableStorage` 的 re-dirty、整格式再生、view 源重铸。四条缓解:(1) **预防主因**:`ResourceCreate/Respecify` 一直携带 `ImageBindableHint`;(2) **拉取异步**:server 发 `OnTexturePullRequest` 并把 twin 标 not-ready,阻塞的是 apply 线程不是应用线程;(3) 有上限的保留默认关(`MOBILEGL_PIPE_TEXEL_RETAIN_MB=0`,`MipmapStorage` 保有每 level 完整 CPU 影子,缓存买的是延迟不是正确性);(4) **显式终止符** `ResourceSubDataComplete`,可携带零个 region(内容只来自渲染 / GPU mip 的 level,client 没有字节;server 带着"已分配但为空"的存储继续)。真实语料上重铸拉取率见 `ROADMAP.md` 开放问题 2(780 个窗口 2 次)。P5b 下这条路径是 `Fatal{UnmigratedEmulation,"texture-remint-pull"}`(三条 trace 的首阻塞),P9 落地。
|
||||
|
||||
### 8.5 XFB scatter 搬到 client
|
||||
### 8.5 XFB scatter 留在 server(本节在 P5c/P5f 之后重写;P3b/P4b espryt D1 更正文本)
|
||||
|
||||
Espryt 的 `ScatterCapturedRecords` 是对 client shadow 的 read-modify-write(`gl_SkipComponents` 的空洞保留应用原本的内容)。server 没有 `MappedData()`,所以 server 把紧密打包的 scratch 经 `OnBufferWriteback` 推给 client,用 `OnXfbScatterReady` 告知布局;client 拥有目的 shadow 与反射归档里的 varying/stride,原样跑补丁循环,补好的范围作为普通 `ResourceSubData` 重发。不新增停顿类。
|
||||
Espryt 的 `ScatterCapturedRecords` 是 read-modify-write:`gl_SkipComponents` 的空洞必须保留目的缓冲原本的内容,所以补丁循环需要「捕获前的字节」。本节原来写的是**搬到 client**——server 把紧密打包的 scratch 经 `OnBufferWriteback` 推给 client,用 `OnXfbScatterReady` 告知布局,client 跑补丁循环再以 `ResourceSubData` 重发。**树上走的是相反的一条路**,这一段按树更正:
|
||||
|
||||
- **捕获前的字节是 server 自己的 staged shadow**(rule C:拆分后权威影子归 server,`GLESBufferResource::hostBytes`),不是 client 的 `MappedData()`——后者在 apply 线程上是 `Fatal{RoleViolation, "buffer-legacy-arm"}`(CONTRACT-P5C §3.8)。所以 server 有它需要的一切:影子、反射归档里的 varying/stride(`ProgramArchive->Link`),补丁循环原地跑完。
|
||||
- **回程只有一条 `OnBufferWriteback`**,载的是**补好的整段**,不是 scratch。`OnXfbScatterReady` 因此是零生产者、零消费者、无 EventKind 的死声明,P3b/P4b espryt D1 slice 3 删除,`kMGPipeCallbackCount` 10 → 9。
|
||||
- **该回写按 SEG_EVENT 容量的四分之一切片**(`MGPipeBufferWritebackSliceBytes`,espryt D1 slice 2):字节内联在 SEG_EVENT 记录里,而环形缓冲拒绝任何超过容量一半的记录,所以整段投递在 >128 KiB 时是 `Fatal{EventRingOverflow}`。切片按序投递,最后一片落地即蕴含之前每一片。
|
||||
- **孤儿目标(`HasDefinedContent == 0`)是合法目标**(espryt D1 slice 1):它没有可读的捕获前字节是应用自己声明的,零填充后照常散射并上传,与 monolith 从新鲜 `MappedData()` 读到的逐字节相同。
|
||||
|
||||
不新增停顿类。
|
||||
|
||||
## 9. 后端状态机改造
|
||||
|
||||
|
||||
@@ -172,7 +172,51 @@ byte-identical.
|
||||
|
||||
## slice 3 — the dead `OnXfbScatterReady` declaration
|
||||
|
||||
`RESULT: (pending)`
|
||||
Plan B's section 7.1 named ten reverse-channel callbacks. The tenth, `OnXfbScatterReady`,
|
||||
belonged to a design the tree does not have: plan B put the XFB scatter on the CLIENT — the
|
||||
server would hand back the packed scratch and the client would run the patch loop over its own
|
||||
shadow — so something had to tell the client the layout. P5c/P5f went the other way. The
|
||||
declaration was left behind with **zero producers, zero consumers and no `EventKind`**, and the
|
||||
`static_assert` beside `kMGPipeCallbackCount` made it look load-bearing.
|
||||
|
||||
Deleted; `kMGPipeCallbackCount` 10 → 9. Three pieces of prose that still described the old design
|
||||
go with it:
|
||||
|
||||
* `MGPipeCallbacks.h`'s own header — "ten callbacks" → nine, with the reason the tenth went.
|
||||
* `MGPipeTypes.h`'s `MGPXfbAccounting` comment, which said the scatter "is a read-modify-write of
|
||||
the client's shadow and lives there". It lives on the server's staged shadow.
|
||||
* `ARCHITECTURE.md` §8.5, retitled from 「XFB scatter 搬到 client」 and rewritten to what the tree
|
||||
does: server staged shadow, one `OnBufferWriteback` carrying the reconciled range (sliced —
|
||||
slice 2), orphaned targets legal (slice 1). Its `OnXfbScatterReady` row in §8.1's table goes too,
|
||||
since leaving it would be a new staleness rather than an inherited one.
|
||||
|
||||
`PipeCatalogue.ReverseChannelHasTenCallbacks` now asserts 9. **The case name is deliberately not
|
||||
renamed**: G2/G14 say the ctest name set only grows, so renaming would delete a name the gate
|
||||
watches. The count it asserts is what has to be right; the comment carries the number.
|
||||
|
||||
**RESULT — red once, then green.** With the member and the count restored (everything else as
|
||||
committed), `PipeCatalogue.ReverseChannelHasTenCallbacks` fails:
|
||||
|
||||
```
|
||||
PipeCatalogueTest.cpp:1111: Failure
|
||||
Expected equality of these values:
|
||||
kMGPipeCallbackCount Which is: 10
|
||||
9u Which is: 9
|
||||
```
|
||||
|
||||
which is what makes the struct's shrink a measured fact rather than a claim — the `static_assert`
|
||||
beside the constant only proves the two agree with each other.
|
||||
|
||||
**Gates.** `integration-split` 202/202, `integration-spawn` 118/118, `integration-tcp` 121/121,
|
||||
`ctest -L unit` 2408/2408, parity / census / ratchet (186) green. **G1 run for this slice in
|
||||
particular**, because `MGPipeCallbacks.h` is included from MG_Backend and the struct is *not*
|
||||
behind `MOBILEGL_BUILD_DISAGGREGATED`, so `sizeof(gMGPipeCallbacks)` really does shrink by 8 bytes
|
||||
in the pull build: `.text` still `0xa52203` and both nm lists still byte-identical. The shrink is
|
||||
in `.bss` and nothing indexes the table by count.
|
||||
|
||||
*(One unrelated flake seen and cleared: `DirectGLES.Tcp.PrimitiveRestartScenario.TheFixedIndex
|
||||
ValueTakesTheForwardingPath` failed once in the tcp lane and passed on re-run, and the full lane
|
||||
was re-run clean at 121/121.)*
|
||||
|
||||
## slice 4 — readback close-out
|
||||
|
||||
|
||||
Reference in New Issue
Block a user