Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OpAccessChain swizzling itself is not handled correctly when input type is widened #2287

Closed
spnda opened this issue Mar 1, 2024 · 4 comments · Fixed by #2290
Closed

OpAccessChain swizzling itself is not handled correctly when input type is widened #2287

spnda opened this issue Mar 1, 2024 · 4 comments · Fixed by #2290
Labels
in progress Issue is being actively worked on

Comments

@spnda
Copy link
Contributor

spnda commented Mar 1, 2024

I found this during investigation of Vulkan CTS failures related KhronosGroup/MoltenVK#2116, but I am too inexperienced with this codebase to properly assess where it's going wrong and what should be changed.

shader.zip

Essentially, when compiling the attached SPIR-V to MSL using --msl-shader-input 0 any32 4 as a command line option, the following MSL is generated:

struct main0_out
{
    float m_56;
};

struct main0_in
{
    float4 m_32;
    uint4 m_79;
};

kernel void main0(uint3 gl_GlobalInvocationID [[thread_position_in_grid]], device main0_out* spvOut [[buffer(25)]], constant uint* spvIndirectParams [[buffer(26)]], device MTLTriangleTessellationFactorsHalf* spvTessLevel [[buffer(23)]], device main0_in* spvIn [[buffer(30)]])
{
    device main0_out* gl_out = &spvOut[gl_GlobalInvocationID.x - gl_GlobalInvocationID.x % 1];
    device main0_in* gl_in = &spvIn[min(gl_GlobalInvocationID.x / 1, spvIndirectParams[1] - 1) * spvIndirectParams[0]];
    uint gl_InvocationID = gl_GlobalInvocationID.x % 1;
    uint gl_PrimitiveID = min(gl_GlobalInvocationID.x / 1, spvIndirectParams[1] - 1);
    spvTessLevel[gl_PrimitiveID].insideTessellationFactor = half(1.0);
    spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[0] = half(1.0);
    spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[1] = half(1.0);
    spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[2] = half(1.0);
    float _27 = float(abs(gl_in[0].m_32.x.x - (-4.0)) < 0.001000000047497451305389404296875) * float(abs(gl_in[0].m_32.y.x - (-9.0)) < 0.001000000047497451305389404296875);
    gl_out[gl_InvocationID].m_56 = _27;
}

And the Metal compiler returns these errors:

program_source:27:42: error: member reference base type 'device float' is not a structure or union
    float _27 = float(abs(gl_in[0].m_32.x.x - (-4.0)) < 0.001000000047497451305389404296875) * float(abs(gl_in[0].m_32.y.x - (-9.0)) < 0.001000000047497451305389404296875);
                          ~~~~~~~~~~~~~~~^~
program_source:27:121: error: member reference base type 'device float' is not a structure or union
    float _27 = float(abs(gl_in[0].m_32.x.x - (-4.0)) < 0.001000000047497451305389404296875) * float(abs(gl_in[0].m_32.y.x - (-9.0)) < 0.001000000047497451305389404296875);
                                                                                                         ~~~~~~~~~~~~~~~^~

After looking through the SPIRV-Cross source, I found these lines being the culprit as they add the additional .x and .y swizzles:

SPIRV-Cross/spirv_msl.cpp

Lines 8390 to 8406 in 5f7a6de

// Get the actual type of the object that was accessed. If it's a vector type and we changed it,
// then we'll need to add a swizzle.
// For this, we can't necessarily rely on the type of the base expression, because it might be
// another access chain, and it will therefore already have the "correct" type.
auto *expr_type = &get_variable_data_type(*var);
if (has_extended_decoration(ops[2], SPIRVCrossDecorationTessIOOriginalInputTypeID))
expr_type = &get<SPIRType>(get_extended_decoration(ops[2], SPIRVCrossDecorationTessIOOriginalInputTypeID));
for (uint32_t i = 3; i < length; i++)
{
if (!is_array(*expr_type) && expr_type->basetype == SPIRType::Struct)
expr_type = &get<SPIRType>(expr_type->member_types[get<SPIRConstant>(ops[i]).scalar()]);
else
expr_type = &get<SPIRType>(expr_type->parent_type);
}
if (!is_array(*expr_type) && !is_matrix(*expr_type) && expr_type->basetype != SPIRType::Struct &&
expr_type->vecsize > result_ptr_type.vecsize)
e += vector_swizzle(result_ptr_type.vecsize, 0);

Here, the code is intended to add swizzles when the vector width got widened (for example through shader inputs) to correctly access only the relevant components that the original code intended to access. However, this doesn't work in this case, as the vector width is increased from 2 to 4, and the original SPIR-V assembly already swizzled the original vector (the %uint_0 and %uint_1 operands essentially do the .x and .y respectively):

         %35 = OpAccessChain %_ptr_Input_float %32 %int_0 %uint_0
         %36 = OpLoad %float %35
         ...
         %46 = OpAccessChain %_ptr_Input_float %32 %int_0 %uint_1
         %47 = OpLoad %float %46

The expr_type initially contains a type with a vecsize of 4, and through the parent_type loop thing it get's changed to a type with a vecsize of 2 (I guess this is the original type).

I see two solutions for this, but I don't know how well they'd integrate with the codebase and other cases I might not be considering. Perhaps @cdavis5e can chip in here, as you wrote the original code I linked.
(1) The access_chain_internal function should somehow return which type the last access in the chain has, which in this case would simply be a float. Not sure how well this would work with the original use case for that block.
(2) There's perhaps some issue with the vector extension, and the expr_type should actually be a type with a vecsize of 1 and there's some bug somewhere else I don't know about. Or that the swizzling should only happen when we know the type was actually modified.

@cdavis5e
Copy link
Contributor

cdavis5e commented Mar 2, 2024

However, this doesn't work in this case, as the vector width is increased from 2 to 4

In that case, it was supposed to insert a .xy swizzle. In general, it's supposed to insert a swizzle based on the original variable type. Clearly, the code of mine you cited is wrong. I must have assumed that any OpAccessChain would terminate at the variable and not attempt to drill down into the vector.

Actually, in this case, since we're already chaining into the vector, we could probably avoid inserting the swizzle entirely.

@HansKristian-Work HansKristian-Work added the in progress Issue is being actively worked on label Mar 6, 2024
@HansKristian-Work
Copy link
Contributor

This seems more like a case of the type hierarchy for padded vectors being completely wrong, causing the code to think that the expressions vecsize is larger than what it's supposed to be, which causes it to insert the wrong swizzle.

@spnda
Copy link
Contributor Author

spnda commented Mar 6, 2024

I'll test the changes you made in a second locally. Thanks for fixing!

@spnda
Copy link
Contributor Author

spnda commented Mar 6, 2024

Yup, this fixed all related failures of maintenace4 related CTS tests. Thanks again!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in progress Issue is being actively worked on
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants