Alloy 是一个基于 Apple 的 Metal 框架的小型实用工具和扩展集合,旨在使您的 Swift GPU 代码更简洁,并让您更快地构建管道原型。
虽然这个库没有引入任何新的范例或概念来显着改变您处理 Metal 实现的方式,但它有一些可选使用的东西,如果您发现它们像库作者一样有用,您可以将它们整合到您的应用程序中 :)
通过共享资源内存结合 CoreGraphics 和 Metal 的强大功能
首先,这个框架提供了一组实用工具,可以隐藏您 Metal 代码中大部分冗余的显式性,同时又不会限制灵活性。您可以轻松地混合使用 Alloy 和原生 Metal 代码。
Alloy 引入的唯一新概念是 MTLContext
。从内部来说,它旨在存储通常在您的应用程序中共享和注入的对象。
特别是,它是
MTLDevice
MTLCommandQueue
MTLLibrary?
在内部,它还管理着一个 MTKTextureLoader
和一个 MTLLibraries
缓存,但此逻辑应被视为私有的。目前,MTLContext
不是线程安全的。
MTLContext
通常作为依赖项注入到任何与 Metal 设备交互的对象中。
它可以为您做很多事情,以下是一些示例
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)
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 引入的其他类型包括
MTLOffscreenRenderer
:这是一个类,允许您创建简单的离屏渲染器,将内容绘制到任意 MTLTextures
中ComputeCommand
:这是一个实验性类,它对 Metal 内核进行反射,并允许您按名称而不是索引分配参数。这是一个需要改进的主题。BlendingMode
:此类型包含八种 Alloy 内置混合模式的枚举。您可以通过调用 setup(blending:)
函数轻松设置其中一种。let renderPipelineDescriptor = MTLRenderPipelineDescriptor()
renderPipelineDescriptor.colorAttachments[0].setup(blending: .alpha)
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 闭包对 blit
、compute
和 render
命令编码进行分组,同时保持 Metal API 的完全灵活性。
CocoaPods 是 Cocoa 项目的依赖管理器。有关使用和安装说明,请访问他们的网站。要使用 CocoaPods 将 Alloy 集成到您的 Xcode 项目中,请在您的 Podfile
中指定它
# Optionally add version, i.e. '~> 0.9.0'
pod 'Alloy'
MIT