Skip to content

Commit

Permalink
perf: optimize element performance (#5800)
Browse files Browse the repository at this point in the history
* perf: remove util isType usage

* fix(plugins): fix background plugin

* fix: fix autoFit not effect after draw

* test: add test case to draw 20000 elements

* feat(utils): add createGeometry util

* refactor(3d): optimize massive shape performance

* test: add massive 3d elements case
  • Loading branch information
Aarebecca authored Jun 3, 2024
1 parent 59e8bcd commit 1c9fb18
Show file tree
Hide file tree
Showing 25 changed files with 286 additions and 51 deletions.
1 change: 1 addition & 0 deletions packages/g6-extension-3d/__tests__/demos/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export * from './behavior-roll-canvas';
export * from './behavior-zoom-canvas';
export * from './layer-top';
export * from './layout-d3-force-3d';
export { massiveElements } from './massive-elements';
export * from './position';
export * from './shapes';
export * from './solar-system';
63 changes: 63 additions & 0 deletions packages/g6-extension-3d/__tests__/demos/massive-elements.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { CameraSetting, ExtensionCategory, Graph, register } from '@antv/g6';
import { Light, Line3D, ObserveCanvas3D, Sphere, ZoomCanvas3D, renderer } from '../../src';

export const massiveElements: TestCase = async (context) => {
register(ExtensionCategory.PLUGIN, '3d-light', Light);
register(ExtensionCategory.NODE, 'sphere', Sphere);
register(ExtensionCategory.EDGE, 'line3d', Line3D);
register(ExtensionCategory.PLUGIN, 'camera-setting', CameraSetting);
register(ExtensionCategory.BEHAVIOR, 'zoom-canvas-3d', ZoomCanvas3D);
register(ExtensionCategory.BEHAVIOR, 'observe-canvas-3d', ObserveCanvas3D);

const data = await fetch('https://assets.antv.antgroup.com/g6/eva-3d-data.json').then((res) => res.json());

const graph = new Graph({
...context,
animation: false,
renderer,
data,
node: {
type: 'sphere',
style: {
materialType: 'phong',
size: 50,
x: (d) => d.data!.x,
y: (d) => d.data!.y,
z: (d) => d.data!.z,
},
palette: {
color: 'tableau',
type: 'group',
field: 'cluster',
},
},
edge: {
type: 'line3d',
},
behaviors: ['observe-canvas-3d', 'zoom-canvas-3d'],
plugins: [
{
type: 'camera-setting',
projectionMode: 'orthographic',
near: 1,
far: 10000,
fov: 45,
aspect: 1,
},
{
type: '3d-light',
directional: {
direction: [0, 0, 1],
},
},
],
});

console.time('time');

await graph.draw();

console.timeEnd('time');

return graph;
};
5 changes: 4 additions & 1 deletion packages/g6-extension-3d/__tests__/demos/solar-system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ export const solarSystem: TestCase = async (context) => {
const graph = new Graph({
...context,
renderer,
background: 'black',
data: {
nodes: [
{
Expand Down Expand Up @@ -68,6 +67,10 @@ export const solarSystem: TestCase = async (context) => {
direction: [0, 0, 1],
},
},
{
type: 'background',
background: 'black',
},
],
});

Expand Down
14 changes: 14 additions & 0 deletions packages/g6-extension-3d/__tests__/unit/utils/geometry.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { CubeGeometry } from '@antv/g-plugin-3d';
import { createGeometry } from '../../../src/utils/geometry';

describe('geometry', () => {
it('createGeometry', () => {
const device: any = {};
const geometry1 = createGeometry('cube', device, CubeGeometry, { width: 1, height: 1, depth: 1 });
const geometry2 = createGeometry('cube', device, CubeGeometry, { depth: 1, height: 1, width: 1 });
const geometry3 = createGeometry('cube', device, CubeGeometry, { width: 2, height: 2, depth: 2 });

expect(geometry1).toBe(geometry2);
expect(geometry1).not.toBe(geometry3);
});
});
3 changes: 2 additions & 1 deletion packages/g6-extension-3d/src/elements/capsule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { DisplayObjectConfig } from '@antv/g';
import type { CapsuleGeometryProps, ProceduralGeometry as GGeometry } from '@antv/g-plugin-3d';
import { CapsuleGeometry } from '@antv/g-plugin-3d';
import { deepMix } from '@antv/util';
import { createGeometry } from '../utils/geometry';
import type { BaseNode3DStyleProps } from './base-node-3d';
import { BaseNode3D } from './base-node-3d';

Expand All @@ -22,6 +23,6 @@ export class Capsule extends BaseNode3D<CapsuleStyleProps> {
protected getGeometry(attributes: Required<CapsuleStyleProps>): GGeometry<any> | undefined {
const size = this.getSize();
const { radius = size[0] / 2, height = size[1], heightSegments, sides } = attributes;
return new CapsuleGeometry(this.device, { radius, height, heightSegments, sides });
return createGeometry('capsule', this.device, CapsuleGeometry, { radius, height, heightSegments, sides });
}
}
9 changes: 8 additions & 1 deletion packages/g6-extension-3d/src/elements/cone.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { DisplayObjectConfig } from '@antv/g';
import type { ConeGeometryProps, ProceduralGeometry as GGeometry } from '@antv/g-plugin-3d';
import { ConeGeometry } from '@antv/g-plugin-3d';
import { deepMix } from '@antv/util';
import { createGeometry } from '../utils/geometry';
import type { BaseNode3DStyleProps } from './base-node-3d';
import { BaseNode3D } from './base-node-3d';

Expand All @@ -28,6 +29,12 @@ export class Cone extends BaseNode3D<ConeStyleProps> {
heightSegments,
capSegments,
} = attributes;
return new ConeGeometry(this.device, { baseRadius, peakRadius, height, heightSegments, capSegments });
return createGeometry('cone', this.device, ConeGeometry, {
baseRadius,
peakRadius,
height,
heightSegments,
capSegments,
});
}
}
10 changes: 9 additions & 1 deletion packages/g6-extension-3d/src/elements/cube.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { DisplayObjectConfig } from '@antv/g';
import type { CubeGeometryProps, ProceduralGeometry as GGeometry } from '@antv/g-plugin-3d';
import { CubeGeometry } from '@antv/g-plugin-3d';
import { deepMix } from '@antv/util';
import { createGeometry } from '../utils/geometry';
import type { BaseNode3DStyleProps } from './base-node-3d';
import { BaseNode3D } from './base-node-3d';

Expand All @@ -28,6 +29,13 @@ export class Cube extends BaseNode3D<CubeStyleProps> {
heightSegments,
depthSegments,
} = attributes;
return new CubeGeometry(this.device, { width, height, depth, widthSegments, heightSegments, depthSegments });
return createGeometry('cube', this.device, CubeGeometry, {
width,
height,
depth,
widthSegments,
heightSegments,
depthSegments,
});
}
}
3 changes: 2 additions & 1 deletion packages/g6-extension-3d/src/elements/cylinder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { DisplayObjectConfig } from '@antv/g';
import type { CylinderGeometryProps, ProceduralGeometry as GGeometry } from '@antv/g-plugin-3d';
import { CylinderGeometry } from '@antv/g-plugin-3d';
import { deepMix } from '@antv/util';
import { createGeometry } from '../utils/geometry';
import type { BaseNode3DStyleProps } from './base-node-3d';
import { BaseNode3D } from './base-node-3d';

Expand All @@ -22,6 +23,6 @@ export class Cylinder extends BaseNode3D<CylinderStyleProps> {
protected getGeometry(attributes: Required<CylinderStyleProps>): GGeometry<any> | undefined {
const size = this.getSize();
const { radius = size[0] / 2, height = size[1], heightSegments, capSegments } = attributes;
return new CylinderGeometry(this.device, { radius, height, heightSegments, capSegments });
return createGeometry('cylinder', this.device, CylinderGeometry, { radius, height, heightSegments, capSegments });
}
}
3 changes: 2 additions & 1 deletion packages/g6-extension-3d/src/elements/plane.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { DisplayObjectConfig } from '@antv/g';
import type { ProceduralGeometry as GGeometry, PlaneGeometryProps } from '@antv/g-plugin-3d';
import { CullMode, PlaneGeometry } from '@antv/g-plugin-3d';
import { deepMix } from '@antv/util';
import { createGeometry } from '../utils/geometry';
import type { BaseNode3DStyleProps } from './base-node-3d';
import { BaseNode3D } from './base-node-3d';

Expand All @@ -19,6 +20,6 @@ export class Plane extends BaseNode3D<PlaneStyleProps> {
protected getGeometry(attributes: Required<PlaneStyleProps>): GGeometry<any> | undefined {
const size = this.getSize();
const { width = size[0], depth = size[1], widthSegments, depthSegments } = attributes;
return new PlaneGeometry(this.device, { width, depth, widthSegments, depthSegments });
return createGeometry('plane', this.device, PlaneGeometry, { width, depth, widthSegments, depthSegments });
}
}
3 changes: 2 additions & 1 deletion packages/g6-extension-3d/src/elements/sphere.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { DisplayObjectConfig } from '@antv/g';
import type { ProceduralGeometry as GGeometry, SphereGeometryProps } from '@antv/g-plugin-3d';
import { SphereGeometry } from '@antv/g-plugin-3d';
import { deepMix } from '@antv/util';
import { createGeometry } from '../utils/geometry';
import type { BaseNode3DStyleProps } from './base-node-3d';
import { BaseNode3D } from './base-node-3d';

Expand All @@ -22,6 +23,6 @@ export class Sphere extends BaseNode3D<SphereStyleProps> {
protected getGeometry(attributes: Required<SphereStyleProps>): GGeometry<any> | undefined {
const size = this.getSize();
const { radius = size[0] / 2, latitudeBands, longitudeBands } = attributes;
return new SphereGeometry(this.device, { radius, latitudeBands, longitudeBands });
return createGeometry('sphere', this.device, SphereGeometry, { radius, latitudeBands, longitudeBands });
}
}
3 changes: 2 additions & 1 deletion packages/g6-extension-3d/src/elements/torus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { DisplayObjectConfig } from '@antv/g';
import type { ProceduralGeometry as GGeometry, TorusGeometryProps } from '@antv/g-plugin-3d';
import { TorusGeometry } from '@antv/g-plugin-3d';
import { deepMix } from '@antv/util';
import { createGeometry } from '../utils/geometry';
import type { BaseNode3DStyleProps } from './base-node-3d';
import { BaseNode3D } from './base-node-3d';

Expand All @@ -22,6 +23,6 @@ export class Torus extends BaseNode3D<TorusStyleProps> {
protected getGeometry(attributes: Required<TorusStyleProps>): GGeometry<any> | undefined {
const size = this.getSize();
const { tubeRadius = size[0] / 2, ringRadius = size[1] / 2, segments, sides } = attributes;
return new TorusGeometry(this.device, { tubeRadius, ringRadius, segments, sides });
return createGeometry('torus', this.device, TorusGeometry, { tubeRadius, ringRadius, segments, sides });
}
}
39 changes: 39 additions & 0 deletions packages/g6-extension-3d/src/utils/geometry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import type { Device } from '@antv/g-device-api';
import type { ProceduralGeometry } from '@antv/g-plugin-3d';

const GEOMETRY_CACHE = new Map<string, unknown>();

/**
* <zh/> 创建几何体
*
* <en/> Create geometry
* @param type - <zh/> 几何体类型 <en/> geometry type
* @param device - <zh/> 设备对象 <en/> device object
* @param Ctor - <zh/> 几何体构造函数 <en/> geometry constructor
* @param style - <zh/> 几何体样式 <en/> geometry style
* @returns <zh/> 几何体对象 <en/> geometry object
*/
export function createGeometry<T extends ProceduralGeometry<any>>(
type: string,
device: Device,
Ctor: new (...args: any[]) => T,
style: Record<string, unknown>,
) {
const cacheKey =
type +
'|' +
Object.entries(style)
.sort(([a], [b]) => a.localeCompare(b))
.map(([k, v]) => `${k}:${v}`)
.join(',');

if (GEOMETRY_CACHE.has(cacheKey)) {
return GEOMETRY_CACHE.get(cacheKey) as T;
}

const geometry = new Ctor(device, style);

GEOMETRY_CACHE.set(cacheKey, geometry);

return geometry;
}
1 change: 1 addition & 0 deletions packages/g6/__tests__/demos/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ export { layoutRadialConfigurationTranslate } from './layout-radial-configuratio
export { layoutRadialPreventOverlap } from './layout-radial-prevent-overlap';
export { layoutRadialPreventOverlapUnstrict } from './layout-radial-prevent-overlap-unstrict';
export { layoutRadialSort } from './layout-radial-sort';
export { perf20000Elements } from './perf-20000-elements';
export { perfFCP } from './perf-fcp';
export { pluginBackground } from './plugin-background';
export { pluginBubbleSets } from './plugin-bubble-sets';
Expand Down
86 changes: 86 additions & 0 deletions packages/g6/__tests__/demos/perf-20000-elements.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import type { GraphOptions } from '@/src';
import { Graph } from '@/src';

export const perf20000Elements: TestCase = async (context) => {
const data = await fetch('https://assets.antv.antgroup.com/g6/20000.json').then((res) => res.json());

const graph = new Graph({
...context,
animation: false,
data,
node: {
style: {
size: 8,
},
palette: {
type: 'group',
field: 'cluster',
},
},
behaviors: ['zoom-canvas', 'drag-canvas', 'drag-element'],
autoFit: 'view',
plugins: [{ type: 'background', background: '#fff' }],
});

console.time('time');
await graph.draw();
console.timeEnd('time');

perf20000Elements.form = (gui) => {
const themes: Record<string, GraphOptions> = {
'🌞 Light': {
theme: 'light',
node: {
palette: {
type: 'group',
field: 'cluster',
},
},
plugins: [{ type: 'background', background: '#fff' }],
},
'🌚 Dark': {
theme: 'dark',
node: {
palette: {
type: 'group',
field: 'cluster',
},
},
plugins: [{ type: 'background', background: '#000' }],
},
'🌎 Blue': {
theme: 'light',
node: {
palette: {
type: 'group',
field: 'cluster',
color: 'blues',
invert: true,
},
},
plugins: [{ type: 'background', background: '#f3faff' }],
},
'🌕 Yellow': {
background: '#fcf9f1',
theme: 'light',
node: {
palette: {
type: 'group',
field: 'cluster',
color: ['#ffe7ba', '#ffd591', '#ffc069', '#ffa940', '#fa8c16', '#d46b08', '#ad4e00', '#873800', '#612500'],
},
},
plugins: [{ type: 'background', background: '#fcf9f1' }],
},
};

return [
gui.add({ theme: '🌞 Light' }, 'theme', Object.keys(themes)).onChange((theme: string) => {
graph.setOptions(themes[theme]);
graph.draw();
}),
];
};

return graph;
};
1 change: 1 addition & 0 deletions packages/g6/__tests__/demos/perf-fcp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export const perfFCP: TestCase = async (context) => {

const graph = new Graph({
...context,
animation: false,
data,
node: {
type: 'circle', // 👈🏻 Node shape type.
Expand Down
Loading

0 comments on commit 1c9fb18

Please sign in to comment.