From b0d98621644ed0e02d9a0d1bd6e364a69d7068ce Mon Sep 17 00:00:00 2001 From: YiQianYao <42212176+yiiiiiiqianyao@users.noreply.github.com> Date: Tue, 14 Feb 2023 17:26:11 +0800 Subject: [PATCH] feat: add DebugService (#1590) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 新增 debugService step1 * chore: layer_init test 测试文件修改 * refactor: 重构日志系统结构 * feat: add remove layer log * feat: add render debug * feat: 增加 debugService 的开关控制 * feat: add simple source log * feat: 调整 souce getLog 方法的输出 * feat: add scene webgl context lost * docs: 补充 scene 的事件监听方法 * feat: 标砖 debugService 枚举类型、修复初始化时间节点错误 * style: lint style * fix: 删除单独的sourcelog,优化了log 流程 (#1599) * chore: remove render model log * docs: add debugService contents * style: lint style * docs: add IDebugLog details * style: lint style --------- Co-authored-by: shihui Co-authored-by: @thinkinggis --- dev-demos/features/line/demos/linedash.tsx | 64 +++++++++- packages/core/src/index.ts | 1 + packages/core/src/inversify.config.ts | 6 + .../src/services/config/IConfigService.ts | 1 + .../core/src/services/debug/DebugService.ts | 115 ++++++++++++++++++ .../core/src/services/debug/IDebugService.ts | 38 ++++++ .../core/src/services/layer/ILayerService.ts | 1 + .../core/src/services/layer/LayerService.ts | 77 ++++++------ .../core/src/services/scene/SceneService.ts | 15 +++ packages/core/src/types.ts | 1 + packages/layers/src/core/BaseLayer.ts | 26 +++- .../layers/src/plugins/DataMappingPlugin.ts | 4 + .../layers/src/plugins/DataSourcePlugin.ts | 15 ++- .../layers/src/plugins/FeatureScalePlugin.ts | 3 + .../layers/src/plugins/LayerModelPlugin.ts | 16 +-- .../src/point/__tests__/layer_init.spec.ts | 18 +-- packages/renderer/src/regl/index.ts | 1 + packages/scene/src/index.ts | 13 ++ packages/site/.dumirc.ts | 16 +++ .../site/docs/api/debug/debugservice.en.md | 6 + .../site/docs/api/debug/debugservice.zh.md | 99 +++++++++++++++ packages/site/docs/api/scene.zh.md | 2 + .../site/docs/tutorial/debug/layerInit.en.md | 5 + .../site/docs/tutorial/debug/layerInit.zh.md | 44 +++++++ .../site/docs/tutorial/debug/mapInit.en.md | 5 + .../site/docs/tutorial/debug/mapInit.zh.md | 30 +++++ .../site/docs/tutorial/debug/render.en.md | 5 + .../site/docs/tutorial/debug/render.zh.md | 45 +++++++ packages/utils/src/hash.ts | 7 ++ packages/utils/src/index.ts | 13 +- 30 files changed, 620 insertions(+), 72 deletions(-) create mode 100644 packages/core/src/services/debug/DebugService.ts create mode 100644 packages/core/src/services/debug/IDebugService.ts create mode 100644 packages/site/docs/api/debug/debugservice.en.md create mode 100644 packages/site/docs/api/debug/debugservice.zh.md create mode 100644 packages/site/docs/tutorial/debug/layerInit.en.md create mode 100644 packages/site/docs/tutorial/debug/layerInit.zh.md create mode 100644 packages/site/docs/tutorial/debug/mapInit.en.md create mode 100644 packages/site/docs/tutorial/debug/mapInit.zh.md create mode 100644 packages/site/docs/tutorial/debug/render.en.md create mode 100644 packages/site/docs/tutorial/debug/render.zh.md diff --git a/dev-demos/features/line/demos/linedash.tsx b/dev-demos/features/line/demos/linedash.tsx index 6beb4ecf2b..38db5af444 100644 --- a/dev-demos/features/line/demos/linedash.tsx +++ b/dev-demos/features/line/demos/linedash.tsx @@ -1,5 +1,5 @@ // @ts-ignore -import { LineLayer, Scene } from '@antv/l7'; +import { LineLayer, PointLayer, Scene } from '@antv/l7'; // @ts-ignore import { GaodeMapV1 } from '@antv/l7-maps'; import React, { useEffect } from 'react'; @@ -14,6 +14,7 @@ export default () => { style:'light' // style: 'amap://styles/wine', }), + debug: true, }); scene.on('loaded', () => { @@ -34,14 +35,65 @@ export default () => { }); scene.addLayer(layer); + // const point = new PointLayer({}) + // .source([{ lng: 116.2, lat: 40 }], { + // parser: { + // type: 'json', + // x: 'lng', + // y: 'lat', + // } + // }) + // .size(10) + // .shape('circle') + // .color('#5CCEA1'); + // scene.addLayer(point); + + const debugService = scene.getDebugService(); + + layerAllLoad([layer], () => { + // console.log('debugService id type', debugService.getLog()) + // console.log('debugService id type', debugService.getLog(layer.id)) + // console.log('debugService id type', debugService.getLog('map')) + // console.log('debugService id type', debugService.getLog([layer.id, point.id])) + console.log('debugService id type', debugService.getLog([layer.id])) + }) + + function layerAllLoad(layers: any[], callback: () => void) { + let count = 0; + layers.forEach(l => { + l.on('inited', () => { + console.log('***'); + + count++; + if(count === layers.length) { + setTimeout(() => { + callback(); + }, 100) + // callback(); + } + }) + }) + } // setTimeout(()=>{ - // console.log('setdata') - // layer.setData({ - // type: 'featureCollection', - // features:[], + // console.log('lostContext test') + // scene.on('webglcontextlost', () => { + // console.log('webglcontextlost'); // }) - + // // scene.lostContext(); // },3000) + + // setTimeout(()=>{ + // },3000) + // debugService.renderDebug(true); + + // debugService.on('renderEnd', (renderInfo) => { + // console.log('renderEnd', renderInfo); + // }) + + + // setTimeout(() => { + // debugService.renderDebug(false); + // }, 200) }); }); }, []); diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 33845f2066..6f7ce85c72 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -30,6 +30,7 @@ export { /** 暴露服务接口供其他 packages 实现 */ export * from './services/layer/ILayerService'; +export * from './services/debug/IDebugService'; export * from './services/layer/IStyleAttributeService'; export * from './services/source/ISourceService'; export * from './services/map/IMapService'; diff --git a/packages/core/src/inversify.config.ts b/packages/core/src/inversify.config.ts index 430214b28f..313a5da798 100644 --- a/packages/core/src/inversify.config.ts +++ b/packages/core/src/inversify.config.ts @@ -19,6 +19,7 @@ import { ICoordinateSystemService } from './services/coordinate/ICoordinateSyste import { IInteractionService } from './services/interaction/IInteractionService'; import { IPickingService } from './services/interaction/IPickingService'; import { ILayerService } from './services/layer/ILayerService'; +import { IDebugService } from './services/debug/IDebugService'; import { IStyleAttributeService } from './services/layer/IStyleAttributeService'; import { ISceneService } from './services/scene/ISceneService'; import { IShaderModuleService } from './services/shader/IShaderModuleService'; @@ -35,6 +36,7 @@ import CoordinateSystemService from './services/coordinate/CoordinateSystemServi import InteractionService from './services/interaction/InteractionService'; import PickingService from './services/interaction/PickingService'; import LayerService from './services/layer/LayerService'; +import DebugService from './services/debug/DebugService'; import StyleAttributeService from './services/layer/StyleAttributeService'; import SceneService from './services/scene/SceneService'; import ShaderModuleService from './services/shader/ShaderModuleService'; @@ -153,6 +155,10 @@ export function createSceneContainer() { .bind(TYPES.ILayerService) .to(LayerService) .inSingletonScope(); + sceneContainer + .bind(TYPES.IDebugService) + .to(DebugService) + .inSingletonScope(); sceneContainer .bind(TYPES.ISceneService) .to(SceneService) diff --git a/packages/core/src/services/config/IConfigService.ts b/packages/core/src/services/config/IConfigService.ts index bc77419efc..65ac503668 100644 --- a/packages/core/src/services/config/IConfigService.ts +++ b/packages/core/src/services/config/IConfigService.ts @@ -17,6 +17,7 @@ export interface ISceneConfig extends IRenderConfig { pickBufferScale?: number; // TODO: 场景是否支持 stencil mask stencil?: boolean; + debug?: boolean; } export interface IGlobalConfigService { diff --git a/packages/core/src/services/debug/DebugService.ts b/packages/core/src/services/debug/DebugService.ts new file mode 100644 index 0000000000..9c8f94ec4b --- /dev/null +++ b/packages/core/src/services/debug/DebugService.ts @@ -0,0 +1,115 @@ +import { guid } from '@antv/l7-utils'; +import { EventEmitter } from 'eventemitter3'; +import { injectable } from 'inversify'; +import { IDebugService, ILog, IRenderInfo } from './IDebugService'; + +@injectable() +export default class DebugService + extends EventEmitter + implements IDebugService +{ + private logMap = new Map(); + private renderMap = new Map(); + + private enable: boolean = false; + public renderEnable: boolean = false; + + public setEnable(flag: boolean) { + this.enable = !!flag; + } + + public log(key: string, values: ILog) { + if (!this.enable) { + return; + } + const [k1, k2] = key.split('.'); + const logType = k2; + /** + * map: { + * mapInitStart: { time, ... } + * }, + * 12: { + * layerInitStart: { time, id, ... }, + * layerInitEnd: { time, id, ... }, + * } + */ + const cacheLog = this.logMap.get(k1) || {}; // 一级存储对象 + const cacheLogValues = cacheLog[logType] || {}; // 二级存储对象 + const logValues = { + time: Date.now(), + ...cacheLogValues, + ...values, + }; + this.logMap.set(k1, { + ...cacheLog, + [logType]: logValues, + }); + } + + public getLog(key: string | string[] | undefined) { + switch (typeof key) { + case 'string': + return this.logMap.get(key); + case 'object': + return (key as string[]) + .map((k) => this.logMap.get(k)) + .filter((o) => o !== undefined) as ILog[]; + case 'undefined': + return Array.from(this.logMap.keys()).map((k) => this.logMap.get(k)); + } + } + + /** + * 删除日志 + * @param key + */ + public removeLog(key: string) { + this.logMap.delete(key); + } + + public generateRenderUid() { + if (this.renderEnable) { + return guid(); + } else { + return ''; + } + } + + public renderDebug(enable: boolean) { + this.renderEnable = enable; + } + + public renderStart(guid: string) { + if (!this.renderEnable || !this.enable) { + return; + } + const cacheRenderInfo = this.renderMap.get(guid) || {}; + this.renderMap.set(guid, { + ...cacheRenderInfo, + renderUid: guid, + renderStart: Date.now(), + }); + } + + public renderEnd(guid: string) { + if (!this.renderEnable || !this.enable) { + return; + } + const cacheRenderInfo = this.renderMap.get(guid); + if (cacheRenderInfo) { + const renderStart = cacheRenderInfo.renderStart as number; + const renderEnd = Date.now(); + this.emit('renderEnd', { + ...cacheRenderInfo, + renderEnd, + renderDuration: renderEnd - renderStart, + }); + this.renderMap.delete(guid); + } + } + + public destroy() { + this.logMap.clear(); + this.renderMap.clear(); + } +} diff --git a/packages/core/src/services/debug/IDebugService.ts b/packages/core/src/services/debug/IDebugService.ts new file mode 100644 index 0000000000..1bc09c3665 --- /dev/null +++ b/packages/core/src/services/debug/IDebugService.ts @@ -0,0 +1,38 @@ +export type ILayerId = string; +export interface ILog { + [key: string]: any; +} + +export const enum IDebugLog { + MapInitStart = 'mapInitStart', + LayerInitStart = 'layerInitStart', + LayerInitEnd = 'layerInitEnd', + SourceInitStart = 'sourceInitStart', + SourceInitEnd = 'sourceInitEnd', + ScaleInitStart = 'scaleInitStart', + ScaleInitEnd = 'scaleInitEnd', + MappingStart = 'mappingStart', + MappingEnd = 'mappingEnd', + BuildModelStart = 'buildModelStart', + BuildModelEnd = 'buildModelEnd', +} + +export interface IRenderInfo { + renderUid: string; + renderStart?: number; + renderEnd?: number; + renderDuration?: number; +} +export interface IDebugService { + renderEnable: boolean; + + setEnable(flag: boolean | undefined): void; + + log(key: string, values: ILog): void; + getLog(key: string | string[] | undefined): ILog[] | ILog | undefined; + removeLog(key: string): void; + + generateRenderUid(): string; + renderStart(guid: string): void; + renderEnd(guid: string): void; +} diff --git a/packages/core/src/services/layer/ILayerService.ts b/packages/core/src/services/layer/ILayerService.ts index 8e97e71299..2bcc9de25f 100644 --- a/packages/core/src/services/layer/ILayerService.ts +++ b/packages/core/src/services/layer/ILayerService.ts @@ -443,6 +443,7 @@ export interface ILayer { getMinZoom(): number; getMaxZoom(): number; get(name: string): number; + log(type: string, time?: number): void; setBlend(type: keyof typeof BlendType): ILayer; // animate(field: string, option: any): ILayer; diff --git a/packages/core/src/services/layer/LayerService.ts b/packages/core/src/services/layer/LayerService.ts index ed8933e51f..8f71b0b783 100644 --- a/packages/core/src/services/layer/LayerService.ts +++ b/packages/core/src/services/layer/LayerService.ts @@ -5,13 +5,16 @@ import { throttle } from 'lodash'; import 'reflect-metadata'; import { TYPES } from '../../types'; import Clock from '../../utils/clock'; +import { IDebugService } from '../debug/IDebugService'; import { IMapService } from '../map/IMapService'; import { IRendererService } from '../renderer/IRendererService'; import { ILayer, ILayerService, LayerServiceEvent } from './ILayerService'; @injectable() -export default class LayerService extends EventEmitter - implements ILayerService { +export default class LayerService + extends EventEmitter + implements ILayerService +{ // pickedLayerId 参数用于指定当前存在被选中的 layer public pickedLayerId: number = -1; public clock = new Clock(); @@ -39,6 +42,9 @@ export default class LayerService extends EventEmitter @inject(TYPES.IMapService) private readonly mapService: IMapService; + @inject(TYPES.IDebugService) + private readonly debugService: IDebugService; + public reRender = throttle(() => { this.updateLayerRenderList(); this.renderLayers(); @@ -48,8 +54,8 @@ export default class LayerService extends EventEmitter this.renderLayers(); }, 16); - public needPick(type:string): boolean { - return this.layerList.some((layer=>layer.needPick(type))) + public needPick(type: string): boolean { + return this.layerList.some((layer) => layer.needPick(type)); } public add(layer: ILayer) { this.layers.push(layer); @@ -58,9 +64,7 @@ export default class LayerService extends EventEmitter this.updateLayerRenderList(); this.renderLayers(); }); - } - - + } } public addMask(mask: ILayer) { @@ -102,7 +106,7 @@ export default class LayerService extends EventEmitter return this.layers.find((layer) => layer.name === name); } - public async remove(layer: ILayer, parentLayer?: ILayer):Promise { + public async remove(layer: ILayer, parentLayer?: ILayer): Promise { // Tip: layer.layerChildren 当 layer 存在子图层的情况 if (parentLayer) { const layerIndex = parentLayer.layerChildren.indexOf(layer); @@ -117,13 +121,13 @@ export default class LayerService extends EventEmitter } this.updateLayerRenderList(); layer.destroy(); - this.reRender() + this.reRender(); this.emit('layerChange', this.layers); } - public async removeAllLayers():Promise { + public async removeAllLayers(): Promise { this.destroy(); - this.reRender() + this.reRender(); } public setEnableRender(flag: boolean) { @@ -134,10 +138,12 @@ export default class LayerService extends EventEmitter if (this.alreadyInRendering || !this.enableRender) { return; } + const renderUid = this.debugService.generateRenderUid(); + this.debugService.renderStart(renderUid); this.alreadyInRendering = true; this.clear(); for (const layer of this.layerList) { - if (layer.masks.filter((m)=>m.inited).length > 0) { + if (layer.masks.filter((m) => m.inited).length > 0) { // 清除上一次的模版缓存 this.renderService.clear({ stencil: 0, @@ -146,7 +152,7 @@ export default class LayerService extends EventEmitter }); layer.masks.map(async (m: ILayer) => { m.render(); - }) + }); } if (layer.getLayerConfig().enableMultiPassRenderer) { @@ -154,39 +160,37 @@ export default class LayerService extends EventEmitter await layer.renderMultiPass(); } else { await layer.render(); - } } + this.debugService.renderEnd(renderUid); this.alreadyInRendering = false; } - - public renderMask(masks:ILayer[]) { - masks.filter(m => m.inited) - .map(m =>{ - m.render(); - }) + + public renderMask(masks: ILayer[]) { + masks + .filter((m) => m.inited) + .map((m) => { + m.render(); + }); } public async beforeRenderData(layer: ILayer) { const flag = await layer.hooks.beforeRenderData.promise(); - if(flag) { + if (flag) { this.renderLayers(); } - } - async renderLayer(layer: ILayer){ - - if (layer.masks.filter((m)=>m.inited).length > 0) { - layer.masks.map(mask =>{ - this.renderService.clear({ - stencil: 0, - depth: 1, - framebuffer: null, - }); - mask.render(); - }) - + public async renderLayer(layer: ILayer) { + if (layer.masks.filter((m) => m.inited).length > 0) { + layer.masks.map((mask) => { + this.renderService.clear({ + stencil: 0, + depth: 1, + framebuffer: null, + }); + mask.render(); + }); } if (layer.getLayerConfig().enableMultiPassRenderer) { // multiPassRender 不是同步渲染完成的 @@ -194,7 +198,6 @@ export default class LayerService extends EventEmitter } else { await layer.render(); } - } public updateLayerRenderList() { @@ -210,7 +213,6 @@ export default class LayerService extends EventEmitter .forEach((layer) => { this.layerList.push(layer); }); - } public destroy() { @@ -252,7 +254,6 @@ export default class LayerService extends EventEmitter public getShaderPickStat() { return this.shaderPicking; } - public clear() { const color = rgb2arr(this.mapService.bgColor) as [ @@ -279,6 +280,4 @@ export default class LayerService extends EventEmitter private stopRender() { $window.cancelAnimationFrame(this.layerRenderID); } - - } diff --git a/packages/core/src/services/scene/SceneService.ts b/packages/core/src/services/scene/SceneService.ts index c7f31d78c5..0f7d764828 100644 --- a/packages/core/src/services/scene/SceneService.ts +++ b/packages/core/src/services/scene/SceneService.ts @@ -26,6 +26,7 @@ import { IMapService } from '../map/IMapService'; import { IRenderConfig, IRendererService } from '../renderer/IRendererService'; import { IShaderModuleService } from '../shader/IShaderModuleService'; import { ISceneService } from './ISceneService'; +import { IDebugService } from '../debug/IDebugService'; /** * will emit `loaded` `resize` `destroy` event panstart panmove panend @@ -65,6 +66,9 @@ export default class Scene extends EventEmitter implements ISceneService { @inject(TYPES.ILayerService) private readonly layerService: ILayerService; + @inject(TYPES.IDebugService) + private readonly debugService: IDebugService; + @inject(TYPES.ICameraService) private readonly cameraService: ICameraService; @@ -134,6 +138,9 @@ export default class Scene extends EventEmitter implements ISceneService { * 初始化底图 */ this.hooks.init.tapPromise('initMap', async () => { + this.debugService.log('map.mapInitStart', { + type: this.map.version + }) // 等待首次相机同步 await new Promise((resolve) => { this.map.onCameraChanged((viewport: IViewport) => { @@ -188,6 +195,7 @@ export default class Scene extends EventEmitter implements ISceneService { this.configService.getSceneConfig(this.id) as IRenderConfig, sceneConfig.gl, ); + this.registerContextLost(); this.initContainer(); elementResizeEvent( @@ -208,6 +216,13 @@ export default class Scene extends EventEmitter implements ISceneService { this.render(); } + private registerContextLost() { + const canvas = this.rendererService.getCanvas(); + if(canvas) { + canvas.addEventListener('webglcontextlost', () => this.emit('webglcontextlost')); + } + } + /** * 小程序环境下初始化 Scene * @param sceneConfig diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 436e4e2aff..59112e1f19 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -5,6 +5,7 @@ const TYPES = { ICameraService: Symbol.for('ICameraService'), ICoordinateSystemService: Symbol.for('ICoordinateSystemService'), ILayerService: Symbol.for('ILayerService'), + IDebugService: Symbol.for('IDebugService'), ILayerMappingService: Symbol.for('ILayerMappingService'), ILayerStyleService: Symbol.for('ILayerStyleService'), IMapService: Symbol.for('IMapService'), diff --git a/packages/layers/src/core/BaseLayer.ts b/packages/layers/src/core/BaseLayer.ts index 866e558a73..fd0b2db927 100644 --- a/packages/layers/src/core/BaseLayer.ts +++ b/packages/layers/src/core/BaseLayer.ts @@ -13,6 +13,8 @@ import { ICameraService, ICoordinateSystemService, IDataState, + IDebugLog, + IDebugService, IEncodeFeature, IFontService, IGlobalConfigService, @@ -182,6 +184,8 @@ export default class BaseLayer protected layerService: ILayerService; + protected debugService: IDebugService; + protected interactionService: IInteractionService; protected mapService: IMapService; @@ -331,6 +335,7 @@ export default class BaseLayer TYPES.IRendererService, ); this.layerService = this.container.get(TYPES.ILayerService); + this.debugService = this.container.get(TYPES.IDebugService); this.interactionService = this.container.get( TYPES.IInteractionService, ); @@ -414,9 +419,10 @@ export default class BaseLayer // 颜色纹理服务 this.textureService = new TextureService(this); - + this.log(IDebugLog.LayerInitStart); // 触发 init 生命周期插件 await this.hooks.init.promise(); + this.log(IDebugLog.LayerInitEnd); this.inited = true; // 触发初始化完成事件; @@ -431,6 +437,22 @@ export default class BaseLayer this.hooks.afterInit.call(); } + public log(logType: string, time?: number) { + // @ts-ignore 瓦片、瓦片图层目前不参与日志 + if (this.tileLayer || this.isTileLayer) { + return; + } + const key = `${this.id}.${logType}`; + const values: { [key: string]: any } = { + id: this.id, + type: this.type, + }; + if (time) { + values.time = time; + } + this.debugService.log(key, values); + } + public updateModelData(data: IAttributeAndElements) { if (data.attributes && data.elements) { this.models.map((m) => { @@ -1013,6 +1035,8 @@ export default class BaseLayer this.tileLayer?.destroy(); this.models = []; + // 清除图层日志(如果有的话:非瓦片相关) + this.debugService.removeLog(this.id); this.emit('remove', { target: this, diff --git a/packages/layers/src/plugins/DataMappingPlugin.ts b/packages/layers/src/plugins/DataMappingPlugin.ts index 5507a90229..ff8c4da349 100644 --- a/packages/layers/src/plugins/DataMappingPlugin.ts +++ b/packages/layers/src/plugins/DataMappingPlugin.ts @@ -1,4 +1,5 @@ import { + IDebugLog, IEncodeFeature, IFontService, ILayer, @@ -32,8 +33,10 @@ export default class DataMappingPlugin implements ILayerPlugin { }: { styleAttributeService: IStyleAttributeService }, ) { layer.hooks.init.tapPromise('DataMappingPlugin', async () => { + layer.log(IDebugLog.MappingStart); // 初始化重新生成 map this.generateMaping(layer, { styleAttributeService }); + layer.log(IDebugLog.MappingEnd); }); layer.hooks.beforeRenderData.tapPromise( @@ -42,6 +45,7 @@ export default class DataMappingPlugin implements ILayerPlugin { if (!flag) { return flag; } + layer.dataState.dataMappingNeedUpdate = false; return this.generateMaping(layer, { styleAttributeService }); }, diff --git a/packages/layers/src/plugins/DataSourcePlugin.ts b/packages/layers/src/plugins/DataSourcePlugin.ts index 03cf1790a4..0e79dc6b13 100644 --- a/packages/layers/src/plugins/DataSourcePlugin.ts +++ b/packages/layers/src/plugins/DataSourcePlugin.ts @@ -1,4 +1,10 @@ -import { ILayer, ILayerPlugin, IMapService, TYPES } from '@antv/l7-core'; +import { + IDebugLog, + ILayer, + ILayerPlugin, + IMapService, + TYPES, +} from '@antv/l7-core'; import Source from '@antv/l7-source'; import { injectable } from 'inversify'; import 'reflect-metadata'; @@ -9,6 +15,7 @@ export default class DataSourcePlugin implements ILayerPlugin { public apply(layer: ILayer) { this.mapService = layer.getContainer().get(TYPES.IMapService); layer.hooks.init.tapPromise('DataSourcePlugin', async () => { + layer.log(IDebugLog.SourceInitStart); let source = layer.getSource(); if (!source) { // Tip: 用户没有传入 source 的时候使用图层的默认数据 @@ -19,11 +26,13 @@ export default class DataSourcePlugin implements ILayerPlugin { } if (source.inited) { this.updateClusterData(layer); + layer.log(IDebugLog.SourceInitEnd); } else { await new Promise((resolve) => { source.on('update', (e) => { if (e.type === 'inited') { this.updateClusterData(layer); + layer.log(IDebugLog.SourceInitEnd); } resolve(null); }); @@ -37,7 +46,9 @@ export default class DataSourcePlugin implements ILayerPlugin { const dataSourceNeedUpdate = layer.dataState.dataSourceNeedUpdate; layer.dataState.dataSourceNeedUpdate = false; - return neeUpdateCluster || dataSourceNeedUpdate; + + const needScale = neeUpdateCluster || dataSourceNeedUpdate; + return needScale; }); } diff --git a/packages/layers/src/plugins/FeatureScalePlugin.ts b/packages/layers/src/plugins/FeatureScalePlugin.ts index 4bfa0c33a8..1e1f6dfab6 100644 --- a/packages/layers/src/plugins/FeatureScalePlugin.ts +++ b/packages/layers/src/plugins/FeatureScalePlugin.ts @@ -1,4 +1,5 @@ import { + IDebugLog, ILayer, ILayerPlugin, IScale, @@ -49,6 +50,7 @@ export default class FeatureScalePlugin implements ILayerPlugin { }: { styleAttributeService: IStyleAttributeService }, ) { layer.hooks.init.tapPromise('FeatureScalePlugin', async () => { + layer.log(IDebugLog.ScaleInitStart); this.scaleOptions = layer.getScaleOptions(); const attributes = styleAttributeService.getLayerStyleAttributes(); const dataArray = layer.getSource()?.data.dataArray; @@ -57,6 +59,7 @@ export default class FeatureScalePlugin implements ILayerPlugin { } else { this.caculateScalesForAttributes(attributes || [], dataArray); } + layer.log(IDebugLog.ScaleInitEnd); }); // 检测数据是否需要更新 diff --git a/packages/layers/src/plugins/LayerModelPlugin.ts b/packages/layers/src/plugins/LayerModelPlugin.ts index 94a42a1f42..f45b97e7f5 100644 --- a/packages/layers/src/plugins/LayerModelPlugin.ts +++ b/packages/layers/src/plugins/LayerModelPlugin.ts @@ -1,4 +1,4 @@ -import { ILayer, ILayerPlugin } from '@antv/l7-core'; +import { IDebugLog, ILayer, ILayerPlugin } from '@antv/l7-core'; import { injectable } from 'inversify'; import 'reflect-metadata'; import TileLayer from '../tile/tileLayer/BaseLayer'; @@ -7,21 +7,21 @@ import TileLayer from '../tile/tileLayer/BaseLayer'; */ @injectable() export default class LayerModelPlugin implements ILayerPlugin { - public async initLayerModel(layer: ILayer) { + private async build(layer: ILayer) { // 更新Model 配置项 layer.prepareBuildModel(); // 初始化 Model await layer.buildModels(); + } + + public async initLayerModel(layer: ILayer) { + await this.build(layer); layer.styleNeedUpdate = false; } public async prepareLayerModel(layer: ILayer) { - // 更新Model 配置项 - layer.prepareBuildModel(); - // clear layerModel resource - // 初始化 Model - await layer.buildModels(); + await this.build(layer); // layer.layerModelNeedUpdate = false; } @@ -32,7 +32,9 @@ export default class LayerModelPlugin implements ILayerPlugin { layer.tileLayer = new TileLayer(layer); return; } + layer.log(IDebugLog.BuildModelStart); await this.initLayerModel(layer); + layer.log(IDebugLog.BuildModelEnd); }); layer.hooks.beforeRenderData.tapPromise( diff --git a/packages/layers/src/point/__tests__/layer_init.spec.ts b/packages/layers/src/point/__tests__/layer_init.spec.ts index cc8e8e320f..0aaf61a4a3 100644 --- a/packages/layers/src/point/__tests__/layer_init.spec.ts +++ b/packages/layers/src/point/__tests__/layer_init.spec.ts @@ -3,7 +3,6 @@ import { TestScene } from '@antv/l7-test-utils'; import PointLayer from '../'; describe('template', () => { - const el = document.createElement('div'); el.id = 'test-div-id'; const body = document.querySelector('body') as HTMLBodyElement; @@ -40,8 +39,8 @@ describe('template', () => { scene.on('loaded', () =>{ scene.addLayer(layer) }) - }); + it('scene layer text', async () => { const layer = new PointLayer({name:'text'}).source( testData, @@ -60,10 +59,6 @@ describe('template', () => { scene.addLayer(layer) expect(layer.name).toEqual('text') }) - - scene.addLayer(layer) - - }); it('scene layer extrude', async () => { @@ -79,10 +74,9 @@ describe('template', () => { ).shape('cloumn') .color('red') .size([5,5,10]) - scene.addLayer(layer) - - - + scene.on('loaded', () =>{ + scene.addLayer(layer) + }) }); it('scene layer simplePoint', async () => { @@ -101,9 +95,5 @@ describe('template', () => { scene.on('loaded', () =>{ scene.addLayer(layer) }) - }); - - - }); diff --git a/packages/renderer/src/regl/index.ts b/packages/renderer/src/regl/index.ts index b62a8f3799..f59c1e321e 100644 --- a/packages/renderer/src/regl/index.ts +++ b/packages/renderer/src/regl/index.ts @@ -81,6 +81,7 @@ export default class ReglRendererService implements IRendererService { 'EXT_texture_filter_anisotropic', 'EXT_blend_minmax', 'WEBGL_depth_texture', + 'WEBGL_lose_context', ], profile: true, onDone: (err: Error | null, r?: regl.Regl | undefined): void => { diff --git a/packages/scene/src/index.ts b/packages/scene/src/index.ts index ede0a94a74..2fd782a717 100644 --- a/packages/scene/src/index.ts +++ b/packages/scene/src/index.ts @@ -13,6 +13,7 @@ import { IInteractionService, ILayer, ILayerService, + IDebugService, ILngLat, IMapService, IMarker, @@ -57,6 +58,7 @@ class Scene private mapService: IMapService; private controlService: IControlService; private layerService: ILayerService; + private debugService: IDebugService; private iconService: IIconService; private markerService: IMarkerService; private popupService: IPopupService; @@ -90,6 +92,8 @@ class Scene TYPES.IControlService, ); this.layerService = sceneContainer.get(TYPES.ILayerService); + this.debugService = sceneContainer.get(TYPES.IDebugService); + this.debugService.setEnable(config.debug); this.markerService = sceneContainer.get( TYPES.IMarkerService, @@ -147,6 +151,15 @@ class Scene public getMapService(): IMapService { return this.mapService; } + + /** + * 对外暴露 debugService + * @returns + */ + public getDebugService(): IDebugService{ + return this.debugService; + } + public async exportPng(type?: 'png' | 'jpg'): Promise { return await this.sceneService.exportPng(type); } diff --git a/packages/site/.dumirc.ts b/packages/site/.dumirc.ts index eb52b9bb6c..0846a47f7b 100644 --- a/packages/site/.dumirc.ts +++ b/packages/site/.dumirc.ts @@ -308,6 +308,14 @@ export default defineConfig({ }, order: 11, }, + { + slug: 'tutorial/debug', + title: { + zh: '调试 debug', + en: 'debug', + }, + order: 12, + }, { slug: 'api/map', title: { @@ -412,6 +420,14 @@ export default defineConfig({ }, order: 11, }, + { + slug: 'api/debug', + title: { + zh: '调试 debug', + en: 'debug', + }, + order: 12, + }, { slug: 'api/component/control', title: { diff --git a/packages/site/docs/api/debug/debugservice.en.md b/packages/site/docs/api/debug/debugservice.en.md new file mode 100644 index 0000000000..7f35828ffe --- /dev/null +++ b/packages/site/docs/api/debug/debugservice.en.md @@ -0,0 +1,6 @@ +--- +title: DebugService +order: 0 +--- + + diff --git a/packages/site/docs/api/debug/debugservice.zh.md b/packages/site/docs/api/debug/debugservice.zh.md new file mode 100644 index 0000000000..8cc56d5afa --- /dev/null +++ b/packages/site/docs/api/debug/debugservice.zh.md @@ -0,0 +1,99 @@ +--- +title: DebugService +order: 0 +--- + + + +## 简介 + +L7 在通过 debugService 的形式对外提供调试服务,通过 debugService 用户可以获得一些有助于性能监控的信息。 + +### serEnable(enable: boolean) + +用户可以通过 scene 初始化的时候和 debugService 提供的方法来开启监控。 + +```js +// 可以在 scene 初始化的时候打开监控 +const scene = new Scene({ + debug: true, // 默认为 false +}); + +// 可以通过 debugService 单独控制监控 +const debugService = scene.getDebugService(); +debugService.serEnable(true); // 开启监控 +``` + +### getLog(field: undefined | string | string[]): ILog[] | ILog | undefined + +用户通过 getLog 方法获取日志,通过传入不通的参数,用户可以准确获得自己需要的日志内容。 + +```js +// 获取地图初始化日志 +debugService.getLog('map'); // map 为固定值 + +// 在获取图层的创建日志时,为了获取到全部的数据,需要在 layer 创建完成之后获取 +layer.on('inited', () => { + debugService.getLog(layer.id); // 获取单个图层创建日志 +}); + +layerAllLoad([pointLayer1, pointLayer2], () => { + // layerAllLoad 自己实现监听 + debugService.getLog([pointLayer1.id, pointLayer2.id]); // 获取多个图层创建日志 +}); + +// 获取所有日志 +debugService.getLog(); +``` + +- 通过 getLog 方法可以获得如下的日志信息 + +```js +const enum IDebugLog { + MapInitStart = 'mapInitStart', // 地图初始化时间 + + LayerInitStart = 'layerInitStart', // 图层初始化开始时间 + LayerInitEnd = 'layerInitEnd', // 图层初始化结束时间 + + SourceInitStart = 'sourceInitStart',// souce 初始化开始时间 + SourceInitEnd = 'sourceInitEnd', // souce 初始化结束时间 + + // scale:将数据进行 scale 映射处理 => 将数据从定义域转化到值域 + // 如: layer.size('v', [1, 10]); + // 根据字段 v 表示的定义域将 size 的结果映射到 1 ~ 10 之间 + ScaleInitStart = 'scaleInitStart', // scale 初始化开始时间 + ScaleInitEnd = 'scaleInitEnd', // scale 初始化结束时间 + + // mapping:构建渲染数据 + MappingStart = 'mappingStart', // mapping 初始化开始时间 + MappingEnd = 'mappingEnd', // mapping 初始化结束时间 + + // build model:构建渲染使用的程序对象、构建网格、纹理等 + BuildModelStart = 'buildModelStart',// souce 初始化开始时间 + BuildModelEnd = 'buildModelEnd', // souce 初始化结束时间 +} +``` + +### renderDebug(enable: boolean) + +debugService 提供了监听图层渲染时间的便捷方法, 通过 renderDebug 开启。 + +### on(name: string, options: any) + +debugService 支持事件监听,常用与监听渲染。 + +```js +debugService.on('renderEnd', renderInfo => { + const { + renderUid, // 当前帧渲染唯一编号 + renderStart, // 当前帧渲染开始时间 + renderEnd, // 当前帧渲染结束时间 + renderDuration // 当前帧渲染时间 + } = renderInfo; + ... +} +``` + +### off(name: string, func: Function) + +debugService 事件取消监听。 diff --git a/packages/site/docs/api/scene.zh.md b/packages/site/docs/api/scene.zh.md index a00eea3fbd..cd1a82f076 100644 --- a/packages/site/docs/api/scene.zh.md +++ b/packages/site/docs/api/scene.zh.md @@ -725,6 +725,8 @@ scene.on('contextmenu', (ev) => {}); // 鼠标右键单击事件 scene.on('dragstart', (ev) => {}); //开始拖拽地图时触发 scene.on('dragging', (ev) => {}); // 拖拽地图过程中触发 scene.on('dragend', (ev) => {}); //停止拖拽地图时触发。如地图有拖拽缓动效果,则在拽停止,缓动开始前触发 + +scene.on('webglcontextlost', () => {}); // webgl 上下文丢失 ``` ## 实验参数 diff --git a/packages/site/docs/tutorial/debug/layerInit.en.md b/packages/site/docs/tutorial/debug/layerInit.en.md new file mode 100644 index 0000000000..9e1c73cd84 --- /dev/null +++ b/packages/site/docs/tutorial/debug/layerInit.en.md @@ -0,0 +1,5 @@ +--- +title: Layer Init +order: 1 +--- + diff --git a/packages/site/docs/tutorial/debug/layerInit.zh.md b/packages/site/docs/tutorial/debug/layerInit.zh.md new file mode 100644 index 0000000000..e905b8fbb4 --- /dev/null +++ b/packages/site/docs/tutorial/debug/layerInit.zh.md @@ -0,0 +1,44 @@ +--- +title: 图层初始化 +order: 1 +--- + + +在地图应用中,渲染大数据的地理数据是十分常见的需求,为了保证应用的流畅性,需要追求极致的渲染性能,为此监控引擎渲染内容对于优化性能,建设地图可视化应用性能指标有切实的意义。 + +### 实现 + +下面介绍如何使用 L7 提供的能力简单获取图层的初始化的信息。 + +```javascript +import { Scene, PointLayer } from '@antv/l7'; +import { GaodeMap } from '@antv/l7-maps'; + +const scene = new Scene({ + id: 'map', + map: new GaodeMap({ + center: [ 60, 40.7128 ], + zoom: 2 + }), + debug: true +}); +scene.on('loaded', () => { + const debugService = scene.getDebugService(); + const layer = new PointLayer() + .source([{lng: 120, lat: 30}], { + parser: { + type: 'json', + x: 'lng', + y: 'lat', + } + }) + .shape('circle') + .size(10) + .color('#f00'); + layer.on('inited', () => { + console.log(debugService.getLog(layer.id)); + }) + scene.addLayer(layer); +}); +``` + diff --git a/packages/site/docs/tutorial/debug/mapInit.en.md b/packages/site/docs/tutorial/debug/mapInit.en.md new file mode 100644 index 0000000000..288e39c0f4 --- /dev/null +++ b/packages/site/docs/tutorial/debug/mapInit.en.md @@ -0,0 +1,5 @@ +--- +title: Map Init +order: 2 +--- + diff --git a/packages/site/docs/tutorial/debug/mapInit.zh.md b/packages/site/docs/tutorial/debug/mapInit.zh.md new file mode 100644 index 0000000000..05881f1f58 --- /dev/null +++ b/packages/site/docs/tutorial/debug/mapInit.zh.md @@ -0,0 +1,30 @@ +--- +title: 地图初始化 +order: 1 +--- + + +在地图应用中,渲染大数据的地理数据是十分常见的需求,为了保证应用的流畅性,需要追求极致的渲染性能,为此监控引擎渲染内容对于优化性能,建设地图可视化应用性能指标有切实的意义。 + +### 实现 + +下面介绍如何使用 L7 提供的能力简单获取地图的初始化的信息。 + +```javascript +import { Scene } from '@antv/l7'; +import { GaodeMap } from '@antv/l7-maps'; + +const scene = new Scene({ + id: 'map', + map: new GaodeMap({ + center: [ 60, 40.7128 ], + zoom: 2 + }), + debug: true +}); +scene.on('loaded', () => { + const debugService = scene.getDebugService(); + console.log(debugService.getLog('map');) +}); +``` + diff --git a/packages/site/docs/tutorial/debug/render.en.md b/packages/site/docs/tutorial/debug/render.en.md new file mode 100644 index 0000000000..1c7b5554e6 --- /dev/null +++ b/packages/site/docs/tutorial/debug/render.en.md @@ -0,0 +1,5 @@ +--- +title: Render +order: 2 +--- + diff --git a/packages/site/docs/tutorial/debug/render.zh.md b/packages/site/docs/tutorial/debug/render.zh.md new file mode 100644 index 0000000000..c5750b7316 --- /dev/null +++ b/packages/site/docs/tutorial/debug/render.zh.md @@ -0,0 +1,45 @@ +--- +title: 渲染监控 +order: 3 +--- + + +在地图应用中,渲染大数据的地理数据是十分常见的需求,为了保证应用的流畅性,需要追求极致的渲染性能,为此监控引擎渲染内容对于优化性能,建设地图可视化应用性能指标有切实的意义。 + +### 实现 + +下面介绍如何使用 L7 提供的能力简单获取应用的渲染性能信息。 + +```javascript +import { Scene } from '@antv/l7'; +import { GaodeMap } from '@antv/l7-maps'; + +const scene = new Scene({ + id: 'map', + map: new GaodeMap({ + center: [ 60, 40.7128 ], + zoom: 2 + }), + debug: true +}); +scene.on('loaded', () => { + const debugService = scene.getDebugService(); + // 开启每帧渲染的监控 + debugService.renderDebug(true) + debugService.on('renderEnd', renderInfo => { + const { + renderUid, // 当前帧渲染唯一编号 + renderStart, // 当前帧渲染开始时间 + renderEnd, // 当前帧渲染结束时间 + renderDuration // 当前帧渲染时间 + } = renderInfo; + ... + } + + setTimeout(() => { + debugService.renderDebug(false); + debugService.off('renderEnd'); + }, 1000); // 监听 1s 内的渲染情况 +}); +``` + diff --git a/packages/utils/src/hash.ts b/packages/utils/src/hash.ts index ace2ff1694..c711de758e 100644 --- a/packages/utils/src/hash.ts +++ b/packages/utils/src/hash.ts @@ -22,3 +22,10 @@ export function djb2hash(str: string) { } return hash >>> 0; } + +export function guid() { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { + const r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8); + return v.toString(16); + }); +} diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 8a39e2df76..c074c74027 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -1,17 +1,24 @@ // @ts-ignore +export { djb2hash, BKDRHash, guid } from './hash'; + +import * as DOM from './dom'; +import * as Satistics from './statistics'; + +export { DOM, Satistics }; + +export * from './mini-adapter/index'; export * from './ajax'; export * from './anchor'; export * from './color'; export * from './cull'; -export * as DOM from './dom'; + export * from './env'; export * from './event'; export * from './geo'; -export { BKDRHash, djb2hash } from './hash'; export * from './lineAtOffset'; export * from './lru_cache'; export * from './mini-adapter/index'; -export * as Satistics from './statistics'; + export * from './stencli'; export * from './tileset-manager'; export * from './worker-helper';