Lysa  0.0
Lysa 3D Engine
Engine context

lysa::ContextConfiguration and lysa::Lysa

Every Lysa application is anchored by two objects: a lysa::ContextConfiguration aggregate that describes engine-wide settings, and a lysa::Lysa instance that consumes it and initialises the graphics device, virtual filesystem, and event bus.

In this sample both live in Main.cpp. The configuration is declared at file scope so it can be mutated in lysaMain (to pick the graphics backend) before the engine is constructed:

import lysa;
lysa::ContextConfiguration contextConfiguration {
.displayFPS = true,
.logLevelMin = lysa::LogLevel::INFO,
},
};

Key fields of ContextConfiguration

Field Default Effect
backendConfiguration.backend VULKAN Graphics API (VULKAN or DIRECTX)
deltaTime 1.0/60.0 Fixed timestep for PHYSICS_PROCESS events
framesInFlight 2 Number of GPU frames in flight (double-buffering)
maxShadowMapsPerScene 20 Upper bound on active shadow maps
resourcesCapacity see below GPU pool sizes for meshes, images, materials
eventsReserveCapacity 100 Initial event-queue pre-allocation

resourcesCapacity defaults are generous for small scenes. For large worlds (hundreds of unique meshes, thousands of textures) you should raise meshes, images, and vertices to avoid pool exhaustion at runtime:

contextConfiguration.resourcesCapacity = {
.images = 2000,
.meshes = 5000,
.vertices = 5000 * 5 * 1000, // meshes * surfacesPerMesh * verticesPerSurface
};
Note
Refers to Resource Constraints for more explanations.

Accessing the context at runtime

After lysa::Lysa is constructed the singleton is reachable everywhere via lysa::ctx(). Common members you will use throughout an application:

// Subscribe to a global engine event
// Open a file through the virtual filesystem
auto stream = lysa::ctx().fs.openReadStream("app://res/config.json");
// Schedule a one-shot deferred operation on the next frame
lysa::ctx().defer.push([] { /* safe to run on the main GPU thread */ });
// Signal the main loop to exit
lysa::ctx().exit = true;

The app:// URI scheme maps to the working directory at startup. res:// resolves to the engine's own resource directory.

Initialisation order

lysa::Lysa must be the first member of the Application class because the window, camera, and scene all depend on the context it creates. C++ initialises members in declaration order, so the ordering in the class definition is sufficient:

class Application {
// ...
private:
lysa::Lysa lysa; // creates the engine context first
MainWindow window; // needs the context
Camera camera; // needs the window
std::unique_ptr<RotatingAssetScene> scene; // needs the camera
};

Entry point

Lysa provides its own platform main and calls lysaMain. The backend selection happens here, before Application is constructed, because backendConfiguration is read once during lysa::Lysa construction:

int lysaMain() {
if constexpr (vireo::getPlatform() == vireo::Platform::WINDOWS) {
contextConfiguration.backendConfiguration.backend = vireo::Backend::DIRECTX;
}
Application().run();
return 0;
}
Note
lysaMain uses if constexpr so the DirectX branch is compiled only on Windows. On Linux the Vulkan path is always used and no runtime check is needed.

Next : Rendering window