The design focuses on:
- explicit control flow
- predictable timing
- clear separation of responsibilities
- identical behavior in runtime and editor
1. The Game Class
Every application using GFX-Next derives from the Game base class.
public class MyGame : Game
{
}
The Game class:
- owns the window
- owns the render device
- owns the asset manager
- defines the application lifecycle
- controls the main loop
It is intentionally thin and non-magical.
2. Application Lifecycle Overview
A typical application follows this order:
- Create window & render device
- Load assets
- Initialize GPU resources
- Initialize scene and systems
- Enter main loop
- Shutdown and dispose resources
This lifecycle is explicit and fully controlled by the user.
3. LoadContent: Asset Definition Phase
The LoadContent function allows you to load assets from the file system into the AssetManager. The AssetManager owns the assets and manages their lifetime and resources. Each asset should be handed over to the AssetManager. It also caches resources, meaning you do not need to load the same asset twice.
public override void LoadContent(AssetManager assets)
{
_cubeMesh = assets.Add<Mesh>("CubeMesh", Cube.GetMesh(material));
_model = assets.Load<StaticMeshModel>("model.gltf");
}
4. Asset Initialization Phase
The asset initialisation is happening within the game class itself. You won't do this yourself. However, this will show you what happens. Firstly, the AssetManager uploads all the CPU data from memory to the graphics device memory. This could be vertex data or texture data, for example. After uploading to the GPU memory, the CPU resources for the assets will be freed. This function is enabled by default. However, you can disable it using the FreeCPUResources property in your game class. Set this property to false to prevent the CPU memory from being freed. However, in most cases, you will want to free the CPU memory.
After LoadContent, GPU resources are created:
AssetManager.InitializeAssets(RenderDevice);
Optionally, CPU-side data may be freed:
AssetManager.FreeCPUResources();
5. Initialize: Scene & System Setup
Once the assets have been loaded into the GPU and are ready for use, you can initialise your game state in the 'Initialise' function. For example, this is the perfect place to call the Scene.Init() function. This ensures that all assets are ready for use.
public override void Initialize(IRenderDevice renderer)
{
Scene.Init(Viewport, renderer);
HUD.Init(renderer);
}
No assets should be created here.
6. OnStart: Final Setup Hook
The OnStart function is the final opportunity to perform actions before the loop begins. This function is usually unused, but if you need to perform an action after initialisation, this is where you should do it.
public override void OnStart()
{
}
Typical use cases:
- start animations
- perform sanity checks
7. The Game Loop
The main loop is fully explicit and is also called within the game class. It looks like this:
while (!Window.RequestClose())
{
Window.ProcessEvents();
Update(deltaTime);
Render();
RenderDevice.SwapBuffers();
}
There is:
- no hidden scheduler
- no task graph
- no implicit frame management
8. Update Phase
The update function allows you to manage the logic of your scenes. When you call the update function from your scene, all game elements and their associated behaviours are updated too.
public override void Update(float deltaTime)
{
Scene.UpdatePhysics(deltaTime);
Scene.Update(deltaTime);
}
Responsibilities:
- gameplay logic
- physics simulation
- input handling
- state changes
Important:
Scene structure must not be modified directly here
→ use the enqueue system.
9. Render Phase
In the render function, you can render the shadow pass and the scene itself, as well as the HUD and other elements, onto render targets. If you call the render function from the scene, all visible game elements will be rendered as well. As with the Update function, any attached behaviours will also call the Render function.
public override void Render()
{
Scene.RenderShadowMaps(Viewport, RenderDevice, Camera);
Scene.Render(Viewport, RenderDevice, Camera);
}
Responsibilities:
- shadow pass
- scene rendering
- post processing
- UI rendering
Rendering does not mutate game state.
10. Frame-End
The frame end function is called once the update and render functions have finished. This allows you to perform certain actions at the end of each frame. It is the perfect place to call Scene.EnqueElements(), which adds elements to the scene graph.
public override void OnFrameEnd()
{
Scene.EnqueElements();
}
This phase:
- applies enqueued scene changes
- synchronizes physics and rendering
- finalizes the frame state
This guarantees a clean starting state for the next frame.
11. Timing & Frame Control
The delta time is computed explicitly within the main loop of the Game class. You won't need to calculate this yourself.
float deltaTime = (float)(currentTime - lastTime);
GFX-Next does not enforce:
- fixed timestep
- variable timestep
- vsync policy
These decisions are left to the application.
12. Shutdown & Disposal
When the loop exits, the Game class will execute the following functions, which clean up the resources in the graphics device.
AssetManager.DisposeAssets(RenderDevice);
AssetManager.ClearAssets();
RenderDevice.Dispose();
Summary
The application structure in GFX-Next is built around:
- a minimal Game base class
- explicit lifecycle phases
- user-controlled game loop
- strict separation of concerns
- deterministic execution order
This design favors clarity and control over convenience.