fix: localize retained segment source invalidation
Some checks failed
validate-scripts / validate-scripts (push) Has been cancelled

This commit is contained in:
tei
2026-10-06 17:18:09 +09:00
parent c4b481fd2c
commit 524aaf4af5
16 changed files with 1736 additions and 71 deletions

View File

@@ -0,0 +1,454 @@
# STEP-100 SourceScoped RenderSegment 局部失效 — 完成报告 v1
## 1. 最终结论
**CLOSED / PASS(功能与 Android 运行态目标确认)**。
Codex 实现使用:
- model:`gpt-6-sol`
- reasoning effort:`high`
RDC 独立完成:
- 源码 diff 审计;
- Android x86_64 OpenGL 全量 native build;
- Validation APK;
- FZG1-2 自动安装与 T1/T2/T3/T4;
- STEP-100 60 秒 locality window;
- validation build script 小型工程修复;
- Git diff/status 审计。
本 Step 核心目标已确认:
> 动态 GeoJSON Source 更新只影响依赖它的 RETAINED Segment;无关 R0 cache 不再跟着 invalid/rebuild,并且在 global loaded=false 时仍可安全 reuse。
Native host GTest 因当前 Intel Mac 缺 `libuv` 未执行;曾尝试 Homebrew 安装,但 Homebrew 要连带升级 CMake/OpenSSL/Python 且 Intel 已无 bottle 支持,因此 RDC 主动终止,未把环境变化作为任务依赖。Android 最终 C++ native build 与设备运行态用于本轮功能验收。
Jest:**NOT RUN**,用户未要求。
## 2. Git / 基线
- worktree:`/Users/tei/dev/MapLibreParticleLab-step100`
- branch:`step100-segment-local-source-invalidation`
- start HEAD:`c4b481fd2c5b5da20d31f6ae54011f0552d7dbd0`
- start remote:`origin/particle/main = c4b481fd2c5b5da20d31f6ae54011f0552d7dbd0`
- frozen V17 tag:`maplibre-particle/rn/13.4.1-particle.17`
- frozen V17 commit:`e8b451fe70054107773e82484c80e90df3889147`
- V17 tag/release worktree:未修改。
最终 STEP-100 commit / push / remote HEAD 在 Git 收尾后由最终交付回读记录,避免文档自引用。
## 3. 实际修改
生产源码:
- `include/mbgl/renderer/render_segment.hpp`
- `src/mbgl/renderer/render_orchestrator.hpp`
- `src/mbgl/renderer/render_orchestrator.cpp`
- `src/mbgl/renderer/renderer_impl.cpp`
- `src/mbgl/renderer/retained_underlay_cache.cpp`
- `src/mbgl/renderer/retained_underlay_runtime_state.hpp`
- `src/mbgl/renderer/render_segment_json.hpp`
- `packages/maplibre-particle-rn/src/types.ts`
Test App / tests:
- `platform/android/MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/particle/Step099ReadinessFixture.kt`
- `platform/android/MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/particle/ParticleCoreLayerValidationActivity.kt`
- `test/renderer/retained_underlay_cache.test.cpp`
工具:
- `tools/particle_android_validation/build_validation_apk.sh`
- RDC 修复 `unzip | awk ... exit` 在 `pipefail` 下偶发 SIGPIPE/141;
- 仅移除 awk 的提前 exit,不改变 APK 验证语义。
文档:
- `docs/40_任务/STEP-100_SourceScoped_RenderSegment局部失效_Codex任务_v2.md`
- `docs/50_Step记录/STEP-100_SourceScoped_RenderSegment局部失效_v1.md`
- 本报告;
- `粒子开发记录.md`
## 4. 生产设计
### 4.1 Source-scoped revision
新增正式 source-scoped revision bookkeeping。
明确的 Source/Tile mutation:
- source diff;
- feature state;
- tile changed;
推进对应 sourceId revision。
现有 global `retainedUnderlayContentRevision` 继续增长,供:
- compatibility;
- diagnostics;
- Fast Path fail-closed。
RETAINED Segment cache 不再直接以 global Source/Tile revision 作为内容 revision。
### 4.2 Segment dependency
正式 renderer 与 diagnostics 共用 source→segment 依赖推导。
规则:
- layer sourceId 决定 source dependency;
- source-less background 不制造假 dependency;
- shared source 可影响多个 segment;
- symbol 的 glyph/image/placement 作为明确 global prerequisite;
- 真正无法证明的 dependency fail closed。
### 4.3 Per-segment readiness
未修改:
- `Source::isLoaded()`;
- `renderTreeParameters->loaded = styleLoaded && isLoaded()`。
full CPU retained path 增加 per-segment source readiness/stability:
- dependent source unloaded → 仅该 Segment fallback;
- dependent source fading → 仅该 Segment unstable;
- unrelated source unloaded/fading → 不影响该 Segment;
- style/image/sprite/glyph/placement/context 等 global prerequisite 仍 fail closed。
### 4.4 Mixed Build / Reuse / Fallback
移除 retained full-CPU path 的 all-or-none `useRetainedRanges` 依赖。
每个 RETAINED Segment独立决定:
- Build
- Reuse
- Fallback
主 buffer 按 Style Segment 顺序执行:
- RETAINED Build/Reuse → composite cache
- RETAINED Fallback → live render only that range
- CONTINUOUS → live render
因此允许:
```text
R0 = Reuse
C = Live
R1 = Fallback
```
不再因为 R1 fallback 把 R0 一起变成 live redraw。
Fast Path gate / `singleContinuousSegment` / multi-continuous Fast Path:**未修改**。
## 5. Android build
首次标准 validation script 的 Gradle 本体实际结果:
```text
BUILD SUCCESSFUL in 12s
86 actionable tasks: 6 executed, 80 up-to-date
```
但脚本在 Gradle 成功后以 141 退出。RDC 用 `bash -x` 精确定位到:
```bash
LIB_BYTES="$(unzip ... | awk '/.../ {print $1; exit}')"
```
在 `set -o pipefail` 下触发 SIGPIPE。
RDC 仅去掉 awk 的 `exit`,重跑标准脚本:
```text
RC=0
[validation-apk] BUILD SUCCESS
```
最终 APK:
- variant:OpenglDebug
- ABI:x86_64
- apk bytes:61,956,640
- libmaplibre.so bytes:39,636,472
- SHA-256:
`86e22bf081bf7d3196f0bf0e7c56af51bde01e9f0b01e0bab238cb794273315a`
证据:
`artifacts/maplibre-particle/android-validation/`
## 6. 设备
- serial:`192.168.200.139:5555`
- model:FZG1-2
- Android:13
- API:33
- ABI:x86_64
APK 覆盖安装成功,`ParticleCoreLayerValidationActivity` 冷启动成功。
STEP-100 log:
`artifacts/maplibre-particle/android-validation/step100-logcat.txt`
log SHA-256:
`e9d18ea987d12cb30e321d0d9c669b2588972aac89e5277920bf1fa9fee8146b`
## 7. T1 STATIC
运行约 20 秒。
aggregate:
```text
falseWindowCount=1
falseWindowAvgMs=203
falseWindowMaxMs=203
openFalseWindowMs=0
```
最终:
- R0 build=1
- R0 reuse=1217
- R0 decision=reuse
- R1 build=1
- R1 reuse=1187
- R1 decision=reuse
静态 R-C-R 正常。
## 8. T4 R-C-R 主验收
结构:
```text
R0 -> step099-static
C -> particle-fixture
R1 -> step099-dynamic (+ framework annotation source if present)
```
window marker:
```text
baseline tick=8 17:05:21.642
final tick=130 17:06:23.240
```
窗口约 61.6 秒。
dynamic update 总计:68 次。
global revision:
```text
128 -> 973
```
说明 global compatibility revision **仍正常增长**,没有通过停掉 global revision“作弊”。
### R0
baseline:
```text
build=1
reuse=132
invalidation=20
srcRev=5127273714767523329
decision=reuse
```
final:
```text
build=1
reuse=4042
invalidation=20
srcRev=5127273714767523329
decision=reuse
```
Delta:
```text
buildDelta = 0
invalidationDelta = 0
reuseDelta = +3910
sourceRevision = unchanged
```
**硬门槛 PASS。**
### R1
baseline:
```text
build=5
invalidation=26
```
final:
```text
build=135
invalidation=211
```
动态 Source mutation 正常驱动 R1。
mutation diagnostics 持续:
```text
sourceId=step099-dynamic
affected=[2]
scope=source-local-diagnostic
```
### loaded false windows
T4:
```text
falseWindowCount=5
falseWindowAvgMs=202
falseWindowMaxMs=203
openFalseWindowMs=0
```
即 transient global loaded=false 仍存在,但不再让 R0 build/invalidation 增长。
## 9. T2 双 GeoJSON readiness 回归
约 35 秒。
aggregate:
```text
falseWindowCount=6
falseWindowAvgMs=203
falseWindowMaxMs=205
openFalseWindowMs=0
```
R0 在动态窗口保持:
- build=1
- invalidation=18
- revision unchanged
- reuse 持续增长。
R1 随两个动态 GeoJSON 持续 build/invalidate。
结论:
**DYNAMIC_SOURCE_READINESS_THRASH 仍是 global loaded 事实,但不再误伤无关 retained cache。**
## 10. T3 permanently unready unrelated source
`step099-unready-raster-array`:
- no layer;
- enabled=false;
- isLoaded=false。
global:
```text
renderOrchestratorLoaded=false
openFalseWindowMs=21468
```
约 21.468 秒持续 false。
与此同时:
R0:
- build=1
- invalidation=19
- reuse:11 → 1200
- decision=reuse
R1:
- build=1
- invalidation=19
- reuse:11 → 1200
- decision=reuse
因此:
> global loaded 仍保持旧兼容语义,但 unrelated permanently-unready source 已不能把无关 RETAINED Segment 拖成 style-not-loaded fallback。
**硬门槛 PASS。**
## 11. Mixed fallback
T4 中 R1 `fallbackCount` 从约 21 增长到 76,而 R0 fallback 计数保持不变,同时 R0 reuse 持续增长。
这证明运行期间真实发生了:
```text
R0 = Reuse
R1 = transient Fallback / rebuild
```
而不是只有“两个段都 ready”的理想场景。
未观察到 crash / ANR。
## 12. Fast Path 边界
RDC diff 审计确认未修改:
- `tryRenderParticleFastFrame`
- `particleFastPathNonCameraInputsMatch`
- `singleContinuousSegment`
- multi-continuous fail-closed
- global Fast Path signature/fade/contentRevision/pending/placement 等 gate。
STEP-100 只修 full CPU retained locality。
## 13. Native host tests
Codex 已新增 `RenderSegmentLocality.*` C++ 测试源码。
RDC 尝试恢复 `macos-metal` test runner,但 host configure 需要 `libuv`,当前未安装。
尝试 `brew install libuv` 时发现 Intel Homebrew 会连带升级:
- CMake
- OpenSSL
- Python
- ca-certificates 等
且当前 Intel macOS 已无官方 bottle 支持。为避免污染开发环境,RDC 主动停止。
因此:
- Native host GTest:**ENV BLOCKED / NOT RUN**
- Android C++ full native build:**PASS**
- Android device runtime:**PASS**
## 14. Jest / iOS
- Jest:NOT RUN。
- iOS runtime:本 Step 未运行。
- V18 unified release:在 STEP-100 Git 收尾后单独执行。
## 15. 最终判定
STEP-100 的核心实现目标全部成立:
1. global revision 继续增长;
2. R1 GeoJSON mutation 不再 invalid/rebuild R0;
3. R0 buildDelta=0;
4. R0 invalidationDelta=0;
5. R0 reuseDelta>0;
6. transient unrelated unloaded 不影响 R0 reuse;
7. permanent unrelated unloaded 不影响 R0 reuse;
8. mixed Reuse/Fallback 实际发生;
9. Fast Path 语义未放宽。
因此本 Step 功能与 Android runtime 验收:
**PASS / CLOSED**。
下一步允许进入 V18 unified release。