From abc137ca9637abc18be8c64cd98f2c7f1ed68704 Mon Sep 17 00:00:00 2001 From: tei Date: Sun, 11 Oct 2026 01:12:24 +0900 Subject: [PATCH] docs: investigate STEP-112 particle full render and symbol cache --- ...llRender循环与Symbol缓存准入全面源码调查_v1.md | 426 ++++++++++++++++++ ...-112_V23粒子FullRender与Symbol缓存调查_v1.md | 49 ++ ...FullRender与Symbol缓存调查_Codex完成报告_v1.md | 87 ++++ 粒子开发记录.md | 8 + 4 files changed, 570 insertions(+) create mode 100644 docs/20_调查/STEP-112_V23粒子FullRender循环与Symbol缓存准入全面源码调查_v1.md create mode 100644 docs/50_Step记录/STEP-112_V23粒子FullRender与Symbol缓存调查_v1.md create mode 100644 docs/报告/STEP-112_V23粒子FullRender与Symbol缓存调查_Codex完成报告_v1.md diff --git a/docs/20_调查/STEP-112_V23粒子FullRender循环与Symbol缓存准入全面源码调查_v1.md b/docs/20_调查/STEP-112_V23粒子FullRender循环与Symbol缓存准入全面源码调查_v1.md new file mode 100644 index 000000000..c4fb07fb7 --- /dev/null +++ b/docs/20_调查/STEP-112_V23粒子FullRender循环与Symbol缓存准入全面源码调查_v1.md @@ -0,0 +1,426 @@ +# STEP-112 V23 粒子 Full Render 循环与 Symbol 缓存准入全面源码调查 v1 + +日期:2026-10-11(JST)。状态:本轮只读调查与文档交付完成;综合判定 **PARTIAL(现场逐帧归因及安全修复仍未验证)**。核心结论、证据等级和剩余事项见第11章。 + +## 1. 调查基线与证据规则 + +- 当前机器:Mac.lan,Darwin 24.6.0,x86_64;仓库 `/Users/tei/dev/MapLibreParticleLab`。 +- 分支 `particle/main`,起点 HEAD `41b416c261fa72bd21171328045036a124694f47`。 +- 实际模型 `gpt-6-astra`、推理 `high`:当前会话 `01a12686-49ee-70b1-953e-f95f8a4bd9ca` 的本地 rollout `turn_context.model/effort` 已核对。任务书建议模型由用户本轮明确要求覆盖。 +- 起点只有 STEP-110、STEP-111 两份任务书未跟踪,无 tracked diff;不改动它们。子模块状态均为空格前缀,无未初始化/偏移标记。 +- Integration `main=80667d6c` 使用用户提供的 RDC 核验事实,不重新同步或修改跨项目文件。 +- 已按顺序读取 AGENTS、PARTICLE_BASELINE、8 份项目规范、总则 README 与四份现行总则、STEP-112、STEP-102/106/109/110/111 的相关任务/记录/报告。初始化基线及现行路线 v2 中旧版本坐标不冒充当前 V23;后续按 Git 实际源码比对。 +- 证据分为 **源码证明**(指定基线静态控制流)、**FZG1 实测**(本任务引用历史现场输入,非本轮重测)、**条件推断**(前提明确的推演)、**仍需设备验证**(频率、真实配置、像素/性能)。 +- 只允许写本主报告、STEP-112 阶段记录、完成报告、开发记录追加。禁止源码/测试/Style/脚本/跨项目写入,禁止编译测试、设备操作、Git 提交推送及分支/worktree 变更。 + +## 2. 已核实的入口事实 + +**源码证明**:`src/mln/renderer/renderer.cpp:45-64` 先同步 retained 状态,再尝试 Fast;失败才清除旧 prepared tree、`createRenderTree`、`prepare`、Full render、保存新 prepared tree。Fast 失败与粒子请求下一帧属于不同决策。 + +**源码证明**:`src/mln/renderer/particle_fast_path.hpp:99-126` 顺序短路。`cache-invalid` 在第 3 项返回,后续 revision、camera、staticStable、placement、pending、非粒子 repaint、C 层支持、scene 等条件没有因此得到通过证明。 + +## 3. 核心发现:Symbol R 首次 Build 的控制流矛盾 + +**源码证明(正常 Continuous Full 路径)**:含结构性 Symbol 的 R 没有通过当前逐段 Placement Gate 的窗口。这比“Fade 的 `==0` 可能太严格”更强,且不依赖 Source 变化或实际 Symbol 是否移动。 + +1. `src/mln/renderer/render_orchestrator.cpp:792-807`:`placementChanged=true` 时执行 Placement;否则无条件 `setPlacementStale()`。 +2. `src/mln/text/placement.cpp:160-175`:旧 Placement 禁用 transitions 时 `placementIsRecent()` 必为 false,故必进入 changed=true 分支;若进入 changed=false 分支,旧 Placement 必启用 transitions,随后 stale=true,使 Controller `hasTransitions()` 必为 true。 +3. `src/mln/renderer/renderer_impl.cpp:593-594,655-659` 读取同一 Controller 的 transition;`include/mln/renderer/render_segment.hpp:85-90` 要求 `!changed && fade==0 && !transition`。 +4. 因而 changed=true 被第一项挡住;changed=false 被 stale→transition=true 挡住。对结构性 Symbol R,两个分支均 false。`RenderTreeImpl::prepare`(`render_orchestrator.cpp:133-138`)仅更新 buckets,不清 Controller stale;没有中间解除路径。 + +此结论限定正常单线程 Renderer 调用链,不将任意人工注入/未来改动当作当前可达状态。隐藏/空 Symbol 仍按结构归为 capable(`render_segment.hpp:75-90`),所以也不能绕开这层保护。 + +**源码证明**:原始 `Placement::symbolFadeChange` 是相对 commitTime 的归一化时间增量加 zoomAdjustment,而非“本帧是否改变”布尔量(`placement.cpp:1282-1289`);transitions 关闭或 duration≤0 返回 **1**。正常 Full 的 `prepare()` 不改写它。只有已通过 Fast 的 `prepareParticleFastFrame()` 才把 RenderTree 字段设为 0(`render_tree.hpp:105-109`),不能反过来帮助首次 Build。 + +**FZG1 实测(任务书输入)**:末态 true/0/false 与“transitions 开启、此次重新计算、静止 zoom、先前实际 Fade 已结束”的状态相容;与通常 transitions 关闭后的 true/1/false 不同。不能仅凭此末态推断每帧走默认 300ms 周期或 Host 确实关闭 transitions。 + +**条件推断**:在 retained enabled、正常 Continuous Full、R2 含 Symbol 的前提下,逐帧 `segmentSupported=false` 可令 Build 始终为 0;`retained_underlay_cache.cpp:195-205,359-366` 即使此前没有有效纹理也每次记录 fallback/invalidation。故 1787 次 invalidation 不表示曾成功建成 1787 份纹理。 + +以下第4—10章已补齐调度、全部 Gate、状态机、时间表、反事实和提案;运行时无新增证明,不能据此声称修复 PASS。 + +## 4. 版本核验与 A:调度、Full、GPU 和 Idle + +### 4.1 当前 main 与发行 V23 + +**源码证明**:执行只读 `git diff --name-only 87f3d1c59a52581fb8339f0ea7d106b9f4b94fef HEAD -- <相关路径>`,在 renderer/text/map、RasterArraySource、render_segment、TransitionOptions、Android WeatherRuntime/frontend、iOS FieldRuntime、GL/Metal shader 范围内,仅两份文件不同:`render_particle_validation_layer.cpp` 和 `render_weather_color_layer.cpp`。逐 hunk 核对为 STEP-109 的 immutable snapshot 身份快比较,未改变本调查的调度、Placement、Cache、Fast Gate。以下行号一律对应本轮 HEAD,不照搬 V23 两份 Layer 的旧行号。 + +V23 Source Lock 为 `87f3d1c…`(既有 STEP-110/108 发行证据),Android/RN `13.6.1-particle.23`、iOS `6.29.0-particle.23`。当前 main 包含尚无实机性能验收的 STEP-109;不得称它已在 V23 APK 生效。本轮没有重新下载制品或核对设备内 binary。 + +### 4.2 可核查调用图 + +下图箭头“条件”都不是每次必然发生。路径简称只用于图;实际位置见后表。 + +```mermaid +flowchart TD + H[Host 数据/Style/Camera 更新] --> U[Map::Impl::onUpdate] + W[WeatherRuntime 定时唤醒:条件成立] --> T[Map::triggerRepaint] + T --> U + U --> F[RendererFrontend update / 平台 requestRender] + F --> R[Renderer::render] + R --> G[tryRenderParticleFastFrame:24 项 Gate] + G -->|通过| Q[复用 prepared tree / 更新粒子 / GPU render] + G -->|失败| C[createRenderTree / Layer prepare / 全局 Placement 判定] + C --> P[RenderTree prepare / bucket opacity 与动态属性] + P --> B[各 R evaluate:Build / Reuse / Fallback] + B --> D[离屏 Build 或保序 composite/live / GPU 粒子] + D --> O[onDidFinishRenderingFrame] + Q --> O + O -->|needsRepaint 或 camera transition| U + O -->|loaded 且无需 repaint/transition| IDLE[onDidBecomeIdle] +``` + +| 环节及证据强度 | 文件、函数、行号与条件 | +|---|---| +| **源码证明**:粒子是否活动 | `src/mln/renderer/layers/render_particle_validation_layer.cpp:1112-1127`,`isParticleAnimationActive` 检查 active、正式 Source 的 T0/T1、visibility;`needsRepaint` 再检查 V4 cadence 或旧 timeline。active=false、无双端点、visibility=None 不请求粒子 repaint。它不含“必须 Full”标志。单帧输入可投影成可用双端点,不能以业务仅一帧推断无数据。| +| **源码证明**:粒子暂停和预报暂停不同 | 同上;`RasterArraySource::tickForecastClock`,`src/mln/style/sources/raster_array_source.cpp:487-510` 在 forecast 非 Playing 时不推进预报;这不把粒子 active 设 false。风场时间冻结仍可流动。无向量或语义不匹配另由 Layer `update:1247-1258` 拒绝 GPU 更新。| +| **源码证明**:Core cadence | `raster_array_source.cpp:149-175,208-227,260-281,1237-1255`;非 AUTO 以 `1/fps` 检查,30 对应约 33.33ms;consume 才推进 cadence revision。AUTO 每次可 due,不等于固定 30。| +| **源码证明**:Android SDK 唤醒 | `platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/WeatherRuntime.java:228-234,250-269`;有效可见粒子且非 AUTO,或 V4 forecast Playing 且已提交字段时,Handler 每次安排 34ms(60 为 17ms),调用 `map.triggerRepaint()`;disabled/disposed 停止,隐藏粒子但 forecast Playing 仍可唤醒。这里是 SDK Java 调度,不是 NavSea JS 每粒子循环。| +| **源码证明**:iOS 对应唤醒 | `platform/ios/src/MLNParticleFieldRuntime.mm:173-193`;一次性 NSTimer 0.034/0.017s,触发 `triggerRepaint` 后重新调度。两端意图相同,调度抖动不保证相同。| +| **源码证明**:进入地图更新 | `src/mln/map/map.cpp:93-95` 调 `Impl::onUpdate`;`src/mln/map/map_impl.cpp:108-153` tick forecast、更新 camera transition、装配 UpdateParameters、调用 frontend.update。非 Continuous 且无 still request 直接返回。| +| **源码证明**:Android 跨线程链 | `android_renderer_frontend.cpp:118-133,149-152`(路径前缀 `platform/android/MapLibreAndroid/src/cpp/`)AsyncTask 保存最新 params,再 `MapRenderer.update/requestRender`;`map_renderer.cpp:127-149,153-157,221-247` 请求 Java 渲染、后端 scope 中调用 `Renderer::render`。多个 update 可能合并为一次实际 render。| +| **源码证明**:iOS frontend | `platform/darwin/src/MLNRendererFrontend.h:28-54` 保存 params、setNeedsRerender,backend scope 中 render;不同于强制 Full 命令。| +| **源码证明**:Fast/Full 分岔 | `src/mln/renderer/renderer.cpp:45-64`;Fast 成功直接 return;否则 create/prepare/render/commit tree。`renderer_impl.cpp:324-327` 只在 `!particleFastFrame` 增加 Full CPU 计数。`FullRendererFrame` 在 :323 两种路径都会计数,名称不能混用。| +| **源码证明**:Full repaint 汇总 | `render_orchestrator.cpp:775-823,1107-1128`;light、Layer transition/repaint、Controller transition/stale、fading tiles 的 OR;Continuous 时赋 `RenderTreeParameters.needsRepaint`。| +| **源码证明**:通知与 Idle | `renderer_impl.cpp:1070-1074` → Android forwarding `android_renderer_frontend.cpp:38-44` → `map_impl.cpp:259-288`;needsRepaint 或 transform transition 时 onUpdate,否则 loaded 时通知 Idle。Observer 的 RenderMode::Full 表示 loaded,不表示执行 Full CPU 路径。| +| **源码证明**:GPU 推进 | Layer `update:1271-1283,1994-2055`;未到 cadence 可只重组 composite,不推进 state/trail;到期才准备 State/Trail pass。`renderer_impl.cpp:807-810,968-980,1047-1053` 执行 render targets 和 present。一次 repaint、一次 Full CPU、一次 simulation advance、一次成功呈现不是同一计数。| + +**仍需设备验证**:FZG1 约 30FPS 的真正限速因素是 targetFps=30、平台帧率限制、耗时瓶颈或组合,任务快照不能区分。源码支持“Core 33.33ms 阈值 + SDK 34ms 唤醒”的候选机制,不能把 34ms 直接换算为实测 1792 次 Full 的唯一原因。 + +**源码证明**:所追踪的 PlacementController 仅在 render 链上比较时间,并无独立定时器调用强制 Full;stale/尚有 Fade 会通过 needsRepaint 延续正常地图帧。没有外部输入且粒子关、其它 transition 完成、资源 loaded 时可 Idle;WeatherRuntime 后续 wake 或 Tile 完成仍能再次唤醒,Idle 不是永久无任务保证。 + +### 4.3 Full 与 Fast 工作比较 + +| 工作 | Full | Fast | 证据 | +|---|---|---|---| +| diff Style/Source、prepare RenderLayer、CrossTile/Placement、新 RenderTree | 执行相应流程;不等于每帧都 placeLayers | 跳过,复用旧 tree | `renderer.cpp:53-61`;`render_orchestrator.cpp:445-539,737-848` | +| Source/bucket/atlas upload | 遍历 | 跳过整批 | `renderer_impl.cpp:470-487` | +| Layer update | 全部 render items | 仅 isParticleAnimationLayer | `renderer_impl.cpp:491-509`;`render_orchestrator.cpp:1458-1483` | +| tweaker/upload/postRender | 全范围 | 单 C 范围;render targets 仍处理 | `renderer_impl.cpp:707-749,1031-1040` | +| R GPU | 可 Build、Reuse composite 或 Live | 正常命中应复用;仍进入 cache evaluate/render | `renderer_impl.cpp:637-692,813-862,972-1028` | +| C GPU、State/Trail、最终 framebuffer/present | 执行 | 仍执行 | 同上及 :1047-1053 | + +Fast 不是零 CPU 或零 GPU;Full 不是所有 R 从零重画。PREFIX 中 R0/R3 已复用而 Full CPU 仍增长,正与此结构相符。 + +## 5. B:Fast 全 Gate 矩阵 + +下面按 **实际首错顺序**列出全部 24 项。简称:`F=src/mln/renderer/particle_fast_path.hpp`;`I=src/mln/renderer/renderer_impl.cpp`;`O=src/mln/renderer/render_orchestrator.cpp`。表中“Tick”特指静态场、仅粒子 State/Trail 时间推进,不包括 forecast 播放导致新 Source impl;“Full恢复”是假定其它门槛已满足的一次新 Full 能否建立相应证据;FZG1 列区分输入实测与静态推断。 + +| # / 条件与失败名(F 行) | 输入来源/通过条件 | Tick 本身改变? | 一次新 Full 后可能恢复? | FZG1 证据 | +|---|---|---|---|---| +| 1 prepared tree / no-prepared-tree (100) | I:247,290-302,tree 非空;reload/miss 清旧 :305-316 | 否 | 是,create 成功后保存 | **FZG1实测** cache-invalid 的那些判定已过此项 | +| 2 retained enabled / retained-disabled (101) | I:207-208,248,runtime snapshot.enabled | 否 | 需控制面启用,Full 不自动开启 | **FZG1实测** 同上已通过 | +| 3 所有 R valid / cache-invalid (102) | I:220-228,249,cache 容器与 plan 大小相同,每个 R cache 非空且 isValid | Tick 不改内容;Full Placement gate 可间接 invalidate | 无 Symbol 可建;当前含 Symbol 的 R 不可(第3章) | **FZG1实测** 首拒;R2 Build/Reuse=0 | +| 4 Build/Static revision / revision-mismatch (103) | I:220-230,250,每 R buildRevision==observedStaticRevision,prepared aggregate static revision==tracker.current | 否;Full 稳定状态变化可推进 tracker | 通常可以,但缓存未建仍失败 | **未知**,被 #3 掩盖 | +| 5 Content revision / content-mutation (104) | I:236-237,251,prepared 与 O 的 aggregate content revision 相等 | 固定场否;forecast/source 变化可改 | 是,Full 处理后 commit 新值,异步新变更再失效 | **未知**;有历史 Source Mutation 增量,无逐帧对应 | +| 6 Viewport / viewport-changed (105) | I:214-216,253,Transform size 与 prepared 相等且 backend renderable size 未变 | 否 | resize 后重建可 | **未知**,静止场景不等于逐帧已核验 | +| 7 Camera / camera-changed (106) | I:217,252,精确投影矩阵相等 | 否 | 新相机完成 Full 后可,持续交互继续失效 | **未知** | +| 8 non-camera signature / update-parameters-changed (107) | I:151-196,218-219,254,完整签名相等,字段见下段 | timePoint 不入签名;forecast 可能更换 sources | 是,新 params 被 prepare/commit;源持续变则持续 miss | **未知** | +| 9 Style loaded / style-not-loaded (108) | I:255,UpdateParameters.styleLoaded | 否 | 需真实加载完成 | **未知**(不能以段 ready 代替所有) | +| 10 Boundary/RangeConfig / boundary-changed (109) | I:209-234,256,唯一 C 的 begin/end、configRevision 与 prepared 相等 | 否 | plan 有效时可;多 C 另被 #22 拦截 | **未知**,已知 PREFIX 数字来自现场 | +| 11 Topology / topology-changed (110) | I:235,257,prepared topologyRevision==tracker.current;tracker I:542-563、cache.cpp:62-69 | 否 | Full 新拓扑和 cache 可重建 | **未知** | +| 12 staticStable / static-unstable (111) | I:238-244,258;O:1156-1170,所有 R 稳定、无 light、R 动态 needsPlacement 时 Controller 无 transition、R 无 transition/repaint、**任一 Source** 无 fading tiles | 粒子不直接改 R;时间和 stale 可阻塞 | 取决于 Controller/资源,当前 Symbol 周期不保障恢复 | **条件推断** 部分帧会失败,实际序列未知 | +| 13 Placement transition / placement-or-fade (112) | I:259-263;O:1281 → PlacementController::hasTransitions(now) 为 false;不读旧 tree 的 changed/fade 代替 | 仅时间推进可结束真实 Fade,但 stale 必须新 placement 清 | 稳定重算帧可清 stale;不重算会置 stale | **FZG1实测** 末态 false;不是所有 Fast 时刻都 false | +| 14 Pending / pending-changes (113) | I:264;O:1285,pendingChanges.empty;O:1341-1344 入队、1486-1490 消费 | 正常粒子 changes 于 render 中生成/消费;非必然积压 | 通常可以,异步继续到来则不保证 | **未知** | +| 15 non-particle transition / non-particle-transition (114) | I:265;O:1270-1273,无 light 或任意 Layer.hasTransition | 否;普通 Style transition 可活跃 | 等 transition 真结束再 Full | **未知** | +| 16 non-particle repaint / non-particle-repaint (115) | I:266;O:1275-1278,非粒子 Layer 均不需 repaint | 固定 V4 场通常否;V3 WeatherColor 播放可 true(Layer :177-180) | 活跃动画不会因 Full 自行结束 | **未知** | +| 17 fading tiles / fading-tiles (116) | I:267;O:1282-1284,所有 Source 无 fading tiles | 否 | fade 真结束且状态消费后可 | **未知**;与 #12 重复但诊断次序不同 | +| 18 ParticleNeedsRepaint / no-particle-repaint (117) | I:268;O:1274,至少一个 renderable 且 isParticleAnimationActive;**这里不是 cadenceDue** | active 粒子满足;每次 advance 不取消 active | 数据就绪/显示后可;粒子关不能通过 | **条件推断** 风场活动大概率满足,未逐帧取证 | +| 19 still image / still-image-request (118) | I:269,stillImageRequest=false | 否 | 请求消耗后可 | **未知** | +| 20 MapMode / unsupported-mode (119) | I:270,Continuous | 否 | Static/Tile 不自动转换 | **条件推断** 现场连续地图符合,仍需记录 | +| 21 Debug / debug-options (120) | I:271,NoDebug | 否 | 用户关闭后可 | **未知** | +| 22 C 支持 / unsupported-continuous-layer (121-123) | I:272,624-627;O:1241-1250,有且仅一个 C、每层 startsRetainedDynamicRange、至少一个 particle | 否 | 固定含普通 Symbol/Line 的 C,多少次 Full 都不恢复 | **源码证明+现场布局前提** PREFIX C[13,33) 必失败 | +| 23 scene / unsupported-scene (124) | I:273,619-630,preparedSceneSupported=true 且唯一 C;其内部含 loaded、无3D/debug、所有 R stable、aggregate Placement stable、无 legacy/custom、C 支持 | 否;Full 会重算并保存 false | 仅修 R2 局部 Gate 不够;需一致安全判据 | **条件推断** R 有活动 Symbol 或 C 普通层使其 false | +| 24 Annotation / annotation-mutation (125) | I:202-205,274,AnnotationManager 无 pending changes(weak guard) | 否 | 正常更新消费后可;新 annotation 继续阻塞 | **未知**;不能把末尾 Symbol 存在等同 pending=true | + +#8 的逐字段完整范围(I:154-180,186-196):pixelRatio、mode、debugOptions、styleLoaded、spriteLoaded、transformChanging、gestureInProgress;light/images/sources/layers/fontFaces/fileSource 对象身份;glyphURL;transition duration/delay/enablePlacementTransitions;prefetchZoomDelta、crossSourceCollisions、fastPFOREnabled;tileLodMinRadius/Scale/PitchThreshold/ZoomShift/Mode。viewport、camera 矩阵独立比较;timePoint 不在签名。**对象身份改变可触发 Full,即使负载内容相同;反过来单纯 triggerRepaint 且签名相同没有“必须 Full”语义。** + +所有输入在 I:246-275 装配,但 F:100-125 返回首错;现有记录 I:276-280 只有 candidate/reason。#3 的失败不能证明 #4—24 成功。修完 R2 仍会遇到 PREFIX 的 #22、prepared scene #23,以及真实资源/Source/Annotation 等独立门槛。 + +### 5.1 第二重全局锁 + +**源码证明**:Full 的 `retainedPlacementStable`(I:590-592)依据动态 `retainedRangeUsesPlacement`(O:1135-1141,即 needsPlacement),而逐 R gate 依据结构性 Symbol。活动 Symbol R 存在时:重算帧 aggregate placementStable=false;不重算帧 Controller stale=true,aggregate staticStable=false(O:1158)。二者分别让 `retainedSupported`(I:619-623)为 false,写入 `particlePreparedSceneSupported`(I:630)。这是独立于逐段 cache 的 **Fast scene 锁**,不是 main-buffer 的 global-live 判定;后者 I:998-1028 已不含 aggregate placement,允许无 Symbol R 继续 composite。 + +假设以后仅让 R2 valid,旧 preparedSceneSupported=false 仍会在 #23 阻塞;不能等时间到就认为 prepared flag 自己恢复。Fast 的动态 transition 检查已避免直接沿用旧 tree changed/fade(I:259-263 注释),但没有解除 Full 产出的 scene 保守状态。隐藏 Symbol 可能使动态 usesPlacement=false、scene 不受它影响,结构性每段 gate 仍会拒绝首次 Build。 + +## 6. C:R2 Build / Invalidate / Fallback 状态机 + +### 6.1 从层结构到每段决策 + +**源码证明**,按执行顺序: + +1. `include/mln/renderer/render_segment.hpp:48-90` 由 Style 元数据列举 Source 依赖与 Symbol 能力;type=symbol 就 capable,不看 visibility、当帧 bucket 或可见像素。越界 range 保守 capable;custom/custom-drawable 依赖 unknown。动态 `needsPlacement()` 则要求 CrossTileIndex::Required 且 placementData 非空(`src/mln/renderer/render_layer.cpp:30-32`),两者不等价。 +2. `render_orchestrator.cpp:1173-1202` 按段依赖查询 Source loaded/fading/revision,CustomMVTVector 暂无 revision contract 而 fail-closed;`retainedSegmentIsStable:1205-1216` 检查 light、相关 Placement transition 和本段 Layer transition/repaint,不再把无关 Source fade 当每段阻塞。对比 Fast 的 `retainedRangeIsStable:1156-1170` 仍检查所有 Source fade。 +3. `renderer_impl.cpp:516-535` 拓扑代际或 cache 数量变化时释放/重建每段 cache 容器;:558-559 的 key 为 viewport、begin/end、cameraMatrix。:641-652 组合 immutable layer impl+index fingerprint、source/global revision、pixelRatio。fingerprint 不是 Symbol 最终视觉签名。 +4. :653-666 将 segmentStable、结构性 placement gate、style/sprite/image ready、Continuous/no3D/noDebug/noLegacy/noCustom 合并为 segmentSupported。 +5. :668-680 首错优先级:source not ready → source unstable → style/sprite/global resource → placement → transition → legacy → unsupported → boundary → 3D → debug → mode。`static-placement` 是此处 gate 的名字,不是“R2 像素变了”的探测器,也不证明其它更后项通过。 +6. :681-691 调 evaluate;只有 Build/Reuse 才 prepareResources;失败改为 resource-or-compositor。所有 supported 条件通过仍不代表最终纹理已经绘制成功。 + +### 6.2 状态转换和计数 + +所有状态均据 `src/mln/renderer/retained_underlay_cache.cpp`;这是源码状态机,不是本轮运行截图。 + +```mermaid +stateDiagram-v2 + [*] --> Invalid: 创建 / 资源释放 + Invalid --> Fallback: supported=false + Ready --> Fallback: supported=false / invalidate + Fallback --> Fallback: 下帧仍不支持 / 再计 invalidation + Invalid --> BuildPending: enabled 且 key存在 且 supported + Fallback --> BuildPending: 准入恢复 + Ready --> BuildPending: key或revision变化 + BuildPending --> Ready: 资源准备 + 离屏绘制成功 + BuildPending --> Fallback: resource或draw失败 + Ready --> Reuse: valid 且 key/revision相同 + Reuse --> Ready: 纹理保持有效 + Ready --> Invalid: releaseResources +``` + +| 分支 | 行号及真实后果 | +|---|---| +| 无 key | :173-182,原来 valid/key 存在才 invalidate/release,返回 Disabled;与 R2 static-placement 不同 | +| runtime disabled | :184-193,记 fallback;控制面同步 :129-163 另负责 invalidation/release | +| supported=false | :195-205,**无条件** fallback++、invalidate;revisionChanged 才 release。即使 valid 本来 false,也再次 invalidation++。未走 first-frame 和 BuildPending 分支 | +| revision 变化 | :171-172,207-213,invalidate/release、装新 key;这里 staticRevision 参数实际是每段 content 合成 revision,区别 aggregate tracker | +| 初次无 key / key 改变 | :214-219,first-frame / viewport / camera-or-boundary;设 key。普通 key 变化未必释放同尺寸 GPU allocation,但 valid 失效 | +| buildRevision 不一致 | :220-224,invalidate/release;不能复用旧代际 | +| valid=true | :225-236,reuse++,返回 Reuse;不代表 main-buffer 必 composite,还要看全局 live gate | +| invalid 且可支持 | :238-243,BuildPending,返回 Build;此时 buildCount 未增 | +| GPU 资源 | :246-303,离屏 RGBA unsigned-byte texture(带 depth/stencil 参数)及 composite drawable;失败返回 false | +| Build 真成功 | renderer_impl.cpp:813-862,beginBuild→R opaque/translucent→finishBuild→markBuildSucceeded;cache.cpp:332-345 设置 valid、buildRevision、build++、Ready | +| Build 失败 | cache.cpp:347-356,fallback++、invalidate、release,reason=resource-or-compositor;需要该 reason/资源计数,不能从 Build=0 单独推断 GPU 坏了 | +| invalidate / release | :359-380,前者 valid=false、记原因和计数;后者清 key/buildRevision/texture/compositor,不必增加同一种 invalidation 计数 | + +`lastInvalidationReason` 是最后一次记录,Reuse 或成功 Build 不在此函数中自动抹掉它;采样必须连同本帧 decision、gate、frameId 使用。`plan.valid=true` 也不等于 `cache.isValid=true`。 + +### 6.3 FZG1 数字支持什么 + +**FZG1 实测(任务书第二区,历史输入)**:PREFIX 约60秒,Full +1787,R0 reuse +1787,R2 build/reuse=0/0,fallback/invalidation≈1787,R3 reuse +1755,Fast=0,首拒 cache-invalid。 + +**源码证明 + 条件推断**:每次 Full 的 R2 结构 capable → 第3章矛盾使 placement gate=false → supported=false → evaluate 直接 Fallback/invalidate → 正常 main-buffer live → 新 prepared tree 保存但 R2 invalid → 下一粒子帧 Fast #3 再失败。该闭环不需要假设 Source 每帧变化,也不需要假设 GPU 资源损坏。 + +**仍需设备验证**:本轮没有60秒每帧原始 trace,不能把一次末态 static-placement 扩成每帧唯一首因;若某帧 Source not ready,:668-672 会先报 Source。提供的 build 是绝对值还是窗口增量、topology 重建是否清计数,也应在后续原始 diagnostics 中核对。若“Build=0”只表示窗口增量,不能排除窗口前构建过;当前源码正常含 Symbol Full 无准入窗口则给出更强的机制约束。 + +Full 中 Symbol 正确显示,是 main buffer 的 live 绘制结果;cache Build 是单独离屏 render pass,:817 的 decision!=Build 会直接不画离屏纹理。没有自动把 main framebuffer 的 R2 像素拷贝成缓存的路径。R3 少于1787的32次复用差额可能来自资源/Source/全局条件,**没有足够逐帧证据归因**。 + +### 6.4 Collision 与资源边界 + +- **源码证明**:Placement::placeLayers 按完整 layer 列表逆序碰撞(`placement.cpp:201-206`);CollisionGroups::get(:45-62)在 `crossSourceCollisions=false` 时才按 Source 隔离,开启时使用无过滤组。:46-48 的英文注释与实际 if 的开关文字不一致,本文依执行分支,不照抄注释。相同 Source 跨段仍同组;关闭跨源碰撞也不等于关闭段内碰撞。 +- **源码证明**:`Placement::commit:687-749` 内部同名局部变量 placementChanged 根据 text/icon placed 变化更新 fadeStartTime;外层 RenderTree.placementChanged 表示是否运行新计算。两个变量作用域和语义不同;全局 commit 也没有比较 R2 最终纹理。 +- **源码证明**:Source/Tile mutation 可按依赖映射每段,glyph/image/style/context 未能局部定位时推进 global epoch(`render_orchestrator.cpp:243-275,327-347,1519-1521,1555-1559,1582-1594`;`render_segment.hpp:114-133`)。Camera/viewport/pixelRatio/拓扑另有 key/revision 保护。Source ready 不等于每个 Symbol shader 输入稳定。 +- **条件推断**:别段符号或 Annotation 可能改变 R2 text/icon 可见性;同一位置/ID 也可能 opacity、variable anchor、orientation、line reprojection 改变。只有新一次完整 Placement 后证明最终有效输出及资源代际等价,才有候选复用条件。任何无法比较项必须 fail-closed。 + +## 7. D:Placement/Fade 真语义与可达状态 + +### 7.1 两个时钟、一个 stale 标记 + +**源码证明**:TransitionOptions 默认 enablePlacementTransitions=true(`include/mln/style/transition_options.hpp:12-26`),默认 duration=300ms(`include/mln/util/constants.hpp:59`)。`Placement` 构造保存当次 options、commitTime、placementZoom(`placement.cpp:180-192`)。因此 UI 当前 option 与 Controller 中旧 Placement 的 option 在下一次新建前可能暂时不同,诊断必须同时记录。 + +当 transitions 开启且 duration=D>0: + +- `symbolFadeChange(now) = (now - commitTime)/D + prevZoomAdjustment`(:1282-1289);这是无 clamp 的增量,可能>1,零只说明刚 commit 且 zoomAdjustment 为零等特殊条件。 +- shader 使用 `clamp(baseOpacity ± symbolFadeChange,0,1)`,符号实际已终态时 uniform 仍能变化;Fade 终态不以 uniform==0 定义。 +- `Placement::hasTransitions` 用 **fadeStartTime** 而非 commitTime:`now-fadeStartTime < D`(:1319-1322);commit 没有 text/icon placed 改变时继承旧 fadeStartTime(:748-749)。新计算与新的真实 Fade 不是同义词。 +- `placementIsRecent` 用 commitTime+updatePeriod>now(:160-167);普通 period=`max(300ms,D)*(1-zoomAdjustment(currentZoom))`(:1307-1312);zoomAdjustment=max(0,(oldZoom-newZoom)/1.5)(:1292-1298),zoom-out 可缩短到零甚至负值;此处没有下限 clamp。 +- 新 Symbol buckets 且非 tiltedView 才使用 30ms **periodOverride**(O:778-789),不是把 Fade duration 改成30ms,也不是所有 Source 变化都立即重算。 +- Controller `hasTransitions` 在 transitions 开启时 **stale 优先返回 true**(:170-175)。它含“需要重新评估”的语义,不能直接当作真实 opacity 正在变化的精确检测器。 +- delay 保存在 options/signature,但上述三个 Placement 时间公式没有读取 delay;不能将一般 Style paint transition 的 delay 机械套给 Symbol placement fade。 + +### 7.2 transitions 开启的逐帧示意 + +**条件推断(非 FZG1 实测时间线)**:固定 camera、无新 bucket、D=300ms、约33.33ms一帧,t0 首次 placement 后内容稳定。忽略调度抖动,t0+300ms 的边界实际取决于 Clock。 + +| 时刻/情况 | RenderTree changed | fade increment | Controller transition | Symbol R gate | 解释 | +|---|---:|---:|---:|---:|---| +| t0 新可见 Symbol | true | 0(静止 zoom) | true,fadeStart=t0 | false | 真实淡入开始;base opacity 可能0 | +| t0+33…267ms,不重算 | false | 约0.11…0.89 | **true,stale**(即使真实 Fade 早已结束也如此) | false | O:805-806 无条件 stale | +| ≥t0+300ms,重新计算且显隐未变 | true | 重新归零 | false(旧 fadeStart 已到期、setPlacement 清 stale) | false | 可出现现场 true/0/false,但 changed 阻塞 | +| 再下一帧,显隐仍未变 | false | >0 | true(重新置 stale) | false | 不是新像素变化的证据 | +| 后续每到普通 period 重算 | true | 通常0 | 若无新显隐变化则false | false | 循环重复,未提供 Build 窗口 | +| 新 bucket 触发30ms override | 到期时true | 新 commit 通常0 | 视 actual placed/Fade 而定 | false | 约30FPS时可能每次新增 bucket 帧均到期,需事件证据 | +| zoom-out、period≤帧间隔 | 可每次true | prevZoomAdjustment≥0,可非0 | 取决于真实Fade | false | 不使用固定300ms频率推断 | + +### 7.3 transitions 关闭(及 duration=0 的区别) + +**源码证明**:已有 Placement.transitionsEnabled=false 后,`placementIsRecent` 总是 false(:163),所以**每次到达 Continuous Full 都新建并执行 placeLayers**,即便 layersNeedPlacement 为空也调用函数;Controller.hasTransitions=false(:171),`symbolFadeChange=1`(:1289)。因此正常关闭配置是 **true / 1 / false**,不是 true/0/false。这并不意味着有独立线程每帧强迫 Full:必须先有 render request 且 Fast 失败。 + +若仅 duration=0 而 enablePlacementTransitions=true,recent 仍使用至少300ms(zoom/override 除外),fade=1;真实 Placement.hasTransitions 通常false,但 non-recompute Full 仍 stale=true。仍不能通过 Symbol gate。 + +**源码证明**:Android JNI `platform/android/MapLibreAndroid/src/cpp/native_map_view.cpp:1012-1017` 明确传 duration、delay、enable;iOS `platform/darwin/src/MLNStyle.mm:521-539` 可分别设置 transition 和 performsPlacementTransitions。当前任务没有生产 Host 此项逐帧配置证据;不能拿 SDK 默认值替代 FZG1 实际值。Android getter :1000-1008 对 unset duration 返回0,若只记录 getter 值还需区分“未设置的默认300ms”和“明确设置0”。 + +**对任务假设的明确判定**:`symbolFadeChange==0` 不能代表“Fade 已稳定”,此语义误读已被源码证伪;但缓存无窗口的充分原因是 changed/stale 的互斥准入,不是仅这个比较符。任务要求解释末态 true/0/false:该值与开启配置的稳态重算相容;在未改写字段的正常 Full 中,不支持直接解释为“关闭 transitions”。Fast 重置树字段为 false/0,而不是 true/0。若日志声称关闭且仍 true/0/false,应优先核对同帧采样、运行二进制/选项生命周期,不能改源码迎合假设。 + +### 7.4 真值表:P=!changed && fade==0 && !transition + +下表针对 placementCapable=true;无 Symbol R 直接通过本 gate,但仍受其它保护。以 c=changed、z=(fade==0)、t=Controller transition: + +| c | z | t | P | 正常 Full 可达性/意义 | +|---:|---:|---:|---:|---| +| 0 | 0 | 0 | 0 | Continuous 正常 no-recompute 分支不可达:会 stale;抽象状态不足以证明安全 | +| 0 | 0 | 1 | 0 | 开启且未重算、时间增量>0;Fade 活跃或已结束都可 | +| 0 | 1 | 0 | **1** | 唯一逻辑通过组合;**正常 Continuous Full 不可达**。已命中 Fast 的树重置可有此形态,但不能自举 | +| 0 | 1 | 1 | 0 | 同 timestamp 重复 render、增量仍0而 stale;可达也不通过 | +| 1 | 0 | 0 | 0 | transitions关闭(fade=1);或开启duration0/zoom修正且无实际Fade | +| 1 | 0 | 1 | 0 | 新显隐变化伴随非零zoom修正等 | +| 1 | 1 | 0 | 0 | 开启、静止zoom、重算但旧Fade结束;现场末态相容 | +| 1 | 1 | 1 | 0 | 开启、新显隐/Fade、刚commit | + +Bucket 增减、跨段碰撞可能改变 `Placement::commit` 内部 placed 状态、重置 fadeStart;其是否真实改变 R2 需逐 Symbol 数据。即便完全不改变,也落入上述无窗口分支。没有 Source 变化也足以卡住;Source 变化不是这个机制的必要条件。 + +### 7.5 Shader 与 Full 后安全 Build 的必要证据 + +**源码证明**:`Placement::commit:694-724` 将上一 placement 的增量折入新的 OpacityState;`OpacityState:18-24` 保存 base opacity 和 placed 方向。`RenderTreeImpl::prepare`(O:133-138)→ `Placement::updateLayerBuckets:752-757` → `SymbolBucket::updateVertices`(`src/mln/renderer/buckets/symbol_bucket.cpp:346-366`)更新 opacity 与动态属性;`Placement::updateBucketOpacities:962-1029` 处理 duplicate、default、placed hidden 和属性编码。 + +`renderer_impl.cpp:462,755-766` 把树的 fade 送入全局 shader uniform。OpenGL:`shaders/symbol_sdf.vertex.glsl:153-158`、`symbol_text_and_icon.vertex.glsl:152-158`、`symbol_icon.vertex.glsl:127-129`;Metal:`include/mln/shaders/mtl/symbol.hpp:157-164,372-374,644-646`,都将 opacity 基值加/减 fade 并 clamp。Metal fragment :257-263,495-530 再乘 paint opacity;GL `shaders/symbol_sdf.fragment.glsl:77` 和 `symbol_text_and_icon.fragment.glsl:53-57,94` 也乘 paint opacity。零 fade 可以对应 base=0 的新符号或 base=1 的已完成符号,数值自身不足以判稳。 + +**条件推断(提案前提,尚非安全证明)**:可候选在完整 Placement 和 bucket 更新、shader 输入就绪后,使用一个与 Build 世代绑定的“输出已终态且不会仅随时间变化”证据,再建立 R2。必须同时覆盖 text/icon、duplicate、variable anchor/orientation、沿线重投影(placement.cpp:776-959)、paint opacity transition、glyph/image atlas、Tile/Source/camera/context、层序/深度/混合边界。仅 `!Controller.hasTransitions` 不足以替代这些证据,因 Controller 的 stale 是待重算语义;仅删除 changed 或把 ==0 改成≥1 也无法建立与缓存像素绑定的不变量。 + +## 8. E:S1—S5 反事实与因果闭环 + +### S1:仅粒子持续推进,所有静态输入就绪且不变 + +**源码证明/条件推断**:以任务要求的“仍有含 Symbol 的 R2、粒子30FPS”为前提,而不是把“纯粒子”误解为地图根本没有 Symbol。 + +- f0 无 prepared tree,必 Full;R2 gate 拒绝,R0/R3 若无 Symbol 且其余条件通过可 Build。 +- f1 Fast 首先看到 R2 invalid;Full 中要么 changed=true,要么 stale=true,再次拒绝 R2。R0/R3 可 Reuse。 +- f2…重复;启用 transitions 的重算频率由 period/zoom/bucket 决定,关闭时每次 Full 重算。**即使无任何外部 Source mutation,当前 Symbol R 仍无法从第一次正常 Full 自举。** +- 反例边界:若全部 R 都无 Symbol,单 C 只包含受支持的 WeatherColor/Particle,所有 Gate 满足,正常 Full 能建缓存、随后 Fast 可以命中;Placement 不需每300ms独立强迫新 Full。若 f0 还有真实全局 Fade,则等待必要 Full 结束后才可能命中。 +- **仍需设备验证**:以上状态机不等于已测到30FPS、具体 CPU 成本或缓存最终像素正确。本轮未执行场景。 + +### S2:粒子关闭,只剩偶尔地图刷新 + +**源码证明/条件推断**:关闭 active/visibility 或清掉必要场数据会取消粒子 needsRepaint,#18 不能命中 Particle Fast。若同时停止 forecast、没有其它动画及 pending 资源,就失去高频 SDK wake/repaint 链;最终 loaded 且无 transition 可 Idle。期间每个真实 Full 仍进行 recent 判定,关闭 transitions 时每个 Full 都算 Placement,开启时仍可能用几帧完成 Fade/stale。 + +**FZG1 实测**:关闭粒子对照约83次Full/分钟来自任务输入。**不能证明**这83次都由导航、Placement 或 Tile 任一单源造成;也不能证明 Placement 从不运行。若只隐藏粒子却仍 forecast Playing,WeatherRuntime :252 仍唤醒,因此“粒子关=无天气高频唤醒”必须同时核对预报状态。83次与1792次的差异支持高频粒子场景增加 render 请求,但不提供精确按来源分摊。 + +### S3:真实 Host Source Mutation + Annotation + Tile 异步输入 + +**源码证明**:Style Source 更新先由 `src/mln/style/style_impl.cpp:348-351` 通知 onUpdate;新 sources identity 会令 Fast #8 失败。Full 的 O:525-537 每批非空 sourceDiff 推进一次 aggregate content revision,并为每个增删改 Source 推进 local revision。Tile callback O:1555-1559 则每次推进 aggregate/local 并 invalidate;Glyph/Image 无明确 source 时推进 global。Annotation dirty(`annotation_manager.cpp:213-227`)使 #24 拒绝,Full O:360-363 消费 dirty。 + +**理论量化(条件推断,非测量)**: + +- 一批 Full sourceDiff 含 m 个 Source 修改:aggregate 增1、local advance m 次;m 不等于 m 个 Full。 +- N 次独立 onTileChanged:当前实现 N 次 aggregate/local revision advance;若在下一 render 前合并,仅需要一次承载最新状态的 Full,若穿插 N 帧则可每帧拒 Fast。Fade、stale、继续到来的异步输入又可能延长 Full 序列,故不能用 N 推出总 Full=N 或每次 mutation 固定只需1帧。 +- 已 valid 的无 Symbol R,若仅依赖 Source S,S 的新 revision 使下一次可支持的 evaluate invalidate/build;其它无 Symbol R 可复用。global epoch 变化则所有段的新 revision 都不同。每段 Build次数≤它实际有资格 Build 且绘制成功的 Full 次数;当前 Symbol R 无资格,所以 Build仍0并非 mutation太频繁的唯一解释。 +- 只在 C 的 Source 变化可使整帧 Full,却不必令独立 R 的 local revision改变;碰撞关系仍可使 Symbol R 输出变化,不能以 source依赖表忽略跨段 Symbol。 + +**重要独立来源**:V4 forecast Playing 时 `Map::onUpdate` → `tickForecastClock` → `refreshV4RendererState`;后者 `raster_array_source.cpp:569-608` 在投影 sampleState/端点变化时换 Source impl 并通知,即使 T0/T1 纹理可复用,也可能令 #8 每次失败。这是 Native forecast 的真实输入更新,不等于静态风场粒子 advection,也不能无证据统归 NavSea 导航 Mutation。S1 必须固定 forecast 时间;S3 诊断应分别计 external Source 和 forecast projection。 + +### S4:布局对照,保留真实层序 + +| 布局及证据 | 分段 | R 缓存 | C / Fast 独立门槛 | +|---|---|---|---| +| 原 RC(STEP-110 现场历史) | R[0,31)、C[31,113),R含地名 Symbol | Symbol R当前无Build窗口 | 后段含大量普通业务层,若有普通 Symbol/Line,#22必拒;不能把 RC1/RC2 的所有字段与旧快照自动认作同一采样 | +| 原 RCRR(四段的泛型/候选基准) | 若为 R[0,31)、C[31,33)、R[33,107)、R[107,113),须由原始 plan 确认;**本轮任务未给该原配置完整边界,不将此数字当实测** | 首R和R2若含Symbol仍被阻塞 | 条件下 C仅 WeatherColor+Particle 则类型Gate可过,但 scene及所有R仍要过。泛型RCRR只说明策略排列,不保证边界和类型 | +| **RCRR_PREFIX(任务书现场输入)** | R0[0,13)、C1[13,33)、R2[33,107)、R3[107,113) | R0复用1787、R2不建、R3复用1755;与Full mixed执行相容 | C1含普通Symbol/Line+WeatherColor31+Particle32;`O:1247` 遇普通层返回false,#22确定被#3掩盖 | +| **理想保序反事实,未实施** | R[0,13)、R[13,32)、C[32,33)、R[33,107)、R[107,113) | 新R[13,32)及R2含Symbol,当前一样卡住;WeatherColor31也进入R | 恰好一个C且只有Particle可消除类型障碍;不消除Symbol Gate、scene、资源/source/annotation。若forecast/WeatherColor继续变化,新R必须重建/回退 | + +`singleContinuousSegment` 在 `renderer_impl.cpp:55-63` 限定唯一 C;`RenderLayer::startsRetainedDynamicRange` 默认false(`render_layer.hpp:113-120`),Particle/WeatherColor 才 override true(对应 layer头 :31-33/:27),普通 Symbol/Line 即便暂时隐藏也不会因不可见自动成为受支持 C。多 R 本身不是禁止项;多 C 是当前 Fast 的独立阻塞。STEP-106 的 R-C-R-C 是缓存局部性测试,不是生产 Fast 性能证明。 + +**仍需设备验证**:补原始 RC1/RC2/RCRR plan JSON、113层完整 id/type/source/visibility/顺序,才能逐层定位实际布局;本轮没有跨项目改 Style。理想配置只做反事实,不能承诺 WeatherColor永远静态,更不能删海图Symbol或把普通层批量挪C作为优化。 + +### S5:动态安全回退 + +| 变化 | 必须处理的 R / Full 条件(源码证明+条件推断) | 不可盲复用的理由 | +|---|---|---| +| pan/zoom/rotate/pitch、viewport/pixelRatio | 屏幕缓存 key 改变的所有R重建;Fast camera/viewport/signature拒绝 | 旧屏幕纹理、anchor/reprojection不再对应当前相机;定图仍需碰撞正确 | +| 相关 Tile/source更新、缺失/fading | 依赖段 Source gate/revision失效;Fast aggregate/content/fading可能整帧Full | 异步完成意味着可见内容改变;非依赖段只在全局证据允许时继续复用 | +| Glyph/Sprite/Image/fontFace | 当前实现 global epoch及ready;通常所有R保守失效 | atlas/字形纹理可能变化,sourceReady不足以覆盖 | +| text/icon opacity Fade或paint transition | 受影响Symbol R不能冻结随时间变化的像素;现有全局Controller/gate更保守 | uniform与base opacity共同决定alpha;不能从fade=0/Controller=false单独得出结果等价 | +| 跨段Collision变化 | 所有碰撞影响到的Symbol R必须刷新;当前没有可靠per-R证据时保守回退 | source不等于segment;跨源关闭只隔离不同source,同source仍相互影响 | +| Annotation插入/更新 | 拓扑/Source/pending及碰撞具体属性分别处理 | 自动 points Symbol由 `annotation_manager.cpp:176-192` 添加,默认iconAllowOverlap/iconIgnorePlacement=true,**不能断言它一定排斥R2**;结构gate照样capable,实际影响还看其它Symbol/配置 | +| Style属性/顺序、Layer增删、Plan边界 | 本段fingerprint或全局topology变化;对应cache释放/重建 | 层序和混合语义不能被texture复用改变 | +| context/resource/compositor失效 | 已损坏资源全部重建;失败保持live/fallback | valid标志不能替代真实资源准备和draw成功 | + +## 9. A—D 提案比较(均未实施) + +优先顺序:**A诊断补齐 → B建立严格生命周期准入原型并同步处理scene → C按需求扩展per-R等价;D先交Integration确认公共“强制Full”语义,再决定实现**。C范围布局问题与这些方案独立:保序单粒子C是候选配置,任何产品Style变更都另行授权。 + +| 提案 | 首次Build / 全局scene / C Gate | 正确性与风险 | CPU / GPU / 内存代价 | 候选修改文件、复杂度、双平台与最小验证 | +|---|---|---|---|---| +| **A 全Gate+R2状态诊断,优先P0** | 不改变Build或scene/C结果;一次取齐被短路掩盖项 | 不改变fail-closed;读状态需同frame和phase,不能额外调用有计数副作用的needsRepaint造成伪观测 | CPU约O(现有gate数+R数),复用已算结果;固定环形缓冲有界内存;GPU零额外工作。采样成本须实测 | `particle_fast_path.hpp`、`renderer_impl.cpp`、`render_orchestrator.cpp/.hpp`、`placement.hpp/.cpp`、现有diagnostics结构;**2/5**。Shared Core同字段,Android/iOS只分别接低频导出。最小验证见第10章,记录schema无需新增公共API即可先做Native内部诊断 | +| **B Full后安全Build,优先P1** | **目标解决首次Build**;必须同时替换每段准入和prepared scene的旧证据,单改其中一处无效;不解决普通C层Gate | 保留全图Placement与全部资源保护,以已经完成的Symbol shader输入/终态证据授权。先shadow,不直接删除changed/stale/fade条件。危险为早冻结Fade、漏collision/atlas/paint状态、引用过期bucket | Full本来就做Placement,可在commit/bucket完成点记录代际,稳态尽量O(1)/段;首次离屏多一次R绘制,后续composite;每R现有全屏RGBA颜色约4WH字节,另depth/stencil和backend开销,更多R线性增加 | `placement.hpp/.cpp`、`symbol_bucket.cpp/.hpp`、`render_orchestrator.cpp/.hpp`、`renderer_impl.cpp`、`render_segment.hpp`、cache状态结构;按所需证据可缩减,非已定设计。**4/5**。GL/Metal shader不预设需要改;必须两后端同提交检查opacity精度和texture合成。最小:静止Symbol R开启/关闭transitions、首次可见、fade长尾、camera/context/资源失效,false-reuse=0 | +| **C 更细per-R结果等价,优先P2** | 可以为B提供首建/重建的证据;单纯“新旧签名相等”没有旧Build时不能自举;scene也必须接纳相同证据;不解决C Gate | 与STEP-111一致,仍全图碰撞,再按R比较结果;stable ID、世代和实际GPU输入齐全才允许reuse。hash相等不是完整安全证明;未知必须回退 | 每次Full placement后O(受影响symbol数),避免每粒子tick遍历;签名/版本及必要精确快照占O(symbol数)或O(段数+变化集);降低R GPU重绘但不自动省CPU prepare | `placement.hpp/.cpp`、`cross_tile_symbol_index.cpp/.hpp`、`symbol_bucket`、`render_layer`、`render_orchestrator`、cache/renderer接口。**4/5**;深改分段Collision为**5/5且不推荐本轮**。双平台共用结果证据、验证shader差异。最小:同Source跨R、跨源开关、C1仅影响R2/影响多个R、duplicate/variable-anchor/沿线字/Annotation/重载;以缓存关对照 | +| **D 外部Full与Particle Tick分流,Integration先行** | 单独不解决Symbol首次Build、scene或C类型;可明确某次更新必须Full,并在纯tick时优先Fast | 用户要求外部明确Full不能以数据相同去重/延迟;需明确序号、确认时点、合并规则、生命周期与平台一致性,禁止把重要资源更新当tick | 内部原因bit/epoch通常O(1);若每个命令都必须独立一次Full会增加CPU/GPU,额外队列可能有界内存需求。收益取决于频率,不能承诺2 Full+28 Fast | `UpdateParameters`、`Map::Impl`、Renderer入口、Android frontend、Darwin frontend及必要RN公共层;**3/5**(跨项目/生命周期可到4)。现有triggerRepaint不是此契约。最小:相同数据两次明确Full、命令与tick/异步tile竞态、禁用销毁重建、Android异步合并和iOS,检查每个accepted序号按决议完成;不得私建API | + +### 9.1 B/C 的数据所有权与最轻量可行方向 + +**源码证明**:`Placement` 持有 placements、opacities、variableOffsets、placedOrientations(`placement.hpp:184-201`),以 crossTileID 索引;`getSymbolPlacement`(`placement.cpp:1301-1304`)查具体Symbol。`placeLayers` 后 commit 已完成,但最终bucket opacity/duplicate和动态投影更新在 RenderTree.prepare,GPU drawable/tweaker又在之后。`Placement` 至多保留上一代(:190-192),cache可能跨更多代存在,因此只能与上次成功Build版本绑定,不能仅比较相邻两次Placement。 + +`getPlacedSymbolsData` 对普通 Continuous Placement 返回静态空数组(:1325-1327),TilePlacement有专门实现(:1378-1387);不能把现有public查询当完整R2结果快照。STEP-111的候选字段仍需要逐项实现与验证,本任务不宣称已设计出可直接上生产的签名。 + +**候选轻量方案(条件推断)**:先争取“静态输入代际相同 + 完整Placement结束 + 所有目标symbol有效opacity已终态 + 无未消费资源/桶变更”的全局生命周期证书,授权该次Full离屏Build并保存buildEpoch;下次tick只比epoch和既有Gate。比每次tick全symbol hash轻。但必须证明碰撞/hidden/duplicate/variableOffset/paint动画均被证书覆盖;如不能低成本取得这一证明,继续fail-closed,退回shadow per-R比较,而非改用Controller.stale=false假装稳定。 + +更细C候选字段分工:Source/tile/bucket generation、crossTileID及R归属、text/icon placed/hidden、实际anchor/orientation/动态顶点和编码opacity需要结果证据;Camera/viewport/pixelRatio、Style顺序、Source/global资源代际优先复用已有key/revision;paint opacity/transition必须覆盖时间依赖。首次帧没有参考就Build,未知或资源重建丢弃证书,真实结果变化则重建。内存与比较开销需要新增计数验证,不能从“理论可hash”推出安全或高性能。 + +### 9.2 D 的 Interface Blocker + +**源码证明**:当前 `Map::triggerRepaint`(map.cpp:93-95)只调用onUpdate;`UpdateParameters`(`src/mln/renderer/update_parameters.hpp:25-61`)没有 externalFull/原因序号字段;Fast签名不比较timePoint,入口每次都先试Fast。`packages/maplibre-particle-rn/src/RendererRuntime.ts:12-27` 只有retained控制、continuous range/segment plan和diagnostics,未提供“外部指令强制Full并确认”的方法。Android frontend AsyncTask/最新params设计还有合并行为,不能自动保证每次命令一帧。 + +**Interface Blocker(提案,不是当前调查阻塞)**:若用户希望公共Host“明确Full”命令具有不可被相同数据去重/延期的语义,Integration必须确认:完整更新是CPU prepare/全图Placement重新评估,还是强制所有R GPU重画;两个相邻命令能否同一Full满足、如何回执、如何与异步资源到达排序。既有C-001/C-002没有该公共语义。本轮仅只读 `/Users/tei/dev/navsea-particle-integration/contracts/PARTICLE_NAVSEA_BOUNDARY.md`、`CONTRACT_VERSION.md` 和COORD-005决议;C-001边界继续有效、C-002是QBIN窗口增补。不得通过Native私有通道绕过协调,未修改Integration,也未发送跨项目消息。 + +纯内部性能优化可先留在Shared Core,不需要借口更改Host职责;明确Full请求一旦获授权,不能用“内容相同”跳过或推迟它,普通粒子tick则优先Fast并保留相机/资源/Placement等安全Full。**2 Full+28 Fast只是目标例子,非当前实现承诺或验收门槛。** + +## 10. 下一阶段最小诊断提案与独立验收证据 + +**仅提案,未插桩、未执行、未创建下一Step。** 当前源码已能证明控制流矛盾;诊断用于确认FZG1实际选择的配置/路径、其它Gate和主要CPU成本,并为安全改造建立基线。 + +### 10.1 同帧字段与采样点 + +| 位置 | 最小字段 | 判别目的 | +|---|---|---| +| Map::onUpdate / SDK cadence wake | map generation、update/request序号、SDK wake/observer repaint/Host mutation/forecast tick来源、targetFps、active/visibility、forecastPlaying、sourceId、Clock时间 | 区分下一帧请求与外部数据更新;调度来源标识仅Native内部提案,不私建公共命令 | +| I:199-278,Fast判定前 | frameId、UpdateParameters.timePoint、实际render时间、24项bitset、首错、changed signature字段mask、prepared tree/scene标志、所有R valid/buildRevision/staticRevision、config/topology/content revision | 一次记录所有Gate;多request合并和同一params重渲染可识别;避免只看cache-invalid | +| O:778-810,Placement前后 | **当前options与旧Placement options** enable/duration是否unset/delay、commitTime、fadeStartTime、now、zoom/prevZoomAdjustment、override及原因、bucketAdded/Changed、recent、stale前后、outer changed、commit实际placed变化计数、raw fade和Controller/Placement各自hasTransitions | 验证开关分支、300/30ms和zoom影响;区分实际Fade与stale | +| RenderTree.prepare后 / I:637-691 | tree fade、shader fade、每R capability、动态usesPlacement、placementGate、sourceReady/stable、fingerprint、content revision、supported、fallback各项bitset、cache前状态、evaluate decision | 确认是否Gate阻止首次Build、是否其它失败被placement reason遮蔽 | +| cache Build/execute / I:813-862,998-1028 | BuildPending/成功/失败、valid/key/资源世代、texture/compositor create/release、build/reuse/invalidation/fallback累计及delta、actualExecution、globalLiveReason、GPU pass耗时(如平台已有可靠计时) | 拒绝Build与GPU失败分开;Reuse与实际composite分开 | +| frame完成 | FullCPU/fast/simulationAdvance计数、createTree/placement/prepare/layerUpdate/encode耗时、repaint原因bits、loaded/Idle | 不能用Full计数代替Placement次数或simulation次数 | + +字段不存在时新增只读诊断accessor应另行授权;不可通过额外一次placeLayers获得“测量值”。重用本来算出的gate bool与needsRepaint值,避免观察过程增加计数或推进cadence。 + +采样建议:每组先10秒预热、再60秒统计;前3秒及状态变化前后保留连续逐帧详情,60秒完整保留紧凑gate/段决策和计数;用固定容量Native环形缓冲、超限显式记录dropped计数。日志导出1Hz摘要或窗口结束统一写,不每帧JSON/JS bridge、不每Symbol默认打印。以实测结构大小和容量给出内存上限;对trace开/关同场景比较CPU/frame,目标开销预算先建议≤1%,达不到则缩减详细采样但保留全部gate bitset。这个预算不是当前已达到指标。 + +### 10.2 最小场景与判断 + +1. FZG1原V23、同场/同camera:粒子开/关,分别固定forecast暂停;保留原plan和所有113层元数据。复现计数只用于基线,不要求改变现有结果。 +2. 同输入分别enablePlacementTransitions=true/false;true下记录D=默认/显式值,false预期Full组合true/1/false。每个Symbol R应在每个Full看到gate=false;若有正常Full gate=true,是本报告不变量反例,应保留原始帧/版本并停止假设式修复。 +3. PREFIX,记录#22普通层第一个拒绝的id/type/index,同时#3失败;原RC和真实RCRR分别保留配置,不复用旧布局推断。 +4. 无外部Mutation的固定field、forecast暂停,与导航/Annotation更新、Tile完成、forecastPlaying分别对照,按来源和phase对齐revision。不得靠降低粒子FPS制造好看CPU。 +5. 后续B/C shadow原型才验证:首建可达、fade长尾/variable anchors/跨源碰撞/同源跨段/相机/Style/清场/context;缓存开关同帧结果对照,**false-reuse=0**,不能用总FPS掩盖陈旧字/图标、错层或缺字。Android OpenGL和iOS Metal同源码各跑,不能用一端替代另一端。 + +独立验收需要:源码commit及制品SHA/实际SDK版本、plan JSON与Style层表、全部gate bitset、Placement两套配置和时间、R状态机计数、trace开关开销、CPU阶段耗时、同帧视觉/必要图像差异证据、原始日志路径。任何提升Fast命中/CPU降低数字必须来自新实验。本轮不提供尚不存在的诊断开关命令,也不执行构建、测试或设备动作。 + +## 11. 最终结论、未确定事项与责任边界 + +### 11.1 已证明 + +1. **源码证明**:粒子/SDK只请求下一次正常地图渲染;每次先试Fast,没有粒子专属“必须Full”命令。当前Full CPU频率是请求频率与准入失败共同作用,不等于静态像素每帧变化。 +2. **源码证明**:正常Continuous Full对结构性Symbol R的三项gate无可达通过分支:changed或stale→transition二者必挡其一。它足以解释在静态输入下仍无法首次Build;不是仅仅“可能比较符用错”的猜测。 +3. **源码证明**:fade=0不是终态证明;关闭transitions正常返回1。FZG1 true/0/false快照不能用来断定关闭配置或每帧300ms周期。 +4. **源码证明**:还有prepared全局scene锁与C范围类型锁;PREFIX普通Symbol/Line C不支持Fast。仅修R2不能承诺Fast命中。 +5. **源码证明**:缓存invalidate计数不要求曾valid;Full可复用其它R并live画R2,正常主屏显示不等于离屏Build成功。 +6. **源码证明**:V23相关核心gate与本轮main一致;STEP-109仅改变两Layer的field深比较快路,不解除以上限制。 + +### 11.2 假设判定与历史报告关系 + +| 假设 | 判定与证据强度 | +|---|---| +| 粒子每次tick直接改变静态R内容 | **不成立为必然关系,源码证明**;纯simulation不改R,forecast/Host更新另算 | +| C1每帧碰撞使R2符号真实变化,才导致Build0 | **未证实且非必要条件**;gate控制流在零mutation也不可达 | +| `symbolFadeChange==0`等于Fade结束 | **源码证伪**;它是增量,不是稳定布尔量 | +| FZG1 true/0/false证明关闭transitions | **源码不支持**;关闭的正常Full为true/1/false | +| 删除placementChanged或改==0即可安全修复 | **不支持**;stale/scene/C/资源/碰撞/opacity仍独立存在 | +| 现有Gate是合理保守且没有任何进展性问题 | **需细化**:fail-closed保护正确,但正常Symbol R Full首次Build不可达是可证的功能/性能准入限制;是否按产品契约命名为Bug由后续设计确认,不是视觉错误或可直接放宽安全的证据 | + +STEP-110/111保留原样。它们强调“来源不等于视觉影响、缺逐R安全证据”的结论仍成立;本轮新增的是 **changed/stale互斥导致准入不可达** 的完整控制流证据,不能把旧PARTIAL说成已实现修复,也不重写历史来伪装一致。 + +### 11.3 尚不确定 / 仍需设备验证 + +- FZG1每一帧enable/duration、bucket添加、zoom修正、调用来源和24项Gate真实值;末态仅一个快照。 +- 原RCRR完整边界和RC1/RC2精确同版Style列表;本报告不把推测布局写为实测。 +- 导航Source、Native forecast投影、Annotation、资源异步输入各自占多少Full/CPU;83与1792不是按来源归因数据。 +- R2每个Symbol实际最终输出是否与某个候选缓存时刻完全等价、可接受证书的充分字段及实现成本。 +- B/C实现后的首建、Fast hit、CPU/GPU/内存收益、GL/Metal像素一致性和安全回退;本轮都没有实现或运行结果。 + +**交付判定:本轮A—E源码调查与提案已完成;综合结论保留PARTIAL(生产逐帧归因、完整原始布局及安全修复/性能未验证),不标实现/运行/性能PASS。** 已有决定性源码机制证据,不再以猜测“等Fade结束就可能Build”作为建议。下一阶段最小工作是A的同帧诊断和B的安全证据设计;维持现有fail-closed,不直接修Native。 + +Codex调查阶段只交付四份文档,不承担Git操作;STEP-112任务书第十一节要求RDC独立进行仅文档scoped提交、推送和远端回读。Git最终状态以RDC收尾补记为准。 diff --git a/docs/50_Step记录/STEP-112_V23粒子FullRender与Symbol缓存调查_v1.md b/docs/50_Step记录/STEP-112_V23粒子FullRender与Symbol缓存调查_v1.md new file mode 100644 index 000000000..d7c452f67 --- /dev/null +++ b/docs/50_Step记录/STEP-112_V23粒子FullRender与Symbol缓存调查_v1.md @@ -0,0 +1,49 @@ +# STEP-112 V23 粒子 Full Render 与 Symbol 缓存调查 v1 + +日期:2026-10-11(JST)。状态:**完成(本轮只读调查交付);综合判定 PARTIAL(生产逐帧归因、安全修复与性能未验证)**。 + +## 目标与基线 + +只读完成任务A—E、全Gate矩阵、R2状态机、Placement/Fade两种配置、S1—S5及A—D提案;不实施Native修复。 + +- Mac.lan / Darwin 24.6.0 / x86_64;仓库 `/Users/tei/dev/MapLibreParticleLab`。 +- 起点及结束基线:`particle/main@41b416c261fa72bd21171328045036a124694f47`。 +- 模型已从本会话rollout确认 `gpt-6-astra/high`;无切换、无子代理。 +- Integration HEAD `80667d6c` 采用用户提供的RDC核实事实,只读相关契约/决议。 +- V23 Android/RN `13.6.1-particle.23`、iOS `6.29.0-particle.23`。对V23构建源码 `87f3d1c…` 的相关路径diff只有STEP-109两Layer快比较;本调查核心Gate保持一致。 + +## 必做项与结果 + +- [x] 按序阅读AGENTS、基线、8份规范、总则README和四份有效总则、当前任务与STEP-102/106/109/110/111。 +- [x] A:Core/Android/iOS请求→Fast/Full→GPU→Observer/Idle调用图,区分forecast与粒子cadence。 +- [x] B:24项Fast Gate,逐项来源、通过条件、首错、Tick影响、Full恢复与现场证据强度。 +- [x] C:R2结构能力、Source/local/global revision、evaluate/BuildPending/资源/成功/失败/合成状态机。 +- [x] D:transitions开关、300ms/30ms/zoom、fade公式、8组合真值表、GL/Metal opacity链。 +- [x] E:S1—S5逐帧/事件推演和布局对照;原RCRR完整边界缺失明确标未知。 +- [x] A—D提案:优先级、首建/scene/C覆盖、正确性、资源代价、候选文件、1—5复杂度、双平台最小验证;D列Interface Blocker。 +- [x] 先创建框架、分阶段追加证据,最终三份交付文档和开发记录追加已落盘。 +- [ ] FZG1同帧全Gate/配置/CPU归因、生产Symbol结果等价、GL/Metal运行验证:本轮禁止执行,留给另行授权阶段。 +- [ ] Native修复、插桩、性能改善、commit/push/远端HEAD:本轮未执行,后续RDC或新任务负责。 + +## 主要源码结论 + +正常Continuous Full中,含结构性Symbol的R要求 `!changed && fade==0 && !transition`;重算分支changed=true,不重算分支setPlacementStale令transition=true,因此首次Build没有准入窗口。即使全体Source静态也成立。`fade==0`不是Fade终态;关闭transitions的正常Full为true/1/false,现场true/0/false与开启配置的稳定重算相容。 + +R2 invalid令下一次Fast在cache-invalid返回;另有全局prepared scene及PREFIX中普通Symbol/Line C的独立阻塞。Full可以复用R0/R3,不等于全图全部重画。上述不证明安全放宽Gate,也不证明现场每次Placement都改变R2像素。 + +## 允许范围与必做输出 + +仅四文件: + +1. `docs/20_调查/STEP-112_V23粒子FullRender循环与Symbol缓存准入全面源码调查_v1.md` +2. 本阶段记录。 +3. `docs/报告/STEP-112_V23粒子FullRender与Symbol缓存调查_Codex完成报告_v1.md` +4. `粒子开发记录.md` 仅追加。 + +两份未跟踪STEP-110/111任务书保持原样。未改源码、测试、Style、脚本、跨项目文件、版本或发行产物,未清理dirty或改变branch/worktree。 + +## 检查、阻塞与下一步 + +只执行只读源码/Git比较和文档范围、完整性、空白检查;实际结果详见完成报告。编译/链接/codegen/测试/Jest/Gradle/APK/ADB/设备操作全部未运行,无新制品。 + +剩余证据:FZG1实际Placement options和24项Gate逐帧序列、原始完整plan、Source/forecast/Annotation来源分摊、Symbol最终GPU输出安全等价。下一步仅建议授权A低开销同帧诊断与B生命周期安全证据设计;不得先删Gate。公共明确Full命令语义由Integration决定。Codex不负责Git收尾;STEP-112任务书要求RDC独立完成仅文档scoped提交、推送和远端验收,结果以Git收尾补记为准。 diff --git a/docs/报告/STEP-112_V23粒子FullRender与Symbol缓存调查_Codex完成报告_v1.md b/docs/报告/STEP-112_V23粒子FullRender与Symbol缓存调查_Codex完成报告_v1.md new file mode 100644 index 000000000..a473d6052 --- /dev/null +++ b/docs/报告/STEP-112_V23粒子FullRender与Symbol缓存调查_Codex完成报告_v1.md @@ -0,0 +1,87 @@ +# STEP-112 V23 粒子 Full Render 与 Symbol 缓存调查 Codex 完成报告 v1 + +日期:2026-10-11(JST)。 + +## 1. 执行结论 + +**A—E只读源码调查和四文件交付已完成;综合判定 PARTIAL。** 正常Continuous Full的Symbol R首次缓存准入不可达已有决定性源码证明;生产逐帧原因、原RCRR完整布局、可安全替代的准入证据和真实性能仍不完整,不标全任务/实现/性能PASS。 + +关键证据:`render_orchestrator.cpp:792-807` 的重算/不重算分支,`placement.cpp:160-175` 的recent/stale语义,与 `render_segment.hpp:85-90` 的三条件组合。重算则changed=true,不重算则Controller stale使transition=true,Symbol R均不能首次Build。`symbolFadeChange==0`不是终态条件;transitions关闭时正常Full返回fade=1。全局prepared scene和PREFIX普通Symbol/Line C仍独立阻塞Fast。 + +主报告第4—10章包含完整调用图、24项矩阵、缓存状态机、两种配置与真值表、S1—S5、A—D成本风险提案、最小采样方案;第11章区分源码证明、现场输入、条件推断和设备待验。 + +## 2. 实际模型、机器与Git基线 + +- 机器:Mac.lan,Darwin 24.6.0,x86_64;未连接或操作Android/iOS设备。 +- 仓库:`/Users/tei/dev/MapLibreParticleLab`;branch起终均 `particle/main`。 +- start HEAD:`41b416c261fa72bd21171328045036a124694f47`;end HEAD同此,未commit。 +- **模型 `gpt-6-astra`,reasoning effort `high`**。环境会话ID `01a12686-49ee-70b1-953e-f95f8a4bd9ca`;实际读取本地 `~/.codex/sessions/2026/10/11/rollout-2026-10-11T00-54-57-01a12686-49ee-70b1-953e-f95f8a4bd9ca.jsonl` 的 `turn_context.model/effort` 确认;没有自行切换或升级,没有启动第二实例/子代理。 +- 起点tracked clean;只有两份未跟踪STEP-110/111任务书。`git submodule status` 全为空格前缀,无未初始化/偏移标记;未操作子模块。 +- 终态预期且经收尾检查核对:新增本Step三文档、开发记录修改,加上原有两份未跟踪任务书,无其它变化。 +- V23构建源码 `87f3d1c59a52581fb8339f0ea7d106b9f4b94fef`;相关路径与HEAD只差STEP-109的两Layer snapshot快比较,核心Gate行为一致。V23正式制品不含这项开发快路,本轮未发行。 +- Integration main `80667d6c` 是用户提供的RDC核验事实;本轮未fetch/pull/远端回读,只读相关contract/coordination。 + +## 3. 任务逐项:已完成 / 未完成 / 无法验证 + +| 项目 | 结果 | 证据与限制 | +|---|---|---| +| 前置阅读、实际模型、工作区核对 | 已完成 | 根AGENTS、PARTICLE_BASELINE、8份规范、现行总则README+4份、STEP-112、STEP-102/106/109/110/111任务/记录/报告 | +| A 调度/Idle/cadence/CPU-GPU区别 | 已完成 | 主报告4;SDK Handler/NSTimer唤醒、Core cadence、Observer和prepared-tree链;实测30FPS来源仍未知 | +| B 全部Fast Gate | 已完成 | 主报告5;24项按首错顺序,完整non-camera signature及prepared scene二重锁 | +| C R2缓存状态机 | 已完成 | 主报告6;首次未建、反复无条件invalidate、revision/key变化、资源/Build失败分开 | +| D Placement开关/Fade真义 | 已完成 | 主报告3、7;无窗口的分支证明,300/30ms/zoom和8组合表,GL/Metal shader依据 | +| E S1—S5及布局 | 已完成(缺证据明确标未知) | 主报告8;原RC/PREFIX和理想保序布局;原RCRR完整边界未获得,不伪造 | +| A—D工程提案与最小验证 | 已完成 | 主报告9—10;首次Build/scene/C分别评估,候选文件/成本/风险/1—5复杂度、双后端、Interface Blocker | +| 任务书四项落盘 | 已完成 | Codex本轮直接写入,早期框架与后续章节分阶段保存;不是由RDC补写 | +| FZG1每帧options、请求来源、所有Gate、CPU分摊 | 无法验证 | 只有任务/历史现场计数与末态;本轮禁止插桩和设备操作 | +| Symbol结果等价/安全新Gate充分性 | 无法验证 | 当前无与成功Build版本绑定的最终GPU输入证书;提案需shadow和运行对照 | +| Native修复、首建成功、Fast hit提升、CPU改善 | 未完成(本轮不授权) | 未改实现,不能将只读控制流证明当修复PASS | +| 编译/测试/设备/制品 | 未执行(本轮禁止) | 无新build log、APK/AAR/XCFramework、运行结果或SHA | +| commit/push/远端HEAD | 未执行(用户交RDC) | 无新commit/hash/message/push结果,不冒称Git收尾完成 | + +## 4. 实际写入文件 + +新增: + +1. `docs/20_调查/STEP-112_V23粒子FullRender循环与Symbol缓存准入全面源码调查_v1.md` +2. `docs/50_Step记录/STEP-112_V23粒子FullRender与Symbol缓存调查_v1.md` +3. 本报告 `docs/报告/STEP-112_V23粒子FullRender与Symbol缓存调查_Codex完成报告_v1.md` + +修改:`粒子开发记录.md`,仅在末尾追加STEP-112短条,不重写历史。 + +无Native/C++/Kotlin/Java/Swift/ObjC/JS/TS/Shader/测试/Style/脚本改动;无NavSea/Integration跨项目写入;无分支/worktree变更或dirty清理。 + +## 5. 检查与禁止操作 + +已执行:`pwd`、`uname -a`、branch/HEAD/status/submodule只读核对;`rg`、带行号源码读取;V23相关路径`git diff`;文档内容和变更范围检查。收尾的文档空白、原文件哈希、开发记录前缀检查结果见第8节。 + +**全部NOT RUN**:CMake/Ninja/Bazel、编译/链接、codegen、C++测试、Jest、Gradle、APK/AAR/XCFramework打包、ADB/安装/设备/logcat/截图、新性能测量、发布、commit、push。没有已观察到的编译或测试失败;因为没有执行,不能推断会通过。 + +少数初始按类名探测的路径不存在,随后按实际文件定位,例如RenderRasterParticleLayer实际实现位于 `render_particle_validation_layer.cpp`,RenderTreeImpl位于 `render_orchestrator.cpp`;未用缺失路径作为证据,也没有为此创建源码文件。 + +## 6. 与任务书差异和阻塞 + +- 用户当前明确指定gpt-6-astra/high,覆盖任务书旧建议,已实际核实。 +- 本轮Codex执行指令明确不由Codex提交/推送,遵循AGENTS的Codex→RDC职责分工;STEP-112任务书第十一节要求RDC独立完成scoped Git提交、推送及远端验收。用户并未禁止本任务的Git收尾。 +- 用户已给Integration同步事实,本轮只读相关contract/coordination,不重新做全仓同步。 +- 完整原RCRR plan、现场options/逐帧日志和CPU阶段分摊缺失;限制在主报告单独标记,不将条件推断升格为FZG1实测。 +- 公共“外部明确Full且不按相同数据去重/延迟”目前没有相应force-Full/序号字段。Interface Blocker仅约束未来D提案,需Integration决议,未新增API或改责任边界。 + +## 7. 独立验收与下一步 + +RDC依据STEP-112任务书第十一节,独立复核主报告第3章的两分支证明和第5章24项来源,核对四份交付文件、两份未跟踪任务书及HEAD,然后做scoped Git提交/推送及远端HEAD核验;以独立Git收尾补记为最终证据。 + +下一代码阶段先授权A低开销同帧gate/Placement/R状态诊断,再审查B的Full后安全Build证书;需要per-R结果时用C shadow比较。保护跨Source/跨段Collision、text/icon Fade、camera、资源、层序与GL/Metal一致性。独立验收必须取得源码/制品版本、plan/Style表、逐帧gate及计数、trace成本、CPU阶段耗时和视觉证据。未经新任务授权,不实施Native修复,不降低FPS、不删除Symbol、不放宽fail-closed。 + +## 8. 实际收尾检查结果 + +- `git diff --check -- 粒子开发记录.md`:无空白错误;diff统计仅 **8行追加、0行删除**。 +- 三份新增文档分别执行 `git diff --no-index --check /dev/null <明确路径>`:均无诊断输出;退出码1表示与空文件存在差异,不作为构建或测试结果。正文代码围栏成对,Fast矩阵已人工核对包含顺序1—24,S1—S5齐全。 +- 开发记录与 `git show HEAD:粒子开发记录.md` 的原始 **159061字节前缀完全相同**,原SHA-256=`9a1690188a9c283024c74fa275ec68d7442459c73008126087e0d9ec9704910a`;本轮追加1381字节。 +- STEP-110未跟踪任务书SHA-256仍为 `33037df71b4b30aa7956d59f4b99f77af3085405b24111bc0fe11ec857a93db0`。 +- STEP-111未跟踪任务书SHA-256仍为 `f3a5806f25384bf46055db2edfea0d21bfbc4124184bc813b46e38b8079c1ec4`。 +- 最终 `git status --short` 只显示:开发记录M、本Step三文档??、原有两份任务书??。`git diff --cached --name-only` 无输出,未暂存任何文件。 +- `git branch --show-current`=`particle/main`;`git rev-parse HEAD`=`41b416c261fa72bd21171328045036a124694f47`,与起点一致。 +- 无新commit,commit message不适用;push未执行,远端HEAD未回读。原任务书中的自动提交要求已由用户本轮禁止指令覆盖。 + +这些仅是源码/文档取证与范围完整性结果,不是编译、设备、性能或安全新Gate的PASS证据。 diff --git a/粒子开发记录.md b/粒子开发记录.md index b98b43bd8..80f23a358 100644 --- a/粒子开发记录.md +++ b/粒子开发记录.md @@ -1125,3 +1125,11 @@ Android V3-08 单次综合验收已完成并关闭。Native `13.4.1-particle.2` - GPT/RDC 复核当前全局 Placement Gate 和逐段 Cache:`render_segment.hpp:85-90` / `render_orchestrator.cpp:792-809` / `renderer_impl.cpp:638-686`。建议保留全局 Placement,先研究每 R 结果影子比较,再授权安全复用;复杂度约 3→4,真正独立 Placement 引擎为 5/5。 - 仅落盘 PARTIAL 评估及执行报告,不改 Native/公共接口、未编译/测试/设备、未进行 Source Lock;实际 Git 收尾另行核验。 - 主报告:`docs/20_调查/STEP-111_R段Placement结果等价与独立刷新可行性调查_v1.md`;Step 与执行状态报告见 `docs/50_Step记录/`、`docs/报告/`。 + +### 2026-10-11 STEP-112 V23 Full Render 循环与 Symbol 缓存准入调查(PARTIAL) + +- Codex实际会话已核实 `gpt-6-astra/high`;在Mac `particle/main@41b416c261fa72bd21171328045036a124694f47` 完成A—E只读调查,并直接分阶段落盘三份文档。 +- 源码证明正常Continuous Full中,Symbol R重算时changed=true,不重算时stale使Controller transition=true,三条件Placement Gate没有首次Build窗口;fade=0不代表终态,关闭transitions正常Full为true/1/false。Fast还有prepared scene及PREFIX普通Symbol/Line C的独立阻塞。 +- 已交付24项Gate矩阵、R2状态机、开关/Fade真值表、S1—S5和A—D提案。FZG1逐帧配置/来源/CPU、原RCRR完整布局及安全新准入仍待验证,不能称Native已修复或性能PASS。 +- 仅新增主报告/阶段记录/完成报告和本追加;未改源码/Style/脚本/测试/跨项目文件,未编译/测试/Jest/设备操作,未commit/push,两份未跟踪STEP-110/111任务书保留。 +- 主报告:`docs/20_调查/STEP-112_V23粒子FullRender循环与Symbol缓存准入全面源码调查_v1.md`;阶段记录及完成报告见 `docs/50_Step记录/STEP-112_V23粒子FullRender与Symbol缓存调查_v1.md`、`docs/报告/STEP-112_V23粒子FullRender与Symbol缓存调查_Codex完成报告_v1.md`。后续RDC独立审查和Git收尾,未授权Native修复。