Allow type-fluid GLSL-style vec2(int, bool) ctors in SkSL.
Note that GLSL accepts these sorts of constructors natively, but Metal
and SPIR-V do not. In the generated IR we actually add a cast for
subexpressions where the type does not match. These casts can be seen in
the final output for both GLSL (where they are no-ops) and Metal/SPIR-V
(where they are essential).
This change exposed some missing SPIR-V functionality (vector casts do
not support bool types). This can be fixed up in a followup CL; these
casts were previously disallowed by SkSL entirely, so there won't be any
of them in existing code.
Change-Id: I54ae922e91b38bed032537496428747a081dc774
Bug: skia:11164, skia:11171
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/353576
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: John Stiles <johnstiles@google.com>
Auto-Submit: John Stiles <johnstiles@google.com>
diff --git a/src/sksl/SkSLIRGenerator.cpp b/src/sksl/SkSLIRGenerator.cpp
index 62662c5..1e3e2bc 100644
--- a/src/sksl/SkSLIRGenerator.cpp
+++ b/src/sksl/SkSLIRGenerator.cpp
@@ -2126,7 +2126,7 @@
std::unique_ptr<Expression> IRGenerator::convertScalarConstructor(int offset,
const Type& type,
ExpressionArray args) {
- SkASSERT(type.isNumber() || type.isBoolean());
+ SkASSERT(type.isScalar());
if (args.size() != 1) {
this->errorReporter().error(
offset, "invalid arguments to '" + type.displayName() +
@@ -2135,31 +2135,20 @@
return nullptr;
}
- std::unique_ptr<Expression> converted = Constructor::SimplifyConversion(type, *args[0]);
- if (converted) {
- return converted;
- }
-
const Type& argType = args[0]->type();
- if (!argType.isNumber() && !argType.isBoolean()) {
+ if (!argType.isScalar()) {
this->errorReporter().error(
offset, "invalid argument to '" + type.displayName() +
"' constructor (expected a number or bool, but found '" +
argType.displayName() + "')");
return nullptr;
}
- return std::make_unique<Constructor>(offset, &type, std::move(args));
-}
-static int component_count(const Type& type) {
- switch (type.typeKind()) {
- case Type::TypeKind::kVector:
- return type.columns();
- case Type::TypeKind::kMatrix:
- return type.columns() * type.rows();
- default:
- return 1;
+ std::unique_ptr<Expression> converted = Constructor::SimplifyConversion(type, *args[0]);
+ if (converted) {
+ return converted;
}
+ return std::make_unique<Constructor>(offset, &type, std::move(args));
}
std::unique_ptr<Expression> IRGenerator::convertCompoundConstructor(int offset,
@@ -2167,49 +2156,62 @@
ExpressionArray args) {
SkASSERT(type.isVector() || type.isMatrix());
if (type.isMatrix() && args.size() == 1 && args[0]->type().isMatrix()) {
- // matrix from matrix is always legal
- return std::unique_ptr<Expression>(new Constructor(offset, &type, std::move(args)));
+ // Matrix-from-matrix is always legal.
+ return std::make_unique<Constructor>(offset, &type, std::move(args));
}
- int actual = 0;
+
+ if (args.size() == 1 && args[0]->type().isScalar()) {
+ // A constructor containing a single scalar is a splat (for vectors) or diagonal matrix (for
+ // matrices). In either event, it's legal regardless of the scalar's type. Synthesize an
+ // explicit conversion to the proper type (this is a no-op if it's unnecessary).
+ ExpressionArray castArgs;
+ castArgs.push_back(this->convertConstructor(offset, type.componentType(), std::move(args)));
+ return std::make_unique<Constructor>(offset, &type, std::move(castArgs));
+ }
+
int expected = type.rows() * type.columns();
- if (args.size() != 1 || expected != component_count(args[0]->type()) ||
- type.componentType().isNumber() != args[0]->type().componentType().isNumber()) {
- for (size_t i = 0; i < args.size(); i++) {
- const Type& argType = args[i]->type();
- if (argType.isVector()) {
- if (type.componentType().isNumber() !=
- argType.componentType().isNumber()) {
- this->errorReporter().error(offset,
- "'" + argType.displayName() +
+
+ if (type.isVector() && args.size() == 1 && args[0]->type().isVector() &&
+ args[0]->type().columns() == expected) {
+ // A vector constructor containing a single vector with the same number of columns is a
+ // cast (e.g. float3 -> int3).
+ return std::make_unique<Constructor>(offset, &type, std::move(args));
+ }
+
+ // For more complex cases, we walk the argument list and fix up the arguments as needed.
+ int actual = 0;
+ for (std::unique_ptr<Expression>& arg : args) {
+ if (!arg->type().isScalar() && !arg->type().isVector()) {
+ this->errorReporter().error(offset, "'" + arg->type().displayName() +
"' is not a valid parameter to '" +
type.displayName() + "' constructor");
- return nullptr;
- }
- actual += argType.columns();
- } else if (argType.isScalar()) {
- actual += 1;
- if (!type.isScalar()) {
- args[i] = this->coerce(std::move(args[i]), type.componentType());
- if (!args[i]) {
- return nullptr;
- }
- }
- } else {
- this->errorReporter().error(offset, "'" + argType.displayName() +
- "' is not a valid parameter to '" +
- type.displayName() + "' constructor");
- return nullptr;
- }
- }
- if (actual != 1 && actual != expected) {
- this->errorReporter().error(offset,
- "invalid arguments to '" + type.displayName() +
- "' constructor (expected " + to_string(expected) +
- " scalars, but found " + to_string(actual) + ")");
return nullptr;
}
+
+ // Rely on convertConstructor to force this subexpression to the proper type. If it's a
+ // literal, this will make sure it's the right type of literal. If an expression of
+ // matching type, the expression will be returned as-is. If it's an expression of
+ // mismatched type, this adds a cast.
+ int offset = arg->fOffset;
+ const Type& ctorType = type.componentType().toCompound(fContext, arg->type().columns(),
+ /*rows=*/1);
+ ExpressionArray ctorArg;
+ ctorArg.push_back(std::move(arg));
+ arg = this->convertConstructor(offset, ctorType, std::move(ctorArg));
+ if (!arg) {
+ return nullptr;
+ }
+ actual += ctorType.columns();
}
- return std::unique_ptr<Expression>(new Constructor(offset, &type, std::move(args)));
+
+ if (actual != expected) {
+ this->errorReporter().error(offset, "invalid arguments to '" + type.displayName() +
+ "' constructor (expected " + to_string(expected) +
+ " scalars, but found " + to_string(actual) + ")");
+ return nullptr;
+ }
+
+ return std::make_unique<Constructor>(offset, &type, std::move(args));
}
std::unique_ptr<Expression> IRGenerator::convertConstructor(int offset,
@@ -2220,7 +2222,7 @@
// Strip off redundant casts--i.e., convert Type(exprOfType) into exprOfType.
return std::move(args[0]);
}
- if (type.isNumber() || type.isBoolean()) {
+ if (type.isScalar()) {
return this->convertScalarConstructor(offset, type, std::move(args));
}
if (type.isVector() || type.isMatrix()) {
diff --git a/src/sksl/SkSLSPIRVCodeGenerator.cpp b/src/sksl/SkSLSPIRVCodeGenerator.cpp
index 52c99a5..fc2c759 100644
--- a/src/sksl/SkSLSPIRVCodeGenerator.cpp
+++ b/src/sksl/SkSLSPIRVCodeGenerator.cpp
@@ -1506,9 +1506,9 @@
if (argType.isVector()) {
// SPIR-V doesn't support vector(vector-of-different-type) directly, so we need to
// extract the components and convert them in that case manually. On top of that,
- // as of this writing there's a bug in the Intel Vulkan driver where OpCreateComposite
- // doesn't handle vector arguments at all, so we always extract vector components and
- // pass them into OpCreateComposite individually.
+ // as of this writing there's a bug in the Intel Vulkan driver where
+ // OpCompositeConstruct doesn't handle vector arguments at all, so we always extract
+ // vector components and pass them into OpCompositeConstruct individually.
SpvId vec = this->writeExpression(*c.arguments()[i], out);
SpvOp_ op = SpvOpUndef;
const Type& src = argType.componentType();
@@ -1527,7 +1527,9 @@
src == *fContext.fTypes.fUByte) {
op = SpvOpConvertUToF;
} else {
- SkASSERT(false);
+ fErrors.error(c.arguments()[i]->fOffset, "unsupported cast in SPIR-V: vector " +
+ src.description() + " to " +
+ dst.description());
}
} else if (dst == *fContext.fTypes.fInt ||
dst == *fContext.fTypes.fShort ||
@@ -1545,7 +1547,9 @@
src == *fContext.fTypes.fUByte) {
op = SpvOpBitcast;
} else {
- SkASSERT(false);
+ fErrors.error(c.arguments()[i]->fOffset, "unsupported cast in SPIR-V: vector " +
+ src.description() + " to " +
+ dst.description());
}
} else if (dst == *fContext.fTypes.fUInt ||
dst == *fContext.fTypes.fUShort ||
@@ -1563,7 +1567,9 @@
return vec;
}
} else {
- SkASSERT(false);
+ fErrors.error(c.arguments()[i]->fOffset, "unsupported cast in SPIR-V: vector " +
+ src.description() + " to " +
+ dst.description());
}
}
for (int j = 0; j < argType.columns(); j++) {
diff --git a/tests/sksl/fp/golden/GrMainCoords.cpp b/tests/sksl/fp/golden/GrMainCoords.cpp
index eafa859..3e1cac5 100644
--- a/tests/sksl/fp/golden/GrMainCoords.cpp
+++ b/tests/sksl/fp/golden/GrMainCoords.cpp
@@ -20,7 +20,7 @@
const GrMainCoords& _outer = args.fFp.cast<GrMainCoords>();
(void) _outer;
fragBuilder->codeAppendf(
-R"SkSL(return half4(%s, %s);
+R"SkSL(return half4(half2(%s), half2(%s));
)SkSL"
, args.fSampleCoord, args.fSampleCoord);
}
diff --git a/tests/sksl/shared/golden/VectorConstructors.asm.frag b/tests/sksl/shared/golden/VectorConstructors.asm.frag
index 0082e04..b4dc454 100644
--- a/tests/sksl/shared/golden/VectorConstructors.asm.frag
+++ b/tests/sksl/shared/golden/VectorConstructors.asm.frag
@@ -1,11 +1,4 @@
### Compilation failed:
-error: 10: expected 'int', but found 'float'
-error: 12: expected 'float', but found 'bool'
-error: 13: expected 'float', but found 'bool'
-error: 14: 'bool2' is not a valid parameter to 'float2' constructor
-error: 15: expected 'bool', but found 'float'
-error: 16: 'float2' is not a valid parameter to 'bool2' constructor
-error: 17: expected 'bool', but found 'float'
-error: 21: unknown identifier 'v10'
-8 errors
+error: 14: unsupported cast in SPIR-V: vector bool to float
+1 error
diff --git a/tests/sksl/shared/golden/VectorConstructors.glsl b/tests/sksl/shared/golden/VectorConstructors.glsl
index 0082e04..7737938 100644
--- a/tests/sksl/shared/golden/VectorConstructors.glsl
+++ b/tests/sksl/shared/golden/VectorConstructors.glsl
@@ -1,11 +1,22 @@
-### Compilation failed:
-error: 10: expected 'int', but found 'float'
-error: 12: expected 'float', but found 'bool'
-error: 13: expected 'float', but found 'bool'
-error: 14: 'bool2' is not a valid parameter to 'float2' constructor
-error: 15: expected 'bool', but found 'float'
-error: 16: 'float2' is not a valid parameter to 'bool2' constructor
-error: 17: expected 'bool', but found 'float'
-error: 21: unknown identifier 'v10'
-8 errors
+out vec4 sk_FragColor;
+vec2 v1 = vec2(1.0);
+vec2 v2 = vec2(1.0, 2.0);
+vec2 v3 = vec2(1.0);
+vec3 v4 = vec3(vec2(1.0), 1.0);
+ivec2 v5 = ivec2(1);
+ivec2 v6 = ivec2(vec2(1.0, 2.0));
+vec2 v7 = vec2(ivec2(1, 2));
+vec2 v8 = vec2(v5);
+vec4 v9 = vec4(float(v6.x), sqrt(2.0), vec2(ivec2(3, 4)));
+ivec2 v10 = ivec2(3, int(v1.x));
+bvec4 v11 = bvec4(bvec2(true, false), true, false);
+vec2 v12 = vec2(1.0, 0.0);
+vec2 v13 = vec2(0.0);
+vec2 v14 = vec2(bvec2(false));
+bvec2 v15 = bvec2(true);
+bvec2 v16 = bvec2(vec2(1.0));
+bvec3 v17 = bvec3(true, bvec2(ivec2(77)));
+void main() {
+ sk_FragColor.x = (((((((((((((((v1.x + v2.x) + v3.x) + v4.x) + float(v5.x)) + float(v6.x)) + v7.x) + v8.x) + v9.x) + float(v10.x)) + float(v11.x)) + v12.x) + v13.x) + v14.x) + float(v15.x)) + float(v16.x)) + float(v17.x);
+}
diff --git a/tests/sksl/shared/golden/VectorConstructors.metal b/tests/sksl/shared/golden/VectorConstructors.metal
index 0082e04..9626c59 100644
--- a/tests/sksl/shared/golden/VectorConstructors.metal
+++ b/tests/sksl/shared/golden/VectorConstructors.metal
@@ -1,11 +1,53 @@
-### Compilation failed:
+#include <metal_stdlib>
+#include <simd/simd.h>
+using namespace metal;
+struct Inputs {
+};
+struct Outputs {
+ float4 sk_FragColor [[color(0)]];
+};
+struct Globals {
+ float2 v1;
+ float2 v2;
+ float2 v3;
+ float3 v4;
+ int2 v5;
+ int2 v6;
+ float2 v7;
+ float2 v8;
+ float4 v9;
+ int2 v10;
+ bool4 v11;
+ float2 v12;
+ float2 v13;
+ float2 v14;
+ bool2 v15;
+ bool2 v16;
+ bool3 v17;
+};
-error: 10: expected 'int', but found 'float'
-error: 12: expected 'float', but found 'bool'
-error: 13: expected 'float', but found 'bool'
-error: 14: 'bool2' is not a valid parameter to 'float2' constructor
-error: 15: expected 'bool', but found 'float'
-error: 16: 'float2' is not a valid parameter to 'bool2' constructor
-error: 17: expected 'bool', but found 'float'
-error: 21: unknown identifier 'v10'
-8 errors
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+fragment Outputs fragmentMain(Inputs _in [[stage_in]], bool _frontFacing [[front_facing]], float4 _fragCoord [[position]]) {
+ Globals globalStruct{float2(1.0), float2(1.0, 2.0), float2(1.0), float3(float2(1.0), 1.0), int2(1), int2(float2(1.0, 2.0)), float2(int2(1, 2)), float2(_globals->v5), float4(float(_globals->v6.x), sqrt(2.0), float2(int2(3, 4))), int2(3, int(_globals->v1.x)), bool4(bool2(true, false), true, false), float2(1.0, 0.0), float2(0.0), float2(bool2(false)), bool2(true), bool2(float2(1.0)), bool3(true, bool2(int2(77)))};
+ thread Globals* _globals = &globalStruct;
+ (void)_globals;
+ Outputs _outputStruct;
+ thread Outputs* _out = &_outputStruct;
+ _out->sk_FragColor.x = (((((((((((((((_globals->v1.x + _globals->v2.x) + _globals->v3.x) + _globals->v4.x) + float(_globals->v5.x)) + float(_globals->v6.x)) + _globals->v7.x) + _globals->v8.x) + _globals->v9.x) + float(_globals->v10.x)) + float(_globals->v11.x)) + _globals->v12.x) + _globals->v13.x) + _globals->v14.x) + float(_globals->v15.x)) + float(_globals->v16.x)) + float(_globals->v17.x);
+ return *_out;
+}