raylib performance — backend comparison
rlvk (kosmickrisp) vs rlmtl_mesa | GPU Apple M5 (24576 MB) | Driver KosmicKrisp Mesa 26.2.0 | OS macOS 26.5.1 | 3 runs × 10000 ms full speed
Green = best backend for that example/metric, red = worst. Frame time & FPS are the representative run; software (rlsw) has no GPU so its VRAM is ~0. Shader-heavy examples may not execute custom shaders on the software backend.
° = at that backend's floor (median within 15% of its own bench_idle median): the scene finishes its real work faster than one present/loop turnaround, so the number measures presentation pacing or loop overhead, not rendering cost, and cross-backend ratios there compare present plumbing. On macOS, Metal-backed Vulkan presents pace on drawable acquire (~1.6-1.8 ms) while GL's IOSurface flush has no floor - only rows without ° compare backend rendering cost.
Read this before comparing FPS on macOS. Uncapped FPS on light scenes here measures presentation, not rendering. Apple GL presents by flushing an IOSurface — no per-frame handshake, essentially free — while Vulkan on macOS (MoltenVK or KosmicKrisp) can only present through Metal, which must acquire a CAMetalDrawable from CoreAnimation each frame, paced at ~1.6–1.8 ms on a composited window no matter how little the frame draws. Measured on shapes_basic_shapes, uncapped: frame interval avg 1.95 ms, of which 1.76 ms (90%) is "Retrieve a CAMetalDrawable" (MoltenVK's own performance tracking; the drawing itself costs ~0.12 ms and the GPU is nearly idle). Every native Metal app pays the same pacing; no driver knob removes it (KosmicKrisp's is lowered from ~3.3 to ~1.6 ms by MESA_VK_WSI_PRESENT_MODE=mailbox, MoltenVK's config surface is exhausted). Cells marked ° sit at that backend's floor: their cross-backend ratios compare macOS present plumbing. Rows above the floor compare real rendering — there the Vulkan backend leads (e.g. 8000 draw calls: 120 fps GL vs 581 fps rlvk). Under vsync all backends lock to the display rate and the floor is invisible.
rlmtl_mesa = the native Metal backend + Mesa's shader compiler. This column runs rlmtl's stock pipeline (GLSL → shaderc → SPIR-V → SPIRV-Cross → MSL) on every scene except performance_stress_test_direct, whose fragment shader is precompiled offline by raylib/tools/nir2msl through Mesa's NIR→MSL compiler (the KosmicKrisp backend's kosmicomp, MIT) and injected via RLMTL_MSL_OVERRIDE. On that scene SPIRV-Cross's flattened-SSA output defeats Metal's optimizer; Mesa's NIR pipeline is the only automatic GLSL translation measured faster than Apple's own GL compiler — 231.6 ±14 vs 247.4 ±9 ms over 10 interleaved cycles (2026-08-18), ahead of hand-written MSL (250), ANGLE (281), Slang (289), naga (322) and stock SPIRV-Cross (442). Pixel drift vs SPIRV-Cross is ULP-class only (84% of differing channels off by 1, max 17/255; Mesa lowers sin/cos to conformant polynomials).
speedup = rlmtl_mesa relative to rlvk (kosmickrisp), per metric direction (>1x favors rlmtl_mesa_macos_apple/bench_instanced/run_16.rini); ° on a ratio means at least one side sits at its floor, so it compares present plumbing.
Frames per second (higher is better)
Sustained FPS at full speed (uncapped).| Example | rlvk (kosmickrisp) | rlmtl_mesa | rlmtl_mesa speedup |
|---|
| shapes_basic_shapes | 590° | 17938 | 30.39x° |
| textures_tiled_drawing | 579° | 15115 | 26.12x° |
| textures_particles_blending | 564° | 9896 | 17.54x° |
| shapes_starfield_effect | 584° | 18824 | 32.23x° |
| models_loading | 486 | 13760° | 28.33x° |
| models_heightmap_rendering | 487° | 12564 | 25.78x° |
| models_skybox_rendering | 567° | 15785 | 27.82x° |
| models_waving_cubes | 595° | 1237 | 2.08x° |
| models_first_person_maze | 530° | 13734° | 25.92x° |
| core_3d_camera_free | 563° | 17905 | 31.78x° |
| shaders_basic_lighting | 562° | 9026 | 16.06x° |
| shaders_basic_pbr | 352 | 4915 | 13.96x |
| shaders_mandelbrot_set | 554° | 4146 | 7.49x° |
| shaders_raymarching_rendering | 250 | 232 | 0.93x |
| performance_stress_test | 145 | 163 | 1.12x |
| performance_stress_test_direct | 4 | 4 | 1.03x |
| bench_idle | 567° | 14756° | 26.00x° |
| bench_drawcalls | 548° | 2151 | 3.93x° |
| bench_instanced | 505° | 8199 | 16.24x° |
Median frame time, ms (lower is better)
Median per-frame CPU wall time.| Example | rlvk (kosmickrisp) | rlmtl_mesa | rlmtl_mesa speedup |
|---|
| shapes_basic_shapes | 1.597° | 0.041 | 39.05x° |
| textures_tiled_drawing | 1.598° | 0.040 | 39.76x° |
| textures_particles_blending | 1.600° | 0.071 | 22.66x° |
| shapes_starfield_effect | 1.598° | 0.042 | 38.31x° |
| models_loading | 2.119 | 0.033° | 65.00x° |
| models_heightmap_rendering | 1.611° | 0.047 | 33.92x° |
| models_skybox_rendering | 1.599° | 0.039 | 41.32x° |
| models_waving_cubes | 1.597° | 0.803 | 1.99x° |
| models_first_person_maze | 1.614° | 0.035° | 46.11x° |
| core_3d_camera_free | 1.599° | 0.041 | 39.29x° |
| shaders_basic_lighting | 1.599° | 0.082 | 19.42x° |
| shaders_basic_pbr | 2.667 | 0.177 | 15.03x |
| shaders_mandelbrot_set | 1.603° | 0.236 | 6.79x° |
| shaders_raymarching_rendering | 3.795 | 4.014 | 0.95x |
| performance_stress_test | 6.873 | 5.965 | 1.15x |
| performance_stress_test_direct | 236.909 | 231.606 | 1.02x |
| bench_idle | 1.650° | 0.031° | 52.56x° |
| bench_drawcalls | 1.681° | 0.474 | 3.55x° |
| bench_instanced | 1.759° | 0.114 | 15.39x° |
Average frame time, ms (lower is better)
Mean per-frame wall time.| Example | rlvk (kosmickrisp) | rlmtl_mesa | rlmtl_mesa speedup |
|---|
| shapes_basic_shapes | 1.694° | 0.056 | 30.36x° |
| textures_tiled_drawing | 1.730° | 0.066 | 26.13x° |
| textures_particles_blending | 1.787° | 0.101 | 17.62x° |
| shapes_starfield_effect | 1.715° | 0.053 | 32.26x° |
| models_loading | 2.283 | 0.073° | 31.40x° |
| models_heightmap_rendering | 2.061° | 0.080 | 25.87x° |
| models_skybox_rendering | 1.773° | 0.063 | 27.97x° |
| models_waving_cubes | 1.681° | 0.809 | 2.08x° |
| models_first_person_maze | 1.928° | 0.073° | 26.46x° |
| core_3d_camera_free | 1.776° | 0.056 | 31.78x° |
| shaders_basic_lighting | 1.782° | 0.111 | 16.01x° |
| shaders_basic_pbr | 2.900 | 0.204 | 14.24x |
| shaders_mandelbrot_set | 1.807° | 0.241 | 7.49x° |
| shaders_raymarching_rendering | 4.007 | 4.306 | 0.93x |
| performance_stress_test | 6.883 | 6.147 | 1.12x |
| performance_stress_test_direct | 235.293 | 228.219 | 1.03x |
| bench_idle | 1.764° | 0.068° | 26.02x° |
| bench_drawcalls | 1.826° | 0.465 | 3.92x° |
| bench_instanced | 1.996° | 0.122 | 16.36x° |
CPU utilization, %
Average process CPU as percent of the whole machine.| Example | rlvk (kosmickrisp) | rlmtl_mesa | rlmtl_mesa speedup |
|---|
| shapes_basic_shapes | 3.2 | 13.5 | 0.24x |
| textures_tiled_drawing | 3.2 | 13.4 | 0.24x |
| textures_particles_blending | 3.1 | 16.6 | 0.19x |
| shapes_starfield_effect | 3.2 | 15.2 | 0.21x |
| models_loading | 3.0 | 10.0 | 0.30x |
| models_heightmap_rendering | 3.0 | 12.4 | 0.24x |
| models_skybox_rendering | 3.1 | 11.7 | 0.27x |
| models_waving_cubes | 9.0 | 11.6 | 0.78x |
| models_first_person_maze | 3.1 | 10.4 | 0.29x |
| core_3d_camera_free | 3.1 | 12.8 | 0.24x |
| shaders_basic_lighting | 3.9 | 14.9 | 0.26x |
| shaders_basic_pbr | 3.3 | 8.5 | 0.39x |
| shaders_mandelbrot_set | 4.2 | 8.4 | 0.49x |
| shaders_raymarching_rendering | 0.5 | 0.5 | 1.06x |
| performance_stress_test | 10.4 | 10.4 | 1.00x |
| performance_stress_test_direct | 0.0 | 0.0 | 0.93x |
| bench_idle | 4.0 | 9.3 | 0.43x |
| bench_drawcalls | 8.1 | 12.1 | 0.67x |
| bench_instanced | 3.7 | 16.8 | 0.22x |
RAM, MB
Average working-set memory.| Example | rlvk (kosmickrisp) | rlmtl_mesa | rlmtl_mesa speedup |
|---|
| shapes_basic_shapes | 93.5 | 83.8 | 1.12x |
| textures_tiled_drawing | 93.6 | 84.3 | 1.11x |
| textures_particles_blending | 93.4 | 83.9 | 1.11x |
| shapes_starfield_effect | 93.3 | 83.9 | 1.11x |
| models_loading | 108.2 | 99.6 | 1.09x |
| models_heightmap_rendering | 95.8 | 90.1 | 1.06x |
| models_skybox_rendering | 169.8 | 161.9 | 1.05x |
| models_waving_cubes | 101.0 | 100.7 | 1.00x |
| models_first_person_maze | 93.9 | 84.9 | 1.11x |
| core_3d_camera_free | 93.2 | 83.7 | 1.11x |
| shaders_basic_lighting | 111.5 | 102.2 | 1.09x |
| shaders_basic_pbr | 148.3 | 140.8 | 1.05x |
| shaders_mandelbrot_set | 111.7 | 100.8 | 1.11x |
| shaders_raymarching_rendering | 126.7 | 119.1 | 1.06x |
| performance_stress_test | 199.4 | 164.1 | 1.21x |
| performance_stress_test_direct | 111.3 | 102.9 | 1.08x |
| bench_idle | 93.0 | 83.4 | 1.11x |
| bench_drawcalls | 94.7 | 84.8 | 1.12x |
| bench_instanced | 111.6 | 103.9 | 1.07x |
GPU VRAM, MB
Average per-process video memory.| Example | rlvk (kosmickrisp) | rlmtl_mesa | rlmtl_mesa speedup |
|---|
| shapes_basic_shapes | 0.0 | 0.0 | — |
| textures_tiled_drawing | 0.0 | 0.0 | — |
| textures_particles_blending | 0.0 | 0.0 | — |
| shapes_starfield_effect | 0.0 | 0.0 | — |
| models_loading | 0.0 | 0.0 | — |
| models_heightmap_rendering | 0.0 | 0.0 | — |
| models_skybox_rendering | 0.0 | 0.0 | — |
| models_waving_cubes | 0.0 | 0.0 | — |
| models_first_person_maze | 0.0 | 0.0 | — |
| core_3d_camera_free | 0.0 | 0.0 | — |
| shaders_basic_lighting | 0.0 | 0.0 | — |
| shaders_basic_pbr | 0.0 | 0.0 | — |
| shaders_mandelbrot_set | 0.0 | 0.0 | — |
| shaders_raymarching_rendering | 0.0 | 0.0 | — |
| performance_stress_test | 0.0 | 0.0 | — |
| performance_stress_test_direct | 0.0 | 0.0 | — |
| bench_idle | 0.0 | 0.0 | — |
| bench_drawcalls | 0.0 | 0.0 | — |
| bench_instanced | 0.0 | 0.0 | — |