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).
Examplerlvk (kosmickrisp)rlmtl_mesarlmtl_mesa speedup
shapes_basic_shapes590°1793830.39x°
textures_tiled_drawing579°1511526.12x°
textures_particles_blending564°989617.54x°
shapes_starfield_effect584°1882432.23x°
models_loading48613760°28.33x°
models_heightmap_rendering487°1256425.78x°
models_skybox_rendering567°1578527.82x°
models_waving_cubes595°12372.08x°
models_first_person_maze530°13734°25.92x°
core_3d_camera_free563°1790531.78x°
shaders_basic_lighting562°902616.06x°
shaders_basic_pbr352491513.96x
shaders_mandelbrot_set554°41467.49x°
shaders_raymarching_rendering2502320.93x
performance_stress_test1451631.12x
performance_stress_test_direct441.03x
bench_idle567°14756°26.00x°
bench_drawcalls548°21513.93x°
bench_instanced505°819916.24x°

Median frame time, ms (lower is better)

Median per-frame CPU wall time.
Examplerlvk (kosmickrisp)rlmtl_mesarlmtl_mesa speedup
shapes_basic_shapes1.597°0.04139.05x°
textures_tiled_drawing1.598°0.04039.76x°
textures_particles_blending1.600°0.07122.66x°
shapes_starfield_effect1.598°0.04238.31x°
models_loading2.1190.033°65.00x°
models_heightmap_rendering1.611°0.04733.92x°
models_skybox_rendering1.599°0.03941.32x°
models_waving_cubes1.597°0.8031.99x°
models_first_person_maze1.614°0.035°46.11x°
core_3d_camera_free1.599°0.04139.29x°
shaders_basic_lighting1.599°0.08219.42x°
shaders_basic_pbr2.6670.17715.03x
shaders_mandelbrot_set1.603°0.2366.79x°
shaders_raymarching_rendering3.7954.0140.95x
performance_stress_test6.8735.9651.15x
performance_stress_test_direct236.909231.6061.02x
bench_idle1.650°0.031°52.56x°
bench_drawcalls1.681°0.4743.55x°
bench_instanced1.759°0.11415.39x°

Average frame time, ms (lower is better)

Mean per-frame wall time.
Examplerlvk (kosmickrisp)rlmtl_mesarlmtl_mesa speedup
shapes_basic_shapes1.694°0.05630.36x°
textures_tiled_drawing1.730°0.06626.13x°
textures_particles_blending1.787°0.10117.62x°
shapes_starfield_effect1.715°0.05332.26x°
models_loading2.2830.073°31.40x°
models_heightmap_rendering2.061°0.08025.87x°
models_skybox_rendering1.773°0.06327.97x°
models_waving_cubes1.681°0.8092.08x°
models_first_person_maze1.928°0.073°26.46x°
core_3d_camera_free1.776°0.05631.78x°
shaders_basic_lighting1.782°0.11116.01x°
shaders_basic_pbr2.9000.20414.24x
shaders_mandelbrot_set1.807°0.2417.49x°
shaders_raymarching_rendering4.0074.3060.93x
performance_stress_test6.8836.1471.12x
performance_stress_test_direct235.293228.2191.03x
bench_idle1.764°0.068°26.02x°
bench_drawcalls1.826°0.4653.92x°
bench_instanced1.996°0.12216.36x°

CPU utilization, %

Average process CPU as percent of the whole machine.
Examplerlvk (kosmickrisp)rlmtl_mesarlmtl_mesa speedup
shapes_basic_shapes3.213.50.24x
textures_tiled_drawing3.213.40.24x
textures_particles_blending3.116.60.19x
shapes_starfield_effect3.215.20.21x
models_loading3.010.00.30x
models_heightmap_rendering3.012.40.24x
models_skybox_rendering3.111.70.27x
models_waving_cubes9.011.60.78x
models_first_person_maze3.110.40.29x
core_3d_camera_free3.112.80.24x
shaders_basic_lighting3.914.90.26x
shaders_basic_pbr3.38.50.39x
shaders_mandelbrot_set4.28.40.49x
shaders_raymarching_rendering0.50.51.06x
performance_stress_test10.410.41.00x
performance_stress_test_direct0.00.00.93x
bench_idle4.09.30.43x
bench_drawcalls8.112.10.67x
bench_instanced3.716.80.22x

RAM, MB

Average working-set memory.
Examplerlvk (kosmickrisp)rlmtl_mesarlmtl_mesa speedup
shapes_basic_shapes93.583.81.12x
textures_tiled_drawing93.684.31.11x
textures_particles_blending93.483.91.11x
shapes_starfield_effect93.383.91.11x
models_loading108.299.61.09x
models_heightmap_rendering95.890.11.06x
models_skybox_rendering169.8161.91.05x
models_waving_cubes101.0100.71.00x
models_first_person_maze93.984.91.11x
core_3d_camera_free93.283.71.11x
shaders_basic_lighting111.5102.21.09x
shaders_basic_pbr148.3140.81.05x
shaders_mandelbrot_set111.7100.81.11x
shaders_raymarching_rendering126.7119.11.06x
performance_stress_test199.4164.11.21x
performance_stress_test_direct111.3102.91.08x
bench_idle93.083.41.11x
bench_drawcalls94.784.81.12x
bench_instanced111.6103.91.07x

GPU VRAM, MB

Average per-process video memory.
Examplerlvk (kosmickrisp)rlmtl_mesarlmtl_mesa speedup
shapes_basic_shapes0.00.0
textures_tiled_drawing0.00.0
textures_particles_blending0.00.0
shapes_starfield_effect0.00.0
models_loading0.00.0
models_heightmap_rendering0.00.0
models_skybox_rendering0.00.0
models_waving_cubes0.00.0
models_first_person_maze0.00.0
core_3d_camera_free0.00.0
shaders_basic_lighting0.00.0
shaders_basic_pbr0.00.0
shaders_mandelbrot_set0.00.0
shaders_raymarching_rendering0.00.0
performance_stress_test0.00.0
performance_stress_test_direct0.00.0
bench_idle0.00.0
bench_drawcalls0.00.0
bench_instanced0.00.0