Membaca konten dari MTLBuffer generik?
Dalam aplikasi saya, saya memiliki MTLBufferyang dibuat menggunakan tipe generik. Dalam satu kasus tertentu, buffer akan menyimpan nilai-nilai yang terkait dengan partikel di point cloud, dan didefinisikan seperti itu;
struct ParticleUniforms {
simd_float3 position;
simd_float3 color;
float confidence;
};
Saya memberi contoh MTLBufferseperti saya ;
guard let buffer = device.makeBuffer(length: MemoryLayout<Element>.stride * count, options: options) else {
fatalError("Failed to create MTLBuffer.")
}
Di mana saya berjuang, bagaimanapun, adalah memahami cara membaca konten buffer. Terlebih lagi, saya ingin menyalin satu elemen dari setiap item dalam buffer ke array pada CPU, yang akan saya gunakan di lain waktu.
Secara efektif, buffer menampung koleksi ParticleUniforms, dan saya ingin mengakses positionnilai setiap item, menyimpan posisi itu ke array terpisah.
Semua contoh yang saya lihat di sini di Stack Overflow tampaknya menunjukkan MTLBuffermemegang koleksi Floats, meskipun saya belum melihat yang menggunakan tipe generik.
Jawaban
Tampaknya apa yang ingin Anda capai hanya dapat dilakukan dengan struktur C yang menahan setiap anggota dalam blok yang berdekatan (array dari C struct tidak harus bersebelahan, tetapi MemoryLayout<Type>.strideakan menjelaskan padding potensial). Properti struktur cepat mungkin tidak bersebelahan , jadi metode di bawah ini untuk mengakses nilai anggota tidak akan berfungsi secara praktis. Sayangnya, saat bekerja dengan void*Anda perlu mengetahui apa yang dijelaskan datanya, yang tidak terlalu cocok untuk tipe umum Swift. Namun, saya akan menawarkan solusi potensial.
File C:
#ifndef Test_h
#define Test_h
#include <simd/simd.h>
typedef struct {
vector_float3 testA;
vector_float3 testB;
} CustomC;
#endif /* Test_h */
File Swift (diasumsikan header penghubung)
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) { $0 + $1.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) { $0 + $1.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)]
Ini mirip dengan a MTLVertexDescriptor, kecuali memori diakses secara manual dan tidak melalui [[stage_in]]atribut dan tabel argumen yang diteruskan ke setiap instance dari simpul shader fragmen. Anda bahkan dapat memperluas pengalokasi untuk menerima parameter string dengan nama properti dan menyimpan beberapa kamus yang memetakan ke ID anggota.