How do game engine architectures (not in the game code) organize their different subsystems? Currently, I use a "hybrid" of the Singleton and Service Locator design pattern.
-
Engine
- InputManager
- ...
-
RenderingManager
- RenderingStateManager (encapsulates common blend, depth-stencil, rasterizer and sampler states)
- RenderingOutputManager (encapsulates the main output resources and their views)
- Renderer (encapsulates the rendering passes)
- SwapChain (could be a manager of SwapChains as well in case of multiple windows)
My Engine class can only be instantiated once. Upon instantiation, it constructs all managers responsible for the different subsystems (input, rendering, resources, etc.). These managers can be accessed by invoking getters on the Engine object. To facilitate accessing the managers further, I add a class method to each manager class which internally will invoke the corresponding getter on the singleton Engine object. That way, I can just write code as RenderingManager::Get()->GetDisplayConfiguration() for example, and I do not need to include the engine header every time. Unfortunately, this seems a bit dual: you can create as many managers as you want (not that I create more than one), but there is one manager that has a privileged way of accessing it.
Furthermore, I am not really happy with this approach since you eventually always fall back to the engine itself by including the engine header inside the managers' .cpp files. So I rather want to decouple the first-layer of managers completely from the engine itself into a separate static library each and make each manager a singleton (just like I did with the engine).
The benefit of making these managers singletons is not having to pass pointers around (note that it will never be the case that you have to pass m pointers n levels deep). It allows me to just create zero-argument factory functions such as CreateWhiteTexture(). On the one hand less there is less boiler plate code. On the other hand this makes it harder to spot dependencies or know whether a function is pure.
Any thoughts and suggestions on how it is commonly done?