Metal:为 MSL cikernel 设置编译器和链接器选项后,.metal 文件中定义的 vertexFunction 变为 nil

Metal: vertexFunction defined in .metal file becomes nil once setting Compiler and Linker Options for MSL cikernel

.metal 文件中定义的 VertexFunction 和 FragmentFunction 工作得很好,但是一旦我在 Apple's doc 之后指定了编译器和链接器选项,它们就变成了 nil:其他金属编译器标志选项中的 -fcikernel 标志,以及 -cikernel flat MTLLINKER_FLAGS 在用户定义的设置中。

我需要上面的设置来使用 MSL(金属着色语言)的 cikernel。实际上,在 12.0 中不推荐使用带有 Core Image Kernel Language 的 cikernel。

如何同时使用 vertex/fragment Metal shader 和 MSL cikernel?

let library = self.device?.makeDefaultLibrary()!
let pipeLineDescriptor = MTLRenderPipelineDescriptor()
pipeLineDescriptor.vertexFunction=library.makeFunction(name: "myVertexShader")
pipeLineDescriptor.fragmentFunction=library.makeFunction(name: "myFragmentShader")

我猜你必须单独编译你的过滤器内核,而不是使用你的默认 Metal 库。

为此,您可以给他们另一个文件扩展名,例如 .kernel 并添加自定义 Build Rule,如下所示:

然后添加自定义 Build Phase,将编译的内核 metallibs 复制到您的应用程序包中:

要使用正确的金属源初始化 CIKernel,您可以这样做:

let url = Bundle(for: type(of: self)).url(forResource: "<#name of your .kernel file#>", withExtension: "metallib")!
let data = try! Data(contentsOf: url)
let kernel = try! CIKernel(functionName: "<#kernel function name#>", fromMetalLibraryData: data)

(请注意,您应该再次从项目设置中删除编译器和类似器标志,以便再次正确编译其他 Metal 源代码。)

在 Xcode 11 中有效的 Frank Schlegel 答案的更新,其中,正如 Giraff Wombat 提到的那样,金属编译器只是忽略不以 .metal 结尾的输入文件。为了确保 CI 内核文件远离常规金属管道,我进行了扩展“.mcikernel”并放入构建规则脚本:

mkdir -p ${DERIVED_FILES_DIR}/kernels
cp ${INPUT_FILE_PATH} ${DERIVED_FILES_DIR}/${INPUT_FILE_BASE}.metal
xcrun metal -fcikernel ${DERIVED_FILES_DIR}/${INPUT_FILE_BASE}.metal -c -o ${DERIVED_FILES_DIR}/kernels/${INPUT_FILE_BASE}.air
xcrun metallib -cikernel -o ${DERIVED_FILES_DIR}/kernels/${INPUT_FILE_BASE}.metallib ${DERIVED_FILES_DIR}/kernels/${INPUT_FILE_BASE}.air

我无法使现有答案中的任何一个正常工作(AlterEgo 答案似乎不完整,例如未显示 input/output 文件设置)。

无论如何,WWDC video,苹果推荐如下。

创建一个名为 MyKernels.ci.metal 的 Metal 文件:

#include <metal_stdlib>
using namespace metal;

#include <CoreImage/CoreImage.h>
 
extern "C" float4 do_nothing(coreimage::sample_t s) {
    return s;
}

接下来添加两个构建规则,它们都取消选中“运行 once per architecture”。规则 1:

Source Files with names matching:
*.ci.metal

xcrun metal -c -I $MTL_HEADER_SEARCH_PATHS -fcikernel "${INPUT_FILE_PATH}" -o "${SCRIPT_OUTPUT_FILE_0}"

output files:
$(DERIVED_FILE_DIR)/$(INPUT_FILE_BASE).air

第二个:

Source Files with names matching:
*.ci.air

xcrun metallib -cikernel "${INPUT_FILE_PATH}" -o "${SCRIPT_OUTPUT_FILE_0}"

output files:
$(METAL_LIBRARY_OUTPUT_DIR)/$(INPUT_FILE_BASE).metallib

使用:

let url = Bundle.main.url(forResource: "MyKernels", withExtension: "ci.metallib")!
let data = try! Data(contentsOf: url)
kernel = try! CIColorKernel(functionName: "do_nothing", fromMetalLibraryData: data)

请注意,不建议建议将 fckernelcikernel 标志添加到 Build Settings 的其他答案。如果使用常规金属,它们将导致问题,并可能导致其他问题。此解决方案将仅在具有 .ci.metal 扩展名的文件上使用这些标志。

构建规则截图: