使用Swift 4在Metal Compute Kernel中传递参数

时间:2018-02-10 18:22:43

标签: image-processing kernel swift4 metal metalkit

在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(&params, length: MemoryLayout<BoundingBoxParameters>.size, index: 0)

如果&#39;虚拟&#39;任何选项都可以正常工作。变量存在于结构中,但如果“虚拟”变量失败则会失败。变量不存在。代码在调用中失败:

boundingBoxBuffer.contents().copyBytes(from: &params, 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个字节。

有人对此问题有任何想法吗?

1 个答案:

答案 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类型。