Skip to content

Application

Generated from engine/app/application.h at 862e08b. The text under each declaration is the header's own comment, word for word. About the reference says how these pages are made.

Browse the app module

#include "engine/app/application.h" · namespace labrador

How many logical processors the machine reports, and never fewer than one.

THE SHELL ASKS THE MACHINE TWO QUESTIONS, AND THIS IS ONE OF THEM - the other is where the executable is (content_root.h). What this answer sizes is the thread pool and nothing else: the renderer's view capacity is a property of the layout (ApplicationOptions::view_capacity) and the partition count is a property of the work (Scene::draw). One constant answering all three conflates them.

What a game hands the shell before it opens a window. Everything here is a decision only the game can make - its title, the resolution it read out of its own save file - which is exactly why none of it is compiled into the engine (T1).

std::wstring window_class_name = L"LabradorWindowClass";
std::wstring window_title = L"Labrador";

The window class name has to be unique per process, so a game that ever opens two windows needs two of these.

ScreenResolution resolution = ScreenResolution::s_1280_720;
bool fullscreen = false;
int target_fps = 60;

The fixed step the simulation advances at. Rendering is not capped by it. Must convert to at least one StepTimer tick (at most 10,000,000 FPS).

int min_threads = 1;
int max_threads = default_thread_count();

The render thread pool's floor and ceiling.

The ceiling is a property of the MACHINE, so it defaults to what the machine reports rather than to a number written here. A fan-out wider than the box has hardware for is slower than not fanning out at all, and the box least able to absorb that mistake is the one this engine most wants to run on: a fixed sixteen would make four tasks plus a blocked submitter out of a four-view frame on a two-core part.

int view_capacity = 4;

The widest a frame may ever fan out, which is what sizes the renderer's per-view recording state - a backend that records into per-thread contexts has to make them before any frame starts.

A PROPERTY OF THE LAYOUT, NOT OF THE MACHINE, and taking it from a thread count is not merely untidy: every view costs a deferred context and a sprite batch's dynamic vertex buffer, built eagerly at create_device and rebuilt after every device restore, whether or not a frame ever uses it. A sixteen-thread default would build sixteen of them to draw one pane, out of the same memory the game runs in.

Four is four-player split-screen. A game drawing one pane says 1 and pays for one.

int min_window_width = 320;
int min_window_height = 200;

Smallest the user may drag the window; below this the swap chain is not worth resizing.

void validate() const;

Throws std::invalid_argument naming the field, rather than letting a bad number reach the code that divides by it. Application calls this before it opens a window.

max_threads reaches Partitioner as a divisor on every frame of every view, and target_fps reaches StepTimer as one, so a zero in either would be a hang or a crash on the first frame - and a game may read both from a save file it does not control. view_capacity is the same shape: create_device throws below 1, and throwing here instead names the field rather than the parameter.

class Application : public DeviceNotify, public StateContext, public WindowNotify

The machinery every game needs and no game should write: a window, a device, the services, the main loop, and the state stack. The game constructs it and hands it a first state (PHILOSOPHY, Structural types) - there is no IGame to implement and nothing here to subclass.

It is used in three steps, because the game has something to say between each pair:

Application app(options);
app.initialize(instance, show_command); // window, device, services
app.resource_loader()->register_kind(...); // the game's own asset kinds
app.load_manifest("./manifest.json");
return app.run(std::make_unique<MyFirstState>(...));

The first state is constructed last on purpose: states build drawables, and a drawable resolves handles against resources that do not exist until the manifest has been walked.

explicit Application(ApplicationOptions options);
~Application() override;
Application(const Application&) = delete;
Application& operator=(const Application&) = delete;
void initialize(HINSTANCE instance, int show_command);

Opens the window, creates the device, and builds the services. Throws std::runtime_error naming the step that failed (T6).

void load_manifest(const std::string& manifest_path);

Loads everything the manifest names. Register any game-specific kinds before calling this, or the walk throws naming the kind it does not know.

A RELATIVE PATH IS RELATIVE TO THE EXECUTABLE, not to the working directory, and a relative directory inside the manifest is relative to the manifest (engine/app/content_root.h says why). "./manifest.json" therefore means the file beside the game, wherever the game was started from. An absolute path is taken as it is.

int run(std::unique_ptr<State> first_state);

Takes the first state and runs until the window closes. Returns the process exit code.

void quit() const;

Closes the window, which ends run(). This is the whole of the quit path: a game asking to exit does not need to know it is on Win32.

THE LAYOUT SIZE FOLLOWS THE WINDOW. However the window comes to change size - either call below, or a user dragging an edge, which no game asked for at all - ResolutionManager is re-pointed at the new CLIENT size before the renderer resizes its back buffer. All three arrive as WM_SIZE, so on_window_size_changed is the single place that holds the invariant, and it cannot be got at from a game.

WITHOUT IT, NEITHER HOLDS. A set_fullscreen that restyles to WS_POPUP and shows maximized while the resolution manager still reports the last requested preset has ViewportManager lay out every viewport and divider for 1280x720 inside a 1440p back buffer, and the game renders into the top-left corner of its own window with the rest cleared black. A dragged window edge is the same door.

void set_resolution(ScreenResolution resolution);

Resizes the window so the game gets resolution pixels of CLIENT area, and re-points the resolution manager at what it actually got. The game decides which resolution and when - it is reading its own options menu - and the engine owns what that means to a window, frame included.

void set_fullscreen(bool fullscreen);

Switches between a borderless full-screen window and an ordinary one. Going full screen adopts the monitor's size; coming back adopts the resolution last requested, not the monitor size just vacated.

void reset_elapsed_time();

Tells the clock that the time just spent was not gameplay.

The step is fixed, so a long blocking call - loading a level, reading a save - is not one enormous dt. It is a backlog, and the next tick pays it off by running update() as many times as it takes, at full speed, on a world that has not been drawn yet. A client that swallows the first frame after a load instead pays for one frame and fixes nothing beyond it.

renderer · render_resources · audio_resources · resource_loader · resolution_manager · viewport_manager · thread_pool · partitioner · gamepads

Section titled “renderer · render_resources · audio_resources · resource_loader · resolution_manager · viewport_manager · thread_pool · partitioner · gamepads”
Renderer* renderer() const;
RenderResources* render_resources() const;
AudioResources* audio_resources() const;
ResourceLoader* resource_loader() const;
ResolutionManager* resolution_manager() const;
ViewportManager* viewport_manager() const;
ThreadPool* thread_pool() const;
const Partitioner* partitioner() const;
Gamepads* gamepads() const;

The services. Every one of these is null until initialize() has run - which is what ApplicationOptions is for: anything the game needs to say before there is a window to say it to belongs in the options, not in a call against a service that does not exist yet.

After that they are created once and never reseated, so an object may hold one for its whole life - device loss recreates GPU objects in place and leaves service identity alone (PHILOSOPHY, Services and lifetimes).

Borrowed, every one: the Application owns them and outlives the states it runs.

THAT SENTENCE IS TRUE BECAUSE ~Application DRAINS THE STACK FIRST. The states live in the StateContext base and every service below is a member, so member destruction would otherwise run first and each of these would be gone by the time the states holding them were destroyed. ~Application calls StateContext::clear() as its first statement; that is the whole of what makes a state safe to release a service in its destructor, which is the teardown pattern state_context.h documents.

The tidier arrangement is for StateContext to be a member declared last rather than a base, so that the ordering needs no first statement. It is not taken because it removes push/pop/transition_to /depth from every Application*, and PHILOSOPHY batches source breaks. ~StateContext is virtual only because it is inherited from.

Keyboard* keyboard() const;
Mouse* mouse() const;

The other two devices. Both are polled beside the pads, once per frame before any state updates, so every edge in the engine is measured across the same frame boundary whichever device it came from.

They differ from Gamepads in where their data comes from and in nothing a client can see: the pads are read from XInput on demand, while these are fed from the window's messages as they arrive (engine/input/keyboard.h says why that is forced rather than chosen). The question a game asks is the same shape for all three.

HWND window() const;

Named in the public declarations above: AudioResources, DeviceNotify, Gamepads, Keyboard, Mouse, Partitioner, RenderResources, Renderer, ResolutionManager, ResourceLoader, ScreenResolution, State, StateContext, ThreadPool, ViewportManager, WindowNotify.

The files that include this header directly. A file can also reach it through another header.

Development documentation (unreleased). Built from Labrador 862e08b of 2026-10-08.