Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
SkSLConstructor.cpp
Go to the documentation of this file.
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
9
15#include "src/sksl/SkSLString.h"
25
26namespace SkSL {
27
28static std::unique_ptr<Expression> convert_compound_constructor(const Context& context,
30 const Type& type,
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
151std::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
178std::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
193 SkASSERT(this->type().slotCount() == other.type().slotCount());
194
195 if (!other.supportsConstantValues()) {
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()) {
205 }
206 std::optional<double> right = other.getConstantValue(n);
207 if (!right.has_value()) {
209 }
210 // Both sides are known and can be compared for equality directly.
211 if (*left != *right) {
213 }
214 }
216}
217
219 SkASSERT(this->isAnyConstructor());
220 return static_cast<AnyConstructor&>(*this);
221}
222
224 SkASSERT(this->isAnyConstructor());
225 return static_cast<const AnyConstructor&>(*this);
226}
227
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
SkPoint pos
#define SkDEBUGFAIL(message)
Definition SkAssert.h:118
#define SkASSERT(cond)
Definition SkAssert.h:116
static bool left(const SkPoint &p0, const SkPoint &p1)
static bool right(const SkPoint &p0, const SkPoint &p1)
std::optional< double > getConstantValue(int n) const override
ComparisonResult compareConstant(const Expression &other) const override
virtual SkSpan< std::unique_ptr< Expression > > argumentSpan()=0
static std::unique_ptr< Expression > Convert(const Context &context, Position pos, const Type &type, ExpressionArray args)
static std::unique_ptr< Expression > Make(const Context &context, Position pos, const Type &type, std::unique_ptr< Expression > arg)
static std::unique_ptr< Expression > Make(const Context &context, Position pos, const Type &type, ExpressionArray args)
static std::unique_ptr< Expression > Make(const Context &context, Position pos, const Type &type, std::unique_ptr< Expression > arg)
static std::unique_ptr< Expression > Make(const Context &context, Position pos, const Type &type, std::unique_ptr< Expression > arg)
static std::unique_ptr< Expression > Convert(const Context &context, Position pos, const Type &rawType, ExpressionArray args)
static std::unique_ptr< Expression > Make(const Context &context, Position pos, const Type &type, std::unique_ptr< Expression > arg)
static std::unique_ptr< Expression > Convert(const Context &context, Position pos, const Type &type, ExpressionArray args)
ErrorReporter * fErrors
Definition SkSLContext.h:36
void error(Position position, std::string_view msg)
virtual const Type & type() const
virtual bool supportsConstantValues() const
virtual std::optional< double > getConstantValue(int n) const
std::string description() const final
AnyConstructor & asAnyConstructor()
bool isAnyConstructor() const
virtual int columns() const
Definition SkSLType.h:429
virtual size_t slotCount() const
Definition SkSLType.h:457
std::string description() const override
Definition SkSLType.h:238
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
GAsyncResult * result
std::string void void auto Separator()
Definition SkSLString.h:30
static std::unique_ptr< Expression > convert_compound_constructor(const Context &context, Position pos, const Type &type, ExpressionArray args)
OperatorPrecedence