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!