合金

Alloy 是一个基于 Apple 的 Metal 框架的小型实用工具和扩展集合,旨在使您的 Swift GPU 代码更简洁,并让您更快地构建管道原型。

虽然这个库没有引入任何新的范例或概念来显着改变您处理 Metal 实现的方式,但它有一些可选使用的东西,如果您发现它们像库作者一样有用,您可以将它们整合到您的应用程序中 :)

使用示例

通过共享资源内存结合 CoreGraphics 和 Metal 的强大功能

好的,让我看看是怎么回事

首先,这个框架提供了一组实用工具,可以隐藏您 Metal 代码中大部分冗余的显式性,同时又不会限制灵活性。您可以轻松地混合使用 Alloy 和原生 Metal 代码。

Alloy 引入的唯一新概念是 MTLContext。从内部来说,它旨在存储通常在您的应用程序中共享和注入的对象。

特别是,它是

在内部,它还管理着一个 MTKTextureLoader 和一个 MTLLibraries 缓存,但此逻辑应被视为私有的。目前,MTLContext 不是线程安全的

MTLContext 通常作为依赖项注入到任何与 Metal 设备交互的对象中。

它可以为您做很多事情,以下是一些示例

轻松从 CGImage 创建纹理

let texture = context.texture(from: cgImage,
                              usage: [.shaderRead, .shaderWrite])

以同步/异步方式调度命令缓冲区

了解如何使用 Swift 闭包对编码进行分组。

self.context.scheduleAndWait { buffer in
    buffer.compute { encoder in
      // compute command encoding logic
    }

    buffer.blit { encoder in
      // blit command encoding logic
    }
}

为框架中的函数加载计算管线状态

let lib = context.shaderLibrary(for: Foo.self)
let computePipelineState = try? lib.computePipelineState(function: "brightness")

按值类型分配缓冲区

let buffer = context.buffer(for: InstanceUniforms.self,
                            count: 99,
                            options: .storageModeShared)

序列化和反序列化 MTLTexture

let encoder = JSONEncoder()
let data = try encoder.encode(texture.codable())

let decoder = JSONDecoder()
let decodableTexture = try decoder.decode(MTLTextureCodableBox.self, from: data)
let decodedTexture = try decodableTexture.texture(device: self.context.device)

其他事项

其他 Alloy 特有类型

Alloy 引入的其他类型包括

MTLContext 最小使用示例

MTLContext 通常像您对 MTLDevice 所做的那样注入到类中,您应该缓存上下文和所有重量级对象,以便稍后重用它们,例如:

import Alloy

public class BrightnessEncoder {
    public let context: MTLContext
    fileprivate let pipelineState: MTLComputePipelineState

    /**
     * This variable controls the brightness factor. Should be in range of -1.0...1.0
     */
    public var intensity: Float = 1.0

    public init(context: MTLContext) {
        self.context = context

        guard let lib = context.shaderLibrary(for: BrightnessEncoder.self),
              let state = try? lib.computePipelineState(function: "brightness")
        else { fatalError("Error during shader loading") }

        self.pipelineState = state
    }

    public func encode(input: MTLTexture,
                       in commandBuffer: MTLCommandBuffer) {
        commandBuffer.compute { encoder in
            encoder.set(textures: [input])
            encoder.set(self.intensity, at: 0)

            encoder.dispatch2d(state: self.pipelineState,
                               covering: input.size)
        }
    }

}

请注意使用 Alloy 启动内核是多么简单,不再需要繁琐的线程组大小计算,以及使用平衡 .endEncoding() 调用进行多次编码器初始化。

然后在其他地方您只需执行

context.scheduleAndWait { buffer in
    self.brightnessEncoder.intensity = sender.floatValue
    self.brightnessEncoder.encode(input: texture,
                                  in: buffer)

    // For Mac applications
    if case .managed = texture.storageMode {
        buffer.blit { encoder in
            encoder.synchronize(resource: texture)
        }
    }
}

通过这种方法,您可以轻松地堆叠和构建 GPU 管道层,使用 Swift 闭包对 blitcomputerender 命令编码进行分组,同时保持 Metal API 的完全灵活性。

安装

CocoaPods

CocoaPods 是 Cocoa 项目的依赖管理器。有关使用和安装说明,请访问他们的网站。要使用 CocoaPods 将 Alloy 集成到您的 Xcode 项目中,请在您的 Podfile 中指定它

# Optionally add version, i.e. '~> 0.9.0'
pod 'Alloy'

许可证

MIT