Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Instanced rendering #1377

Merged
merged 6 commits into from
Oct 4, 2023
Merged

Instanced rendering #1377

merged 6 commits into from
Oct 4, 2023

Conversation

jacomyal
Copy link
Owner

@jacomyal jacomyal commented Sep 18, 2023

This PR implements instanced rendering for relevant programs. This should highly reduce RAM usage of sigma.js in most cases, and might increase performances as well. It fixes #1322.

Details:

  • Removes ARRAY_ITEMS_PER_VERTEX, and automatically computes it instead
  • Removes everything indices related from programs, since instanced rendering makes it useless
  • Improves common/program.ts to handle instanced rendering:
    • Adds new Program#drawWebGL(method: GLenum) method, that handles calling the proper WebGL method, with the proper arguments
    • If the program's definition has CONSTANT_DATA and CONSTANT_ATTRIBUTES, it will be rendered using instanced rendering (works with both WebGL and WebGL2)
    • Also, programs now have an #unbind method that clears the related WebGL instance state before another program is used
  • Ports every program that has multiple vertices to instanced rendering

@Yomguithereal
Copy link
Collaborator

Some misc remarks:

  • the canUse32BitsIndices can be dropped from utils because I think it is not being used anymore


// For instanced programs:
if ("CONSTANT_ATTRIBUTES" in definition) {
this.isInstanced = true;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You cannot be instanced without any constant attributes?

@@ -70,21 +79,28 @@ export abstract class Program<Uniform extends string = string> implements Abstra
capacity = 0;
verticesCount = 0;

abstract getDefinition(): ProgramDefinition<Uniform>;
isInstanced: boolean;
constantBuffer: WebGLBuffer = {} as WebGLBuffer;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it normal to set a default buffer as an empty JavaScript object? Just a trick to avoid nastiness of nullable property before constructor is over?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, just that trick...

`Program: error while getting constant data (one vector has ${vector.length} items instead of ${constantAttributesItemsCount})`,
);

constantData.push(...vector);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess the length of this vector cannot overflow JS stack size regarding maximum function arity (I think so but just making sure)?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call, I'll switch to a classic for loop.


this.STRIDE = this.ATTRIBUTES_ITEMS_COUNT;
this.CONSTANT_DATA = new Float32Array(constantData);
this.CONSTANT_ATTRIBUTES = definition.CONSTANT_ATTRIBUTES;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No risk of mutating this property and affecting a user's definition (probably not, it is returned by a function, just making sure).

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No more than with other definition elements (ATTRIBUTES, for instance) before this PR.

gl.vertexAttribDivisor(location, 1);
} else {
const ext = gl.getExtension("ANGLE_instanced_arrays");
if (ext) ext.vertexAttribDivisorANGLE(location, 1);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If ext is not available should we yell?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes we can indeed, I'll add that.

@@ -21,6 +20,15 @@ const SIZE_FACTOR_PER_ATTRIBUTE_TYPE: Record<number, number> = {
[WebGL2RenderingContext.FLOAT]: 4,
};

function getAttributeItemsCount(attr: ProgramAttributeSpecification): number {
return attr.normalized ? 1 : attr.size;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can there be cases where the attribute is normalized but the size is larger than one? I don't think we used something of the kind in the past but could this break in the future?

@@ -102,6 +80,6 @@ export default class EdgeArrowHeadProgram extends EdgeProgram<typeof UNIFORMS[nu
gl.uniform1f(u_sizeRatio, params.sizeRatio);
gl.uniform1f(u_correctionRatio, params.correctionRatio);

gl.drawArrays(gl.TRIANGLES, 0, this.verticesCount);
this.drawWebGL(gl.TRIANGLES);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we discussed this but maybe the gl method can be a property of the class because we won't call drawWebGL with alternating ones at runtime?

Copy link
Owner Author

@jacomyal jacomyal Oct 4, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Branch v3-1370-1371 does that indeed.

],
CONSTANT_DATA: [
[0, 1, 0],
[0, -1, 0],
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wish prettier could let us align the data array to be beautiful :)

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed ^^

@@ -72,6 +58,6 @@ export default class NodeCircleProgram extends NodeProgram<typeof UNIFORMS[numbe
gl.uniform1f(u_correctionRatio, params.correctionRatio);
gl.uniformMatrix3fv(u_matrix, false, params.matrix);

gl.drawArrays(gl.TRIANGLES, 0, this.verticesCount);
this.drawWebGL(gl.TRIANGLES);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe there is something to be done wrt naming to avoid the confusion between draw, drawWebGL and such?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After merging branch v3-1370-1371 the naming will be better.

This value is now deduced by the ATTRIBUTES definitions instead.
Since the next feature to come is instanced rendering, the gain from
indices allocation and "elements rendering" will become negligible.
I prefer to completely remove the code for this optimisation to better
handle the debt.
This ticket partially solves #1322. The remaining programs must be
ported to the instanced system before the ticket could be completely
closed.

Details:
- Adds a new `#drawWebGL` method, that handles calling the proper WebGL
  (1 or 2) methods to draw
- Adds a new InstancedProgramDefinition interface, that contains
  everything ProgramDefinition contains, but with constant attributes
  and data as well
- Enhances the Program class so that it uses instanced rendering when it
  receives an InstancedProgramDefinition instead of ProgramDefinition
- Updates all programs so that they properly use the new `#drawWebGL`
  method
- Ports node.circle.ts to be an instanced program
WARNING: The EdgeArrowHead is currently broken, and I don't know why
yet.

Details:
- InstancedProgramDefinition['CONSTANT_DATA'] becomes a number[][]
  instead of a number[] (it makes more sense, since it's an array of
  vertices)
- Ports edge.arrowHead, edge.clamped and edge.triangle as instanced
  rendering programs
- Fixes CONSTANT_DATA in node.circle
We never cleaned the GL state, while this state is shared between
programs that are used by the same GL context. In this case, some vertex
attributes were using a divisor for non-instanced rendering programs,
which were causing them to fail silently.

The fix is to make program unbind everything they can once they draw, so
that they no more share common state.
@jacomyal jacomyal merged commit 6b27c92 into v3 Oct 4, 2023
2 checks passed
@jacomyal jacomyal deleted the v3-instanced-rendering branch October 4, 2023 14:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants