xref: /aosp_15_r20/external/skia/src/sksl/ir/SkSLSwizzle.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2021 Google LLC
3*c8dee2aaSAndroid Build Coastguard Worker  *
4*c8dee2aaSAndroid Build Coastguard Worker  * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker  * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker  */
7*c8dee2aaSAndroid Build Coastguard Worker 
8*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLSwizzle.h"
9*c8dee2aaSAndroid Build Coastguard Worker 
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSpan.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTArray.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLAnalysis.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLConstantFolder.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLContext.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLDefines.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLErrorReporter.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLOperator.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLString.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLConstructorCompound.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLConstructorCompoundCast.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLConstructorScalarCast.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLConstructorSplat.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLLiteral.h"
24*c8dee2aaSAndroid Build Coastguard Worker 
25*c8dee2aaSAndroid Build Coastguard Worker #include <algorithm>
26*c8dee2aaSAndroid Build Coastguard Worker #include <cstdint>
27*c8dee2aaSAndroid Build Coastguard Worker #include <cstring>
28*c8dee2aaSAndroid Build Coastguard Worker #include <optional>
29*c8dee2aaSAndroid Build Coastguard Worker 
30*c8dee2aaSAndroid Build Coastguard Worker using namespace skia_private;
31*c8dee2aaSAndroid Build Coastguard Worker 
32*c8dee2aaSAndroid Build Coastguard Worker namespace SkSL {
33*c8dee2aaSAndroid Build Coastguard Worker 
validate_swizzle_domain(const ComponentArray & fields)34*c8dee2aaSAndroid Build Coastguard Worker static bool validate_swizzle_domain(const ComponentArray& fields) {
35*c8dee2aaSAndroid Build Coastguard Worker     enum SwizzleDomain {
36*c8dee2aaSAndroid Build Coastguard Worker         kCoordinate,
37*c8dee2aaSAndroid Build Coastguard Worker         kColor,
38*c8dee2aaSAndroid Build Coastguard Worker         kUV,
39*c8dee2aaSAndroid Build Coastguard Worker         kRectangle,
40*c8dee2aaSAndroid Build Coastguard Worker     };
41*c8dee2aaSAndroid Build Coastguard Worker 
42*c8dee2aaSAndroid Build Coastguard Worker     std::optional<SwizzleDomain> domain;
43*c8dee2aaSAndroid Build Coastguard Worker 
44*c8dee2aaSAndroid Build Coastguard Worker     for (int8_t field : fields) {
45*c8dee2aaSAndroid Build Coastguard Worker         SwizzleDomain fieldDomain;
46*c8dee2aaSAndroid Build Coastguard Worker         switch (field) {
47*c8dee2aaSAndroid Build Coastguard Worker             case SwizzleComponent::X:
48*c8dee2aaSAndroid Build Coastguard Worker             case SwizzleComponent::Y:
49*c8dee2aaSAndroid Build Coastguard Worker             case SwizzleComponent::Z:
50*c8dee2aaSAndroid Build Coastguard Worker             case SwizzleComponent::W:
51*c8dee2aaSAndroid Build Coastguard Worker                 fieldDomain = kCoordinate;
52*c8dee2aaSAndroid Build Coastguard Worker                 break;
53*c8dee2aaSAndroid Build Coastguard Worker             case SwizzleComponent::R:
54*c8dee2aaSAndroid Build Coastguard Worker             case SwizzleComponent::G:
55*c8dee2aaSAndroid Build Coastguard Worker             case SwizzleComponent::B:
56*c8dee2aaSAndroid Build Coastguard Worker             case SwizzleComponent::A:
57*c8dee2aaSAndroid Build Coastguard Worker                 fieldDomain = kColor;
58*c8dee2aaSAndroid Build Coastguard Worker                 break;
59*c8dee2aaSAndroid Build Coastguard Worker             case SwizzleComponent::S:
60*c8dee2aaSAndroid Build Coastguard Worker             case SwizzleComponent::T:
61*c8dee2aaSAndroid Build Coastguard Worker             case SwizzleComponent::P:
62*c8dee2aaSAndroid Build Coastguard Worker             case SwizzleComponent::Q:
63*c8dee2aaSAndroid Build Coastguard Worker                 fieldDomain = kUV;
64*c8dee2aaSAndroid Build Coastguard Worker                 break;
65*c8dee2aaSAndroid Build Coastguard Worker             case SwizzleComponent::UL:
66*c8dee2aaSAndroid Build Coastguard Worker             case SwizzleComponent::UT:
67*c8dee2aaSAndroid Build Coastguard Worker             case SwizzleComponent::UR:
68*c8dee2aaSAndroid Build Coastguard Worker             case SwizzleComponent::UB:
69*c8dee2aaSAndroid Build Coastguard Worker                 fieldDomain = kRectangle;
70*c8dee2aaSAndroid Build Coastguard Worker                 break;
71*c8dee2aaSAndroid Build Coastguard Worker             case SwizzleComponent::ZERO:
72*c8dee2aaSAndroid Build Coastguard Worker             case SwizzleComponent::ONE:
73*c8dee2aaSAndroid Build Coastguard Worker                 continue;
74*c8dee2aaSAndroid Build Coastguard Worker             default:
75*c8dee2aaSAndroid Build Coastguard Worker                 return false;
76*c8dee2aaSAndroid Build Coastguard Worker         }
77*c8dee2aaSAndroid Build Coastguard Worker 
78*c8dee2aaSAndroid Build Coastguard Worker         if (!domain.has_value()) {
79*c8dee2aaSAndroid Build Coastguard Worker             domain = fieldDomain;
80*c8dee2aaSAndroid Build Coastguard Worker         } else if (domain != fieldDomain) {
81*c8dee2aaSAndroid Build Coastguard Worker             return false;
82*c8dee2aaSAndroid Build Coastguard Worker         }
83*c8dee2aaSAndroid Build Coastguard Worker     }
84*c8dee2aaSAndroid Build Coastguard Worker 
85*c8dee2aaSAndroid Build Coastguard Worker     return true;
86*c8dee2aaSAndroid Build Coastguard Worker }
87*c8dee2aaSAndroid Build Coastguard Worker 
mask_char(int8_t component)88*c8dee2aaSAndroid Build Coastguard Worker static char mask_char(int8_t component) {
89*c8dee2aaSAndroid Build Coastguard Worker     switch (component) {
90*c8dee2aaSAndroid Build Coastguard Worker         case SwizzleComponent::X:    return 'x';
91*c8dee2aaSAndroid Build Coastguard Worker         case SwizzleComponent::Y:    return 'y';
92*c8dee2aaSAndroid Build Coastguard Worker         case SwizzleComponent::Z:    return 'z';
93*c8dee2aaSAndroid Build Coastguard Worker         case SwizzleComponent::W:    return 'w';
94*c8dee2aaSAndroid Build Coastguard Worker         case SwizzleComponent::R:    return 'r';
95*c8dee2aaSAndroid Build Coastguard Worker         case SwizzleComponent::G:    return 'g';
96*c8dee2aaSAndroid Build Coastguard Worker         case SwizzleComponent::B:    return 'b';
97*c8dee2aaSAndroid Build Coastguard Worker         case SwizzleComponent::A:    return 'a';
98*c8dee2aaSAndroid Build Coastguard Worker         case SwizzleComponent::S:    return 's';
99*c8dee2aaSAndroid Build Coastguard Worker         case SwizzleComponent::T:    return 't';
100*c8dee2aaSAndroid Build Coastguard Worker         case SwizzleComponent::P:    return 'p';
101*c8dee2aaSAndroid Build Coastguard Worker         case SwizzleComponent::Q:    return 'q';
102*c8dee2aaSAndroid Build Coastguard Worker         case SwizzleComponent::UL:   return 'L';
103*c8dee2aaSAndroid Build Coastguard Worker         case SwizzleComponent::UT:   return 'T';
104*c8dee2aaSAndroid Build Coastguard Worker         case SwizzleComponent::UR:   return 'R';
105*c8dee2aaSAndroid Build Coastguard Worker         case SwizzleComponent::UB:   return 'B';
106*c8dee2aaSAndroid Build Coastguard Worker         case SwizzleComponent::ZERO: return '0';
107*c8dee2aaSAndroid Build Coastguard Worker         case SwizzleComponent::ONE:  return '1';
108*c8dee2aaSAndroid Build Coastguard Worker         default: SkUNREACHABLE;
109*c8dee2aaSAndroid Build Coastguard Worker     }
110*c8dee2aaSAndroid Build Coastguard Worker }
111*c8dee2aaSAndroid Build Coastguard Worker 
MaskString(const ComponentArray & components)112*c8dee2aaSAndroid Build Coastguard Worker std::string Swizzle::MaskString(const ComponentArray& components) {
113*c8dee2aaSAndroid Build Coastguard Worker     std::string result;
114*c8dee2aaSAndroid Build Coastguard Worker     for (int8_t component : components) {
115*c8dee2aaSAndroid Build Coastguard Worker         result += mask_char(component);
116*c8dee2aaSAndroid Build Coastguard Worker     }
117*c8dee2aaSAndroid Build Coastguard Worker     return result;
118*c8dee2aaSAndroid Build Coastguard Worker }
119*c8dee2aaSAndroid Build Coastguard Worker 
optimize_constructor_swizzle(const Context & context,Position pos,const ConstructorCompound & base,ComponentArray components)120*c8dee2aaSAndroid Build Coastguard Worker static std::unique_ptr<Expression> optimize_constructor_swizzle(const Context& context,
121*c8dee2aaSAndroid Build Coastguard Worker                                                                 Position pos,
122*c8dee2aaSAndroid Build Coastguard Worker                                                                 const ConstructorCompound& base,
123*c8dee2aaSAndroid Build Coastguard Worker                                                                 ComponentArray components) {
124*c8dee2aaSAndroid Build Coastguard Worker     auto baseArguments = base.argumentSpan();
125*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<Expression> replacement;
126*c8dee2aaSAndroid Build Coastguard Worker     const Type& exprType = base.type();
127*c8dee2aaSAndroid Build Coastguard Worker     const Type& componentType = exprType.componentType();
128*c8dee2aaSAndroid Build Coastguard Worker     int swizzleSize = components.size();
129*c8dee2aaSAndroid Build Coastguard Worker 
130*c8dee2aaSAndroid Build Coastguard Worker     // Swizzles can duplicate some elements and discard others, e.g.
131*c8dee2aaSAndroid Build Coastguard Worker     // `half4(1, 2, 3, 4).xxz` --> `half3(1, 1, 3)`. However, there are constraints:
132*c8dee2aaSAndroid Build Coastguard Worker     // - Expressions with side effects need to occur exactly once, even if they would otherwise be
133*c8dee2aaSAndroid Build Coastguard Worker     //   swizzle-eliminated
134*c8dee2aaSAndroid Build Coastguard Worker     // - Non-trivial expressions should not be repeated, but elimination is OK.
135*c8dee2aaSAndroid Build Coastguard Worker     //
136*c8dee2aaSAndroid Build Coastguard Worker     // Look up the argument for the constructor at each index. This is typically simple but for
137*c8dee2aaSAndroid Build Coastguard Worker     // weird cases like `half4(bar.yz, half2(foo))`, it can be harder than it seems. This example
138*c8dee2aaSAndroid Build Coastguard Worker     // would result in:
139*c8dee2aaSAndroid Build Coastguard Worker     //     argMap[0] = {.fArgIndex = 0, .fComponent = 0}   (bar.yz     .x)
140*c8dee2aaSAndroid Build Coastguard Worker     //     argMap[1] = {.fArgIndex = 0, .fComponent = 1}   (bar.yz     .y)
141*c8dee2aaSAndroid Build Coastguard Worker     //     argMap[2] = {.fArgIndex = 1, .fComponent = 0}   (half2(foo) .x)
142*c8dee2aaSAndroid Build Coastguard Worker     //     argMap[3] = {.fArgIndex = 1, .fComponent = 1}   (half2(foo) .y)
143*c8dee2aaSAndroid Build Coastguard Worker     struct ConstructorArgMap {
144*c8dee2aaSAndroid Build Coastguard Worker         int8_t fArgIndex;
145*c8dee2aaSAndroid Build Coastguard Worker         int8_t fComponent;
146*c8dee2aaSAndroid Build Coastguard Worker     };
147*c8dee2aaSAndroid Build Coastguard Worker 
148*c8dee2aaSAndroid Build Coastguard Worker     int numConstructorArgs = base.type().columns();
149*c8dee2aaSAndroid Build Coastguard Worker     ConstructorArgMap argMap[4] = {};
150*c8dee2aaSAndroid Build Coastguard Worker     int writeIdx = 0;
151*c8dee2aaSAndroid Build Coastguard Worker     for (int argIdx = 0; argIdx < (int)baseArguments.size(); ++argIdx) {
152*c8dee2aaSAndroid Build Coastguard Worker         const Expression& arg = *baseArguments[argIdx];
153*c8dee2aaSAndroid Build Coastguard Worker         const Type& argType = arg.type();
154*c8dee2aaSAndroid Build Coastguard Worker 
155*c8dee2aaSAndroid Build Coastguard Worker         if (!argType.isScalar() && !argType.isVector()) {
156*c8dee2aaSAndroid Build Coastguard Worker             return nullptr;
157*c8dee2aaSAndroid Build Coastguard Worker         }
158*c8dee2aaSAndroid Build Coastguard Worker 
159*c8dee2aaSAndroid Build Coastguard Worker         int argSlots = argType.slotCount();
160*c8dee2aaSAndroid Build Coastguard Worker         for (int componentIdx = 0; componentIdx < argSlots; ++componentIdx) {
161*c8dee2aaSAndroid Build Coastguard Worker             argMap[writeIdx].fArgIndex = argIdx;
162*c8dee2aaSAndroid Build Coastguard Worker             argMap[writeIdx].fComponent = componentIdx;
163*c8dee2aaSAndroid Build Coastguard Worker             ++writeIdx;
164*c8dee2aaSAndroid Build Coastguard Worker         }
165*c8dee2aaSAndroid Build Coastguard Worker     }
166*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(writeIdx == numConstructorArgs);
167*c8dee2aaSAndroid Build Coastguard Worker 
168*c8dee2aaSAndroid Build Coastguard Worker     // Count up the number of times each constructor argument is used by the swizzle.
169*c8dee2aaSAndroid Build Coastguard Worker     //    `half4(bar.yz, half2(foo)).xwxy` -> { 3, 1 }
170*c8dee2aaSAndroid Build Coastguard Worker     // - bar.yz    is referenced 3 times, by `.x_xy`
171*c8dee2aaSAndroid Build Coastguard Worker     // - half(foo) is referenced 1 time,  by `._w__`
172*c8dee2aaSAndroid Build Coastguard Worker     int8_t exprUsed[4] = {};
173*c8dee2aaSAndroid Build Coastguard Worker     for (int8_t c : components) {
174*c8dee2aaSAndroid Build Coastguard Worker         exprUsed[argMap[c].fArgIndex]++;
175*c8dee2aaSAndroid Build Coastguard Worker     }
176*c8dee2aaSAndroid Build Coastguard Worker 
177*c8dee2aaSAndroid Build Coastguard Worker     for (int index = 0; index < numConstructorArgs; ++index) {
178*c8dee2aaSAndroid Build Coastguard Worker         int8_t constructorArgIndex = argMap[index].fArgIndex;
179*c8dee2aaSAndroid Build Coastguard Worker         const Expression& baseArg = *baseArguments[constructorArgIndex];
180*c8dee2aaSAndroid Build Coastguard Worker 
181*c8dee2aaSAndroid Build Coastguard Worker         // Check that non-trivial expressions are not swizzled in more than once.
182*c8dee2aaSAndroid Build Coastguard Worker         if (exprUsed[constructorArgIndex] > 1 && !Analysis::IsTrivialExpression(baseArg)) {
183*c8dee2aaSAndroid Build Coastguard Worker             return nullptr;
184*c8dee2aaSAndroid Build Coastguard Worker         }
185*c8dee2aaSAndroid Build Coastguard Worker         // Check that side-effect-bearing expressions are swizzled in exactly once.
186*c8dee2aaSAndroid Build Coastguard Worker         if (exprUsed[constructorArgIndex] != 1 && Analysis::HasSideEffects(baseArg)) {
187*c8dee2aaSAndroid Build Coastguard Worker             return nullptr;
188*c8dee2aaSAndroid Build Coastguard Worker         }
189*c8dee2aaSAndroid Build Coastguard Worker     }
190*c8dee2aaSAndroid Build Coastguard Worker 
191*c8dee2aaSAndroid Build Coastguard Worker     struct ReorderedArgument {
192*c8dee2aaSAndroid Build Coastguard Worker         int8_t fArgIndex;
193*c8dee2aaSAndroid Build Coastguard Worker         ComponentArray fComponents;
194*c8dee2aaSAndroid Build Coastguard Worker     };
195*c8dee2aaSAndroid Build Coastguard Worker     STArray<4, ReorderedArgument> reorderedArgs;
196*c8dee2aaSAndroid Build Coastguard Worker     for (int8_t c : components) {
197*c8dee2aaSAndroid Build Coastguard Worker         const ConstructorArgMap& argument = argMap[c];
198*c8dee2aaSAndroid Build Coastguard Worker         const Expression& baseArg = *baseArguments[argument.fArgIndex];
199*c8dee2aaSAndroid Build Coastguard Worker 
200*c8dee2aaSAndroid Build Coastguard Worker         if (baseArg.type().isScalar()) {
201*c8dee2aaSAndroid Build Coastguard Worker             // This argument is a scalar; add it to the list as-is.
202*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(argument.fComponent == 0);
203*c8dee2aaSAndroid Build Coastguard Worker             reorderedArgs.push_back({argument.fArgIndex,
204*c8dee2aaSAndroid Build Coastguard Worker                                      ComponentArray{}});
205*c8dee2aaSAndroid Build Coastguard Worker         } else {
206*c8dee2aaSAndroid Build Coastguard Worker             // This argument is a component from a vector.
207*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(baseArg.type().isVector());
208*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(argument.fComponent < baseArg.type().columns());
209*c8dee2aaSAndroid Build Coastguard Worker             if (reorderedArgs.empty() ||
210*c8dee2aaSAndroid Build Coastguard Worker                 reorderedArgs.back().fArgIndex != argument.fArgIndex) {
211*c8dee2aaSAndroid Build Coastguard Worker                 // This can't be combined with the previous argument. Add a new one.
212*c8dee2aaSAndroid Build Coastguard Worker                 reorderedArgs.push_back({argument.fArgIndex,
213*c8dee2aaSAndroid Build Coastguard Worker                                          ComponentArray{argument.fComponent}});
214*c8dee2aaSAndroid Build Coastguard Worker             } else {
215*c8dee2aaSAndroid Build Coastguard Worker                 // Since we know this argument uses components, it should already have at least one
216*c8dee2aaSAndroid Build Coastguard Worker                 // component set.
217*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(!reorderedArgs.back().fComponents.empty());
218*c8dee2aaSAndroid Build Coastguard Worker                 // Build up the current argument with one more component.
219*c8dee2aaSAndroid Build Coastguard Worker                 reorderedArgs.back().fComponents.push_back(argument.fComponent);
220*c8dee2aaSAndroid Build Coastguard Worker             }
221*c8dee2aaSAndroid Build Coastguard Worker         }
222*c8dee2aaSAndroid Build Coastguard Worker     }
223*c8dee2aaSAndroid Build Coastguard Worker 
224*c8dee2aaSAndroid Build Coastguard Worker     // Convert our reordered argument list to an actual array of expressions, with the new order and
225*c8dee2aaSAndroid Build Coastguard Worker     // any new inner swizzles that need to be applied.
226*c8dee2aaSAndroid Build Coastguard Worker     ExpressionArray newArgs;
227*c8dee2aaSAndroid Build Coastguard Worker     newArgs.reserve_exact(swizzleSize);
228*c8dee2aaSAndroid Build Coastguard Worker     for (const ReorderedArgument& reorderedArg : reorderedArgs) {
229*c8dee2aaSAndroid Build Coastguard Worker         std::unique_ptr<Expression> newArg = baseArguments[reorderedArg.fArgIndex]->clone();
230*c8dee2aaSAndroid Build Coastguard Worker 
231*c8dee2aaSAndroid Build Coastguard Worker         if (reorderedArg.fComponents.empty()) {
232*c8dee2aaSAndroid Build Coastguard Worker             newArgs.push_back(std::move(newArg));
233*c8dee2aaSAndroid Build Coastguard Worker         } else {
234*c8dee2aaSAndroid Build Coastguard Worker             newArgs.push_back(Swizzle::Make(context, pos, std::move(newArg),
235*c8dee2aaSAndroid Build Coastguard Worker                                             reorderedArg.fComponents));
236*c8dee2aaSAndroid Build Coastguard Worker         }
237*c8dee2aaSAndroid Build Coastguard Worker     }
238*c8dee2aaSAndroid Build Coastguard Worker 
239*c8dee2aaSAndroid Build Coastguard Worker     // Wrap the new argument list in a compound constructor.
240*c8dee2aaSAndroid Build Coastguard Worker     return ConstructorCompound::Make(context,
241*c8dee2aaSAndroid Build Coastguard Worker                                      pos,
242*c8dee2aaSAndroid Build Coastguard Worker                                      componentType.toCompound(context, swizzleSize, /*rows=*/1),
243*c8dee2aaSAndroid Build Coastguard Worker                                      std::move(newArgs));
244*c8dee2aaSAndroid Build Coastguard Worker }
245*c8dee2aaSAndroid Build Coastguard Worker 
Convert(const Context & context,Position pos,Position maskPos,std::unique_ptr<Expression> base,std::string_view componentString)246*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<Expression> Swizzle::Convert(const Context& context,
247*c8dee2aaSAndroid Build Coastguard Worker                                              Position pos,
248*c8dee2aaSAndroid Build Coastguard Worker                                              Position maskPos,
249*c8dee2aaSAndroid Build Coastguard Worker                                              std::unique_ptr<Expression> base,
250*c8dee2aaSAndroid Build Coastguard Worker                                              std::string_view componentString) {
251*c8dee2aaSAndroid Build Coastguard Worker     if (componentString.size() > 4) {
252*c8dee2aaSAndroid Build Coastguard Worker         context.fErrors->error(Position::Range(maskPos.startOffset() + 4,
253*c8dee2aaSAndroid Build Coastguard Worker                                                maskPos.endOffset()),
254*c8dee2aaSAndroid Build Coastguard Worker                                "too many components in swizzle mask");
255*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
256*c8dee2aaSAndroid Build Coastguard Worker     }
257*c8dee2aaSAndroid Build Coastguard Worker 
258*c8dee2aaSAndroid Build Coastguard Worker     // Convert the component string into an equivalent array.
259*c8dee2aaSAndroid Build Coastguard Worker     ComponentArray components;
260*c8dee2aaSAndroid Build Coastguard Worker     for (size_t i = 0; i < componentString.length(); ++i) {
261*c8dee2aaSAndroid Build Coastguard Worker         char field = componentString[i];
262*c8dee2aaSAndroid Build Coastguard Worker         switch (field) {
263*c8dee2aaSAndroid Build Coastguard Worker             case '0': components.push_back(SwizzleComponent::ZERO); break;
264*c8dee2aaSAndroid Build Coastguard Worker             case '1': components.push_back(SwizzleComponent::ONE);  break;
265*c8dee2aaSAndroid Build Coastguard Worker             case 'x': components.push_back(SwizzleComponent::X);    break;
266*c8dee2aaSAndroid Build Coastguard Worker             case 'r': components.push_back(SwizzleComponent::R);    break;
267*c8dee2aaSAndroid Build Coastguard Worker             case 's': components.push_back(SwizzleComponent::S);    break;
268*c8dee2aaSAndroid Build Coastguard Worker             case 'L': components.push_back(SwizzleComponent::UL);   break;
269*c8dee2aaSAndroid Build Coastguard Worker             case 'y': components.push_back(SwizzleComponent::Y);    break;
270*c8dee2aaSAndroid Build Coastguard Worker             case 'g': components.push_back(SwizzleComponent::G);    break;
271*c8dee2aaSAndroid Build Coastguard Worker             case 't': components.push_back(SwizzleComponent::T);    break;
272*c8dee2aaSAndroid Build Coastguard Worker             case 'T': components.push_back(SwizzleComponent::UT);   break;
273*c8dee2aaSAndroid Build Coastguard Worker             case 'z': components.push_back(SwizzleComponent::Z);    break;
274*c8dee2aaSAndroid Build Coastguard Worker             case 'b': components.push_back(SwizzleComponent::B);    break;
275*c8dee2aaSAndroid Build Coastguard Worker             case 'p': components.push_back(SwizzleComponent::P);    break;
276*c8dee2aaSAndroid Build Coastguard Worker             case 'R': components.push_back(SwizzleComponent::UR);   break;
277*c8dee2aaSAndroid Build Coastguard Worker             case 'w': components.push_back(SwizzleComponent::W);    break;
278*c8dee2aaSAndroid Build Coastguard Worker             case 'a': components.push_back(SwizzleComponent::A);    break;
279*c8dee2aaSAndroid Build Coastguard Worker             case 'q': components.push_back(SwizzleComponent::Q);    break;
280*c8dee2aaSAndroid Build Coastguard Worker             case 'B': components.push_back(SwizzleComponent::UB);   break;
281*c8dee2aaSAndroid Build Coastguard Worker             default:
282*c8dee2aaSAndroid Build Coastguard Worker                 context.fErrors->error(Position::Range(maskPos.startOffset() + i,
283*c8dee2aaSAndroid Build Coastguard Worker                                                        maskPos.startOffset() + i + 1),
284*c8dee2aaSAndroid Build Coastguard Worker                                        String::printf("invalid swizzle component '%c'", field));
285*c8dee2aaSAndroid Build Coastguard Worker                 return nullptr;
286*c8dee2aaSAndroid Build Coastguard Worker         }
287*c8dee2aaSAndroid Build Coastguard Worker     }
288*c8dee2aaSAndroid Build Coastguard Worker 
289*c8dee2aaSAndroid Build Coastguard Worker     if (!validate_swizzle_domain(components)) {
290*c8dee2aaSAndroid Build Coastguard Worker         context.fErrors->error(maskPos, "invalid swizzle mask '" + MaskString(components) + "'");
291*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
292*c8dee2aaSAndroid Build Coastguard Worker     }
293*c8dee2aaSAndroid Build Coastguard Worker 
294*c8dee2aaSAndroid Build Coastguard Worker     const Type& baseType = base->type().scalarTypeForLiteral();
295*c8dee2aaSAndroid Build Coastguard Worker 
296*c8dee2aaSAndroid Build Coastguard Worker     if (!baseType.isVector() && !baseType.isScalar()) {
297*c8dee2aaSAndroid Build Coastguard Worker         context.fErrors->error(pos, "cannot swizzle value of type '" +
298*c8dee2aaSAndroid Build Coastguard Worker                                     baseType.displayName() + "'");
299*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
300*c8dee2aaSAndroid Build Coastguard Worker     }
301*c8dee2aaSAndroid Build Coastguard Worker 
302*c8dee2aaSAndroid Build Coastguard Worker     ComponentArray maskComponents;
303*c8dee2aaSAndroid Build Coastguard Worker     bool foundXYZW = false;
304*c8dee2aaSAndroid Build Coastguard Worker     for (int i = 0; i < components.size(); ++i) {
305*c8dee2aaSAndroid Build Coastguard Worker         switch (components[i]) {
306*c8dee2aaSAndroid Build Coastguard Worker             case SwizzleComponent::ZERO:
307*c8dee2aaSAndroid Build Coastguard Worker             case SwizzleComponent::ONE:
308*c8dee2aaSAndroid Build Coastguard Worker                 // Skip over constant fields for now.
309*c8dee2aaSAndroid Build Coastguard Worker                 break;
310*c8dee2aaSAndroid Build Coastguard Worker             case SwizzleComponent::X:
311*c8dee2aaSAndroid Build Coastguard Worker             case SwizzleComponent::R:
312*c8dee2aaSAndroid Build Coastguard Worker             case SwizzleComponent::S:
313*c8dee2aaSAndroid Build Coastguard Worker             case SwizzleComponent::UL:
314*c8dee2aaSAndroid Build Coastguard Worker                 foundXYZW = true;
315*c8dee2aaSAndroid Build Coastguard Worker                 maskComponents.push_back(SwizzleComponent::X);
316*c8dee2aaSAndroid Build Coastguard Worker                 break;
317*c8dee2aaSAndroid Build Coastguard Worker             case SwizzleComponent::Y:
318*c8dee2aaSAndroid Build Coastguard Worker             case SwizzleComponent::G:
319*c8dee2aaSAndroid Build Coastguard Worker             case SwizzleComponent::T:
320*c8dee2aaSAndroid Build Coastguard Worker             case SwizzleComponent::UT:
321*c8dee2aaSAndroid Build Coastguard Worker                 foundXYZW = true;
322*c8dee2aaSAndroid Build Coastguard Worker                 if (baseType.columns() >= 2) {
323*c8dee2aaSAndroid Build Coastguard Worker                     maskComponents.push_back(SwizzleComponent::Y);
324*c8dee2aaSAndroid Build Coastguard Worker                     break;
325*c8dee2aaSAndroid Build Coastguard Worker                 }
326*c8dee2aaSAndroid Build Coastguard Worker                 [[fallthrough]];
327*c8dee2aaSAndroid Build Coastguard Worker             case SwizzleComponent::Z:
328*c8dee2aaSAndroid Build Coastguard Worker             case SwizzleComponent::B:
329*c8dee2aaSAndroid Build Coastguard Worker             case SwizzleComponent::P:
330*c8dee2aaSAndroid Build Coastguard Worker             case SwizzleComponent::UR:
331*c8dee2aaSAndroid Build Coastguard Worker                 foundXYZW = true;
332*c8dee2aaSAndroid Build Coastguard Worker                 if (baseType.columns() >= 3) {
333*c8dee2aaSAndroid Build Coastguard Worker                     maskComponents.push_back(SwizzleComponent::Z);
334*c8dee2aaSAndroid Build Coastguard Worker                     break;
335*c8dee2aaSAndroid Build Coastguard Worker                 }
336*c8dee2aaSAndroid Build Coastguard Worker                 [[fallthrough]];
337*c8dee2aaSAndroid Build Coastguard Worker             case SwizzleComponent::W:
338*c8dee2aaSAndroid Build Coastguard Worker             case SwizzleComponent::A:
339*c8dee2aaSAndroid Build Coastguard Worker             case SwizzleComponent::Q:
340*c8dee2aaSAndroid Build Coastguard Worker             case SwizzleComponent::UB:
341*c8dee2aaSAndroid Build Coastguard Worker                 foundXYZW = true;
342*c8dee2aaSAndroid Build Coastguard Worker                 if (baseType.columns() >= 4) {
343*c8dee2aaSAndroid Build Coastguard Worker                     maskComponents.push_back(SwizzleComponent::W);
344*c8dee2aaSAndroid Build Coastguard Worker                     break;
345*c8dee2aaSAndroid Build Coastguard Worker                 }
346*c8dee2aaSAndroid Build Coastguard Worker                 [[fallthrough]];
347*c8dee2aaSAndroid Build Coastguard Worker             default:
348*c8dee2aaSAndroid Build Coastguard Worker                 // The swizzle component references a field that doesn't exist in the base type.
349*c8dee2aaSAndroid Build Coastguard Worker                 context.fErrors->error(Position::Range(maskPos.startOffset() + i,
350*c8dee2aaSAndroid Build Coastguard Worker                                                        maskPos.startOffset() + i + 1),
351*c8dee2aaSAndroid Build Coastguard Worker                                        String::printf("invalid swizzle component '%c'",
352*c8dee2aaSAndroid Build Coastguard Worker                                                       mask_char(components[i])));
353*c8dee2aaSAndroid Build Coastguard Worker                 return nullptr;
354*c8dee2aaSAndroid Build Coastguard Worker         }
355*c8dee2aaSAndroid Build Coastguard Worker     }
356*c8dee2aaSAndroid Build Coastguard Worker 
357*c8dee2aaSAndroid Build Coastguard Worker     if (!foundXYZW) {
358*c8dee2aaSAndroid Build Coastguard Worker         context.fErrors->error(maskPos, "swizzle must refer to base expression");
359*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
360*c8dee2aaSAndroid Build Coastguard Worker     }
361*c8dee2aaSAndroid Build Coastguard Worker 
362*c8dee2aaSAndroid Build Coastguard Worker     // Coerce literals in expressions such as `(12345).xxx` to their actual type.
363*c8dee2aaSAndroid Build Coastguard Worker     base = baseType.coerceExpression(std::move(base), context);
364*c8dee2aaSAndroid Build Coastguard Worker     if (!base) {
365*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
366*c8dee2aaSAndroid Build Coastguard Worker     }
367*c8dee2aaSAndroid Build Coastguard Worker 
368*c8dee2aaSAndroid Build Coastguard Worker     // Swizzles are complicated due to constant components. The most difficult case is a mask like
369*c8dee2aaSAndroid Build Coastguard Worker     // '.x1w0'. A naive approach might turn that into 'float4(base.x, 1, base.w, 0)', but that
370*c8dee2aaSAndroid Build Coastguard Worker     // evaluates 'base' twice. We instead group the swizzle mask ('xw') and constants ('1, 0')
371*c8dee2aaSAndroid Build Coastguard Worker     // together and use a secondary swizzle to put them back into the right order, so in this case
372*c8dee2aaSAndroid Build Coastguard Worker     // we end up with 'float4(base.xw, 1, 0).xzyw'.
373*c8dee2aaSAndroid Build Coastguard Worker     //
374*c8dee2aaSAndroid Build Coastguard Worker     // First, we need a vector expression that is the non-constant portion of the swizzle, packed:
375*c8dee2aaSAndroid Build Coastguard Worker     //   scalar.xxx  -> type3(scalar)
376*c8dee2aaSAndroid Build Coastguard Worker     //   scalar.x0x0 -> type2(scalar)
377*c8dee2aaSAndroid Build Coastguard Worker     //   vector.zyx  -> vector.zyx
378*c8dee2aaSAndroid Build Coastguard Worker     //   vector.x0y0 -> vector.xy
379*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<Expression> expr = Swizzle::Make(context, pos, std::move(base), maskComponents);
380*c8dee2aaSAndroid Build Coastguard Worker 
381*c8dee2aaSAndroid Build Coastguard Worker     // If we have processed the entire swizzle, we're done.
382*c8dee2aaSAndroid Build Coastguard Worker     if (maskComponents.size() == components.size()) {
383*c8dee2aaSAndroid Build Coastguard Worker         return expr;
384*c8dee2aaSAndroid Build Coastguard Worker     }
385*c8dee2aaSAndroid Build Coastguard Worker 
386*c8dee2aaSAndroid Build Coastguard Worker     // Now we create a constructor that has the correct number of elements for the final swizzle,
387*c8dee2aaSAndroid Build Coastguard Worker     // with all fields at the start. It's not finished yet; constants we need will be added below.
388*c8dee2aaSAndroid Build Coastguard Worker     //   scalar.x0x0 -> type4(type2(x), ...)
389*c8dee2aaSAndroid Build Coastguard Worker     //   vector.y111 -> type4(vector.y, ...)
390*c8dee2aaSAndroid Build Coastguard Worker     //   vector.z10x -> type4(vector.zx, ...)
391*c8dee2aaSAndroid Build Coastguard Worker     //
392*c8dee2aaSAndroid Build Coastguard Worker     // The constructor will have at most three arguments: { base expr, constant 0, constant 1 }
393*c8dee2aaSAndroid Build Coastguard Worker     ExpressionArray constructorArgs;
394*c8dee2aaSAndroid Build Coastguard Worker     constructorArgs.reserve_exact(3);
395*c8dee2aaSAndroid Build Coastguard Worker     constructorArgs.push_back(std::move(expr));
396*c8dee2aaSAndroid Build Coastguard Worker 
397*c8dee2aaSAndroid Build Coastguard Worker     // Apply another swizzle to shuffle the constants into the correct place. Any constant values we
398*c8dee2aaSAndroid Build Coastguard Worker     // need are also tacked on to the end of the constructor.
399*c8dee2aaSAndroid Build Coastguard Worker     //   scalar.x0x0 -> type4(type2(x), 0).xyxy
400*c8dee2aaSAndroid Build Coastguard Worker     //   vector.y111 -> type2(vector.y, 1).xyyy
401*c8dee2aaSAndroid Build Coastguard Worker     //   vector.z10x -> type4(vector.zx, 1, 0).xzwy
402*c8dee2aaSAndroid Build Coastguard Worker     const Type* scalarType = &baseType.componentType();
403*c8dee2aaSAndroid Build Coastguard Worker     ComponentArray swizzleComponents;
404*c8dee2aaSAndroid Build Coastguard Worker     int maskFieldIdx = 0;
405*c8dee2aaSAndroid Build Coastguard Worker     int constantFieldIdx = maskComponents.size();
406*c8dee2aaSAndroid Build Coastguard Worker     int constantZeroIdx = -1, constantOneIdx = -1;
407*c8dee2aaSAndroid Build Coastguard Worker 
408*c8dee2aaSAndroid Build Coastguard Worker     for (int i = 0; i < components.size(); i++) {
409*c8dee2aaSAndroid Build Coastguard Worker         switch (components[i]) {
410*c8dee2aaSAndroid Build Coastguard Worker             case SwizzleComponent::ZERO:
411*c8dee2aaSAndroid Build Coastguard Worker                 if (constantZeroIdx == -1) {
412*c8dee2aaSAndroid Build Coastguard Worker                     // Synthesize a '0' argument at the end of the constructor.
413*c8dee2aaSAndroid Build Coastguard Worker                     constructorArgs.push_back(Literal::Make(pos, /*value=*/0, scalarType));
414*c8dee2aaSAndroid Build Coastguard Worker                     constantZeroIdx = constantFieldIdx++;
415*c8dee2aaSAndroid Build Coastguard Worker                 }
416*c8dee2aaSAndroid Build Coastguard Worker                 swizzleComponents.push_back(constantZeroIdx);
417*c8dee2aaSAndroid Build Coastguard Worker                 break;
418*c8dee2aaSAndroid Build Coastguard Worker             case SwizzleComponent::ONE:
419*c8dee2aaSAndroid Build Coastguard Worker                 if (constantOneIdx == -1) {
420*c8dee2aaSAndroid Build Coastguard Worker                     // Synthesize a '1' argument at the end of the constructor.
421*c8dee2aaSAndroid Build Coastguard Worker                     constructorArgs.push_back(Literal::Make(pos, /*value=*/1, scalarType));
422*c8dee2aaSAndroid Build Coastguard Worker                     constantOneIdx = constantFieldIdx++;
423*c8dee2aaSAndroid Build Coastguard Worker                 }
424*c8dee2aaSAndroid Build Coastguard Worker                 swizzleComponents.push_back(constantOneIdx);
425*c8dee2aaSAndroid Build Coastguard Worker                 break;
426*c8dee2aaSAndroid Build Coastguard Worker             default:
427*c8dee2aaSAndroid Build Coastguard Worker                 // The non-constant fields are already in the expected order.
428*c8dee2aaSAndroid Build Coastguard Worker                 swizzleComponents.push_back(maskFieldIdx++);
429*c8dee2aaSAndroid Build Coastguard Worker                 break;
430*c8dee2aaSAndroid Build Coastguard Worker         }
431*c8dee2aaSAndroid Build Coastguard Worker     }
432*c8dee2aaSAndroid Build Coastguard Worker 
433*c8dee2aaSAndroid Build Coastguard Worker     expr = ConstructorCompound::Make(context, pos,
434*c8dee2aaSAndroid Build Coastguard Worker                                      scalarType->toCompound(context, constantFieldIdx, /*rows=*/1),
435*c8dee2aaSAndroid Build Coastguard Worker                                      std::move(constructorArgs));
436*c8dee2aaSAndroid Build Coastguard Worker 
437*c8dee2aaSAndroid Build Coastguard Worker     // Create (and potentially optimize-away) the resulting swizzle-expression.
438*c8dee2aaSAndroid Build Coastguard Worker     return Swizzle::Make(context, pos, std::move(expr), swizzleComponents);
439*c8dee2aaSAndroid Build Coastguard Worker }
440*c8dee2aaSAndroid Build Coastguard Worker 
IsIdentity(const ComponentArray & components)441*c8dee2aaSAndroid Build Coastguard Worker bool Swizzle::IsIdentity(const ComponentArray& components) {
442*c8dee2aaSAndroid Build Coastguard Worker     for (int index = 0; index < components.size(); ++index) {
443*c8dee2aaSAndroid Build Coastguard Worker         if (components[index] != index) {
444*c8dee2aaSAndroid Build Coastguard Worker             return false;
445*c8dee2aaSAndroid Build Coastguard Worker         }
446*c8dee2aaSAndroid Build Coastguard Worker     }
447*c8dee2aaSAndroid Build Coastguard Worker     return true;
448*c8dee2aaSAndroid Build Coastguard Worker }
449*c8dee2aaSAndroid Build Coastguard Worker 
Make(const Context & context,Position pos,std::unique_ptr<Expression> expr,ComponentArray components)450*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<Expression> Swizzle::Make(const Context& context,
451*c8dee2aaSAndroid Build Coastguard Worker                                           Position pos,
452*c8dee2aaSAndroid Build Coastguard Worker                                           std::unique_ptr<Expression> expr,
453*c8dee2aaSAndroid Build Coastguard Worker                                           ComponentArray components) {
454*c8dee2aaSAndroid Build Coastguard Worker     const Type& exprType = expr->type();
455*c8dee2aaSAndroid Build Coastguard Worker     SkASSERTF(exprType.isVector() || exprType.isScalar(),
456*c8dee2aaSAndroid Build Coastguard Worker               "cannot swizzle type '%s'", exprType.description().c_str());
457*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(components.size() >= 1 && components.size() <= 4);
458*c8dee2aaSAndroid Build Coastguard Worker 
459*c8dee2aaSAndroid Build Coastguard Worker     // Confirm that the component array only contains X/Y/Z/W. (Call MakeWith01 if you want support
460*c8dee2aaSAndroid Build Coastguard Worker     // for ZERO and ONE. Once initial IR generation is complete, no swizzles should have zeros or
461*c8dee2aaSAndroid Build Coastguard Worker     // ones in them.)
462*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(std::all_of(components.begin(), components.end(), [](int8_t component) {
463*c8dee2aaSAndroid Build Coastguard Worker         return component >= SwizzleComponent::X &&
464*c8dee2aaSAndroid Build Coastguard Worker                component <= SwizzleComponent::W;
465*c8dee2aaSAndroid Build Coastguard Worker     }));
466*c8dee2aaSAndroid Build Coastguard Worker 
467*c8dee2aaSAndroid Build Coastguard Worker     // SkSL supports splatting a scalar via `scalar.xxxx`, but not all versions of GLSL allow this.
468*c8dee2aaSAndroid Build Coastguard Worker     // Replace swizzles with equivalent splat constructors (`scalar.xxx` --> `half3(value)`).
469*c8dee2aaSAndroid Build Coastguard Worker     if (exprType.isScalar()) {
470*c8dee2aaSAndroid Build Coastguard Worker         return ConstructorSplat::Make(context, pos,
471*c8dee2aaSAndroid Build Coastguard Worker                                       exprType.toCompound(context, components.size(), /*rows=*/1),
472*c8dee2aaSAndroid Build Coastguard Worker                                       std::move(expr));
473*c8dee2aaSAndroid Build Coastguard Worker     }
474*c8dee2aaSAndroid Build Coastguard Worker 
475*c8dee2aaSAndroid Build Coastguard Worker     // Detect identity swizzles like `color.rgba` and optimize them away.
476*c8dee2aaSAndroid Build Coastguard Worker     if (components.size() == exprType.columns() && IsIdentity(components)) {
477*c8dee2aaSAndroid Build Coastguard Worker         expr->fPosition = pos;
478*c8dee2aaSAndroid Build Coastguard Worker         return expr;
479*c8dee2aaSAndroid Build Coastguard Worker     }
480*c8dee2aaSAndroid Build Coastguard Worker 
481*c8dee2aaSAndroid Build Coastguard Worker     // Optimize swizzles of swizzles, e.g. replace `foo.argb.rggg` with `foo.arrr`.
482*c8dee2aaSAndroid Build Coastguard Worker     if (expr->is<Swizzle>()) {
483*c8dee2aaSAndroid Build Coastguard Worker         Swizzle& base = expr->as<Swizzle>();
484*c8dee2aaSAndroid Build Coastguard Worker         ComponentArray combined;
485*c8dee2aaSAndroid Build Coastguard Worker         for (int8_t c : components) {
486*c8dee2aaSAndroid Build Coastguard Worker             combined.push_back(base.components()[c]);
487*c8dee2aaSAndroid Build Coastguard Worker         }
488*c8dee2aaSAndroid Build Coastguard Worker 
489*c8dee2aaSAndroid Build Coastguard Worker         // It may actually be possible to further simplify this swizzle. Go again.
490*c8dee2aaSAndroid Build Coastguard Worker         // (e.g. `color.abgr.abgr` --> `color.rgba` --> `color`.)
491*c8dee2aaSAndroid Build Coastguard Worker         return Swizzle::Make(context, pos, std::move(base.base()), combined);
492*c8dee2aaSAndroid Build Coastguard Worker     }
493*c8dee2aaSAndroid Build Coastguard Worker 
494*c8dee2aaSAndroid Build Coastguard Worker     // If we are swizzling a constant expression, we can use its value instead here (so that
495*c8dee2aaSAndroid Build Coastguard Worker     // swizzles like `colorWhite.x` can be simplified to `1`).
496*c8dee2aaSAndroid Build Coastguard Worker     const Expression* value = ConstantFolder::GetConstantValueForVariable(*expr);
497*c8dee2aaSAndroid Build Coastguard Worker 
498*c8dee2aaSAndroid Build Coastguard Worker     // `half4(scalar).zyy` can be optimized to `half3(scalar)`, and `half3(scalar).y` can be
499*c8dee2aaSAndroid Build Coastguard Worker     // optimized to just `scalar`. The swizzle components don't actually matter, as every field
500*c8dee2aaSAndroid Build Coastguard Worker     // in a splat constructor holds the same value.
501*c8dee2aaSAndroid Build Coastguard Worker     if (value->is<ConstructorSplat>()) {
502*c8dee2aaSAndroid Build Coastguard Worker         const ConstructorSplat& splat = value->as<ConstructorSplat>();
503*c8dee2aaSAndroid Build Coastguard Worker         return ConstructorSplat::Make(
504*c8dee2aaSAndroid Build Coastguard Worker                 context, pos,
505*c8dee2aaSAndroid Build Coastguard Worker                 splat.type().componentType().toCompound(context, components.size(), /*rows=*/1),
506*c8dee2aaSAndroid Build Coastguard Worker                 splat.argument()->clone());
507*c8dee2aaSAndroid Build Coastguard Worker     }
508*c8dee2aaSAndroid Build Coastguard Worker 
509*c8dee2aaSAndroid Build Coastguard Worker     // Swizzles on casts, like `half4(myFloat4).zyy`, can optimize to `half3(myFloat4.zyy)`.
510*c8dee2aaSAndroid Build Coastguard Worker     if (value->is<ConstructorCompoundCast>()) {
511*c8dee2aaSAndroid Build Coastguard Worker         const ConstructorCompoundCast& cast = value->as<ConstructorCompoundCast>();
512*c8dee2aaSAndroid Build Coastguard Worker         const Type& castType = cast.type().componentType().toCompound(context, components.size(),
513*c8dee2aaSAndroid Build Coastguard Worker                                                                       /*rows=*/1);
514*c8dee2aaSAndroid Build Coastguard Worker         std::unique_ptr<Expression> swizzled = Swizzle::Make(context, pos, cast.argument()->clone(),
515*c8dee2aaSAndroid Build Coastguard Worker                                                              std::move(components));
516*c8dee2aaSAndroid Build Coastguard Worker         return (castType.columns() > 1)
517*c8dee2aaSAndroid Build Coastguard Worker                        ? ConstructorCompoundCast::Make(context, pos, castType, std::move(swizzled))
518*c8dee2aaSAndroid Build Coastguard Worker                        : ConstructorScalarCast::Make(context, pos, castType, std::move(swizzled));
519*c8dee2aaSAndroid Build Coastguard Worker     }
520*c8dee2aaSAndroid Build Coastguard Worker 
521*c8dee2aaSAndroid Build Coastguard Worker     // Swizzles on compound constructors, like `half4(1, 2, 3, 4).yw`, can become `half2(2, 4)`.
522*c8dee2aaSAndroid Build Coastguard Worker     if (value->is<ConstructorCompound>()) {
523*c8dee2aaSAndroid Build Coastguard Worker         const ConstructorCompound& ctor = value->as<ConstructorCompound>();
524*c8dee2aaSAndroid Build Coastguard Worker         if (auto replacement = optimize_constructor_swizzle(context, pos, ctor, components)) {
525*c8dee2aaSAndroid Build Coastguard Worker             return replacement;
526*c8dee2aaSAndroid Build Coastguard Worker         }
527*c8dee2aaSAndroid Build Coastguard Worker     }
528*c8dee2aaSAndroid Build Coastguard Worker 
529*c8dee2aaSAndroid Build Coastguard Worker     // The swizzle could not be simplified, so apply the requested swizzle to the base expression.
530*c8dee2aaSAndroid Build Coastguard Worker     return std::make_unique<Swizzle>(context, pos, std::move(expr), components);
531*c8dee2aaSAndroid Build Coastguard Worker }
532*c8dee2aaSAndroid Build Coastguard Worker 
MakeExact(const Context & context,Position pos,std::unique_ptr<Expression> expr,ComponentArray components)533*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<Expression> Swizzle::MakeExact(const Context& context,
534*c8dee2aaSAndroid Build Coastguard Worker                                                Position pos,
535*c8dee2aaSAndroid Build Coastguard Worker                                                std::unique_ptr<Expression> expr,
536*c8dee2aaSAndroid Build Coastguard Worker                                                ComponentArray components) {
537*c8dee2aaSAndroid Build Coastguard Worker     SkASSERTF(expr->type().isVector() || expr->type().isScalar(),
538*c8dee2aaSAndroid Build Coastguard Worker               "cannot swizzle type '%s'", expr->type().description().c_str());
539*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(components.size() >= 1 && components.size() <= 4);
540*c8dee2aaSAndroid Build Coastguard Worker 
541*c8dee2aaSAndroid Build Coastguard Worker     // Confirm that the component array only contains X/Y/Z/W.
542*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(std::all_of(components.begin(), components.end(), [](int8_t component) {
543*c8dee2aaSAndroid Build Coastguard Worker         return component >= SwizzleComponent::X &&
544*c8dee2aaSAndroid Build Coastguard Worker                component <= SwizzleComponent::W;
545*c8dee2aaSAndroid Build Coastguard Worker     }));
546*c8dee2aaSAndroid Build Coastguard Worker 
547*c8dee2aaSAndroid Build Coastguard Worker     return std::make_unique<Swizzle>(context, pos, std::move(expr), components);
548*c8dee2aaSAndroid Build Coastguard Worker }
549*c8dee2aaSAndroid Build Coastguard Worker 
description(OperatorPrecedence) const550*c8dee2aaSAndroid Build Coastguard Worker std::string Swizzle::description(OperatorPrecedence) const {
551*c8dee2aaSAndroid Build Coastguard Worker     return this->base()->description(OperatorPrecedence::kPostfix) + "." +
552*c8dee2aaSAndroid Build Coastguard Worker            MaskString(this->components());
553*c8dee2aaSAndroid Build Coastguard Worker }
554*c8dee2aaSAndroid Build Coastguard Worker 
555*c8dee2aaSAndroid Build Coastguard Worker }  // namespace SkSL
556