Handmade Hero»Forums»Code
13 posts
On game as a service to platform layer (or other layers)
Edited by void on Reason: Initial post
I really like this design where the game is a service to the platform layer instead of the other way around as often done in other engines. I wonder if this architecture lends itself to other areas in games? I'm thinking what if the graphics or audio systems are also designed this way, pulling data from the game DLL what to render instead of the other way around? I imagine that would simplify supporting multiple different third-party APIs, since you can leverage everything in the low-level API directly instead of worrying about abstracting the lowest common denominator. What do you think? Would this design break down for more advanced techniques down the line?
Simon Anciaux
1337 posts
On game as a service to platform layer (or other layers)
Handmade hero already does something like that.

The audio function, GameGetSoundSamples, mixes the sounds and the win32 platform layer put them in the audio buffer.

If I remember correctly the graphics part doesn't do that because it needs to read the result of the rendering for certain steps to be able to continue (depth peeling and lighting I think). There might also be something with texture storage/transfer. But the game code in hmh call an intermediate layer instead of calling directly OpenGL so that it could be replaced for other platforms.

If your graphics requirements allow it, you could create a command buffer and return that to the platform layer and let it speak to the graphics api.
13 posts
On game as a service to platform layer (or other layers)
Edited by void on
For the graphics I had an even higher level of abstraction in mind where the renderer would ask for a complete scene description from the game and then each renderer implemention does with that in any way it chooses, able to utilize everything available in it's respective domain to render it. But I guess command buffers would allow something similar.

What I want to do is support a wide variety of APIs and techniques, say GL 4 and geometry+tesselation shaders in one implementation, a software rasterizer in another, and a GL 1.1 with fixed-function and immediate mode in third, and so on. Admittedly a dumb way to do it, I just find it interesting in a way as a learning exercise.
Simon Anciaux
1337 posts
On game as a service to platform layer (or other layers)
You can search for retain mode graphics api. People tried, but it was pretty bad (to my knowledge). Maybe it would work if it's tailored for your project/style instead of trying to be too general.
13 posts
On game as a service to platform layer (or other layers)
Thanks, but that will be basically impossible to search for given that it also refers to retained-mode openGL (as opposed to immediate mode openGL).
13 posts
On game as a service to platform layer (or other layers)
Edited by void on
I found a chapter (Scene Description Interface) in the Physically Based Rendering book that is exactly what I was trying to explain: the part about two main approaches to scene descriptions in which the first exposes internals of the rendering pipeline and the second where the scene description describes everything in high-level concepts. So seems this a pretty well established approach in some applications (notably offline rendering and physically-based rendering, apparently.) Cool.
511 posts
On game as a service to platform layer (or other layers)
At the end of the day you are going to need to go over your scene and issue render commands for each object you need to render. And the naive OOP-style of scene graph is pretty inefficient for doing so.

Retained-style guis has most of its advantages when you can cache calculations/render results. This is important when filling the screen takes a lot of time.

However nowadays computers are fast enough that you can afford to recalculate most of everything you need to render straight from your gamestate every frame. That doesn't mean immediate mode guis cannot cache anything. It's not as straight forward to manage that cache but still doable.
13 posts
On game as a service to platform layer (or other layers)
Edited by void on
@ratchetfreak I don't think retained vs immediate mode is what I'm talking about here, and not scene graphs either. From what I've gathered the terms are _imperative_ vs _declarative_ graphics API. The imperative style being where the renderer exposes low-level concepts like vertex/index buffers, pipeline states, etc. And the declarative being where the renderer is aware of things like materials, lights, meshes, etc. at a higher level. The chapter I linked to previously from the Physically Based Rendering book is exactly what I had in mind.

Declarative/imperative and immediate/retained are orthogonal concepts. You can have a declarative API being either retained or immediate (or both.)
13 posts
On game as a service to platform layer (or other layers)
To tie back to my original question, instead of doing this:

renderer.clear
renderer.begin_frame
renderer.set_state
renderer.push .. push.. push..
renderer.end_frame

It would be like this:

renderer.draw(scene)

where "scene" in this case is a declarative description of the scene to be drawn. Within the render draw call it would then retrieve the potentially-visible-set of everything in the scene from the current camera view point, and within that the list of models, lights, materials. Each renderer implementation is then free to implement this in any way. One could be physically based using ray tracing, another using fixed-function openGL as I mentioned. Or say if you have a Vulkan backend and a D3D12 backend you could implement everything in it's respective shading language, OR decide to use a common shading languange. The point is that the renderer implementation have the power to decide everything.

This "scene declaration" could even be a domain-specific scripting language with built-in programmable shaders that the renderer implementation then compiles into it's own variant, etc.

This design to me seems much more attractive than abstracting the renderer at a low-level. So the original question was if there is anything that would be problematic with this way of structuring it for interactive applications like games. I could also just write it and find out myself, but just in case anyone have any experience with this already I was curious to ask.
Mārtiņš Možeiko
2559 posts / 2 projects
On game as a service to platform layer (or other layers)
I feel like this approach will make your game rendering engine not very generic. It will be tailored for specific game use case and doing anything custom will be inefficient or a lot of work. Also I think that a lot of duplication will happen in renderer.draw for each backend that could be avoided.

In HH style low-level command buffer renderer the game-specific rendering logic happens in game layer. It is common for all backends and tailored for specific game, it is as efficient as game can make it to be - it knows everything about game.

It almost sounds like programming in low level vs high level language. High level language will require each backend (machine code emitter) to implement more or less same work, and it is harder in language itself to control or predict what will be generated. But in low level language you control much more what kind of code will be executed in application code itself.
13 posts
On game as a service to platform layer (or other layers)
Edited by void on
I feel like this approach will make your game rendering engine not very generic. It will be tailored for specific game use case and doing anything custom will be inefficient or a lot of work. Also I think that a lot of duplication will happen in renderer.draw for each backend that could be avoided.


I agree, but assuming the renderer has all the features you need, I think my approach could actually make everything more efficient? The hard part will be to identify most of those custom features in the first place. The duplication means it will be more work to introduce a new backend, true, but in terms of actual code there is nothing preventing each backend to use reuse some parts from a shared library.

In HH style low-level command buffer renderer the game-specific rendering logic happens in game layer. It is common for all backends and tailored for specific game, it is as efficient as game can make it to be - it knows everything about game.


This make a lot of sense when you are targeting modern hardware, where the underlying platform graphics API is more or less the same. But as soon as you need to target ancient hardware or legacy APIs or very specific rendering techniques (PBR), would that type of interface still work? I feel that the efficiency claim is no longer true since the backend would need to simulate it working like the other backends that are more tailored for that type of interface.


It almost sounds like programming in low level vs high level language.


If you ignore the part about me mentioning the scene being a scripting languange, it could also just be a plain struct of data, with plain arrays for every concept. So you would actually eliminate a lot of function calls between the game and the renderer, and each renderer is free to optimize to its heart's content. For example, it would be trivial to find out exactly what changed from one frame to the next.

But overall I agree with everything you said. I'm just not completely convinced yet this is a bad idea ;-)
13 posts
On game as a service to platform layer (or other layers)
mrmixer
Handmade hero already does something like that.
The audio function, GameGetSoundSamples, mixes the sounds and the win32 platform layer put them in the audio buffer.


Actually now that I think about it I think this is the wrong level of abstraction for what I'm suggesting here. In my approach the game would just provide a list of sound sources, their position and state and optional reverb effects. Each audio backend then takes that and implements it in the best possible way for the respective platform. It could mean just using openAL or FMOD if hardware mixing is available, or doing the software mixing yourself. Same idea, you place the abstraction at a high-level for each implementation to do whatever it chooses without the game having preconceived notion what that means in practice.