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

Fix Entity#destroy #5414

Merged
merged 9 commits into from Jun 22, 2023
Merged

Fix Entity#destroy #5414

merged 9 commits into from Jun 22, 2023

Conversation

kungfooman
Copy link
Collaborator

Fixes #4649

The main issue is simply that GraphNode has no destroy method and if a Entity is inside a GraphNode... it will never be deleted.

I can imagine many solutions, but simply to get the conversations going I propose this one...

I confirm I have read the contributing guidelines and signed the Contributor License Agreement.

@yaustar
Copy link
Contributor

yaustar commented Jun 19, 2023

Just to confirm, that scene basically has:

entity
-> graph node
- -> entity

@kungfooman
Copy link
Collaborator Author

@yaustar yep exactly, testable like:

const isEntityChildOfGraphNode = _ => _ instanceof pc.Entity && _.parent?.constructor === pc.GraphNode;
const entitiesInGraphNodes = pc.app.root.find(isEntityChildOfGraphNode);
entitiesInGraphNodes.map(ent => {
    const {name, path, parent} = ent;
    return {name, path, ent, parent};
});

Output in Seemore:

image

More ideas for (possible?) solutions:

  1. As in PR, find these special cases before recursively destroy entities "normally" (as it always was)
  2. Implement GraphNode#destroy?
  3. Simply find every entity using GraphNode#find and delete them all iteratively?

Probably everything has pros/cons, but so far I tend to (1) or (2)

@mvaligursky
Copy link
Contributor

personally I would find the Implement GraphNode#destroy option a lot cleaner, any reasons not to go that route?

@kungfooman
Copy link
Collaborator Author

I just thought about performance, lets say having 20 skeletons with ~70 GraphNode's and GraphNode is a bit "lower level" and calling destroy on them even tho they may have no entities might be useless. But I guess in real-life people put entities in GraphNode's all the time to track animated positions or attach particle effects, so being "pure" with GraphNode's is not really a thing.

personally I would find the Implement GraphNode#destroy option a lot cleaner, any reasons not to go that route?

So I pretty much agree, I would also like to see GraphNode#detachFromParent, because I see it all over the place handled via nested if's:

image

Or other tricks are GraphNode#reparent(null)... which feels "okayish" once you know it, but still not 100% intuitive. I guess therefore it's only used once:

image

Would it be okay to implement GraphNode#detachFromParent and GraphNode#destroy?

@mvaligursky
Copy link
Contributor

Would it be okay to implement GraphNode#detachFromParent and GraphNode#destroy?

Absolutely, I'd like that. If possible, create two separate PRs please.

@kungfooman
Copy link
Collaborator Author

Debugging and debugging 🤓

image

Found another reason why it fails...

Called from:

image

Simply said, the "component removal code" is snatching away our entities (which we want to delete recursively) before our recursion ever has a chance to get to them (they just "disappear" 🤯👻💥).

This is easily fixed though, simply first disabled all component, then do the recursion, and then to component deletion.

Or 2nd possible solution: mark the entire hierarchy we want to delete (something like _destroying flag) beforehand and then make GraphNode#removeChild do nothing, because we know better (namely that this is not a removal but a destruction).

But I like the 1st solution, simply destroying the components after the recursion, so I will research that route a bit more... I realize that historically this stuff was a mine field, a change here breaks a project there etc... please just link me all scenes I shall test this with 🔍 (after all, we want to fix bugs, not introduce new ones).

@kungfooman
Copy link
Collaborator Author

Ready for review... pretty happy about the result.

I'm testing with Seemore and besides the event bug I also found a room-loading bug in demo-loader.js:

kungfooman/seemore@25e565c

Co-authored-by: Martin Valigursky <59932779+mvaligursky@users.noreply.github.com>
@GSterbrant GSterbrant added the open source contribution Issues related to contributions from people outside the PlayCanvas team label Jun 21, 2023
Revert order in Entity#destroy
@kungfooman
Copy link
Collaborator Author

kungfooman commented Jun 21, 2023

Context for latest commit:

image

Output of this code in src/framework/components/model/component.js:

            this.removeModelFromLayers();
            const ents = this._model.getGraph().find(_ => _ instanceof Entity);
            if (ents.length) {
                console.log(ents);
            }
            this._model.getGraph().destroy();

Edit:

This looks like a potential other entity orphan trap:

if (this._node) {
if (this._node.parent)
this._node.parent.removeChild(this._node);
this._node = null;
}

(but I have no test scene right now to validate quickly)

Copy link
Contributor

@mvaligursky mvaligursky left a comment

Choose a reason for hiding this comment

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

Approving, looks great to me, I don't see any problems now. Thanks!

Co-authored-by: Will Eastcott <will@playcanvas.com>
Co-authored-by: Will Eastcott <will@playcanvas.com>
@mvaligursky mvaligursky self-assigned this Jun 22, 2023
@mvaligursky mvaligursky merged commit b437a40 into playcanvas:main Jun 22, 2023
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
open source contribution Issues related to contributions from people outside the PlayCanvas team
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Seemore double renders when destroying all the entities and loading the same scene hierarchy again
5 participants