无法在 Metal 的片段着色器中正确访问结构对象?
Not able to access a struct object properly inside fragment shader in Metal?
我的问题分为两个部分:
第一节
我有一个要求,我必须传递一个结构,其中包含 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) ]]
中的参数颜色更改为 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].'`
我不知道发生了什么。有人可以推荐我吗
谢谢。
SwiftFloat
类型不对应Metalhalf
类型。据我所知,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.
。
我的问题分为两个部分:
第一节
我有一个要求,我必须传递一个结构,其中包含 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) ]]
中的参数颜色更改为 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].'`
我不知道发生了什么。有人可以推荐我吗
谢谢。
SwiftFloat
类型不对应Metalhalf
类型。据我所知,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.
。