The render hardware interface
A backend-abstract GPU service
The render hardware interface is Platform's GPU service: a backend-abstract API for creating GPU resources and submitting draw work, with Metal behind it today. It is raster- and draw-focused, the surface Kinogaki UI's 2-D canvas is built on, and it holds Metal entirely behind the interface so the rest of the stack talks to the RHI alone.
#include "kinogaki/platform/Rhi.h"
using namespace kinogaki::platform::rhi;
auto device = Device::create(Backend::Metal); // or Device::createHeadless()
auto buffer = device->newBuffer(vertices, bytes);
auto texture = device->newRenderTexture(w, h, PixelFormat::RGBA8Unorm);
auto shader = device->newShader(mslSource);
auto pipeline = device->newPipeline({shader, /* vertex layout, blend … */});
The resource set
The interface covers exactly what a 2-D drawer and a viewport need:
- Device: creates everything; real (from a window context) or headless (for tests).
- Buffers & textures: vertex/index/uniform buffers, sampled and render textures.
- Shaders & pipeline state: compile MSL, bake a pipeline (shader + vertex layout + blend).
- Encoders & passes: record draw commands into a command buffer for an offscreen texture or a window drawable.
- Readback:
readbackRGBApulls pixels back to the CPU.
Pipelining, the right way
Responsiveness depends on letting the CPU run ahead of the GPU each frame. The RHI commits work and keeps going during interaction, and blocks at just two boundaries: an explicit readback and present.
endsubmits a pass withwait = false, so live frames pipeline and pacing comes from drawable acquisition rather than a per-pass stall.readbackRGBAdoes the onewaitUntilCompletedit needs (same-queue ordering covers earlier offscreen passes), so a--shotrender stays correct.
This pattern removed a per-frame CPU↔GPU stall that used to make panning lag: the CPU blocks only at the two boundaries that need it. The per-region offscreen buffers in the UI toolkit are RHI render textures used exactly this way: render a panel once, then re-composite its texture.
Headless = testable
Device::createHeadless() gives a windowless GPU device, so rendering is covered by automated tests: render an offscreen triangle, read it back, assert on the pixels, all on a headless machine. That is why the drawing stack ships with real GPU tests that verify exact pixels. The RHI stays separate from any compute-focused render engine: a tracer creates its own device abstraction sharing the same MTLDevice, so two abstractions ride one piece of hardware. The other cross-cutting infrastructure Platform carries is the buses.