minetest / irrlicht

Minetest's fork of Irrlicht
Other
115 stars 87 forks source link

Optimize scene node child removal to constant time #275

Closed appgurueu closed 10 months ago

appgurueu commented 10 months ago

An attempt at fixing #274, briefly tested with Shadow Forest, but not profiled.

This relies on two invariants in removeChild:

You can experimentally verify that these seem to hold by replacing the body of removeChild with the less efficient:

ISceneNodeList::iterator it = Children.begin();
for (; it != Children.end(); ++it)
    if ((*it) == child)
    {
        assert(child->Parent == this);
        assert(it == child->Iterator);
        child->Iterator = std::nullopt;
        child->Parent = 0;
        child->drop();
        Children.erase(it);
        return true;
    }

assert(child->Parent != this);
return false;

The asserts should not be tripped.

lhofhansl commented 10 months ago

I know we discussed on the issue, but still wanted to ask:

  1. Do we need to keep the insert order?
  2. If not, can we perf-compare this with just using an unordered_set (assuming children are unique)?

(The idea is clever, btw. Just seems easy to accidentally break in the future.)

Desour commented 10 months ago

Tested, works.

appgurueu commented 10 months ago

Do we need to keep the insert order?

I'm not sure. I can imagine keeping the insertion order to be useful, for example when scene nodes need to be drawn in a particular order due to depth sorting. I don't know whether our current implementation relies on this however, since entities, particles, and mapblocks are all depth-sorted by Minetest.

If not, can we perf-compare this with just using an unordered_set (assuming children are unique)?

We probably could, but I'm not sure an unordered set is a good idea.

Pros:

Cons:

Just seems easy to accidentally break in the future.

As said on the issue, I believe this is well encapsulated by the add / remove child methods of ISceneNode. If you want further encapsulation, I can stick Children and ChildIterator in their own tiny "class" and make them private, only allowing access through methods which ensure consistency (though I feel that would be overengineered).

I also don't expect this (the children management in particular) to be touched often, looking at the history.