| layout | page |
|---|---|
| title | Metal FAQ |
This page documents various odd Metal behaviors that I have not found anywhere else.
After raising my deployment target to iOS 14 (macOS 11, etc...) I got numerous new compile errors. These errors do not occur with the same compiler with a deployment target of iOS 13 (macOS 10.15, etc...)
It appears this is because various C99/C11 features were 'removed' (I gather they were never really supported) in Metal, which is based on C++, not on C. In particular, this affects restrict, which is not a valid C++ keyword.
I am advised this works as intended. For restrict anyway, the compiler keyword __restrict still compiles, although I don't know if it has any effect.
FB7831220
As of iOS 14, assert is defined as
#define assert(condition) ((void) 0)For this reason it has no effect.
If you want assert-like behavior in Metal, you can use
#define assert(X) if (__builtin_expect(!(X),0)) {float device *f = 0; *f = 0;}along with the "metal shader validation" diagnostic in Debug GPU-side errors in Metal. It will not trip without this diagnostic.
For a production-ready solution, stdmetal ships with SM_ASSERT and SM_PRECONDITION cross-platform macros.
FB7731230
This is usually caused by performing a metal capture programmatically during application launch. The workaround is to perform the capture "later", such as with .asyncAfter.
FB7741457
The best way I know of to do this is to concatenate .c files into a .metal file, and compile that.
- Create a 'run script' phase
-
COUNTER=0 rm -f "${SCRIPT_OUTPUT_FILE_0}" while [ $COUNTER -lt ${SCRIPT_INPUT_FILE_COUNT} ]; do tmp="SCRIPT_INPUT_FILE_$COUNTER" FILE=${!tmp} cat "$FILE" >> "${SCRIPT_OUTPUT_FILE_0}" let COUNTER=COUNTER+1 done
- Set the input files to all your
.cinput files. You need to keep them up to date, alterantively you can use a file list - Set the output files to your
.metalfile. After this file is built, drag into xcode and attach it to the right project
See blitcurveMetal.xcodeproj for an example.
The best way I know of to do this is to build a .a file, or a script to build one, and distribute that.
- Create a "run script" phase
-
metal-libtool -static ${TARGET_TEMP_DIR}/Metal/mylib.air -o ${BUILT_PRODUCTS_DIR}/libmylib.a
3. Set the "input files" to `${TARGET_TEMP_DIR}/Metal/mylib.air`
4. Set the "output files" to `${BUILT_PRODUCTS_DIR}/libmylib.a`
### If you are also doing this as part of a Swift package,
...nobody can use your xcodeproj directly, because the xcodeproj is readonly and you will get errors about "The file "project.pbxproj" could not be unlocked" (FB8095945)
Instead, you need to create a build script that calls xcodebuild:
```sh
#!/bin/sh
# makemetal.sh
if [ "$PLATFORM_NAME" = "macosx" ]; then
INNER_TARGET="mylib-macOS"
else
INNER_TARGET="mylib-iOS"
fi
if [ "$PLATFORM_NAME" = "iphonesimulator" ]; then
SDK_ARGS="-sdk iphonesimulator"
else
SDK_ARKS = ""
fi
# we want to use BUILT_PRODUCTS_DIR directly. This avoids a lot of tricky problems
# about how to predict where the output will be on a per-target per-architecture basis
xcodebuild build -project ${MYLIB_DIR}/project.xcodeproj -configuration ${CONFIGURATION} -target "$INNER_TARGET" ${SDK_ARGS} BUILT_PRODUCTS_DIR=${BUILT_PRODUCTS_DIR}
Then, instruct users to
- Set
MYLIB_DIRto the path to the script as a custom build setting. For scripts in the root of a Swift package managed in git by xcode, the value of this is typically${SHARED_PRECOMPS_DIR}/../../../SourcePackages/Checkouts/packagename - Set the
MTLLINKER_FLAGSto-L ${BUILT_PRODUCTS_DIR} -l mylib. This assumes that your library has thelibmylib.anaming scheme. Also, this build setting is undocumented. - Set the "Metal Compiler - BuildOptions"
MTL_HEADER_SEARCH_PATHSto include${HEADER_SEARCH_PATHS}. This will let Metal sources find the header files from the Swift package.
See blitcurve for a complete example.
- FB7776777
- FB7744335
Yeah, so it turns out there is no good environment variable to get a path to the swift package is on disk.
In general, a project's dependencies can be specified by a local path, a git url, or a mix of both strategies. So to start with there's not a single location where you can expect your Swift package dependencies.
The git urls are resolved by Xcode to path like ~/Library/Developer/DerivedData/YourProject-stuffhere/SourcePackages/Checkouts/packagename, so you can theoretically get a fixed path for that case specifically. However,
- This is undocumented and could change
- There is no environment variable that gets the
Checkouts,SourcePackagesor evenYourProject-stuffherepaths. - There are environment variables that return an arbitrary folder inside the
YourProject-stuffherepath. These include plausible candidates likeBUILD_DIRandBUILD_ROOT. So in theory, you concatenateBUILD_DIR, some../../updiring and then the undocumentedSourcePackages/Checkouts/packagenameat the end.
However, in various situations Xcode will increase the directory depth of these variables. For example, when building your scheme as part of running a playground, BUILD_DIR is
~/Library/Developer/DerivedData/YourProject-stuffhere/Build/Intermediates.noindex/Playgrounds/Products
but when built on its own, it's
~/Library/Developer/DerivedData/YourProject-stuffhere/Build/Products
SHARED_PRECOMPS_DIR is pretty much the only value I'm aware of that seems to have a fixed number of subdirectories and is suitable for this purpose.
I'm sorry.
FB8102669- environment variable for swift packagesFB8095382- environment variable for metal projects
The best solution I'm aware of on this problem is to create a custom phase for copying the .metallib into the target manually.
COUNTER=0
while [ $COUNTER -lt ${SCRIPT_INPUT_FILE_COUNT} ]; do
tmp="SCRIPT_INPUT_FILE_$COUNTER"
INPUT=${!tmp}
tmp="SCRIPT_OUTPUT_FILE_$COUNTER"
OUTPUT=${!tmp}
cp ${INPUT} ${OUTPUT}
let COUNTER=COUNTER+1
done
- input files:
${BUILT_PRODUCTS_DIR}/my.metallib - output files:
${BUILT_PRODUCTS_DIR}/${EXECUTABLE_FOLDER_PATH}/my.metallib
FB8276893
Error Domain=MTLCaptureError Code=1 "Capturing is not supported.
Add MetalCaptureEnabled=1 to Info.plist.
Apple documents that this happens automatically, but I'm aware of some cases where it doesn't.
FB7870713 – works as designed