在CPU方面,我有一个我想传递给计算内核的结构:
putMVar
在运行内核之前,我将数据传递给MTLComputeCommandEncoder:
选项1(直接):
private struct BoundingBoxParameters {
var x: Float = 0
var y: Float = 0
var width: Float = 0
var height: Float = 0
var levelOfDetail: Float = 1.0
var dummy: Float = 1.0 // Needed for success
}
选项2(间接通过MTLBuffer):
commandEncoder!.setBytes(¶ms, length: MemoryLayout<BoundingBoxParameters>.size, index: 0)
如果&#39;虚拟&#39;任何选项都可以正常工作。变量存在于结构中,但如果“虚拟”变量失败则会失败。变量不存在。代码在调用中失败:
boundingBoxBuffer.contents().copyBytes(from: ¶ms, count: MemoryLayout<BoundingBoxParameters>.size)
commandEncoder!.setBuffer(boundingBoxBuffer, offset: 0, index: 0)
错误:
commandEncoder!.dispatchThreadgroups(threadGroups, threadsPerThreadgroup: threadGroupCount)
在Metal Kernel Side,这里是相关的代码片段:
validateComputeFunctionArguments:820: failed assertion `Compute Function(resizeImage): argument params[0] from buffer(0) with offset(0) and length(20) has space for 20 bytes, but argument has a length(24).'
在尝试将3x3矩阵传递到另一个计算内核时,我也遇到了类似的问题。它抱怨提供了36个字节但期望48个字节。
有人对此问题有任何想法吗?
答案 0 :(得分:4)
首先,我想指出,当您需要获取内存中布置的Swift类型的实际长度时,不应该使用size
。您应该使用stride
。根据斯威夫特的Type Layout:
最终尺寸和对齐方式是聚合体的大小和对齐方式。该类型的步幅是最终尺寸四舍五入到对齐方式。
This answer详细介绍了Swift中的内存布局,如果你想更好地理解这个主题的话。
问题是,使用Metal struct
的{{1}}和使用两个单独的float2
字段替换它的Swift struct
具有不同的内存布局。
结构的大小(在Swift的情况下是大步)需要是任何结构成员的最大对齐的倍数。 Float
中最大的对齐方式是 8个字节(Metal struct
的对齐方式),因此在float2
值之后的struct尾部有一个填充。
float
因此,您的struct BoundingBoxParameters {
float2 topLeft; // 8 bytes
float2 size; // 8 bytes
float levelOfDetail; // 4 bytes
// 4 bytes of padding so that size of struct is multiple
// of the largest alignment (which is 8 bytes)
}; // 24 bytes in total
实际上最终会占用 24个字节,如错误所示。
与此同时, 4字节最大对齐的Metal struct
只需 20个字节。
Swift struct
这就是为什么他们最终互不兼容,private struct BoundingBoxParameters {
var x: Float = 0 // 4 bytes
var y: Float = 0 // 4 bytes
var width: Float = 0 // 4 bytes
var height: Float = 0 // 4 bytes
var levelOfDetail: Float = 1.0 // 4 bytes
// no need for any padding
} // 20 bytes in total
字段补偿 4个丢失的字节到dummy
。
要解决此问题,我建议您使用Swift中的Swift struct
代替float2
代替simd
:
Float
不要忘记使用import simd
private struct BoundingBoxParameters {
var topLeft = float2(x: 0, y: 0)
var size = float2(x: 0, y: 0)
var levelOfDetail: Float = 1.0
}
( 24字节)来获取长度而不是MemoryLayout<BoundingBoxParameters>.stride
( 20字节< / em>的)。
同样适用于3x3矩阵情况:Metal size
的大小为 48字节, 16字节的对齐方式。我假设您创建了一个float3x3
,其中包含9个Swift struct
s,其大小/大小为 36个字节,并且 4个字节的对齐 。因此,错位。使用Float
中的matrix_float3x3
。
通常,对于在Metal中使用向量或矩阵的任何情况,您应该在Swift中使用相应的simd
类型。