Skip to content

Commit

Permalink
fix: merge simple path into a single draw call
Browse files Browse the repository at this point in the history
  • Loading branch information
xiaoiver committed Jul 14, 2023
1 parent 578a440 commit 88ddb75
Show file tree
Hide file tree
Showing 9 changed files with 411 additions and 37 deletions.
4 changes: 2 additions & 2 deletions packages/g-plugin-device-renderer/src/PickingPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@ export class PickingPlugin implements RenderingPlugin {

renderingService.hooks.pick.tapPromise(
PickingPlugin.tag,
(result: PickingResult) => {
throw new Error('Async version is not implemented.');
async (result: PickingResult) => {
return this.pick(result);
},
);
}
Expand Down
97 changes: 78 additions & 19 deletions packages/g-plugin-device-renderer/src/meshes/Instanced.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type {
Tuple4Number,
} from '@antv/g-lite';
import { CSSRGB, isPattern, isCSSRGB, parseColor, Shape } from '@antv/g-lite';
import { mat4 } from 'gl-matrix';
import { mat4, vec3 } from 'gl-matrix';
import { BufferGeometry, GeometryEvent } from '../geometries';
import type { LightPool } from '../LightPool';
import type { Fog } from '../lights';
Expand Down Expand Up @@ -80,7 +80,7 @@ export enum VertexAttributeLocation {
}

/**
* Instanced mesh
* Draw call.
*/
export abstract class Instanced {
/**
Expand Down Expand Up @@ -141,10 +141,20 @@ export abstract class Instanced {
protected lightReceived = false;

/**
*
* Divisor of instanced array.
*/
protected divisor = 1;

/**
* Account for anchor and merge it into modelMatrix.
*/
protected mergeAnchorIntoModelMatrix = false;

/**
* Create a new instance when exceed.
*/
protected maxInstance = 5000;

protected abstract createMaterial(objects: DisplayObject[]): void;

get instance() {
Expand Down Expand Up @@ -235,6 +245,10 @@ export abstract class Instanced {
return true;
}

if (this.objects.length >= this.maxInstance) {
return false;
}

// Path / Polyline could be rendered as Line
if (
this.instance.nodeName !== object.nodeName &&
Expand Down Expand Up @@ -303,18 +317,18 @@ export abstract class Instanced {
];
}

if (this.clipPathTarget) {
// account for target's rts
mat4.copy(modelMatrix, object.getLocalTransform());
fillColor = [255, 255, 255, 255];
mat4.mul(
modelMatrix,
this.clipPathTarget.getWorldTransform(),
modelMatrix,
);
} else {
mat4.copy(modelMatrix, object.getWorldTransform());
}
// if (this.clipPathTarget) {
// // account for target's rts
// mat4.copy(modelMatrix, object.getLocalTransform());
// fillColor = [255, 255, 255, 255];
// mat4.mul(
// modelMatrix,
// this.clipPathTarget.getWorldTransform(),
// modelMatrix,
// );
// } else {
// mat4.copy(modelMatrix, object.getWorldTransform());
// }
mat4.mul(
modelViewMatrix,
this.context.camera.getViewTransform(),
Expand All @@ -325,6 +339,30 @@ export abstract class Instanced {
// @ts-ignore
object.renderable3D?.encodedPickingColor) || [0, 0, 0];

if (this.mergeAnchorIntoModelMatrix) {
const { anchor } = object.parsedStyle as ParsedBaseStyleProps;
let translateX = 0;
let translateY = 0;
let translateZ = 0;
const contentBounds = object.getGeometryBounds();
if (contentBounds) {
const { halfExtents } = contentBounds;
translateX = -halfExtents[0] * anchor[0] * 2;
translateY = -halfExtents[1] * anchor[1] * 2;
translateZ = -halfExtents[2] * (anchor[2] || 0) * 2;
}

mat4.mul(
modelMatrix,
object.getWorldTransform(), // apply anchor
mat4.fromTranslation(
modelMatrix,
vec3.fromValues(translateX, translateY, translateZ),
),
);
} else {
mat4.copy(modelMatrix, object.getWorldTransform());
}
packedModelMatrix.push(...modelMatrix);
packedFillStroke.push(
packUint8ToFloat(fillColor[0], fillColor[1]),
Expand Down Expand Up @@ -798,11 +836,32 @@ export abstract class Instanced {
);
} else if (name === 'modelMatrix') {
const packed: number[] = [];
const modelMatrix = mat4.create();
objects.forEach((object) => {
const modelMatrix = mat4.copy(
mat4.create(),
object.getWorldTransform(),
);
if (this.mergeAnchorIntoModelMatrix) {
const { anchor } = object.parsedStyle;
let translateX = 0;
let translateY = 0;
let translateZ = 0;
const contentBounds = object.getGeometryBounds();
if (contentBounds) {
const { halfExtents } = contentBounds;
translateX = -halfExtents[0] * anchor[0] * 2;
translateY = -halfExtents[1] * anchor[1] * 2;
translateZ = -halfExtents[2] * (anchor[2] || 0) * 2;
}

mat4.mul(
modelMatrix,
object.getWorldTransform(), // apply anchor
mat4.fromTranslation(
modelMatrix,
vec3.fromValues(translateX, translateY, translateZ),
),
);
} else {
mat4.copy(modelMatrix, object.getWorldTransform());
}
packed.push(...modelMatrix);
});

Expand Down
40 changes: 32 additions & 8 deletions packages/g-plugin-device-renderer/src/meshes/InstancedPath.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,26 +43,50 @@ export class InstancedPathMesh extends Instanced {
const {
path: { absolutePath },
} = object.parsedStyle as ParsedPathStyleProps;
if (
absolutePath.length === 2 &&
absolutePath[0][0] === 'M' &&
(absolutePath[1][0] === 'C' ||
absolutePath[1][0] === 'A' ||
absolutePath[1][0] === 'Q')
) {
if (absolutePath.length >= 2 && absolutePath.length <= 5) {
return true;
}
}
return false;
}

protected mergeAnchorIntoModelMatrix = true;

private segmentNum = -1;

private calcSegmentNum(object: DisplayObject) {
return (
object.parsedStyle as ParsedPathStyleProps
).path.absolutePath.reduce((prev, cur) => {
let segment = 0;
if (cur[0] === 'M') {
segment = 0;
} else if (cur[0] === 'L') {
segment = 1;
} else if (cur[0] === 'A' || cur[0] === 'Q' || cur[0] === 'C') {
segment = SEGMENT_NUM;
} else if (cur[0] === 'Z') {
segment = 1;
}
return prev + segment;
}, 0);
}
/**
* Paths with the same number of vertices should be merged.
*/
shouldMerge(object: DisplayObject, index: number) {
const shouldMerge = super.shouldMerge(object, index);
if (!shouldMerge) {
return false;
}

return true;
if (this.segmentNum === -1) {
this.segmentNum = this.calcSegmentNum(this.instance);
}

const segmentNum = this.calcSegmentNum(object);

return this.segmentNum === segmentNum;
}

createMaterial(objects: DisplayObject[]): void {
Expand Down
7 changes: 3 additions & 4 deletions packages/g-plugin-device-renderer/src/meshes/Line.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
import { arcToCubic } from '@antv/util';
import earcut from 'earcut';
import { mat4, vec3 } from 'gl-matrix';
import { CullMode, Format, VertexBufferFrequency } from '../platform';
import { Format, VertexBufferFrequency } from '../platform';
import { RENDER_ORDER_SCALE } from '../renderer/Batch';
import frag from '../shader/line.frag';
import vert from '../shader/line.vert';
Expand Down Expand Up @@ -190,8 +190,8 @@ export class LineMesh extends Instanced {
});
} else if (name === 'lineDash') {
this.material.setUniforms({
[Uniform.DASH]: lineDash[0] || 0,
[Uniform.GAP]: lineDash[1] || 0,
[Uniform.DASH]: (lineDash && lineDash[0]) || 0,
[Uniform.GAP]: (lineDash && lineDash[1]) || 0,
});
} else if (name === 'lineDashOffset') {
this.material.setUniforms({
Expand Down Expand Up @@ -299,7 +299,6 @@ export class LineMesh extends Instanced {
[Uniform.VISIBLE]: visibility === 'visible' ? 1 : 0,
[Uniform.Z_INDEX]: instance.sortable.renderOrder * RENDER_ORDER_SCALE,
});
this.material.cullMode = CullMode.None;
}

createGeometry(objects: DisplayObject[]): void {
Expand Down
2 changes: 1 addition & 1 deletion packages/g-plugin-device-renderer/src/renderer/Path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export class PathRenderer extends Batch {
}

if (index === 3) {
return isOneCommandCurve;
return !isLine && isOneCommandCurve;
}

return true;
Expand Down
Loading

0 comments on commit 88ddb75

Please sign in to comment.