Alloy
Alloy是一组在Apple的Metal框架之上提供的实用工具和扩展,致力于使您的Swift GPU代码更简洁,并让您快速原型化您的管线。
虽然这个库没有引入任何新的范式或概念,以显著改变您处理Metal实现的方式,但它有一些可选的功能,如果您觉得它们和库作者一样有用,您可以将其整合到您的应用程序中:)
- 覆盖vanilla Metal API的极小层
- 无外部依赖项
- 跨平台支持
- 非常Swift化
使用示例
通过共享资源内存结合CoreGraphics和Metal的力量
好吧,让我看看发生了什么
首先,这个框架提供了一组实用工具,这些工具隐藏了大多数在您的Metal代码中的重复显式操作,同时又不限制任何灵活性。您可以将Alloy和vanilla Metal代码轻松混合。
Alloy引入的唯一一个新概念是MTLContext
。内部,这被用来存储通常在您的应用程序中共享和注入的对象。
具体来说,这是
- device:
MTLDevice
- commandQueue:
MTLCommandQueue
- standardLibrary:
MTLLibrary?
内部,它还管理一个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 中引入的其他类型包括:
MTLOffscreenRenderer
:这是一个类,允许您创建简单的离屏渲染器,将内容绘制到任意的MTLTextures
中。ComputeCommand
:这是一个 实验性类,可以对 Metal 核进行反射,并通过名称指定参数而不是索引。这是改进的对象。BlendingMode
:此类型包含 Alloy 内置的八种混合模式的枚举。您只需调用setup(blending:)
函数即可轻松设置其中一个。let renderPipelineDescriptor = MTLRenderPipelineDescriptor() renderPipelineDescriptor.colorAttachments[0].setup(blending: .alpha)
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 闭包分组 blit
、compute
和 render
命令编码,同时保持 full flexibility of Metal API。
安装
CocoaPods
CocoaPods 是 Cocoa 项目的依赖项管理器。有关用法和安装说明,请访问他们的网站。要使用 CocoaPods 将 Alloy 集成到您的 Xcode 项目中,请在 Podfile
中指定它。
# Optionally add version, i.e. '~> 0.9.0'
pod 'Alloy'
许可证
MIT