Shader hotloading broken - newLibraryWithData on metallib returns cached not new metallib

This breaks shader hotloading and has been a persistent bug in Metal for the past many years. Metal holds onto some existing lib, returns it, without checking that the data content has changed. Similar bugs happen with Metal's shader cache not checking modification timestamps.

In my case, I'm just changing a color in the shader from float3(1,0,0) to float3(1,1,0) and then never seeing the result of the shader change. The new metallib is loaded from disk, and handed off to newLibraryWithData.

I can tell that it's returning a cached metallib, because we set a label on the MTLFunction that is returned. That's not nil on the first load of the shader, and after the hotload of the new metallib the label is non-nil. So we just see the old shader content.

This is a very important Radar to fix.

Edit: That's nil on first load of the shader, ...

This is on macOS Big Sur 11.2.3, but was broken 2 or 3 macOS ago when I first hit this. I think the workaround is to somehow load the library from memory, but I thought that's what newLibraryWithData did.

Hey Alecazam,

newLibraryWithData is supposed to create a MTLLibrary as if you were loading a .metallib file, but from a NSData object rather than the contents of a file. It's typically used by an app that would like to pack the .metallib along with other binary data in some app proprietary asset file.

I'm not actually sure any one has reported this bug regarding the behavior you've seen. It would be helpful if you created report with Feedback Assistant. It would be especially helpful if you were able to provide an Xcode project building an app that reproduces the problem.

Our hotloading, for better or worse, builds a metallib per vert/frag file. Then when that file changes or any dependencies, a new metallib is built and we load that one. The problem is that we hand this data off to Metal and it doesn't run it.

Previously, I saw this problem even with a single metallib containing all the shaders. The Metal shader cache doesn't test for modification date on the library, or do any hash tests on the shader content. So it sees the same named library, and returns the old version not the new one. So then the old shaders are return over and over again. There is already a Radar on this issue.

I'll try to build a sample app that demonstrates this. Not being able to hotload shaders is quite limiting and unique to the Metal api.

Hey Alecazam, thanks for surfacing this. We are aware of this limitation and are tracking it. If you would like updates on the progress of this - please file an feedback request.

Yes, this makes it hard to visually and performance tune games. Here's the feedback link. https://feedbackassistant.apple.com/feedback/9149476

So I did finally get time to write a test case and newLibraryWithData did indeed hotload. In the engine, it looks like the render/compute pipelines were not reset to point to the new MTLFunction. That's why the old shaders were used despite recompiling and replacing them with new MTLFunction objects.

The URL load path should still be fixed to test modstamp and/or hash of each shader. And some path to update the shaders on existing render/compute pipelines would be helpful. Maybe this will save others some pain. I also have hotloading in my ktx/ktx2 tool called kram. Shader hotloading should be a pervasive part of all Apple demos, but I never see it used anywhere.

There's still the issue on iOS of where we can reload a new metallib from in development builds for the hotload. I'm assuming we have some access to a Downloads or public folder where the metallib can be copied into.

Not sure about iOS, but even newLibraryWithData and hotloading don't work on OSX. This, seriously, is pretty annoying, Apple.

Shader hotloading broken - newLibraryWithData on metallib returns cached not new metallib
 
 
Q