xref: /aosp_15_r20/external/skia/src/sksl/ir/SkSLConstructor.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2020 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/SkSLConstructor.h"
9 
10 #include "include/core/SkTypes.h"
11 #include "include/private/base/SkTArray.h"
12 #include "src/sksl/SkSLContext.h"
13 #include "src/sksl/SkSLErrorReporter.h"
14 #include "src/sksl/SkSLOperator.h"
15 #include "src/sksl/SkSLString.h"
16 #include "src/sksl/ir/SkSLConstructorArray.h"
17 #include "src/sksl/ir/SkSLConstructorCompound.h"
18 #include "src/sksl/ir/SkSLConstructorCompoundCast.h"
19 #include "src/sksl/ir/SkSLConstructorDiagonalMatrix.h"
20 #include "src/sksl/ir/SkSLConstructorMatrixResize.h"
21 #include "src/sksl/ir/SkSLConstructorScalarCast.h"
22 #include "src/sksl/ir/SkSLConstructorSplat.h"
23 #include "src/sksl/ir/SkSLConstructorStruct.h"
24 #include "src/sksl/ir/SkSLType.h"
25 
26 namespace SkSL {
27 
convert_compound_constructor(const Context & context,Position pos,const Type & type,ExpressionArray args)28 static std::unique_ptr<Expression> convert_compound_constructor(const Context& context,
29                                                                 Position pos,
30                                                                 const Type& type,
31                                                                 ExpressionArray args) {
32     SkASSERT(type.isVector() || type.isMatrix());
33 
34     // The meaning of a compound constructor containing a single argument varies significantly in
35     // GLSL/SkSL, depending on the argument type.
36     if (args.size() == 1) {
37         std::unique_ptr<Expression>& argument = args.front();
38         if (type.isVector() && argument->type().isVector() &&
39             argument->type().componentType().matches(type.componentType()) &&
40             argument->type().slotCount() > type.slotCount()) {
41             // Casting a vector-type into a smaller matching vector-type is a slice in GLSL.
42             // We don't allow those casts in SkSL; recommend a swizzle instead.
43             // Only `.xy` and `.xyz` are valid recommendations here, because `.x` would imply a
44             // scalar(vector) cast, and nothing has more slots than `.xyzw`.
45             const char* swizzleHint;
46             switch (type.slotCount()) {
47                 case 2:  swizzleHint = "; use '.xy' instead"; break;
48                 case 3:  swizzleHint = "; use '.xyz' instead"; break;
49                 default: swizzleHint = ""; SkDEBUGFAIL("unexpected slicing cast"); break;
50             }
51 
52             context.fErrors->error(pos, "'" + argument->type().displayName() +
53                     "' is not a valid parameter to '" + type.displayName() + "' constructor" +
54                     swizzleHint);
55             return nullptr;
56         }
57 
58         if (argument->type().isScalar()) {
59             // A constructor containing a single scalar is a splat (for vectors) or diagonal matrix
60             // (for matrices). It's legal regardless of the scalar's type, so synthesize an explicit
61             // conversion to the proper type. (This cast is a no-op if it's unnecessary; it can fail
62             // if we're casting a literal that exceeds the limits of the type.)
63             std::unique_ptr<Expression> typecast = ConstructorScalarCast::Convert(
64                         context, pos, type.componentType(), std::move(args));
65             if (!typecast) {
66                 return nullptr;
67             }
68 
69             // Matrix-from-scalar creates a diagonal matrix; vector-from-scalar creates a splat.
70             return type.isMatrix()
71                        ? ConstructorDiagonalMatrix::Make(context, pos, type, std::move(typecast))
72                        : ConstructorSplat::Make(context, pos, type, std::move(typecast));
73         } else if (argument->type().isVector()) {
74             // A vector constructor containing a single vector with the same number of columns is a
75             // cast (e.g. float3 -> int3).
76             if (type.isVector() && argument->type().columns() == type.columns()) {
77                 return ConstructorCompoundCast::Make(context, pos, type, std::move(argument));
78             }
79         } else if (argument->type().isMatrix()) {
80             // A matrix constructor containing a single matrix can be a resize, typecast, or both.
81             // GLSL lumps these into one category, but internally SkSL keeps them distinct.
82             if (type.isMatrix()) {
83                 // First, handle type conversion. If the component types differ, synthesize the
84                 // destination type with the argument's rows/columns. (This will be a no-op if it's
85                 // already the right type.)
86                 const Type& typecastType = type.componentType().toCompound(
87                         context,
88                         argument->type().columns(),
89                         argument->type().rows());
90                 argument = ConstructorCompoundCast::Make(context, pos, typecastType,
91                                                          std::move(argument));
92 
93                 // Casting a matrix type into another matrix type is a resize.
94                 return ConstructorMatrixResize::Make(context, pos, type,
95                                                      std::move(argument));
96             }
97 
98             // A vector constructor containing a single matrix can be compound construction if the
99             // matrix is 2x2 and the vector is 4-slot.
100             if (type.isVector() && type.columns() == 4 && argument->type().slotCount() == 4) {
101                 // Casting a 2x2 matrix to a vector is a form of compound construction.
102                 // First, reshape the matrix into a 4-slot vector of the same type.
103                 const Type& vectorType = argument->type().componentType().toCompound(context,
104                                                                                      /*columns=*/4,
105                                                                                      /*rows=*/1);
106                 std::unique_ptr<Expression> vecCtor =
107                         ConstructorCompound::Make(context, pos, vectorType, std::move(args));
108 
109                 // Then, add a typecast to the result expression to ensure the types match.
110                 // This will be a no-op if no typecasting is needed.
111                 return ConstructorCompoundCast::Make(context, pos, type, std::move(vecCtor));
112             }
113         }
114     }
115 
116     // For more complex cases, we walk the argument list and fix up the arguments as needed.
117     int expected = type.rows() * type.columns();
118     int actual = 0;
119     for (std::unique_ptr<Expression>& arg : args) {
120         if (!arg->type().isScalar() && !arg->type().isVector()) {
121             context.fErrors->error(pos, "'" + arg->type().displayName() +
122                     "' is not a valid parameter to '" + type.displayName() + "' constructor");
123             return nullptr;
124         }
125 
126         // Rely on Constructor::Convert to force this subexpression to the proper type. If it's a
127         // literal, this will make sure it's the right type of literal. If an expression of matching
128         // type, the expression will be returned as-is. If it's an expression of mismatched type,
129         // this adds a cast.
130         const Type& ctorType = type.componentType().toCompound(context, arg->type().columns(),
131                                                                /*rows=*/1);
132         ExpressionArray ctorArg;
133         ctorArg.push_back(std::move(arg));
134         arg = Constructor::Convert(context, pos, ctorType, std::move(ctorArg));
135         if (!arg) {
136             return nullptr;
137         }
138         actual += ctorType.columns();
139     }
140 
141     if (actual != expected) {
142         context.fErrors->error(pos, "invalid arguments to '" + type.displayName() +
143                                      "' constructor (expected " + std::to_string(expected) +
144                                      " scalars, but found " + std::to_string(actual) + ")");
145         return nullptr;
146     }
147 
148     return ConstructorCompound::Make(context, pos, type, std::move(args));
149 }
150 
Convert(const Context & context,Position pos,const Type & type,ExpressionArray args)151 std::unique_ptr<Expression> Constructor::Convert(const Context& context,
152                                                  Position pos,
153                                                  const Type& type,
154                                                  ExpressionArray args) {
155     if (args.size() == 1 && args[0]->type().matches(type) && !type.componentType().isOpaque()) {
156         // Don't generate redundant casts; if the expression is already of the correct type, just
157         // return it as-is.
158         args[0]->fPosition = pos;
159         return std::move(args[0]);
160     }
161     if (type.isScalar()) {
162         return ConstructorScalarCast::Convert(context, pos, type, std::move(args));
163     }
164     if (type.isVector() || type.isMatrix()) {
165         return convert_compound_constructor(context, pos, type, std::move(args));
166     }
167     if (type.isArray() && type.columns() > 0) {
168         return ConstructorArray::Convert(context, pos, type, std::move(args));
169     }
170     if (type.isStruct() && type.fields().size() > 0) {
171         return ConstructorStruct::Convert(context, pos, type, std::move(args));
172     }
173 
174     context.fErrors->error(pos, "cannot construct '" + type.displayName() + "'");
175     return nullptr;
176 }
177 
getConstantValue(int n) const178 std::optional<double> AnyConstructor::getConstantValue(int n) const {
179     SkASSERT(n >= 0 && n < (int)this->type().slotCount());
180     for (const std::unique_ptr<Expression>& arg : this->argumentSpan()) {
181         int argSlots = arg->type().slotCount();
182         if (n < argSlots) {
183             return arg->getConstantValue(n);
184         }
185         n -= argSlots;
186     }
187 
188     SkDEBUGFAIL("argument-list slot count doesn't match constructor-type slot count");
189     return std::nullopt;
190 }
191 
compareConstant(const Expression & other) const192 Expression::ComparisonResult AnyConstructor::compareConstant(const Expression& other) const {
193     SkASSERT(this->type().slotCount() == other.type().slotCount());
194 
195     if (!other.supportsConstantValues()) {
196         return ComparisonResult::kUnknown;
197     }
198 
199     int exprs = this->type().slotCount();
200     for (int n = 0; n < exprs; ++n) {
201         // Get the n'th subexpression from each side. If either one is null, return "unknown."
202         std::optional<double> left = this->getConstantValue(n);
203         if (!left.has_value()) {
204             return ComparisonResult::kUnknown;
205         }
206         std::optional<double> right = other.getConstantValue(n);
207         if (!right.has_value()) {
208             return ComparisonResult::kUnknown;
209         }
210         // Both sides are known and can be compared for equality directly.
211         if (*left != *right) {
212             return ComparisonResult::kNotEqual;
213         }
214     }
215     return ComparisonResult::kEqual;
216 }
217 
asAnyConstructor()218 AnyConstructor& Expression::asAnyConstructor() {
219     SkASSERT(this->isAnyConstructor());
220     return static_cast<AnyConstructor&>(*this);
221 }
222 
asAnyConstructor() const223 const AnyConstructor& Expression::asAnyConstructor() const {
224     SkASSERT(this->isAnyConstructor());
225     return static_cast<const AnyConstructor&>(*this);
226 }
227 
description(OperatorPrecedence) const228 std::string AnyConstructor::description(OperatorPrecedence) const {
229     std::string result = this->type().description() + "(";
230     auto separator = SkSL::String::Separator();
231     for (const std::unique_ptr<Expression>& arg : this->argumentSpan()) {
232         result += separator();
233         result += arg->description(OperatorPrecedence::kSequence);
234     }
235     result.push_back(')');
236     return result;
237 }
238 
239 }  // namespace SkSL
240