2017-04-26 84 views
1

我想要在时间增加的粒子。我得到了advice,它将缓冲值设置得更高,这样我就可以玩弄粒子的数量。我在想的是我将有一个最大计数大小设置为缓冲区,然后在shader,我将有一个struct与数组采取粒子属性。金属着色语言 - 缓冲区绑定

我有这个在我swift:相应

vectBuffer = device!.makeBuffer(length: MemoryLayout<float3>.size * vectMaxCount, options: []) 

和更新buffer

var vectMaxCount = 10 
var metalvects = [float3(0.0,0.0,0.0),float3(1.0,0.0,0.0),float3(2.0,0.0,0.0)] 
var vectBuffer: MTLBuffer! 

然后我注册buffer

... 
command_encoder.setBuffer(vectBuffer, offset: 0, at: 2) 
var bufferPointer = vectBuffer.contents() 
memcpy(bufferPointer, &metalvects, MemoryLayout<float3>.size * vectMaxCount) 

let threadGroupCount = MTLSizeMake(8, 8, 1) 
let threadGroups = MTLSizeMake(drawable.texture.width/threadGroupCount.width, drawable.texture.height/threadGroupCount.height, 1) 
command_encoder.dispatchThreadgroups(threadGroups, threadsPerThreadgroup: threadGroupCount) 
command_encoder.endEncoding() 
command_buffer.present(drawable) 
command_buffer.commit() 

,并试图得到它metal file:

struct Vects 
{ 
    float3 position[100]; 
}; 

kernel void compute(texture2d<float, access::write> output [[texture(0)]], 
        constant Vects &vects [[buffer(2)]], 
        uint2 gid [[thread_position_in_grid]]) { 
... 
} 

,我得到了一个错误:

validateComputeFunctionArguments:727: failed assertion `(length - offset)(160) must be >= 1600 at buffer binding at index 2 for vects[0].'

它指示线command_encoder.dispatchThreadgroups(threadGroups, threadsPerThreadgroup: threadGroupCount)给我的错误。我读了一些关于buffer binding,我认为是我发送给我的问题threadGroupCountsThreadGroup的方式。

如果我将float3 position[100];更改为float3 position[7];,它仍然有效。超过7的任何东西都会得到类似的错误。

我该如何解决这个问题?

是否有一个很好的公式来估计threadGroupsthreadGroupCount?即使经验法则做到这一点?

Update01

基于肯Thomases的答案,我在我的代码更改为:

迅速:

vectBuffer = device!.makeBuffer(length: MemoryLayout<float3>.stride * metalvects.count, options: []) 
... 
memcpy(bufferPointer, &metalvects, MemoryLayout<float3>.stride * metalvects.count) 
... 

金属:

struct Vects 
{ 
    float3 position[3]; 
}; 
... 

它的工作现在。但是我如何分配更高的缓冲区内存,以便在后面提到的this post等应用中使用?

回答

1

这里有很多问题。

您正在定义具有特定尺寸的Vects。这可以让Metal检查索引2处缓冲区的大小是否足够大,以便与您的变量vects的大小相匹配。它抱怨,因为它不够大。 (例如,如果vects被宣布为constant float3 *vects [[buffer(2)]],将无法执行此检查。)

其次,缓冲区的大小 - MemoryLayout<float3>.size * vectMaxCount - 不正确。它未考虑到float3的对齐方式,因此您的[float3]阵列中的元素之间存在填充。如documentation for MemoryLayout中所述,在计算分配大小时,应始终使用stride,而不是size

这就是为什么当Vects::position是8个或更多元素长时发生故障。你会期望它从11个元素开始,因为vectMaxCount是10,但是你的缓冲区比vectMaxCountfloat3 s的数组短。具体来说,你的缓冲区是10 * 12 == 120字节长。 float3的步幅是16和120/16 == 7.5。

如果您分配缓冲区时,切换从sizestride和改变Vects::position以10元计,以匹配vectMaxCount,那么你就会得到过去这个紧迫的问题。但是,潜伏着其他问题。

您当前的计算功能并不知道实际填充了多少个vects.position元素。您需要传入元素的实际数量。

这条线:

memcpy(bufferPointer, &metalvects, MemoryLayout<float3>.size * vectMaxCount) 

不正确(即使stride替换size之后)。它读过metalvects的末尾。这是因为metalvects中元素的数量少于vectMaxCount。您应该使用metalvects.count而不是vectMaxCount

+0

请检查update01。我从你回答的另一篇文章中引用了参考文献。 – sooon

+0

当*分配缓冲区时,您可以并应该继续使用'vextMaxCount'。这只是对'memcpy()'的调用,你应该使用'metalvects.count'。 –

+0

是的,它的工作原理。我需要阅读更多关于“内存布局”等内容。你有什么好的建议吗? – sooon

相关问题