I have two sections in my question:
First Section
I have a requirement where I have to pass a structure containing 3 values of a color (RGB) varying from 0 - 1 but it turns out whatever values Im receiving is different when I tested the code hardcoding the values.
Here is my fragment shader method
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;
}
My swift structure looks like this,
struct RGBColors {
var r: Float
var g: Float
var b: Float
func floatBuffers() -> [Float] {
return [r,g,b]
}
}
I pass the buffers to the fragment like this,
let colors = color.floatBuffers()
let colorBuffer = device.makeBuffer(bytes: colors, length: 16, options: [])
renderEncoder.setFragmentBuffer(colorBuffer, offset: 0, at: 0)
But if I change the parameter color in const device struct RGBColors *color [[ buffer(0) ]]
to float3 like this constant float3 *color [[ buffer(0) ]]
and access through rgb
values it works properly.
Second Section
As you can see in my code that in
let colorBuffer = device.makeBuffer(bytes: colors, length: 16, options: [])
the length is 16 but if I change it to
`MemoryLayout.size(ofValue: colors[0]) * colors.count`
it is crashing and saying
`failed assertion `(length - offset)(12) must be >= 16 at buffer binding at index 0 for color[0].'`
Im not able to figure out what is happening. Can someone suggest me.
Thanks.
The Swift
Float
type does not correspond to the Metalhalf
type. To my knowledge, there's no good representation ofhalf
in Swift (or, for that matter, C or Objective-C). You're providing 3 32-bit values to something which is expecting 3 16-bit values. The values you're providing do not line up with with how the receiving code accesses them, so it's accessing sub-parts of the values.So, I recommend switching from using
half
in your shader to usingfloat
, which is more easily represented in Swift.Next, your
RGBColors
struct is basically just redundant with the built-in typehalf3
or, if you took my above advice,float3
. So, I recommend you just usefloat3
. That type is even available in Swift if youimport simd
. In Metal (and C), you'd be able to access its members using either.r
,.g
,.b
or.x
,.y
,.z
, but Swift seems to only support the latter. Both languages support accessing the members using array subscript syntax.As documented in the
MemoryLayout
overview, you should not use thesize
property orsize(ofValue:)
method when calculating buffer sizes or offsets. You should usestride
/stride(ofValue:)
. Furthermore, you shouldn't use the stride of one element of a compound type multiplied by the number of elements. You need to use the stride of the whole compound type. That's because the compiler can add padding to a compound type to maintain alignment requirements and the former technique does not take that into account.One final note: in your shader, the
color
variable is only used to access a single color. That is, it's not an array of colors. So, you should probably declare it as a reference type rather than a pointer type. That lets the compiler know so it can generate better code.Of course, then you'd need to change
color->
tocolor.
.