With this PR we refactor `StringUtils::replaceAll` to accept `string_view` as suggested in a code comment. In addition, while we are touching this rebuild happy file, we slim it down a bit by moving a few sparingly used functions elsewhere.
This PR removes dummy serialisers for `StateSetUpdater`, `NodeCallback` and the respective `META` macros that trigger serialisation requirement here.
`StateSetUpdater` and `NodeCallback` are just base classes that can not be used on their own, so there is no need to incorporate them into serialisation. These changes might have minor effects on derived classes that forget to override `className()`, `libraryName()` through `META`, but it makes hardly a difference to now serialise such classes as a dysfunctional `osg::Callback` instead of a dysfunctional `SceneUtil::NodeCallback`.
We currently apply a strange algorithm to `LightManager::mStateSetCache`. For some reason this algorithm inserts hashed keys into `std::map` in a way that fails to handle hash collisions and exhibits worse lookup complexity than `std::unordered_map`. With this PR we just use `std::unordered_map` here.
With this PR we apply `lightMask` to a `Transform` node we create specifically for a light. This mask will allow us to stop traversing such nodes sooner and avoid costly processing associated with `Transform` nodes in the cull visitor.
With this PR we refactor `SceneUtil::KeyframeController` not to require `virtual osg::Callback` inheritance. I suppose such `virtual` overhead is not justified here because it negatively impacts many other classes we derive from `osg::Callback`.
This PR aims to spark the retirement of a questionable pattern I have found all over our code base. I will illustrate how this pattern encourages code duplication, lacks type safety, requires documentation and can be prone to bugs.
```
std::map<std::string, Object> mMap; // Stored in all lowercase for a case-insensitive lookup
std::string lowerKey = Misc::StringUtils::lowerCase(key);
mMap.emplace(lowerKey, object);
std::string lowerKey = Misc::StringUtils::lowerCase(key);
mMap.find(lowerKey);
mMap.find(key); // Not found. Oops!
```
An alternative approach produces no such issues.
```
std::unordered_map<std::string, Object, Misc::StringUtils::CiHash, Misc::StringUtils::CiEqual> mMap;
mMap.emplace(key, object);
mMap.find(key);
```
Of course, such an alternative will work for a `map` as well, but an `unordered_map` is generally preferable over a `map` with these changes because we have moved `lowerCase` into the comparison operator.
In this PR I have refactored `Animation::mNodeMap` accordingly. I have reviewed and adapted all direct and indirect usage of `Animation::mNodeMap` to ensure we do not change behaviour with this PR.
This PR fixes a crash caused by the improperly ensured lifetime of RigGeometry::mSourceGeometry. mSourceGeometry was not adequate to ensure mSourceGeometry would outlive mGeometry because we extend mGeometrys lifetime beyond this lifetime by passing mGeometry to the draw traversal instead of this.
In addition,
We add important comments.
We detect and prevent generally unsafe operations in high level code.
We add a sprinkling of const to help clarify intentions.
Currently, we use an `UnrefQueue` which supposedly aims to transfer destruction costs to another thread. The implications of this unusual pattern can not be well understood because some allocators might free resources more efficiently if they are freed by the same thread that allocated them. In addition, `UnrefQueue` complicates the validation of thread safety in our engine. Lastly, our current usage of `UnrefQueue` triggers `ref()`, `unref()` atomic operations as objects are passed into the queue. These operations could be more expensive than the actual destruction.
With this PR we thus remove `UnrefQueue`. We can expect these changes to have a minor impact at most because we free most resources elsewhere in `ResourceSystem::updateCache`.
This PR proposes a simple change to `RigGeometry` `dirtyGLObjects` logic.
1. We will avoid dirtying unmodified arrays.
2. We can drop an osg version guard since `Drawable::dirtyGLObjects` is not nearly as harmful as `Geometry::dirtyGLObjects`.
3. We will avoid crashes in an as yet unfinished future PR concerning `Array` sharing improvements.
osg::NodeVisitor is designed to recursively call virtual apply signatures until we find an implemented signature. Encountered nodes that we do not explicitely handle will trigger additional virtual function calls. With this PR we avoid these additional virtual function calls in the particularly costly ComputeLightSpaceBounds by adding some explicit signatures.
Currently, we always traverse the scene graph an additional time with a ComputeLightSpaceBounds visitor during shadow casting. ComputeLightSpaceBounds is only useful when the shadow casting mask allows us to shrink the bounds of the rendered scene, so we guard its traversal with a check against getCastsShadowTraversalMask. In practice, this guard never works because we build the traversal mask inclusively.
With this PR we limit the getCastsShadowTraversalMask check to relevant masks. This new check allows us to skip a superfluous ComputeLightSpaceBounds traversal with most settings.
We skip this during node path iterations. this is not a node we are interested in.
We avoid allocating a new mGeomToSkelMatrix per frame and avoid a ref_ptr associated with its update.
We speed up a search for the Skeleton node by adding a continue; condition prior to an expensive dynamic_cast.
We can expect marginally improved loading times with this PR. Drawable, Transform and Node counts in stats panels are expected to remain unchanged - this PR does not add new scene graph optimisations, it just increases the speed with which we apply existing ones.
1. We add explicit `NodeVisitor::apply` overrides for commonly encountered node types to avoid additional virtual function calls per node associated with the default `apply` implementation.
2. We skip pushing `StateSet`s when `_mergeAlphaBlending` is enabled or the `StateSet` contains no relevant state.
3. We add a specialised variant of `CollectLowestTransformsVisitor::addTransform` accepting `MatrixTransform` to avoid matrix copies and multiplications.
With this PR we optimise a function that is called quite often when loading new cells.
We remove avoidable dynamic_casts.
We remove an unused pair.second element.
We convert a map to an unordered_map because its ordering is irrelevant in this case.
We avoid adding the root Skeleton node to the bones' node path.
Currently, we create a new ComputeLightSpaceBounds visitor per frame. Within this visitor, we require excessive memory allocations, mainly a new osg::RefMatrix per encountered Transform node.
With this PR we reuse a single ComputeLightSpaceBounds visitor across frames and enable the createOrReuseMatrix functionality to avoid allocating new matrices every frame. osgUtil::CullVisitor internally uses the same approach.
Currently, we run culling tests against all lights in the scene during LightListCallback::pushLightState. We can avoid most of these tests by removing off-screen lights at an earlier stage. We should benchmark the cumulative time spent within LightListCallback::pushLightState before and after this PR.
`handle_stateset` is not needed because `UpdateMatrixTransform` is a `NodeCallback` only allowed to be set on a `Node`. `Geode` and `Drawable` do not need explicit logic because they are both derived from `Node`.