发布
社区首页 >问答首页 >从泛型MTLBuffer中读取内容?

从泛型MTLBuffer中读取内容?
EN

Stack Overflow用户
提问于 2020-08-27 06:46:57
回答 1查看 397关注 0票数 1

在我的应用程序中,我有一个使用泛型类型实例化的MTLBuffer。在一种特定情况下,缓冲区将保存与点云中的粒子相关的值,并如此定义;

代码语言:javascript
代码运行次数:0
复制
struct ParticleUniforms {
    simd_float3 position;
    simd_float3 color;
    float confidence;
};

我像这样实例化我的MTLBuffer

代码语言:javascript
代码运行次数:0
复制
guard let buffer = device.makeBuffer(length: MemoryLayout<Element>.stride * count, options: options) else {
   fatalError("Failed to create MTLBuffer.")
}

然而,我正在努力理解如何读取缓冲区的内容。更重要的是,我希望将缓冲区中每个项目的一个元素复制到CPU上的一个数组中,稍后我将使用该数组。

实际上,buffer保存了一个ParticleUniforms集合,我希望访问每个项目的position值,将该位置保存到一个单独的数组中。

我在Stack Overflow上看到的所有示例似乎都显示MTLBuffer持有一个浮点数的集合,尽管我还没有看到任何使用泛型类型的示例。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-08-28 21:40:22

似乎你想要实现的目标只能用C结构来实现,C结构将每个成员放在一个连续的块中(C结构的数组必须是连续的,但MemoryLayout<Type>.stride将考虑任何潜在的填充)。Swift结构属性may not be contiguous,所以下面访问成员值的方法不能以实际的方式工作。不幸的是,在使用void*时,您需要知道数据描述了什么,这并不是特别适合Swift泛型类型。但是,我将提供一个潜在的解决方案。

C文件:

代码语言:javascript
代码运行次数:0
复制
#ifndef Test_h
#define Test_h

#include <simd/simd.h>

typedef struct {
    vector_float3 testA;
    vector_float3 testB;
} CustomC;

#endif /* Test_h */

Swift文件(假定为桥接头)

代码语言:javascript
代码运行次数:0
复制
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)]

这类似于MTLVertexDescriptor,不同之处在于内存是手动访问的,而不是通过传递给片段顶点着色器的每个实例的[[stage_in]]属性和参数表访问的。您甚至可以扩展分配器以接受带有属性名称的字符串参数,并保存一些映射到成员ID的字典。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/63606753

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档