唯杰地图 logo唯杰地图
VJMAP
EN

render-cache

vjmapext/references/perf/render-cache.md 5.1 KB128 行
原文

本文回答:RenderCache 失效与 forceFullRender() —— 怎么强制重绘、什么时候会自动失效。

一、RenderCache 是什么

RenderCache 是逐实体的 GeoJSON 缓存(Map<entityId, Feature[]>):getOrCompute(entity) 在 entity.isDirty === false 且命中缓存时直接返回上次的 Feature 数组,否则重跑 entity.toGeoJSON() + CAD→经纬度坐标变换并写回缓存。

效果:

  • 未变化的实体跳过重编码——几万实体的图,只动一条时编码代价是 O(1) 而不是 O(N)
  • 配合桶的增量摘除/重插,真正被重建的只有变化实体的那几个 feature

二、失效触发

要区分两件事:RenderCache 失效(逐实体)与渲染调度(按桶标脏→rAF 合并重绘)。

触发 效果
entity.changed() 该实体 isDirty=true → 下次 getOrCompute 重算
entity.styleChanged() 走 updateStyleProps 快路径,原地修缓存 feature 的样式属性
store.add / delete 增量更新:从所属桶摘除 / 重编码插入
renderPipeline.forceFullRender() renderCache.invalidateAll() 清全部缓存 + _needFullRebuild + store.setDirty()
setMode(browse/edit 切换) 内部调 forceFullRender → 全量重建

不会让 RenderCache 失效的:map.panBy / zoomTo / rotateTo(缓存里存的是经纬度 feature,视图变换不影响)、map.setStyle(底座样式,与实体缓存无关)——它们只是让底座重绘。

正常业务场景不用管;框架自动处理。

三、手动强刷:forceFullRender()

某些边缘场景需要手动:

mapcadLayer.renderPipeline.forceFullRender();   // 是方法不是属性
// 强制重绘一切,跳过所有缓存

典型场景:

  • GPU context lost 后(比如手机切后台再切回)
  • 外部改了 WebGL state(少见)
  • 调试:对比渲染输出

四、标脏即重绘

没有公开的 requestRender():实体 changed() / styleChanged() / 增删都会标脏,重绘由内部 requestAnimationFrame 自动合并到下一帧。一般不需要手动触发。

五、bus 事件 render:requested(⚠️ 1.0.1 未发射)

VjmapextEvents.RenderRequested 在枚举中有定义,但 1.0.1 bundle 内没有任何 emit 点——下面订阅永远收不到:

bus.on(VjmapextEvents.RenderRequested, () => {   // ⚠️ 死代码
  console.log('request render');
});

实体标脏的观测出口用 EntityAdded / EntityModified / EntityDeleted;重绘本身由内部 requestRender() → requestAnimationFrame 驱动,无公开的重绘完成事件。

六、画面没更新?checklist

  1. 实体属性改了但画面不变 → 调 changed() 或 styleChanged()。
  2. 改了还是不变 → 确认实体没挂 editable=false/visible=false,也没被 setHiddenLayerChecker 隐藏。
  3. 底座切换样式后图层不见 → styledata 事件里 re-addLayer。
  4. resize 后地图模糊 → map.resize() 强制重算 canvas 尺寸。
  5. 最后兜底 → mapcadLayer.renderPipeline.forceFullRender()。

七、GPU context lost

浏览器切后台 / 切标签 / 长时间休眠可能 lose context:

map.getCanvas().addEventListener('webglcontextlost', e => {
  e.preventDefault();
  console.warn('GPU context lost');
});
map.getCanvas().addEventListener('webglcontextrestored', () => {
  console.log('GPU context restored');
  mapcadLayer.renderPipeline.forceFullRender();
});

底座和 FX 会自动恢复,但 vjmapext 实体缓存 + 自定义 plugin 可能需要手动重建。

八、热重载开发时

Vite HMR 后 mapcadLayer 已销毁,但 map 实例未销毁 → 画面残留:

if (import.meta.hot) {
  import.meta.hot.dispose(() => {
    map.removeControl(mapcadLayer);
    map.remove();
  });
}

九、性能监测

// ⚠️ 'render:requested' 1.0.1 从未发射;用实体标脏事件做脏帧率代理
//    (要盯底座渲染帧数可用 map.on('render', ...))
let dirtyCount = 0, lastTs = performance.now();
for (const ev of ['entity:added', 'entity:modified', 'entity:deleted'])
  mapcadLayer.eventBus.on(ev, () => dirtyCount++);

setInterval(() => {
  const now = performance.now();
  console.log(`entity dirty events: ${dirtyCount} / ${((now - lastTs) / 1000).toFixed(1)}s`);
  dirtyCount = 0;
  lastTs = now;
}, 1000);

如果每秒脏事件 > 60 → 有反复标脏的源头(如 handler 里又改实体造成回环),排查。

十、常见坑

  1. 每帧都调 forceFullRender():等价于禁用所有缓存,FPS 断崖。只在调试或极端场景调用。
  2. webglcontextlost 后不重建:画面永黑;监听 restored。
  3. map.resize 忘调:容器尺寸变化后画面模糊 / 对不齐;监听父元素 ResizeObserver。
  4. 自定义 WebGL layer 没遵守 map 的 GL 状态:破坏了缓存假设;在 onRender 里保存 / 恢复 state。

相关