swift - 无法在 Metal 的片段着色器中正确访问结构对象?

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

我的问题分为两个部分:

第一节

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

这是我的片段着色器方法

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) ]] 中的参数 color 更改为 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 Float 类型与 Metal half 类型不对应。据我所知,half 在 Swift(或者,就此而言,C 或 Objective-C)中没有很好的表示。您正在为需要 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-> 更改为color.

关于swift - 无法在 Metal 的片段着色器中正确访问结构对象?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46002564/

相关文章:

ios - 无法将 (Timer!) -> Void 转换为 ((CFRunLoopTimer?) ->Void)! - 将 NSTimer 扩展转换为 Swift 3

ios - 如何快速在 sceneKit 中连续闪烁 3d 对象?

ios - 如何以特定顺序显示数组中的项目?

ios - 使用 Core Graphics 合成图像并保留 alpha channel

javascript - 色度键片段着色器找不到颜色

swift - 使用 CVMetalTextureCacheCreateTextureFromImage 在 Swift 中将 CMSampleBuffer 转换为 CVMetalTexture

swift : Crash when zoom out on map with cluster

opengl - 模糊 OpenGL 中的深度缓冲区 - 如何访问片段着色器中的 mipmap 级别?

ios - MTKTextureLoader 使图像饱和

ios - 创建 MTLTexture 对象时出现无法识别的选择器错误