CesiumJS completely BROKEN on Safari / macOS 27 beta — ANGLE Metal backend fails to link any shader with GLSL out/inout parameters

Summary

On macOS 27 beta, every CesiumJS scene fails to render in Safari. linkProgram fails on the very first frame with an MSL compilation error coming from ANGLE’s Metal backend. This isn’t a Cesium-specific shader bug — it’s a regression in how the system Metal compiler now handles the ANGLE_Out/ANGLE_InOut helper templates ANGLE generates for any GLSL function that takes an out or inout parameter. Since Cesium’s atmosphere and 3D Tiles styling stages both rely on out parameters, the globe never renders at all: black canvas, console full of link errors.

Given how much of the Cesium user base is on Mac, this is worth flagging loudly before macOS 27 ships. Filing here so other Mac users can pile on with +1 / their own environment, and so this thread can be linked from the WebKit/Apple bug report.

Environment

  • macOS 27.0 beta 2
  • Safari Version 27.0 (22625.1.24.11.2)
  • CesiumJS (all versions)
  • Reproduces on every scene tested so far, both Scene3DOnly globe (atmosphere) and 3D Tiles (CPU styling)

Two separate Cesium shaders confirmed broken

  1. Ground/sky atmosphere scattering (czm_computeGroundAtmosphereScattering) — the first error I hit.
  2. 3D Tiles CPU styling stage (cpuStylingStage, inout on position + feature struct) — fails identically even with the atmosphere disabled.

Because both failures are in the same linked program in practice (a normal 3D Tiles scene draws atmosphere and styled tiles together), there’s no scene-level workaround. Turning off globe.showGroundAtmosphere / scene.skyAtmosphere.show removes error #1 but not #2, since #2 fires for any styled/batched glTF regardless of atmosphere settings.

Error output (atmosphere shader)

RuntimeError: Program failed to link. Link log: Internal error while linking shader. MSL compilation error:
program_source:94:36: error: reference to type 'thread float3' (vector of 3 'float' values) could not bind to an lvalue of type '__metal_generic float3' (vector of 3 'float' values)
    operator thread T &() { return mTemp; }
                                   ^~~~~
program_source:483:89: note: in instantiation of member function 'ANGLE_Out::operator float thread & __attribute__((ext_vector_type(3)))' requested here
  _ucomputeAtmosphereScattering(ANGLE_userUniforms, _upositionWC.xyz, _ulightDirection, ANGLE_out(ANGLE_vertexOut._uv_rayleighColor), ANGLE_out(ANGLE_vertexOut._uv_mieColor), ANGLE_out(ANGLE_vertexOut._uv_opacity), ANGLE_out(ANGLE_vertexOut._uv_translucent));

Same pattern for float and for the tiles-styling inout struct case, with ANGLE_InOut instead of ANGLE_Out. Full translated MSL for both cases available on request — long, happy to paste or link a gist.

Root cause (as far as I can tell from the outside)

ANGLE’s MSL translator emits an RAII wrapper for every GLSL out/inout parameter, because MSL has no native equivalent:

template <typename T>
struct ANGLE_Out
{
    T mTemp;
    thread T &mDest;
    ~ANGLE_Out() { mDest = mTemp; }
    ANGLE_Out(thread T &dest) : mTemp(dest), mDest(dest) {}
    operator thread T &() { return mTemp; }   // <-- rejected on macOS 27 beta
};

The conversion operator’s implicit object parameter is now apparently deduced as __metal_generic instead of thread by the macOS 27 Metal compiler, so it can’t bind to the thread T& it needs to return. This affects scalars, vectors, and struct instantiations alike — it’s the template itself that’s broken, not any one call site. Since out/inout parameters are ordinary GLSL ES 3.00, this will hit essentially any nontrivial WebGL app on this backend, not just Cesium.

Minimal repro (no Cesium involved)

#version 300 es
out vec3 vColor;
void compute(out vec3 c) { c = vec3(0.5); }
void main() {
  compute(vColor);
  gl_Position = vec4(vColor, 1.0);
}

Linking a WebGL2 program with this vertex shader fails the same way on macOS 27 beta Safari. Passes everywhere else I’ve tested (macOS 26, Chrome/Firefox on the same beta machine — need more people to confirm the Chrome/Firefox result across different machines, since they ship their own ANGLE build).

Ask

  • Anyone else seeing this on macOS 27 beta Safari, please reply with your Safari build, whether the globe renders at all, and whether you see the same ANGLE_Out/ANGLE_InOut signature in your console.

Hi @Theo_Benazzi, thanks for the thorough write-up and the minimal repro. This is a great bug report.

This issue was reported to the CesiumJS GitHub repo as well: #13650. Apple fixed the ANGLE Metal compiler regression in Safari 27 Beta 5, which has been available since August 13th. Since you’re on Beta 2, updating to the latest beta should resolve it.

As you identified, this was an Apple-side regression in their Metal compiler’s handling of ANGLE’s ANGLE_Out/ANGLE_InOut templates, so no CesiumJS changes were needed.

If you can’t update right away, a partial workaround is to disable the atmosphere rendering that triggers the first shader failure:

viewer.scene.globe.showGroundAtmosphere = false;
viewer.scene.fog.enabled = false;

Though this may not help with the 3D Tiles CPU styling failure you also identified, so updating Safari may be the only complete fix.

Let us know if the latest beta resolves it for you!