godotengine/godot
Servers and the multi-threaded wrapper
Purpose
servers/ collects the engine's thread-safe façades for heavyweight subsystems: rendering, physics (2D and 3D), audio, navigation (2D and 3D), display, text, and XR. Each server presents a public C++ API plus a WrapMT shim that funnels calls onto a dedicated thread when the engine runs in multi-threaded mode. This page describes the server pattern itself; one page per server covers the specifics.
Directory layout
servers/
├── audio/ AudioServer mixer + AudioStream playback infrastructure
├── camera/ CameraServer (webcam capture, used for AR)
├── debugger/ ServersDebugger client (for the editor remote debugger)
├── display/ (display server lives in display_server.cpp at this level)
├── movie_writer/ Headless movie capture (--write-movie)
├── navigation_2d/ NavigationServer2D
├── navigation_3d/ NavigationServer3D
├── physics_2d/ PhysicsServer2D (default backend in modules/godot_physics_2d)
├── physics_3d/ PhysicsServer3D (default in modules/godot_physics_3d, alt in modules/jolt_physics)
├── rendering/ RenderingServer + Forward+/Mobile/Compatibility methods + RD layer
├── text/ TextServerManager (active + secondary servers)
├── xr/ XRServer + XRPose, XRController, XRTracker
├── register_server_types.cpp / .h Registers all server classes with ClassDB
├── server_wrap_mt_common.h Macro toolkit for generating WrapMT wrappers
├── nav_heap.h Shared heap used by 2D/3D navigation
├── audio_server.h / .cpp Public AudioServer API (in audio/ as well)
├── physics_server_3d.h / .cpp Public PhysicsServer3D
├── physics_server_2d.h / .cpp Public PhysicsServer2D
├── rendering_server.h / .cpp Public RenderingServer
├── display_server.h / .cpp Public DisplayServer (impls live under platform/)
├── navigation_server_2d.h / .cpp
├── navigation_server_3d.h / .cpp
└── text_server.h / .cpp TextServer interface + TextServerExtension hookThe server pattern
The constraints that drive the pattern:
- GPU access must be single-threaded in many graphics APIs; the rendering thread is the only thread allowed to issue draw calls.
- Engine code is single-threaded by default for sanity; nodes call into rendering, physics, and audio from the main thread.
- Some subsystems run on their own threads (rendering and physics, in particular) for performance.
Solution: every server has
- A pure-virtual interface (e.g.,
RenderingServer). - A "default" implementation that owns the actual state and runs on the server's thread (e.g.,
RenderingServerDefaultfor rendering,GodotNavigationServer3Dfrom the navigation backend for navigation). - A
WrapMTwrapper generated by macros inservers/server_wrap_mt_common.h(or a hand-writtenRenderingServerWrapMT) that exposes the same API to callers but enqueues commands onto the server thread when running threaded, and forwards directly when running synchronously.
graph LR
Caller[Main thread<br/>scene/editor/script] -->|API call| WrapMT["RenderingServerWrapMT /<br/>NavigationServer*WrapMT"]
WrapMT -->|synchronous| Server[RenderingServerDefault<br/>(actual state)]
WrapMT -->|deferred command| Queue[Command queue]
Queue -->|on server thread| Server
Server --> Driver[Driver: Vulkan / D3D12 / Metal / GLES3]server_wrap_mt_common.h provides macros (FUNC0RC, FUNC1, FUNC2, FUNC3R, … with the suffix encoding "R" for "returns" and the digit being the parameter count) that expand to a switch between synchronous and queued execution. The header is ~29 KB of macro forwarding because servers expose dozens of methods.
RIDs as the lingua franca
Servers do not return raw pointers. They return RIDs — opaque 64-bit handles that the server can reverse-map to its internal state via RID_Owner<T>. Examples:
| API | RID points to |
|---|---|
RenderingServer::texture_2d_create(image) |
a Texture in the renderer's internal table |
RenderingServer::mesh_create() |
a Mesh storage |
RenderingServer::canvas_item_create() |
a 2D draw command list |
RenderingServer::instance_create() |
a 3D scene instance |
PhysicsServer3D::body_create() |
a rigid body simulated by Godot Physics or Jolt |
PhysicsServer3D::shape_create_box() |
a collision shape |
AudioServer::bus_* |
audio bus indices (these are int, not RID, but follow the same pattern) |
NavigationServer3D::map_create() |
a navigation map |
NavigationServer3D::region_create() |
a region within a map |
Because the data is owned by the server, RIDs survive across threads safely — the server lock-protects mutations, while clients only hold the handle. Many node types own one or more RIDs and forward setter calls to the server.
Initialization order
Servers come up in Main::setup2() in roughly this order:
- DisplayServer — created by the platform layer. Owns the window and the GL/Vulkan/D3D12/Metal context.
- RenderingServer — created with a backend selected by the rendering driver setting. Wrapped in
RenderingServerWrapMTifrendering/driver/threads/thread_model = Multi-Threaded. - AudioServer — picks an audio driver (ALSA, PulseAudio, WASAPI, CoreAudio, XAudio2, Web).
- PhysicsServer3D / PhysicsServer2D — chosen via
physics/3d/physics_engineandphysics/2d/physics_engineproject settings; default to the built-inGodot Physics. Jolt 3D backends ship with the engine and can be selected. - NavigationServer3D / NavigationServer2D — single backend.
- TextServer — set of TextServers managed by
TextServerManager. Default istext_server_adv(HarfBuzz + ICU + FreeType); fallbacktext_server_fbif advanced features are not needed. - XRServer — created unconditionally; XR interfaces (OpenXR, Mobile VR, WebXR) plug in later.
register_server_types.cpp performs the ClassDB registration of every server class, plus auxiliary types like XRPose, AudioEffect* and the RenderingDevice API surface.
Thread model nuances
- Rendering: in multi-threaded mode, every
RenderingServercall returns immediately. The server thread drains the command queue once per frame and flushes commands to the underlying driver; the engine waits atRenderingServer::sync()if the renderer is behind. - Physics: the physics simulation runs on its own thread when
physics/common/physics_thread_model = Single-Threadedis overridden. Most simulation work happens during_physics_process. - Audio: drivers run their own callback thread (
AudioDriver::*) and pull mixed buffers fromAudioServer. Mixing happens insideAudioServer::_driver_processon that thread. - Worker threads:
WorkerThreadPool(core/object/worker_thread_pool.cpp) is independent of the servers; it is used by resource loaders, navigation baking, and lightmappers.
RenderingDevice
RenderingDevice (servers/rendering/rendering_device.cpp, the largest file in servers/ at ~452 KB) is a vendor-neutral GPU API that sits below the RenderingServer. It is what the modern renderer_rd/ backends target, and is also exposed to scripting so users can write compute pipelines in GDScript/C# without dropping into shader source. It abstracts:
- Buffers, textures, samplers, render targets, framebuffers.
- Pipelines (graphics + compute), descriptor sets ("uniform sets"), push constants.
- Render passes with Vulkan-like semantics.
- Synchronization via
RenderingDeviceGraph(rendering_device_graph.cpp, ~160 KB), which builds a DAG of GPU commands and schedules barriers automatically.
The drivers under drivers/vulkan, drivers/d3d12, and drivers/metal implement the RenderingDeviceDriver C++ interface to back this API. See Drivers.
Key abstractions
| Abstraction | File | Role |
|---|---|---|
RenderingServer |
servers/rendering_server.h |
Public rendering API |
RenderingServerWrapMT |
servers/rendering_server_wrap_mt.h |
Threaded shim for the above |
RenderingDevice |
servers/rendering/rendering_device.h |
Vendor-neutral GPU device |
PhysicsServer3D / PhysicsServer2D |
servers/physics_server_3d.h, physics_server_2d.h |
Physics interfaces |
AudioServer |
servers/audio_server.h |
Audio mixer + bus graph |
DisplayServer |
servers/display_server.h |
Window + input from OS |
TextServer, TextServerManager, TextServerExtension |
servers/text_server.h |
Text shaping/breaking |
NavigationServer2D, NavigationServer3D |
servers/navigation_server_*.h |
Navigation maps and agents |
XRServer, XRInterface, XRPose, XRController |
servers/xr_server.h, xr/*.h |
XR origin, tracker, camera plumbing |
RID_Owner<T> |
core/templates/rid_owner.h |
Per-server RID-to-state mapping |
Entry points for modification
- Adding a new server method → declare on the public interface, add a
FUNC*line in the WrapMT header, and implement on the default backend. - Swapping a backend → set the relevant project setting (
rendering/renderer/rendering_method,physics/3d/physics_engine,audio/driver/driver, …) or pass the matching CLI flag. - Writing a new physics engine → see Physics servers; registration happens through
PhysicsServer3DManager/PhysicsServer2DManager. - Writing a new text server → subclass
TextServerExtension(or implementTextServerdirectly for a built-in module) and register it inregister_module_types.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.