Long time no post... I've done some profiling recently, and now I'm implementing point light lists, which is the last missing feature before we can make "realistic" performance comparisons.
The basic issue is that MW environments contain a ton of lights, so to apply them efficiently you need to check which lights could possibly affect any given mesh before you render it.
Ogre did this automatically for us - for better or worse, as it wasn't very efficient doing so.
OSG on the other hand while not offering this feature, gives us all the tools we need to implement it.
I have a proof of concept using push/popStateSet from a CullCallback, meaning we can inject state without actually modifying the scene graph. The CullCallback has access to the current modelview matrix and the node bounds, so from there we can determine what lights need to be active. For example:
- Get the list of all affecting lights
- If there's more than MAX_LIGHTS, sort by proximity to the camera
- Take the lights up to MAX_LIGHTS and sort them by their light IDs (this ensures a consistent ordering, so we don't create more than one StateSet for permutations of the same light list)
From there we could look at some optimizations, for example pre-culling the global light list by the camera frustum. Or reusing a similar enough StateSet when appropriate, e.g. one containing all the requested lights plus some extra ones. I'm sure keeping the unneeded lights enabled is cheaper than changing state again. Or skipping the callback if the scene currently has less than MAX_LIGHTS.
We also need to think about handling multiple cameras. Currently we have one camera only, but later on cameras for
shadow mapping and reflection mapping will be added. For
shadow mapping no point lights are needed, so this stage can skip the callback all together. Reflection mapping however needs it. We should be able to reuse at least partially the light lists from the scene pass. "reuse" may not be an appropriate word since
OSG can run multiple CullVisitors in parallel when multiple cameras are used, so we'd need some form of synchronization. That's a bit further down the line though, I'll start looking at shadows and reflections once the game is fully playable.
Chris wrote:I don't think we want to do it for every drawable. One of the supposed benefits of OSG is that it can better sort drawables by GL state to avoid unnecessary state changes. But if each drawable has a unique set of lights, thus unique state that's changing on every draw call, that would defeat the purpose.
But we need it per drawable in some cases. That is exactly the problem with the StaticGeometry approach in the Ogre branch. If you define a fixed region size we'll have the blinking lights back. I don't think anyone wants that. A more dynamic approach would be possible though. Applying a few heuristics (e.g. overall number of lights in the scene, bounds of the current node) we can probably attach the callback to a Node in some cases instead of the Drawable. A very small mesh consisting of multiple sub-meshes wouldn't need a fine grained light list.
I'm not sure if that's worth the effort though. I found that if pushing the same StateSet pointer,
OSG is smart enough to not change any state. So for drawables with the same light list, in theory no light state changes need to be made, and the state tracking shouldn't have a big overhead given that all it needs to do is compare the StateSet pointers. I haven't tested though how that interacts with state sorting.
Oh, and before Chris mentions deferred shading again: yes, that is a solution too, but it has implications on the renderer architecture that I do not want to commit on right now. For one, implementing vertex lighting would be impossible, which is necessary for vanilla assets compatibility (don't laugh, it really is - they're using negative lights in some corners to achieve ambient occlusion, but with pixel lighting that just looks like a black spot. That, and some lights at the ghostgate). Second, I highly doubt performance would be better, given the bandwidth requirements.