【问题标题】:Not able to access a struct object properly inside fragment shader in Metal?无法在 Metal 的片段着色器中正确访问结构对象?
【发布时间】:2017-09-01 14:34:07
【问题描述】:

我的问题有两个部分:

第一节

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

这是我的片段着色器方法

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;
}

我的 swift 结构是这样的,

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 值访问它可以正常工作。

第二部分

正如您在我的代码中看到的那样 let colorBuffer = device.makeBuffer(bytes: colors, length: 16, options: []) 长度为 16 但如果我将其更改为

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

它正在崩溃并说

`failed assertion `(length - offset)(12) must be >= 16 at buffer binding at index 0 for color[0].'`

我无法弄清楚发生了什么。有人可以推荐我吗?

谢谢。

【问题讨论】:

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


    【解决方案1】:

    Swift Float 类型与 Metal half 类型不对应。据我所知,在 Swift(或者,就此而言,C 或 Objective-C)没有很好的 half 表示。您正在为需要 3 个 16 位值的东西提供 3 个 32 位值。您提供的值与接收代码访问它们的方式不一致,因此它正在访问值的子部分。

    所以,我建议从在着色器中使用 half 切换到使用 float,这在 Swift 中更容易表示。

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

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

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

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

    当然,那么您需要将color-&gt; 更改为color.

    【讨论】:

    • 那行得通。但是我仍然无法弄清楚为什么我仍然会崩溃,如果我使用MemoryLayout.stride(ofValue: colors) 并传递给缓冲区的缓冲区长度我会崩溃为failed assertion (length - offset)(8) must be &gt;= 16 at buffer binding at index 0 for color[0]. 但是当我使用MemoryLayout.stride(ofValue: colors[0]) * colors.count 时,它对结构不起作用对于float3 类型参数。你能告诉我为什么吗?
    • float3 类型有填充。它的步幅等于单个 float/Float 大小的 3 倍。检查 Metal Shader Language 规范(第 2.9 节,表 3),您会看到 float3 的大小和对齐方式为 16 字节。这就是为什么将一个元素的步幅乘以元素的数量是无效的。你得到错误的结果。至于MemoryLayout.stride(ofValue: colors) 显然给出的值为8,那是因为colors 是一个Swift 数组,它不是一个简单的缓冲区。这是一个内部布局未知的对象。不要尝试这样做。
    猜你喜欢
    • 2019-01-24
    • 1970-01-01
    • 2017-08-12
    • 1970-01-01
    • 2018-02-12
    • 2015-08-30
    • 1970-01-01
    • 2021-04-27
    • 1970-01-01
    相关资源
    最近更新 更多