Metal
Ray-traced sun shadows
On Apple silicon (macOS 13+), the sun's shadows are traced with GLSL ray queries in a pass of their own: one ray per pixel at half size from the depth buffer, read by the next frame through reprojection.
Technology
Kiln is the C++ engine under Shardbound: our own renderer on Metal and Vulkan, and the layers a game stands on. Here's what it does today, what it doesn't yet, and every number with the hardware it was measured on.
Shardbound began on raylib, which gave it a window, input, audio and an OpenGL 3.3 renderer. OpenGL is deprecated on macOS, frozen at 4.1 and translated to Metal by the system, and it shuts the door on modern upscalers, which need native Metal or Vulkan resources.
So we replaced raylib with Kiln, layer by layer. There is no OpenGL backend: Metal on macOS, Vulkan on Windows and Linux (and on macOS through MoltenVK, for testing). If the chosen API can't start, the other is tried.
The game kept its code. Kiln offers the roughly 145 raylib-shaped functions Shardbound used, built on the new layers, so the move didn't need the interface and screens rewritten. New code uses Kiln's own interfaces.
The stack
Each layer stands on a small, well-known library or on the graphics API itself.
platformWindow, events, keyboard, mouse and text input, time, files, DPI, fullscreenSDL3audioSounds, aliases, streamed musicminiaudiogfxBuffers, textures, render targets, shaders, pipelines, drawsMetal · Vulkan 1.2shadersGLSL to SPIR-V and Metal Shading Language at load time, with reflection and a disk cacheglslang · SPIRV-CrossdrawA 2D canvas: shapes, textures, gradients, scissor, blend modes, render-to-texture, textgfx · stb_truetypemodelglTF meshes, materials and skins; generated meshescgltfimageDecode, encode, resizestb_imageMSL from SPIRV-Cross, compiled at load. Three frames in flight; uploads and readbacks are blits ordered with the draws around them.
Dynamic rendering, VMA and volk. Two frames in flight; textures rest in a read-only layout between uses.
GLSL 330, as before the move. Loose uniforms are gathered into one block per stage and still set by name.
Rendering
Everything here is in Shardbound today.
Metal
On Apple silicon (macOS 13+), the sun's shadows are traced with GLSL ray queries in a pass of their own: one ray per pixel at half size from the depth buffer, read by the next frame through reprojection.
Metal · Vulkan
In the manner of FSR 2, on every backend: each output pixel is rebuilt from the render-size samples around it, weighted by where they really fell, and blended into history at the motion vectors.
Options
Apple's temporal upscaler where it exists. A spatial path in the manner of FSR 1: a Lanczos upscale clamped against ringing, then contrast-adaptive sharpening.
Default
An 8-step Halton jitter, per-pixel motion for the camera, characters, animation and wind, a Catmull-Rom history clipped by variance in YCoCg so moving things don't trail.
Shared
The hour comes from the server clock: a game day is two real hours. Weather drifts from clear to storm on the same clock, with cloud shadows, rain, wet ground and puddles that mirror the sky.
Scene
Waves from drifting noise, the bottom seen through them and fading with depth, screen-space reflections of the shore, glitter from the sun or moon, foam where it's shallow.
Characters
Pre-integrated subsurface scattering for skin. Hair lit along its strands with Kajiya-Kay and Marschner's three highlights, alpha-hashed cards a temporal resolve turns into soft strands.
Surfaces
Parallax occlusion with self-shadowing on the ground and on walls; on walls the relief cuts the silhouette, so edges follow the stones instead of a flat line.
Post
When the sun or moon is near the view, a quarter-resolution march through the scene's depth lets open sky through, so trees, towers and hills cut the light into beams.
Effects
Effects are data. Every particle lives in one pool of 12,000 reserved at start, so playing an effect never allocates, and each blend mode is one instanced draw.
Memory
Mip chains made on the CPU and block-compressed on every core (BC1, BC5 for normals), cached on disk. Character parts load as placeholders and become real the first time they're drawn.
Performance
Props are culled to what the camera and the light can see, with two simpler levels made at load by meshoptimizer.
Measured, not promised
An Apple M4 at 2646×1602, from Kiln's own benchmark and overlay.
Tools
A performance overlay shows frame time and the 1% low as frames actually reached the display, GPU time, CPU time by part, draw calls, memory and a graph of the last 240 frames. On request, the GPU's time splits by section of the frame, from the shadow map to the interface.
A benchmark mode settles in the world, averages a run and quits, so changes are compared on numbers. The client and server were leak-free at last check.
Editors
Dear ImGui drawn by Kiln, viewports and cameras, transform gizmos with snapping, one undo history, preferences with key rebinding, overlays on the ground, and file writes that can't leave a half file behind.
Shardbound's world editor is built on it. So is a small demo, the starting point for the next game's.
Planned, not started
The groundwork first: compute dispatch, storage buffers and indirect draws on both backends. Then meshes cut offline into clusters in a tree of simpler levels, each part of an object drawn at its own detail, and a compute pass that culls clusters against the view and last frame's depth.
It isn't the bottleneck yet, so it waits for the denser art that will need it.