无法在Metal中的片段着色器内正确访问struct对象?

时间:2017-09-01 14:34:07

标签: swift fragment-shader metal metal-performance-shaders

我的问题中有两个部分:

第一部分

我有一个要求,我必须传递一个包含3个颜色(RGB)值的结构,从0到1不等,但是当我测试硬编码值的代码时,我得到的任何值都不同。

这是我的片段着色器方法

<script>

shortcut.add("Ctrl+J",function() { 

         //your code here
});
</script>

我的快速结构看起来像这样,

struct RGBColors {
    half red;
    half green;
    half blue;
};
fragment float4
samplingShader(RasterizerData in [[stage_in]],
               texture2d<half> colorTexture [[ texture(0) ]],
               const device struct RGBColors *color [[ buffer(0) ]]
)
{
    constexpr sampler textureSampler (mag_filter::linear,
                                      min_filter::linear,
                                      s_address::repeat,
                                      t_address::repeat,
                                      r_address::repeat);
        const half4 colorSample = colorTexture.sample (textureSampler, in.textureCoordinate);

    float4 outputColor = float4(0,0,0,0);

    half red = color->red;
    half blue = color->blue;
    half green = color->green;

    outputColor = float4(colorSample.r * red, colorSample.g * green, colorSample.b * blue, 0);

    return outputColor;
}

我将缓冲区传递给像这样的片段,

struct RGBColors {
    var r: Float
    var g: Float
    var b: Float

    func floatBuffers() -> [Float] {
        return [r,g,b]
    }
}

但是,如果我将let colors = color.floatBuffers() let colorBuffer = device.makeBuffer(bytes: colors, length: 16, options: []) renderEncoder.setFragmentBuffer(colorBuffer, offset: 0, at: 0) 中的参数颜色更改为像此const device struct RGBColors *color [[ buffer(0) ]]一样的float3,并通过constant float3 *color [[ buffer(0) ]]值进行访问,则可以正常使用。

第二部分

正如你在我的代码中看到的那样  rgb长度为16,但如果我将其更改为

let colorBuffer = device.makeBuffer(bytes: colors, length: 16, options: [])

崩溃并说

 `MemoryLayout.size(ofValue: colors[0]) * colors.count`

我无法弄清楚发生了什么。有人可以建议我吗。

感谢。

1 个答案:

答案 0 :(得分:2)

Swift Float类型与Metal half类型不对应。据我所知,在Swift(或者就此而言,C或Objective-C)中没有half的良好代表性。您将3个32位值提供给期望3个16位值的值。您提供的值与接收代码访问它们的方式不一致,因此它访问了值的子部分。

因此,我建议您在着色器中使用half切换为使用float,这更容易在Swift中表示。

接下来,您的RGBColors结构基本上只是内置类型half3的冗余,或者,如果您接受了我的上述建议,float3。所以,我建议你只使用float3。如果您import simd,甚至可以在Swift中使用该类型。在Metal(和C)中,您可以使用.r.g.b.x.y访问其成员, .z,但斯威夫特似乎只支持后者。两种语言都支持使用数组下标语法访问成员。

正如MemoryLayout overview中所述,在计算缓冲区大小或偏移量时,您应该使用size属性或size(ofValue:)方法。您应该使用stride / stride(ofValue:)。此外,您不应该使用复合类型的一个元素的步幅乘以元素的数量。您需要使用整个复合类型的步幅。这是因为编译器可以为复合类型添加填充以维持对齐要求,而前一种技术不考虑这一点。

最后一点:在着色器中,color变量仅用于访问单一颜色。也就是说,它不是一系列颜色。因此,您可能应该将其声明为引用类型而不是指针类型。这让编译器知道它可以生成更好的代码。

const device float3 &color [[ buffer(0) ]]

当然,您需要将color->更改为color.