Enabling rain increases performance on Apple Silicon... wait, what?

Hi everyone,

TL;DR: I have heavy custom terrain shaders (ShaderMeshTerrain). On my Mac only, FPS jumps from 30 to 60 as soon as I enable my RainSystem (which should actually add GPU overhead, not remove it!). Rain OFF → 30 FPS; Rain ON → 60 FPS (with VSync enabled).

Recently I got my hands on a MacBook Air M4 and, surprisingly, I’m loving the macOS experience; this is my first time using a Mac and I used to be a die-hard Linux user (maybe I’m just getting old).

I’m currently working on a Panda3D project that has grown in ambition over time, and I’ve noticed a few weird behaviors when running on macOS. Apart from the lack of native Apple Silicon deploy targets (not a dealbreaker, though it would certainly be awesome to have!), I’m seeing OpenGL rendering quirks on macOS that do not happen on Windows or Linux. My gut feeling tells me it might have something to do with the underlying OpenGL-to-Metal translation layer, but I could be wrong.

Here is what’s happening. A bit of context first (sorry for the long read):

My game renders an island (ShaderMeshTerrain + Bullet heightfield) and an ocean. I rely heavily on custom GLSL code for terrain texturing and painting (TerrainSystem). Note that I have “gl-version 4 6” set in my Panda3D PRC configuration, which on Mac falls back to OpenGL 4.1 (Core Profile).

My terrain shaders perform a lot of operations and represent the heaviest GPU pass in my game (along with the ocean shader). It features a custom lit surface shader (not strictly PBR, tailored to my artistic direction): control-map blending, height blending, triplanar mapping, detail normals, stochastic tiling, ray-marched heightfield self-shadowing, LOD gates, etc.

I also implemented an internal 3D resolution scale mechanism (render_scale), which allows me to maintain 60 FPS at higher resolutions (like 4K) while looking crisp, thanks to a contrast-adaptive sharpening post-process pass. This lets me render internally at lower resolutions and save precious fragment shader work. (Maybe this interacts with the issue somehow?)

Here is the issue: my island has a central mountain where terrain shader blending work peaks. When the camera faces this mountain, FPS drops, and depending on settings/resolution, it can be quite dramatic. On the Mac, this is particularly noticeable: with maxed-out graphics settings and LODs disabled, looking at the mountain yields ~30 FPS (I can reach 60 FPS by tweaking settings, but I keep everything maxed for testing).

However, the moment I enable my custom RainSystem, the frame rate jumps back up to 60 FPS like magic.

This behavior occurs only on Apple Silicon (M4). On Windows and Linux, enabling rain behaves as expected: FPS drops slightly because rain has a small GPU cost.

My RainSystem is a GPU-instanced rain renderer built on point primitives expanded into wind-slanted line streaks through a Geometry Shader. Looking at the code, everything indicates it should add GPU load. Nothing in my logic explains why drawing extra geometry would increase performance.

From the tests I’ve conducted, I suspect it’s related to how macOS translates OpenGL to Metal. As far as I know, Metal doesn’t have native Geometry Shaders in the traditional OpenGL sense (or emulates them via compute/mesh shaders behind the scenes), so the translation layer might be doing some heavy lifting under the hood.

My theory is that introducing geometry shaders forces Metal into a different pipeline state or alters render pass batching/tiling behavior in a way that accidentally optimizes the terrain render pass, pushing FPS back up to 60. But I honestly have no idea how to verify or debug this on macOS.

Mac isn’t my primary platform (though I’ve been doing a lot of active development on it lately and would love to support it properly), but this behavior has sparked my curiosity.

Has anyone experienced something similar in Panda3D on Apple Silicon? Any ideas on what might be causing this or how to profile/inspect it?

Thanks in advance!

Same here, since lately I’m on mac laptop often. I found that for my use cases using an build of latest panda with moltenvk is the best.

This sounds like the classic graphics driver going into power-save mode because it detects the graphics as not being demanding enough to bother, and adding extra load kicks the auto-detect into performance mode.

That’s totally speculative though as my newest Mac is from 2013.

That actually makes a lot of sense. I hadn’t considered Apple Silicon’s aggressive GPU power-management and clock scaling states. It completely fits the symptoms: the heavy terrain alone might be triggering a low-power GPU state that throttles clocks and settles at 30 FPS, but adding the extra draw calls/geometry from the rain kicks the GPU scheduler into a high-performance power state, instantly pushing it to 60 FPS.

That raises an interesting question: is there any way in Panda3D, macOS settings, or via system flags to force Apple Silicon GPUs to stay in a high-performance state (or disable GPU power-saving mode for the application)?

1 Like

Try setting your mac to high power mode. Power Modes and Apple Silicon GPUs – The Eclectic Light Company

I would experiment with disabling vsync in panda etc

I ran some profiling on my MacBook Air M4 using powermetrics (sudo powermetrics --samplers gpu_power), and the GPU clock scaling logs confirmed exactly what’s happening under the hood.

In short:

Rain OFF (VSync ON): The heavy terrain shader alone doesn’t trigger macOS’s GPU scheduler to boost clocks, leaving the GPU idling in low-power states (P1/P2) at around ~840–1000 MHz. At these lower clock speeds, rendering the frame takes ~20 ms—just barely missing the 16.6 ms target for 60 FPS. Because VSync is active, macOS immediately snaps the frame rate down to 30 FPS.

Rain ON (VSync ON): Enabling the geometry shader forces the driver into high-performance states (P8/P9), instantly boosting the GPU clock to its maximum 1470 MHz (~9.4W power draw). At full clock speed, the GPU renders the entire frame (terrain + rain) in under 16.6 ms, allowing VSync to lock smoothly back to 60 FPS.

Disabling VSync: The problem becomes almost non-existent. Without VSync harshly snapping the framerate in half when missing the 16.6 ms window by a millisecond.

On Apple Silicon (and especially on a fanless MacBook Air M4), there doesn’t seem to be any native way or system toggle to override macOS’s GPU power-saving behavior or force high-performance GPU states for an application.

Thanks a lot to everyone who pointed me towards GPU power management, it was an interesting quirk to diagnose.

3 Likes