GameFoundry / bsf

Modern C++14 library for the development of real-time graphical applications
https://www.bsframework.io
MIT License
1.74k stars 195 forks source link

Scene Visibility/culling #140

Closed bmsq closed 6 years ago

bmsq commented 6 years ago

This framework looks great and I'm considering switching from my own engine.

I've got a graph of sectors and portals that I can use to quickly cull renderables based on camera positions. What are the possible approaches for integrating this culling with bsf?

BearishSun commented 6 years ago

Addition of portals would certainly be a nice thing to have.

Currently culling happens in RendererViewGroup::determineVisibility. Its only doing frustum culling with no spatial acceleration structures at the moment. That would be the place to enhance with any additional culling logic.

You'd also need to extend the scene graph with new component(s)/scene actors(s) so users can define the portals/sectors. You can take a look at CSkybox component, and see how its implemented and how it passes its data along all the way to the renderer - you'd need something similar for portals/sectors.

Let me know if you need more details.

bmsq commented 6 years ago

Thanks for the direction. Let's see if I understand both your suggestion and the RenderBeast plugin:

My existing portal engine has a precalculated portal/sector graph and a BSP tree where the leaves represent sector IDs.

I could add "sectorID" to CullInfo and update RendererScene.registerRenderable and RendererScene.updateRenderable to calculate CullInfo.sectorID by walking my BSP tree with the renderable's current position.

During RendererView.calculateVisibility, I could use the current view position and frustrum to walk my portal/sector graph and identify the visible sectors. The list of visible sector ids would then be compared to the CullInfo vector to update visibility.

If I wanted to expose portals as components (e.g. enable game logic to activate/deactivate portals) I'd need to add new core/simulation objects and add register them with the renderer.

The culling changes could be made by subclassing RenderBeast, therefore keeping my changes separate and avoiding the need to update bsf. Is there anyway to register/update/unregister new components with a custom rendererer without needing to update the Renderer interface in bsfCore?

BearishSun commented 6 years ago

Your understanding of the system is good, as is your integration plan.

This is a small thing but I'd wouldn't store sectorId in CullInfo. CullInfo is kept small on purpose, so it can be quickly iterated over without wasting any space in the CPU cache. In case users aren't using the portal system, sector ID will just take up space, slowing down access and normal frustum culling. So I'd keep it in a separate array.

Ideally the BSP/sector logic would not be active until the user actually registers a portal (or explicitly enables this functionality somewhere). So we don't end up paying for it in case the user doesn't use portals.

There is currently no way to register custom types with Renderable, but I wouldn't be opposed to adding a set of methods that accept some overridable type.

bmsq commented 6 years ago

This is great, thanks for your help. I've only spent a couple of hours looking at the docs and browsing the code but I really like what I've seen. bsf is an impressive body of work.

What do you think about introducing a new visibility/culling subsystem to core and moving the frustrum culling logic out of RenderBeast and into it's own plugin? This way a user could introduce new spacial partitioning strategies without needing to touch the renderer.

I don't get much time to work on my hobby projects like game development but, if you're in no rush, I'd be happy to expend some of my limited spare time to experimenting with bsf and this idea. If anything comes of it, I'll raise new issues and or pull requests.

Given the discussion so far has been about my specific needs, I'll mark this issue as done. Thanks!

BearishSun commented 6 years ago

Performance is very important within that part of RenderBeast, and having generalized/abstracted solutions usually means you need to give up performance in turn, so that worries me in regards to extensible visibility/culling system. If it can be done in a way so it affects performance in an absolutely minimal way then sure.

I wouldn't make it a plugin though (i.e. a separate dynamic library), as I am stepping away from the plugin based approach myself. The main benefit of the current plugin system is that code is kept separate and modular, and that can be achieved through compile-time means without the associated runtime costs.

So perhaps just a class you can override, with a basic set of implementations (like frustum culling) built into the core. But of course, as long as my first requirement is satisfied.

bmsq commented 6 years ago

I understand your concerns regarding performance. I don't know the the code base well enough to understand the areas which are called frequently vs those that are not.

The current frustrum culling process is initiated per renderer view right? If the culling API is designed to be kept at this level (rather than per renderable), call overhead could be limited and cache coherence could be maintained by the given implementation. No point carrying portal specific sectorID baggage on CullInfo if it's not used right? I can see lights go down a similar path, is this where performance could become a concern?

When i mentioned plugin, I was more referring to the separation between core interfaces and implementation. From a pure design perspective, rendering and spatial culling seem orthogonal to me. However, I can appreciate that the realities of performance may make this untenable. I certainly hadn't really thought of the performance implications of dynamic vs static linking. Following the "don't pay for what you don't use" mantra, I can't see why you would want to pay the price for being able to swap spatial partitioning algorithms at runtime :p.

Keen to hear your thoughts, I've got an itch to scratch but wouldn't want to spend time on something that is doomed to failure before I even start..

BearishSun commented 6 years ago

Culling system is per-view, and if that is maintained the calls to the abstracted culling system should not be a performance concern. Sharing data between the renderer & culling systems could be another problem area, but I think it can probably be solved in a decent way as well.

In short, I think it should be doable, and it will be nice to have culling in its own black box :)

Light culling is also performance critical, as will be the particle systems and decals that are being added with the next update (something to maybe keep in mind, to make sure it can handle various obj. types - but ultimately they all boil down to bounds).