/src/skia/src/sksl/ir/SkSLConstructorScalarCast.cpp
Line | Count | Source |
1 | | /* |
2 | | * Copyright 2021 Google LLC |
3 | | * |
4 | | * Use of this source code is governed by a BSD-style license that can be |
5 | | * found in the LICENSE file. |
6 | | */ |
7 | | |
8 | | #include "src/sksl/ir/SkSLConstructorScalarCast.h" |
9 | | |
10 | | #include "include/core/SkTypes.h" |
11 | | #include "include/private/base/SkTArray.h" |
12 | | #include "src/sksl/SkSLConstantFolder.h" |
13 | | #include "src/sksl/SkSLContext.h" |
14 | | #include "src/sksl/SkSLDefines.h" |
15 | | #include "src/sksl/SkSLErrorReporter.h" |
16 | | #include "src/sksl/ir/SkSLLiteral.h" |
17 | | #include "src/sksl/ir/SkSLType.h" |
18 | | |
19 | | #include <string> |
20 | | |
21 | | namespace SkSL { |
22 | | |
23 | | std::unique_ptr<Expression> ConstructorScalarCast::Convert(const Context& context, |
24 | | Position pos, |
25 | | const Type& rawType, |
26 | 12.1k | ExpressionArray args) { |
27 | | // As you might expect, scalar-cast constructors should only be created with scalar types. |
28 | 12.1k | const Type& type = rawType.scalarTypeForLiteral(); |
29 | 12.1k | SkASSERT(type.isScalar()); |
30 | | |
31 | 12.1k | if (args.size() != 1) { |
32 | 458 | context.fErrors->error(pos, "invalid arguments to '" + type.displayName() + |
33 | 458 | "' constructor, (expected exactly 1 argument, but found " + |
34 | 458 | std::to_string(args.size()) + ")"); |
35 | 458 | return nullptr; |
36 | 458 | } |
37 | | |
38 | 11.6k | const Type& argType = args[0]->type(); |
39 | 11.6k | if (!argType.isScalar()) { |
40 | | // Casting a vector-type into its scalar component type is treated as a slice in GLSL. |
41 | | // We don't allow those casts in SkSL; recommend a .x swizzle instead. |
42 | 1.02k | const char* swizzleHint = ""; |
43 | 1.02k | if (argType.componentType().matches(type)) { |
44 | 160 | if (argType.isVector()) { |
45 | 86 | swizzleHint = "; use '.x' instead"; |
46 | 86 | } else if (argType.isMatrix()) { |
47 | 63 | swizzleHint = "; use '[0][0]' instead"; |
48 | 63 | } |
49 | 160 | } |
50 | | |
51 | 1.02k | context.fErrors->error(pos, |
52 | 1.02k | "'" + argType.displayName() + "' is not a valid parameter to '" + |
53 | 1.02k | type.displayName() + "' constructor" + swizzleHint); |
54 | 1.02k | return nullptr; |
55 | 1.02k | } |
56 | 10.6k | if (type.checkForOutOfRangeLiteral(context, *args[0])) { |
57 | 137 | return nullptr; |
58 | 137 | } |
59 | | |
60 | 10.5k | return ConstructorScalarCast::Make(context, pos, type, std::move(args[0])); |
61 | 10.6k | } SkSL::ConstructorScalarCast::Convert(SkSL::Context const&, SkSL::Position, SkSL::Type const&, SkSL::ExpressionArray) Line | Count | Source | 26 | 12.1k | ExpressionArray args) { | 27 | | // As you might expect, scalar-cast constructors should only be created with scalar types. | 28 | 12.1k | const Type& type = rawType.scalarTypeForLiteral(); | 29 | 12.1k | SkASSERT(type.isScalar()); | 30 | | | 31 | 12.1k | if (args.size() != 1) { | 32 | 458 | context.fErrors->error(pos, "invalid arguments to '" + type.displayName() + | 33 | 458 | "' constructor, (expected exactly 1 argument, but found " + | 34 | 458 | std::to_string(args.size()) + ")"); | 35 | 458 | return nullptr; | 36 | 458 | } | 37 | | | 38 | 11.6k | const Type& argType = args[0]->type(); | 39 | 11.6k | if (!argType.isScalar()) { | 40 | | // Casting a vector-type into its scalar component type is treated as a slice in GLSL. | 41 | | // We don't allow those casts in SkSL; recommend a .x swizzle instead. | 42 | 1.02k | const char* swizzleHint = ""; | 43 | 1.02k | if (argType.componentType().matches(type)) { | 44 | 160 | if (argType.isVector()) { | 45 | 86 | swizzleHint = "; use '.x' instead"; | 46 | 86 | } else if (argType.isMatrix()) { | 47 | 63 | swizzleHint = "; use '[0][0]' instead"; | 48 | 63 | } | 49 | 160 | } | 50 | | | 51 | 1.02k | context.fErrors->error(pos, | 52 | 1.02k | "'" + argType.displayName() + "' is not a valid parameter to '" + | 53 | 1.02k | type.displayName() + "' constructor" + swizzleHint); | 54 | 1.02k | return nullptr; | 55 | 1.02k | } | 56 | 10.6k | if (type.checkForOutOfRangeLiteral(context, *args[0])) { | 57 | 137 | return nullptr; | 58 | 137 | } | 59 | | | 60 | 10.5k | return ConstructorScalarCast::Make(context, pos, type, std::move(args[0])); | 61 | 10.6k | } |
Unexecuted instantiation: SkSL::ConstructorScalarCast::Convert(SkSL::Context const&, SkSL::Position, SkSL::Type const&, SkSL::ExpressionArray) |
62 | | |
63 | | std::unique_ptr<Expression> ConstructorScalarCast::Make(const Context& context, |
64 | | Position pos, |
65 | | const Type& type, |
66 | 59.5k | std::unique_ptr<Expression> arg) { |
67 | 59.5k | SkASSERT(type.isScalar()); |
68 | 59.5k | SkASSERT(type.isAllowedInES2(context)); |
69 | 59.5k | SkASSERT(arg->type().isScalar()); |
70 | | |
71 | | // No cast required when the types match. |
72 | 59.5k | if (arg->type().matches(type)) { |
73 | 102 | arg->setPosition(pos); |
74 | 102 | return arg; |
75 | 102 | } |
76 | | // Look up the value of constant variables. This allows constant-expressions like `int(zero)` to |
77 | | // be replaced with a literal zero. |
78 | 59.4k | arg = ConstantFolder::MakeConstantValueForVariable(pos, std::move(arg)); |
79 | | |
80 | | // We can cast scalar literals at compile-time when possible. (If the resulting literal would be |
81 | | // out of range for its type, we report an error and return zero to minimize error cascading. |
82 | | // This can occur when code is inlined, so we can't necessarily catch it during Convert. As |
83 | | // such, it's not safe to return null or assert.) |
84 | 59.4k | if (arg->is<Literal>()) { |
85 | 55.6k | double value = arg->as<Literal>().value(); |
86 | 55.6k | if (type.checkForOutOfRangeLiteral(context, value, arg->fPosition)) { |
87 | 51 | value = 0.0; |
88 | 51 | } |
89 | 55.6k | return Literal::Make(pos, value, &type); |
90 | 55.6k | } |
91 | | |
92 | | // We allow scalar casts to abstract types `$floatLiteral` or `$intLiteral`. This can be used to |
93 | | // represent various expressions where SkSL still allows type flexibility. For instance, the |
94 | | // expression `float x = myBool ? 1 : 0` is allowed in SkSL despite the apparent type mismatch, |
95 | | // and the resolved type of expression `myBool ? 1 : 0` is actually `$intLiteral`. This |
96 | | // expression could also be rewritten as `$intLiteral(myBool)` to replace a ternary with a cast. |
97 | | // |
98 | | // If we are casting an expression of the form `$intLiteral(...)` or `$floatLiteral(...)`, we |
99 | | // can eliminate the intermediate constructor-cast since it no longer adds value. |
100 | 3.83k | if (arg->is<ConstructorScalarCast>() && arg->type().isLiteral()) { |
101 | 95 | std::unique_ptr<Expression> inner = std::move(arg->as<ConstructorScalarCast>().argument()); |
102 | 95 | return ConstructorScalarCast::Make(context, pos, type, std::move(inner)); |
103 | 95 | } |
104 | | |
105 | 3.73k | return std::make_unique<ConstructorScalarCast>(pos, type, std::move(arg)); |
106 | 3.83k | } |
107 | | |
108 | | } // namespace SkSL |