简体   繁体   English

iOS Metal缓冲区不符合跨步值

[英]iOS Metal buffer not honouring stride value

I have a per-instance uniform buffer where each element in the buffer is 64-bytes but I only use the first 16-bytes (float3) of each element in the vertex shader. 我有一个按实例的统一缓冲区,其中缓冲区中的每个元素都是64字节,但是我只使用顶点着色器中每个元素的前16个字节(float3)。 I set the stride up to describe this. 我大步向前来描述这一点。 The problem is it does not stride over the other 48 bytes unless I add padding to the struct in the shader so that it is also 64 bytes. 问题是, 除非我在着色器的结构中添加填充以使它也是64个字节, 否则它不会跨越其他48个字节。

// Particle Instance Position
vertexDescriptor.attributes[2].format = .Float3   // 16 bytes with padding.     
vertexDescriptor.attributes[2].offset = 0
vertexDescriptor.attributes[2].bufferIndex = 2
vertexDescriptor.layouts[2].stride = strideof(Particle)
vertexDescriptor.layouts[2].stepFunction = .PerInstance

...

commandEncoder.setVertexBuffer(instanceUniformBuffer, offset:0, atIndex:2)

App side particle struct: 应用程式侧粒子结构:

struct Particle {

    var position = float3()
    var prevPos = float3()
    var attractPoint = float3()

    var ref: DataRef!
    var state = State.Free

    enum State: Int {
        case Free = 0
        case Active
    }
}

And here's the corresponding Metal struct, ie Particle.position corresponds to InstanceUniforms.instanceTranslate in the shader. 这是对应的Metal结构,即Particle.position对应于着色器中的InstanceUniforms.instanceTranslate I expect the stride setup above to mean that this gets loaded with the Particle.position for each instance and the other 48 bytes of each Particle in the buffer is skipped over. 我希望上面的步幅设置意味着每个实例都将使用Particle.position加载,并且缓冲区中每个Particle的其他48个字节将被跳过。

struct InstanceUniforms
{
    float3 instanceTranslate [[ attribute(2) ]];
};

Some sanity checks, everything makes sense: 进行一些健全性检查,一切都会变得有意义:

sizeof(float3) 16
alignof(float3) 16
sizeof(Particle) 57
alignof(Particle) 16
strideof(Particle) 64

But it doesn't work unless I pad the shader struct out to 64 bytes: 但是,除非我将着色器结构填充到64个字节,否则它将不起作用:

struct InstanceUniforms
{
    float3 instanceTranslate [[ attribute(2) ]];
    float4 pad[3];
};

Otherwise for the second instance the shader actually sets instanceTranslate to the Particle.prevPos of the first element in the buffer, like it only strides over the size of the InstanceUniforms struct, regardless of what the stride is set to in the vertex descriptor. 否则,对于第二个实例,着色器实际上会将instanceTranslate设置为缓冲区中第一个元素的Particle.prevPos ,就像它仅跨越InstanceUniforms结构的大小一样,而与在顶点描述符中设置的跨度无关。

I'm sure I must be doing something wrong here, it seems like you shouldn't need to pad your shader structs. 我确定我在这里一定做错了,看来您不需要填充着色器结构。

So it seems like this is Just The Way It Is™ with Metal buffers: you have to pad the shader struct to match the stride of the app struct. 因此,似乎这就是带有金属缓冲区的Just Way Way Is™™:您必须填充着色器结构以匹配应用程序结构的步幅。 I wouldn't think it would be that difficult for the Metal shader runtime to load stride bytes from the buffer and cast the first sizeof(MyShaderStruct) of them. 我不认为这将是困难的金属着色器运行时加载stride从缓冲区的字节和投第一sizeof(MyShaderStruct)他们。 Or autopad the struct when the pipeline state is attached. 或在连接管道状态时自动填充结构。 But these conveniences probably have efficiency implications. 但是这些便利可能会影响效率。

Here's what my Swift / Shader structs look like now for reference: 这是我的Swift / Shader结构现在的样子,仅供参考:

Metal: 金属:

struct Particle
{
   float3 position;
   float3 prevPos;
   float4 color;
   float4 lineColor;
   float  scale;
   char   pad[60];
};

Swift: 迅速:

struct Particle {
    var position = float3()
    var prevPos = float3()
    var color = float4()
    var lineColor = float4()
    var scale:Float = 0
    var gravity = float3()

    var data: DataRef!
    weak var physics: Physics? = nil
    var state = State.Free

    enum State: Int {
        case Free = 0
        case Active
    }
}

sizeof(Particle) 113
alignof(Particle) 16
strideof(Particle) 128

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM