Đọc nội dung từ một MTLBuffer chung?
Trong ứng dụng của tôi, tôi có một ứng dụng MTLBufferđang được khởi tạo bằng cách sử dụng loại chung. Trong một trường hợp cụ thể, bộ đệm sẽ giữ các giá trị liên quan đến các hạt trong đám mây điểm và được định nghĩa như vậy;
struct ParticleUniforms {
simd_float3 position;
simd_float3 color;
float confidence;
};
Tôi đang tạo ra của tôi MTLBuffernhư vậy;
guard let buffer = device.makeBuffer(length: MemoryLayout<Element>.stride * count, options: options) else {
fatalError("Failed to create MTLBuffer.")
}
Tuy nhiên, nơi tôi đang gặp khó khăn là hiểu cách đọc nội dung của bộ đệm. Hơn nữa, tôi đang tìm cách sao chép một phần tử của mỗi mục trong bộ đệm vào một mảng trên CPU, mảng này tôi sẽ sử dụng sau này.
Thực tế, bộ đệm chứa một tập hợp ParticleUniformsvà tôi muốn truy cập positiongiá trị của từng mục, lưu vị trí đó vào một mảng riêng biệt.
Tất cả các ví dụ tôi đã thấy ở đây trên Stack Overflow dường như cho thấy MTLBuffernhư đang nắm giữ một bộ sưu tập các Floats, mặc dù tôi chưa thấy bất kỳ ví dụ nào sử dụng loại chung.
Trả lời
Có vẻ như những gì bạn đang tìm kiếm để đạt được chỉ có thể được thực hiện với cấu trúc C giữ mỗi thành viên trong một khối liền kề (các mảng của cấu trúc C không nhất thiết phải liền kề, nhưng MemoryLayout<Type>.stridesẽ chiếm bất kỳ phần đệm tiềm năng nào). Các thuộc tính cấu trúc Swift có thể không liền nhau , vì vậy phương pháp dưới đây để truy cập các giá trị thành viên sẽ không hoạt động theo cách thực tế. Thật không may, khi làm việc với void*bạn, bạn cần biết dữ liệu mô tả những gì, điều này không đặc biệt phù hợp với các kiểu chung của Swift. Tuy nhiên, tôi sẽ đưa ra một giải pháp tiềm năng.
Tệp C:
#ifndef Test_h
#define Test_h
#include <simd/simd.h>
typedef struct {
vector_float3 testA;
vector_float3 testB;
} CustomC;
#endif /* Test_h */
Tệp Swift (giả định tiêu đề bắc cầu)
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)]
Điều này tương tự như a MTLVertexDescriptor, ngoại trừ bộ nhớ được truy cập theo cách thủ công chứ không phải thông qua [[stage_in]]thuộc tính và bảng đối số được truyền cho mỗi trường hợp của đỉnh của bộ đổ bóng phân mảnh. Bạn thậm chí có thể mở rộng trình cấp phát để chấp nhận một tham số chuỗi với tên của thuộc tính và giữ một số từ điển ánh xạ tới các ID thành viên.