minetest / irrlicht

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

Optimize scene node child removal to constant time #275

Closed appgurueu closed 8 months ago

appgurueu commented 9 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 9 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 9 months ago

Tested, works.

appgurueu commented 9 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.