Files
MapLibreParticleLab/docs/40_任务/STEP-112_V23粒子FullRender循环与Symbol缓存准入全面源码调查_Codex任务_v1.md
tei 41b416c261
Some checks failed
validate-scripts / validate-scripts (push) Has been cancelled
docs: add STEP-112 particle full-render cache investigation task
2026-10-11 00:48:49 +09:00

15 KiB
Raw Blame History

STEP-112:V23 粒子 Full Render 循环与 Symbol 缓存准入全面源码调查 — Codex 任务 v1

状态:待执行|类型:全面只读源码调查|编写日期:2026-10-10

一、用户目标与必须回答的问题

NavSea 的 FZG1 连续风场场景中,粒子约 30 FPS,Native Particle Fast Path 持续 0 hit,约 1,787~1,796 次 Full CPU Frame/分钟;粒子关闭对照约 83 次 Full CPU Frame/分钟。含 Symbol 的 R2 缓存长期失效,理由为 static-placement。用户要求由粒子系统全面分析 Native 源码,找出导致“粒子 Tick → Fast Path 失败 → Full Render → R2 仍无法建立缓存 → 下一次继续失败”的真实原因,并提出安全修复方向。本任务不授权修复。

必须明确回答:

  1. 谁请求每帧重绘?粒子是否只是要求下一帧,而非要求 Full Render?什么时候 MapLibre 能进入 Idle?
  2. Fast Path 全部准入条件的实际来源与执行顺序是什么?为什么 FZG1 的首个拒绝原因是 cache-invalid?
  3. R2 为什么 Build=0,而不是建好后偶尔失效?static-placement 实际检查了什么?
  4. placementChanged、symbolFadeChange、placementTransitionActive 是否在正常连续运行下形成难以通过的组合?原因来自时间机制、Transition 配置、Source,还是其它条件?
  5. Full Frame 已经正确绘制 Symbol 时,什么条件下可以安全地建立缓存供后续 Particle Tick 复用?修复 R2 后还会遇到哪些独立阻塞?

假设不是结论:此前讨论认为 symbolFadeChange == 0 可能被误用为“Fade 已稳定”,这只是必须严谨验证或推翻的假设;不得直接断言为 Bug。

二、环境、权威文档和既有事实

  • Particle 源码:Mac /Users/tei/dev/MapLibreParticleLab;执行时记录实际 branch/HEAD/dirty。编写时 branch=particle/main,HEAD=50ae48ad70,不能把该 HEAD 当执行时已固定版本。
  • 按 AGENTS.md 顺序先读 PARTICLE_BASELINE.md、docs/00_项目规范/、docs/00_项目总则/README.md 及现行总则、本任务、STEP-102/106/109/110/111 相关调查与报告;以执行时源码事实为优先。
  • 跨项目事实依照 /Users/tei/dev/navsea-particle-integration 的最新 COORD/RFC/ADR/Contract/Status。公共 API、Host/Native 职责改变只写提案,跨项目协调另立事项。
  • 现有未跟踪 STEP-110 与 STEP-111 任务书不得移动、覆盖、代为提交。现有 STEP-110/111 调查属于 PARTIAL,不表示 per-R 安全复用已经实现。
  • 执行模型建议 GPT-6 Sol Medium,实际完整模型与 reasoning effort 在开始前核实并报告;MD 不负责自动切换。提高推理档位须先停止说明理由、由用户确认。

FZG1 现场证据(不等于逐帧因果证明)

指标(约 60 秒连续风场) RC1 RCRR_PREFIX RC2
Full CPU Frame 增量 1792 1787 1796
Fast Path Hit 0 0 0
R0 Reuse 0 1787 0
R2 Build/Reuse — 0/0 —
R2 Invalidation/Fallback — 1787/1787 —
R3 Reuse — 1755 —
Fast 首个拒绝 cache-invalid cache-invalid cache-invalid

RCRR_PREFIX 分段:[0,13) R0;[13,33) C1;[33,107) R2;[107,113) R3。C1 含普通 Symbol/Line,以及 index31 WeatherColor 与 index32 Particle,并非纯粒子段。R2 含大量 Symbol。FZG1 末态记录 placementChanged=true、symbolFadeChange=0、placementTransitionActive=false,只是快照不是每帧时间序列。此前也观察到导航 Source Mutation 计数增长,不能未经证据归咎于它。Full CPU Frame 不等于所有 Layer 重绘,不等于 Placement 实际重算次数。

三、必须完成的源码分析 A:从粒子请求到 Full Render 的调度链

逐级核查:

  • RenderRasterParticleLayer::isParticleAnimationActive / needsRepaint,SourceTimeline / V4Cadence / cadenceDue,粒子启动、暂停、无数据、隐藏时的返回条件。
  • RenderOrchestrator::hasTransitions,RenderTreeParameters::needsRepaint,Map::Impl::onDidFinishRenderingFrame / onUpdate,RendererObserver、RendererFrontend/Backend、Renderer::render / tryRenderParticleFastFrame。
  • 明确粒子 Tick、下一帧调度、Native Full 准备、GPU 粒子更新及 MapLibre Idle 的不同概念。检查 30 FPS 来自哪个实际 cadence,以及是否存在独立的 Placement 定时器强制 Full。
  • 输出可核查调用图,带文件路径、函数、行号及关键条件;区别“必然调用”“在特定配置调用”和“只是假设”。

四、必须完成的源码分析 B:Fast Path 全条件审计

检查 src/mln/renderer/particle_fast_path.hpp 的 evaluateParticleFastPath 与 renderer_impl.cpp 的 tryRenderParticleFastFrame:

  • Prepared RenderTree、Retained enabled、所有 R 段 Cache valid、Build/Static Revision、Content Revision、Camera/Viewport、non-camera update signature、Style、Boundary/Topology/RangeConfig。
  • staticStable、Placement transition、pending changes、non-particle transition/repaint、fading tiles、ParticleNeedsRepaint、still image、MapMode、Debug、Continuous 支持层、scene support、annotation mutation。
  • 必须产生全 Gate 矩阵:条件/来源函数与行号/通过条件/失败原因/是否被粒子 Tick 本身改变/是否在一次新 Full 后可能恢复/FZG1 已证实或未知。
  • 确认短路返回首个错误是否掩盖 unsupported-continuous-layer、unsupported-scene 等后续失败;不得因只看见 cache-invalid 就宣布其他条件已通过。
  • 比较 Full 与 Fast 路径的 CPU Prepare、RenderTree、GPU 绘制工作,避免将 Fast 解释成零 GPU render。

五、必须完成的源码分析 C:R2 缓存 Build/Invalidate 状态机

从图层结构到 GPU 缓存逐级核查:

  • render_segment.hpp 的 renderSegmentHasPlacementCapableLayer / renderSegmentPlacementGatePassed;隐藏 Symbol 是否同样触发结构级保护。
  • render_orchestrator.cpp 的 retainedRangeUsesPlacement / retainedRangeIsStable、Source/Tile/Glyph/Sprite/Annotation 的有效性及跨段 Collision 依赖。
  • renderer_impl.cpp 的 retainedPlacementStable 全局 Scene Gate、每段 segmentPlacementGatePassed、segmentSupported、Segment fingerprint/source revision、segmentFallbackReason、缓存 evaluate 与合成控制。
  • retained_underlay_cache.cpp 的 RetainedUnderlayCache::evaluate / invalidate / BuildPending / markBuildSucceeded / markBuildFailed / releaseResources;区分“首次未建立”“有效缓存因 Gate 被主动 invalidated”“底层资源损坏”“revision 变化”。
  • 以 FZG1 的 R2 Build=0、Reuse=0、Fallback/Invalidation≈1787 为约束,写出完整状态转换和所有可支持或反驳的来源;解释为什么 Full 的正常绘制不自动等于缓存离屏纹理 Build 成功。
  • 对 placementChanged=true 分清“发生 Placement 计算”与“R2 Symbol 最终可见性/位置/像素变化”。还须分析跨 Source/跨段 Collision、Fade、Shader opacity、Camera、异步资源所需安全边界。

六、必须完成的源码分析 D:Placement 和 Fade 真正语义

逐函数追踪 PlacementController::placementIsRecent / setPlacementStale / hasTransitions;Placement::getUpdatePeriod / commit / symbolFadeChange / hasTransitions;Style TransitionOptions;Symbol 在 OpenGL/Metal Shader 中如何消费 symbol_fade_change、opacity。

必须分别分析两种运行配置:

  1. enablePlacementTransitions=true:默认 300ms 更新间隔、Symbol Bucket Added 可能导致 30ms override、Zoom 修正、Fade 从 0 到终态的数值变化、hasTransitions 与 setPlacementStale 的交互。
  2. enablePlacementTransitions=false:placementIsRecent() 为什么可能持续返回 false、Placement 是否每次 Full 都重算、symbolFadeChange 实际取值、为何末态可能为 true/0/false。

针对 Gate !placementChanged && symbolFadeChange == 0 && !placementTransitionActive,逐行给出真值表与相应真实可达时刻:

  • Placement 刚计算后;
  • Placement 未重算但仍有淡入淡出;
  • Fade 完全结束后;
  • 关闭 Transitions;
  • Symbol Bucket 增减、跨段碰撞等变化;
  • Camera/Style/Source 不变、只有粒子的稳态。

回答:在正常运行下是否存在可用于 Build 的安全窗口?如果不存在,是哪一条造成的?全局 Scene Gate 与每段 Placement Gate 是否构成第二重锁死?FZG1 末态数据不能被机械套进默认 300ms 时间表。绝不通过直接删除 Gate 或仅更改 ==0 比较符来宣布安全修复。

七、必须完成的源码分析 E:反事实场景与因果闭环

对下列情况分别输出逐帧状态推演与 Fast Gate 判定(明确“已证明/条件推断/需设备验证”):

  • S1 纯粒子稳态:Camera 不动、所有 Source/Tile/Style/Glyph/Sprite ready 且不变、无外部 Full 指令、粒子 30 FPS。能否从第一次正常 Full 构建 R2 缓存并进入 Fast?何时会卡死?
  • S2 粒子关闭:没有粒子连续重绘,仅偶尔正常地图刷新。分析为何 Full CPU Frame 频率低,不能误判 Placement 从不执行。
  • S3 真实 Host Source Mutation:导航相关 Source/Annotation 更新与 Tile 异步输入同时存在,量化其对 Revision、Cache Build、Full Frame 的理论影响;不能假定所有变化都来自粒子。
  • S4 不同布局:原 RC/RCRR、实验 RCRR_PREFIX、理想保留图层顺序的 [0,13) R / [13,32) R / [32,33) C / [33,107) R / [107,113) R。检查 C 范围有普通 Symbol/Line 是否会触发 unsupported-continuous-layer;不能将最后一个仅为反事实的配置写成已实现。
  • S5 动态安全回退:相机缩放/旋转、Tile/Glyph/Sprite 完成加载、Opacity Fade、跨段 Collision、Style/Layer 改变时,哪些 R 必须重建,哪些状态不能盲目复用。

务必清楚界定:“粒子导致下一帧”与“粒子本身让静态 R 内容变了”不等价;“需要 Full 准备”与“所有图层从零画一次”也不等价。

八、方案对比与建议(仅提交提案)

至少比较四类选项并给优先级:

  • A 全 Gate 与 R2 状态诊断:一帧内采集所有 Gate 状态,而不是仅返回第一个 Miss;记录 Placement 配置、计算原因、Fade 值、各 R 的 Build/Reuse/Fallback/Invalidate,控制 tracing CPU 开销。
  • B Full Frame 后安全建立 Symbol R 缓存:寻找 Placement、Opacity、GPU Shader、资源与 RenderTree 都满足安全条件的 Build 时机,打破可能的“永远无法首次 Build”自举问题。
  • C 更细粒度 per-R 结果等价:与 STEP-111 对照,是否有必要做 per-R Signature/Revision 或可采用更轻量的生命周期证据;跨段 Collision 不能被忽略。
  • D 外部 Full 更新与 Particle Tick 分流:用户要求外部明确 Full 指令到来即完整更新,不因数据相同去重/延迟;无外部指令时粒子优先 Fast,仍保留 Native 必要安全 Full 回退。核对目前内部/公共接口是否支持;涉及责任或公共语义必须由 Integration 正式协调,本任务不得私建 API。

每项必须列:是否解决首次 Build、是否能解决全局 Scene Gate 和 C 段 Gate、数据正确性、GPU/CPU/内存代价、需改文件、风险、复杂度 1~5、Android OpenGL / iOS Metal 一致性、最小验证方案。

性能目标是 30 FPS 粒子与低频外部 Full 更新各司其职;2 Full + 28 Fast 是理想例子,不是当前 V23 已实现或零安全回退的承诺。 不以降低粒子 FPS、删除海图 Symbol、移动一批普通静态 Layer 到 C、放宽 fail-closed 或忽略外部指令作为优化手段。

九、本轮权限、限制与停止条件

仅执行全面只读调查,允许阅读本仓库源码、历史 Git、已有 FZG1 数据、现有调研报告;允许写入本任务要求的调查文档、Step 记录、Codex 完成报告与对粒子开发记录的短条追加。不得修改 Native、Android/iOS、Shader、JS/TS、构建脚本、测试源码、Host/NavSea、Integration 契约、Style、Renderer Gate 或公共 API。

不执行编译、链接、打包、设备操作、发布或新的性能测试。本轮如无法通过源码证实最终触发频率,必须给出下一阶段最小诊断插桩提案,记录字段、调用点、采样窗口、FZG1 验收场景与预期判断方法;不可未经授权直接实施插桩。严禁清理其他 dirty/untracked、变更上游、强推、改历史 Step。无法证明安全则维持 fail-closed。

需要涉及跨项目职责/API 调整的提案,先输出 Interface Blocker,返回 Integration 决策。遇到重大事实冲突时报告并停止冲突部分,不得自行扩大修改范围。

十、必做输出(调查完成时必须真实落盘)

  1. 主报告:docs/20_调查/STEP-112_V23粒子FullRender循环与Symbol缓存准入全面源码调查_v1.md。必须包含:
    • 完整粒子调度→Fast→Full 调用图、源码路径/函数/行号;
    • Fast Path 全部 Gate 对照矩阵(含短路掩盖条件);
    • R2 Cache 的首次 Build、Invalidate、Fallback 状态机与实际决策链;
    • enablePlacementTransitions 开/关及 Fade 时间/数值状态表;
    • 原始、PREFIX 和理想单粒子 C 的分段对照;
    • S1~S5 反事实和证据强度;
    • A~D 方案风险/成本/工程改动/优先级及推荐的下一步;
    • 不同问题分别标注“源码证明 / FZG1 实测 / 条件推断 / 仍需设备验证”。
  2. 阶段记录:docs/50_Step记录/STEP-112_V23粒子FullRender与Symbol缓存调查_v1.md。写状态、目标、完成项、未完成、阻塞与下一步。
  3. Codex 完成报告:docs/报告/STEP-112_V23粒子FullRender与Symbol缓存调查_Codex完成报告_v1.md。逐项按“已完成 / 未完成 / 无法验证”报告,必须解释未完成原因;记录实际模型/推理档位、Git 起终 HEAD、branch、dirty、变更文件、检查结果、未做构建/运行的事实、Git/Push 实际结果。
  4. 粒子开发记录.md:本阶段追加简短事实记录,不能更改历史。

十一、验收与 Git 收尾

只有 A~E 的关键问题有源码级证据、全部 Gate 有出处、root-cause 假设被验证或明确证伪、方案风险和最小验证路径充分时,才能写“源码调查 PASS”;运行时未验证、Native 未修复、Fast Hit 未改善必须明确列出,不能写实现/性能 PASS。没有决定性证据,结论应 PARTIAL,明确下一轮需要的最小运行时信息。

本任务研究完成后,对本任务范围的文档变更进行 scoped Git 提交并 push 到 origin/particle/main,核对远端 HEAD;不得纳入现有未跟踪 STEP-110/STEP-111 任务书或无关修改。依 AGENTS.md 的 Codex→RDC 职责分工,Codex 交付调查结果,RDC/获授权操作方独立完成 Git/Push 与核验;Push 未确认不得冒称 Git 收尾完成。记录真实 commit hash、message、远端分支与 push 结果。禁止 force push、upstream push。

本任务结束条件:调研证据、报告与 Git 收尾;不得继续直接实施 Native 修复。