SceneKit Metal shader compilation performance bug in iOS 14 workaround ideas

776 views Asked by At

I'm looking for a SceneKit expert to suggest some way to work around the bad performance bug in SceneKit's built-in Metal shader compilation in iOS 14.x and macOS Catalyst 11.x. Since I've tried everything I can think of to get Apple to fix the problem (TSI, Feedback, Twitter, Developer Forums, Apple Evangelist), we need a workaround.

Using the Xcode Instruments app with SceneKit tracing, it appears that SceneKit shader compilation can about 100x slower in iOS 14.0, 14.1 and 14.2 Beta (including Beta 4) vs. iOS 13.7. For example, it typically takes SceneKit 1-2ms to compile a shader in iOS 13.7 whereas it can take as much as 200ms in iOS 14.

SceneKit appears to automatically compile a shader each time we add a new model node tree to a parent node in a visible scene.

Loading a typical SceneKit scene of dozens of model nodes in our app can go from taking less than 2 seconds to well over 30 seconds. And since SceneKit shader compilation occurs on the render thread which which appears to be invoked by a CADisplayLink handler, the entire app UI can be completely frozen for many seconds (30+ in testing). This drastically affects the usability of our SceneKit-based app.

And if you consider that to get anything like 60-30 fps scene updating, you need to spend no more than 16-33 msec per frame to avoid frame drops, spending 200 msec each for many frames is really bad. (And if you're trying to get 120 fps on an iPad Pro...)

One issue with this is bug is that it's very inconsistent: I can do one run of the app with Instruments and the compile times will be fine (1-2 msec) and I'll do another few runs and it will be bad for each shader compile (100-200 msec). I think there is some Metal shader caching going on, but I don't know how it works and what causes it to be invalidated.

I've tried dozens of ways to work around this: multiple queues/threads/operations, transactions, deferring node adds to add multiple nodes at once, using perform (avoid: fatal crash bugs), etc. I've tried adding nodes with lights before all other nodes or after all other nodes.

We use PBR materials throughout our models and we have multiple lights with shadows enabled in most scenes (e.g. sun or moon outdoors, lamps indoors) along with environment lighting. Reflections are not enabled in SCNView non-AR mode, though apparently ARSceneView does enable them. We don't typically use any of our own shaders, though a few scenes use custom shader modifiers, which don't appear to have any effect when removed.

Any and all suggestions are greatly appreciated. Thanks!

Here is a sample scene screenshot and the shader compile times from Instruments 12.1 running the app on a iPad Pro 11 (2018) with iOS 14:

enter image description here

00:01.347.989   667.58 µs   Compile shader  
00:01.349.275   579.62 µs   Compile shader  
00:01.350.529   779.17 µs   Compile shader  
00:01.353.772   809.92 µs   Compile shader  
00:01.358.928   909.54 µs   Compile shader  
00:01.361.232   831.96 µs   Compile shader  
00:01.363.443   779.17 µs   Compile shader  
00:01.371.931     1.09 ms   Compile shader  
00:02.336.543   786.88 µs   Compile shader  
00:02.338.079   594.67 µs   Compile shader  
00:02.344.969   805.38 µs   Compile shader  
00:02.349.393   845.71 µs   Compile shader  
00:02.351.679   775.33 µs   Compile shader  
00:02.353.969   714.75 µs   Compile shader  
00:02.355.389   578.96 µs   Compile shader  
00:02.374.813   243.54 ms   Compile shader  
00:02.619.090   204.87 ms   Compile shader  
00:02.824.654   203.34 ms   Compile shader  
00:03.033.721   203.22 ms   Compile shader  
00:03.245.523   199.92 ms   Compile shader  
00:03.446.541   641.67 µs   Compile shader  
00:03.458.231   200.25 ms   Compile shader  
00:03.670.527   202.03 ms   Compile shader  
00:03.875.402   200.12 ms   Compile shader  
00:04.089.184   725.21 µs   Compile shader  
00:04.091.070   201.98 ms   Compile shader  
00:04.297.075   212.00 ms   Compile shader  
00:04.509.799   200.11 ms   Compile shader  
00:04.710.886   203.88 ms   Compile shader  
00:04.915.507   199.86 ms   Compile shader  
00:05.116.368   199.27 ms   Compile shader  
00:05.316.424   200.51 ms   Compile shader  
00:05.517.957   203.45 ms   Compile shader  
00:05.722.065   646.38 µs   Compile shader  
00:05.723.205   582.29 µs   Compile shader  
00:05.802.072   831.96 µs   Compile shader  
00:05.808.265   203.29 ms   Compile shader  
1

There are 1 answers

2
arthurschiller On

I've seen similar performance regressions in iOS 14. There are even complete freezes occurring on a SceneKit based ARKit project running in an App Clip. Have you heard about any improvements or workarounds recently?