Add support for mat2-to-ivec4 conversions in SkSL.

The fuzzer quickly discovered that the newly introduced mat2-to-vec4
conversion code did not account for integer vectors. We now handle
`ivec4(mat2)` casts properly. This required some non-trivial
restructuring of the logic, but in the vast majority of cases, the types
will match and the end result will be identical.

Change-Id: If07c2fe4b4345bd767384b1802374910f65cd3f0
Bug: oss-fuzz:35998
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/426756
Auto-Submit: John Stiles <johnstiles@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: John Stiles <johnstiles@google.com>
This commit is contained in:
John Stiles 2021-07-10 11:45:29 -04:00 committed by Skia Commit-Bot
parent ac69e12664
commit f9ad6ec852
9 changed files with 108 additions and 34 deletions

View File

@ -70,6 +70,7 @@ sksl_error_tests = [
"/sksl/errors/LayoutMultiplePrimitiveTypes.sksl",
"/sksl/errors/LayoutRepeatedQualifiers.sksl",
"/sksl/errors/MatrixToVectorCast3x3.sksl",
"/sksl/errors/MatrixToVectorCastInteger.sksl",
"/sksl/errors/MatrixToVectorCastTooSmall.sksl",
"/sksl/errors/MismatchedNumbers.sksl",
"/sksl/errors/ModifiersInStruct.sksl",

View File

@ -0,0 +1,7 @@
// Expect 3 errors
const half2x2 testMatrix2x2 = half2x2(1, 2, 3, 4);
int testScalar = int (testMatrix2x2);
int2 testVec2 = int2(testMatrix2x2);
int3 testVec3 = int3(testMatrix2x2);
int4 testVec4 = int4(testMatrix2x2); // not an error

View File

@ -14,5 +14,9 @@ half4 main(float2 coords) {
ok = ok && float4(half2x2(1, 2, 3, 4)) == float4(1, 2, 3, 4);
ok = ok && float4(mat1234) == float4(1, 2, 3, 4);
ok = ok && int4(testMatrix2x2) == int4(1, 2, 3, 4);
ok = ok && int4(half2x2(1, 2, 3, 4)) == int4(1, 2, 3, 4);
ok = ok && int4(mat1234) == int4(1, 2, 3, 4);
return ok ? colorGreen : colorRed;
}

View File

@ -51,9 +51,9 @@ static std::unique_ptr<Expression> convert_compound_constructor(const Context& c
return ConstructorCompoundCast::Make(context, offset, type, std::move(argument));
}
} else if (argument->type().isMatrix()) {
// A vector or matrix constructor containing a single matrix can be a resize, typecast,
// or both. GLSL lumps these into one category, but internally SkSL keeps them distinct.
if (type.isVector() || type.isMatrix()) {
// A matrix constructor containing a single matrix can be a resize, typecast, or both.
// GLSL lumps these into one category, but internally SkSL keeps them distinct.
if (type.isMatrix()) {
// First, handle type conversion. If the component types differ, synthesize the
// destination type with the argument's rows/columns. (This will be a no-op if it's
// already the right type.)
@ -64,17 +64,25 @@ static std::unique_ptr<Expression> convert_compound_constructor(const Context& c
argument = ConstructorCompoundCast::Make(context, offset, typecastType,
std::move(argument));
// Next, wrap the typecasted expression in another constructor depending on its
// type.
if (type.isMatrix()) {
// Casting a matrix type into another matrix type is a resize.
return ConstructorMatrixResize::Make(context, offset, type,
std::move(argument));
}
if (type.isVector() && type.columns() == 4 && argument->type().slotCount() == 4) {
// Casting a 2x2 matrix into a 4-slot vector is compound construction.
return ConstructorCompound::Make(context, offset, type, std::move(args));
}
// Casting a matrix type into another matrix type is a resize.
return ConstructorMatrixResize::Make(context, offset, type,
std::move(argument));
}
// A vector constructor containing a single matrix can be compound construction if the
// matrix is 2x2 and the vector is 4-slot.
if (type.isVector() && type.columns() == 4 && argument->type().slotCount() == 4) {
// Casting a 2x2 matrix to a vector is a form of compound construction.
// First, reshape the matrix into a 4-slot vector of the same type.
const Type& vectorType = argument->type().componentType().toCompound(context,
/*columns=*/4,
/*rows=*/1);
std::unique_ptr<Expression> vecCtor =
ConstructorCompound::Make(context, offset, vectorType, std::move(args));
// Then, add a typecast to the result expression to ensure the types match.
// This will be a no-op if no typecasting is needed.
return ConstructorCompoundCast::Make(context, offset, type, std::move(vecCtor));
}
}
}

View File

@ -52,10 +52,10 @@ static std::unique_ptr<Expression> cast_constant_composite(const Context& contex
std::move(arg)));
} else {
// Convert inner constant-composites recursively.
SkASSERT(argType.isVector());
SkASSERT(argType.isVector() || (argType.isMatrix() && argType.slotCount() == 4));
typecastArgs.push_back(cast_constant_composite(
context,
scalarType.toCompound(context, /*columns=*/argType.columns(), /*rows=*/1),
scalarType.toCompound(context, /*columns=*/argType.slotCount(), /*rows=*/1),
std::move(arg)));
}
}

View File

@ -0,0 +1,6 @@
### Compilation failed:
error: 4: invalid argument to 'int' constructor (expected a number or bool, but found 'half2x2')
error: 5: 'half2x2' is not a valid parameter to 'int2' constructor
error: 6: 'half2x2' is not a valid parameter to 'int3' constructor
3 errors

View File

@ -37,10 +37,26 @@ OpDecorate %43 RelaxedPrecision
OpDecorate %48 RelaxedPrecision
OpDecorate %53 RelaxedPrecision
OpDecorate %57 RelaxedPrecision
OpDecorate %58 RelaxedPrecision
OpDecorate %59 RelaxedPrecision
OpDecorate %60 RelaxedPrecision
OpDecorate %61 RelaxedPrecision
OpDecorate %62 RelaxedPrecision
OpDecorate %66 RelaxedPrecision
OpDecorate %70 RelaxedPrecision
OpDecorate %71 RelaxedPrecision
OpDecorate %72 RelaxedPrecision
OpDecorate %73 RelaxedPrecision
OpDecorate %74 RelaxedPrecision
OpDecorate %75 RelaxedPrecision
OpDecorate %76 RelaxedPrecision
OpDecorate %78 RelaxedPrecision
OpDecorate %79 RelaxedPrecision
OpDecorate %80 RelaxedPrecision
OpDecorate %82 RelaxedPrecision
OpDecorate %93 RelaxedPrecision
OpDecorate %102 RelaxedPrecision
OpDecorate %104 RelaxedPrecision
OpDecorate %105 RelaxedPrecision
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Output_v4float = OpTypePointer Output %v4float
@ -71,10 +87,14 @@ OpDecorate %79 RelaxedPrecision
%float_4 = OpConstant %float 4
%48 = OpConstantComposite %v4float %float_1 %float_2 %float_3 %float_4
%v4bool = OpTypeVector %bool 4
%v4int = OpTypeVector %int 4
%int_1 = OpConstant %int 1
%int_3 = OpConstant %int 3
%int_4 = OpConstant %int 4
%89 = OpConstantComposite %v4int %int_1 %int_2 %int_3 %int_4
%_ptr_Function_v4float = OpTypePointer Function %v4float
%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float
%int_0 = OpConstant %int 0
%int_1 = OpConstant %int 1
%_entrypoint_v = OpFunction %void None %17
%18 = OpLabel
%21 = OpVariable %_ptr_Function_v2float Function
@ -87,7 +107,7 @@ OpFunctionEnd
%25 = OpFunctionParameter %_ptr_Function_v2float
%26 = OpLabel
%ok = OpVariable %_ptr_Function_bool Function
%67 = OpVariable %_ptr_Function_v4float Function
%94 = OpVariable %_ptr_Function_v4float Function
OpStore %ok %true
%31 = OpLoad %bool %ok
OpSelectionMerge %33 None
@ -124,19 +144,45 @@ OpBranch %55
%65 = OpPhi %bool %false %33 %64 %54
OpStore %ok %65
%66 = OpLoad %bool %ok
OpSelectionMerge %71 None
OpBranchConditional %66 %69 %70
%69 = OpLabel
%72 = OpAccessChain %_ptr_Uniform_v4float %10 %int_0
%75 = OpLoad %v4float %72
OpStore %67 %75
OpBranch %71
%70 = OpLabel
%76 = OpAccessChain %_ptr_Uniform_v4float %10 %int_1
%78 = OpLoad %v4float %76
OpStore %67 %78
OpBranch %71
%71 = OpLabel
%79 = OpLoad %v4float %67
OpReturnValue %79
OpSelectionMerge %68 None
OpBranchConditional %66 %67 %68
%67 = OpLabel
%69 = OpAccessChain %_ptr_Uniform_mat2v2float %10 %int_2
%70 = OpLoad %mat2v2float %69
%71 = OpCompositeExtract %float %70 0 0
%72 = OpCompositeExtract %float %70 0 1
%73 = OpCompositeExtract %float %70 1 0
%74 = OpCompositeExtract %float %70 1 1
%75 = OpCompositeConstruct %v4float %71 %72 %73 %74
%76 = OpCompositeExtract %float %75 0
%77 = OpConvertFToS %int %76
%78 = OpCompositeExtract %float %75 1
%79 = OpConvertFToS %int %78
%80 = OpCompositeExtract %float %75 2
%81 = OpConvertFToS %int %80
%82 = OpCompositeExtract %float %75 3
%83 = OpConvertFToS %int %82
%84 = OpCompositeConstruct %v4int %77 %79 %81 %83
%90 = OpIEqual %v4bool %84 %89
%91 = OpAll %bool %90
OpBranch %68
%68 = OpLabel
%92 = OpPhi %bool %false %55 %91 %67
OpStore %ok %92
%93 = OpLoad %bool %ok
OpSelectionMerge %98 None
OpBranchConditional %93 %96 %97
%96 = OpLabel
%99 = OpAccessChain %_ptr_Uniform_v4float %10 %int_0
%102 = OpLoad %v4float %99
OpStore %94 %102
OpBranch %98
%97 = OpLabel
%103 = OpAccessChain %_ptr_Uniform_v4float %10 %int_1
%104 = OpLoad %v4float %103
OpStore %94 %104
OpBranch %98
%98 = OpLabel
%105 = OpLoad %v4float %94
OpReturnValue %105
OpFunctionEnd

View File

@ -7,5 +7,6 @@ vec4 main() {
bool ok = true;
ok = ok && vec4(testMatrix2x2) == vec4(1.0, 2.0, 3.0, 4.0);
ok = ok && vec4(testMatrix2x2) == vec4(1.0, 2.0, 3.0, 4.0);
ok = ok && ivec4(vec4(testMatrix2x2)) == ivec4(1, 2, 3, 4);
return ok ? colorGreen : colorRed;
}

View File

@ -20,6 +20,7 @@ fragment Outputs fragmentMain(Inputs _in [[stage_in]], constant Uniforms& _unifo
bool ok = true;
ok = ok && all(float4_from_float2x2(_uniforms.testMatrix2x2) == float4(1.0, 2.0, 3.0, 4.0));
ok = ok && all(float4_from_float2x2(_uniforms.testMatrix2x2) == float4(1.0, 2.0, 3.0, 4.0));
ok = ok && all(int4(float4_from_float2x2(_uniforms.testMatrix2x2)) == int4(1, 2, 3, 4));
_out.sk_FragColor = ok ? _uniforms.colorGreen : _uniforms.colorRed;
return _out;
}