I am currently designing the scripting side of our engine. The idea is an entity-component system. That means most of the coding is done via jobs.
The language we are using is compatible to C. The actual implementation does not matter for this question. Basically, the application - written in C++ - calls the entry point for our scripting environment. User code is added to this environment, generating one library for client code and our code. Via some engine magic we then make the assembly aware of the jobs that were written by users. So far so good.
The design decision we are currently struggling with is how the scripting interacts with the renderer.
For that, a short brief of our architecture:
- Every module (Rendering, Audio, Physics) is laid out into dynamic libraries, so our scripting language can interact with them.
- Rendering is API-agnostic. Currently we have OpenGL and Dx12 in the making, OpenGL will get removed by Vulkan on the long run.
- The scripting language handles everything. Only the high-performance critical systems are C++ dynamic libraries.
That having said, when interacting with the renderer we have two concepts. We want to decide which one to implement, but are unsure. The techniques here are simplified, in the actual engine there are of course some managers etc. going on.
-
- Every mesh instance issues a drawcall once per frame.
- The renderer has a
draw
method - Requires fewer instructions
- Every mesh calls
draw
for themselves, once per frame - Does mean that some meshes will be drawn before other entities even had their logic running
-
- Every mesh instance issues a draw request once per frame.
- The renderer has a
render
method - The assembly calls
render
after all jobs is finished - The GPU can run every drawcall at once
- The GPU can only start rendering once the whole frame is finished on the assembly side.
Each of the systems has their pros and cons. Personally, system 2 makes the most sense to me as the renderer is fully responsible for rendering and only gets instructed. Furthermore, APIs like DX12 and Vulkan are already command-based, so this follows the API rules in some way.
Though I have seen many people do the drawcalls on a per mesh basis. There might be some advantages to that, but I feel like architecture 2 is more robust and scalable.