render-cache
本文回答:
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
- 实体属性改了但画面不变 → 调
changed()或styleChanged()。 - 改了还是不变 → 确认实体没挂
editable=false/visible=false,也没被setHiddenLayerChecker隐藏。 - 底座切换样式后图层不见 →
styledata事件里 re-addLayer。 - resize 后地图模糊 →
map.resize()强制重算 canvas 尺寸。 - 最后兜底 →
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 里又改实体造成回环),排查。
十、常见坑
- 每帧都调 forceFullRender():等价于禁用所有缓存,FPS 断崖。只在调试或极端场景调用。
- webglcontextlost 后不重建:画面永黑;监听 restored。
- map.resize 忘调:容器尺寸变化后画面模糊 / 对不齐;监听父元素 ResizeObserver。
- 自定义 WebGL layer 没遵守 map 的 GL 状态:破坏了缓存假设;在
onRender里保存 / 恢复 state。
相关
- 渲染桶 → render-buckets
- 内存泄漏 → memory-leaks
- 调试 → profiling