-
Notifications
You must be signed in to change notification settings - Fork 1.4k
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
Improve the performance of certain GraphNode methods and avoid recalculating the AABB multiple times during rendering. #7245
base: main
Are you sure you want to change the base?
Conversation
…e aabb a few times while rendering
Please test the memory footprint and allocation/garbage collection difference in dynamic scenes with/without this PR. Glancing into the code, this PR will introduce a lot of allocations (arrays, pop, unshift, etc) into hot code (the code that might be running multiple times on every frame). This might lead to a minor/major GC stalls. |
@Maksims It is a good point, that's why _tmpGraphNodeStack was added. It doesn's looks like there is a big difference in terms of memory
|
src/scene/graph-node.js
Outdated
@@ -98,6 +73,12 @@ function findNode(node, test) { | |||
* a powerful set of features that are leveraged by the `Entity` class. | |||
*/ | |||
class GraphNode extends EventHandler { | |||
/** | |||
* @type {GraphNode[]} | |||
* @ignore |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be @private
instead?
This is very cool. I was wondering...since this is such performance critical code, would it be more efficient to store the stack pointer (index)? Then, a pop is a decrement and a push is an array 'write at index'. And to start using the stack, just set the pointer number to |
src/scene/graph-node.js
Outdated
@@ -594,11 +593,22 @@ class GraphNode extends EventHandler { | |||
const results = []; | |||
const test = createTest(attr, value); | |||
|
|||
this.forEach((node) => { | |||
const stack = GraphNode._tmpGraphNodeStack; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since forEach
is now optimized to execute iteratively, why duplicate the iterative loop again here?
This approach eats up a lot of memory, maybe we should move away from storing references to objects in arrays on indexes? about: https://www.mattzeunert.com/2018/01/25/v8-javascript-memory-quiz.html jsbench: https://jsbench.me/cvm5h498pk/1
|
@AlexAPPi I'll look at your suggestion tomorrow, ok? Regarding my solution, as noted in the comment, I'm using shift() in the const queue = [node];
while (queue.length > 0) {
// shift() is used for breadth-first approach; use pop() for depth-first
const current = queue.shift();
// . . .
const children = current.children;
for (let i = 0; i < children.length; i++) {
queue.push(children[i]);
}
} While breadth-first traversal tends to be slower and consume more memory than depth-first, it's necessary here to pass unit tests that expect a specific order. I'd prefer to use Note: It appears that the third solution from https://jsbench.me/cvm5h498pk/1 has a bug (I'm not sure about purpose of the I cannot run the fourth solution: But as I can see the depth-first solution is faster than the second solution: Thank you for this article! @willeastcott can I change breadth-first to depth-first? And a unit test that requires breadth-first? I'll try to use an offset instead of |
… tests for them, use the classes in the forEach method of GraphNode
I experimented with placing multiple animated skinned meshes in Playcanvas and Unity 6.1, which is going to support WebGPU to address huge performance issues with skinned meshes.
I understand that it's best to bake animations for skinned meshes instead of using the Anim component when dealing with high number of animated skinned meshes. But still...
The PR proposes replacing the recursive implementation of core GraphNode methods with an iterative approach and avoid recalculating the AABB multiple times during rendering to improve performance of skinned meshes.
Here are my results: