从通用 MTLBuffer 读取内容?
Reading contents from a generic MTLBuffer?
在我的应用程序中,我有一个 MTLBuffer
正在使用泛型实例化。在一种特殊情况下,缓冲区将保存与点云中的粒子相关的值,并如此定义;
struct ParticleUniforms {
simd_float3 position;
simd_float3 color;
float confidence;
};
我正在像这样实例化我的 MTLBuffer
;
guard let buffer = device.makeBuffer(length: MemoryLayout<Element>.stride * count, options: options) else {
fatalError("Failed to create MTLBuffer.")
}
然而,我苦苦挣扎的地方是了解如何读取缓冲区的内容。更重要的是,我希望将缓冲区中每个项目的一个元素复制到 CPU 上的一个数组,稍后我将使用它。
实际上,缓冲区包含 ParticleUniforms
的集合,我想访问每个项目的 position
值,将该位置保存到单独的数组中。
我在 Stack Overflow 上看到的所有示例似乎都显示 MTLBuffer
包含 Floats 的集合,但我没有看到任何使用通用类型的示例。
看来您想要实现的目标只能通过将每个成员保存在一个连续块中的 C 结构来完成(C 结构的数组是 not necessarily contiguous, but MemoryLayout<Type>.stride
will account for any potential padding). Swift structure properties may not be contiguous,因此下面用于访问成员值的方法不会以实用的方式工作。不幸的是,在使用 void*
时,您需要知道数据描述的内容,这并不特别适合 Swift 通用类型。但是,我将提供一个潜在的解决方案。
C文件:
#ifndef Test_h
#define Test_h
#include <simd/simd.h>
typedef struct {
vector_float3 testA;
vector_float3 testB;
} CustomC;
#endif /* Test_h */
Swift 文件(假设桥接头)
import Metal
// MARK: Convenience
typealias MTLCStructMemberFormat = MTLVertexFormat
@_functionBuilder
struct ArrayLayout { static func buildBlock<T>(_ arr: T...) -> [T] { arr } }
extension MTLCStructMemberFormat {
var stride: Int {
switch self {
case .float2: return MemoryLayout<simd_float2>.stride
case .float3: return MemoryLayout<simd_float3>.stride
default: fatalError("Case unaccounted for")
}
}
}
// MARK: Custom Protocol
protocol CMetalStruct {
/// Returns the type of the `ith` member
static var memoryLayouts: [MTLCStructMemberFormat] { get }
}
// Custom Allocator
class CustomBufferAllocator<Element> where Element: CMetalStruct {
var buffer: MTLBuffer!
var count: Int
init(bytes: UnsafeMutableRawPointer, count: Int, options: MTLResourceOptions = []) {
guard let buffer = device.makeBuffer(bytes: bytes, length: count * MemoryLayout<Element>.stride, options: options) else {
fatalError("Failed to create MTLBuffer.")
}
self.buffer = buffer
self.count = count
}
func readBufferContents<T>(element_position_in_array n: Int, memberID: Int, expectedType type: T.Type = T.self)
-> T {
let pointerAddition = n * MemoryLayout<Element>.stride
let valueToIncrement = Element.memoryLayouts[0..<memberID].reduce(0) { [=11=] + .stride }
return buffer.contents().advanced(by: pointerAddition + valueToIncrement).bindMemory(to: T.self, capacity: 1).pointee
}
func extractMembers<T>(memberID: Int, expectedType type: T.Type = T.self) -> [T] {
var array: [T] = []
for n in 0..<count {
let pointerAddition = n * MemoryLayout<Element>.stride
let valueToIncrement = Element.memoryLayouts[0..<memberID].reduce(0) { [=11=] + .stride }
let contents = buffer.contents().advanced(by: pointerAddition + valueToIncrement).bindMemory(to: T.self, capacity: 1).pointee
array.append(contents)
}
return array
}
}
// Example
// First extend the custom struct to conform to out type
extension CustomC: CMetalStruct {
@ArrayLayout static var memoryLayouts: [MTLCStructMemberFormat] {
MTLCStructMemberFormat.float3
MTLCStructMemberFormat.float3
}
}
let device = MTLCreateSystemDefaultDevice()!
var CTypes = [CustomC(testA: .init(59, 99, 0), testB: .init(102, 111, 52)), CustomC(testA: .init(10, 11, 5), testB: .one), CustomC(testA: .zero, testB: .init(5, 5, 5))]
let allocator = CustomBufferAllocator<CustomC>(bytes: &CTypes, count: 3)
let value = allocator.readBufferContents(element_position_in_array: 1, memberID: 0, expectedType: simd_float3.self)
print(value)
// Prints SIMD3<Float>(10.0, 11.0, 5.0)
let group = allocator.extractMembers(memberID: 1, expectedType: simd_float3.self)
print(group)
// Prints [SIMD3<Float>(102.0, 111.0, 52.0), SIMD3<Float>(1.0, 1.0, 1.0), SIMD3<Float>(5.0, 5.0, 5.0)]
这类似于 MTLVertexDescriptor
,除了内存是手动访问的,而不是通过 [[stage_in]]
属性和传递给片段着色器顶点的每个实例的参数 table .您甚至可以扩展分配器以接受名称为 属性 的字符串参数,并保存一些映射到成员 ID 的字典。
在我的应用程序中,我有一个 MTLBuffer
正在使用泛型实例化。在一种特殊情况下,缓冲区将保存与点云中的粒子相关的值,并如此定义;
struct ParticleUniforms {
simd_float3 position;
simd_float3 color;
float confidence;
};
我正在像这样实例化我的 MTLBuffer
;
guard let buffer = device.makeBuffer(length: MemoryLayout<Element>.stride * count, options: options) else {
fatalError("Failed to create MTLBuffer.")
}
然而,我苦苦挣扎的地方是了解如何读取缓冲区的内容。更重要的是,我希望将缓冲区中每个项目的一个元素复制到 CPU 上的一个数组,稍后我将使用它。
实际上,缓冲区包含 ParticleUniforms
的集合,我想访问每个项目的 position
值,将该位置保存到单独的数组中。
我在 Stack Overflow 上看到的所有示例似乎都显示 MTLBuffer
包含 Floats 的集合,但我没有看到任何使用通用类型的示例。
看来您想要实现的目标只能通过将每个成员保存在一个连续块中的 C 结构来完成(C 结构的数组是 not necessarily contiguous, but MemoryLayout<Type>.stride
will account for any potential padding). Swift structure properties may not be contiguous,因此下面用于访问成员值的方法不会以实用的方式工作。不幸的是,在使用 void*
时,您需要知道数据描述的内容,这并不特别适合 Swift 通用类型。但是,我将提供一个潜在的解决方案。
C文件:
#ifndef Test_h
#define Test_h
#include <simd/simd.h>
typedef struct {
vector_float3 testA;
vector_float3 testB;
} CustomC;
#endif /* Test_h */
Swift 文件(假设桥接头)
import Metal
// MARK: Convenience
typealias MTLCStructMemberFormat = MTLVertexFormat
@_functionBuilder
struct ArrayLayout { static func buildBlock<T>(_ arr: T...) -> [T] { arr } }
extension MTLCStructMemberFormat {
var stride: Int {
switch self {
case .float2: return MemoryLayout<simd_float2>.stride
case .float3: return MemoryLayout<simd_float3>.stride
default: fatalError("Case unaccounted for")
}
}
}
// MARK: Custom Protocol
protocol CMetalStruct {
/// Returns the type of the `ith` member
static var memoryLayouts: [MTLCStructMemberFormat] { get }
}
// Custom Allocator
class CustomBufferAllocator<Element> where Element: CMetalStruct {
var buffer: MTLBuffer!
var count: Int
init(bytes: UnsafeMutableRawPointer, count: Int, options: MTLResourceOptions = []) {
guard let buffer = device.makeBuffer(bytes: bytes, length: count * MemoryLayout<Element>.stride, options: options) else {
fatalError("Failed to create MTLBuffer.")
}
self.buffer = buffer
self.count = count
}
func readBufferContents<T>(element_position_in_array n: Int, memberID: Int, expectedType type: T.Type = T.self)
-> T {
let pointerAddition = n * MemoryLayout<Element>.stride
let valueToIncrement = Element.memoryLayouts[0..<memberID].reduce(0) { [=11=] + .stride }
return buffer.contents().advanced(by: pointerAddition + valueToIncrement).bindMemory(to: T.self, capacity: 1).pointee
}
func extractMembers<T>(memberID: Int, expectedType type: T.Type = T.self) -> [T] {
var array: [T] = []
for n in 0..<count {
let pointerAddition = n * MemoryLayout<Element>.stride
let valueToIncrement = Element.memoryLayouts[0..<memberID].reduce(0) { [=11=] + .stride }
let contents = buffer.contents().advanced(by: pointerAddition + valueToIncrement).bindMemory(to: T.self, capacity: 1).pointee
array.append(contents)
}
return array
}
}
// Example
// First extend the custom struct to conform to out type
extension CustomC: CMetalStruct {
@ArrayLayout static var memoryLayouts: [MTLCStructMemberFormat] {
MTLCStructMemberFormat.float3
MTLCStructMemberFormat.float3
}
}
let device = MTLCreateSystemDefaultDevice()!
var CTypes = [CustomC(testA: .init(59, 99, 0), testB: .init(102, 111, 52)), CustomC(testA: .init(10, 11, 5), testB: .one), CustomC(testA: .zero, testB: .init(5, 5, 5))]
let allocator = CustomBufferAllocator<CustomC>(bytes: &CTypes, count: 3)
let value = allocator.readBufferContents(element_position_in_array: 1, memberID: 0, expectedType: simd_float3.self)
print(value)
// Prints SIMD3<Float>(10.0, 11.0, 5.0)
let group = allocator.extractMembers(memberID: 1, expectedType: simd_float3.self)
print(group)
// Prints [SIMD3<Float>(102.0, 111.0, 52.0), SIMD3<Float>(1.0, 1.0, 1.0), SIMD3<Float>(5.0, 5.0, 5.0)]
这类似于 MTLVertexDescriptor
,除了内存是手动访问的,而不是通过 [[stage_in]]
属性和传递给片段着色器顶点的每个实例的参数 table .您甚至可以扩展分配器以接受名称为 属性 的字符串参数,并保存一些映射到成员 ID 的字典。