| /* |
| * Copyright 2021 Google LLC |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "src/sksl/ir/SkSLConstructorCompound.h" |
| |
| #include "include/core/SkTypes.h" |
| #include "src/sksl/SkSLConstantFolder.h" |
| #include "src/sksl/SkSLContext.h" |
| #include "src/sksl/SkSLProgramSettings.h" |
| #include "src/sksl/ir/SkSLExpression.h" |
| #include "src/sksl/ir/SkSLType.h" |
| |
| #include <algorithm> |
| #include <cstddef> |
| #include <numeric> |
| #include <string> |
| |
| namespace SkSL { |
| |
| static bool is_safe_to_eliminate(const Type& type, const Expression& arg) { |
| if (type.isScalar()) { |
| // A scalar "compound type" with a single scalar argument is a no-op and can be eliminated. |
| // (Pedantically, this isn't a compound at all, but it's harmless to allow and simplifies |
| // call sites which need to narrow a vector and may sometimes end up with a scalar.) |
| SkASSERTF(arg.type().matches(type), "Creating type '%s' from '%s'", |
| type.description().c_str(), arg.type().description().c_str()); |
| return true; |
| } |
| if (type.isVector() && arg.type().matches(type)) { |
| // A vector compound constructor containing a single argument of matching type can trivially |
| // be eliminated. |
| return true; |
| } |
| // This is a meaningful single-argument compound constructor (e.g. vector-from-matrix, |
| // matrix-from-vector). |
| return false; |
| } |
| |
| std::unique_ptr<Expression> ConstructorCompound::Make(const Context& context, |
| Position pos, |
| const Type& type, |
| ExpressionArray args) { |
| SkASSERT(type.isAllowedInES2(context)); |
| |
| // All the arguments must have matching component type. |
| SkASSERT(std::all_of(args.begin(), args.end(), [&](const std::unique_ptr<Expression>& arg) { |
| const Type& argType = arg->type(); |
| return (argType.isScalar() || argType.isVector() || argType.isMatrix()) && |
| (argType.componentType().matches(type.componentType())); |
| })); |
| |
| // The slot count of the combined argument list must match the composite type's slot count. |
| SkASSERT(type.slotCount() == |
| std::accumulate(args.begin(), args.end(), /*initial value*/ (size_t)0, |
| [](size_t n, const std::unique_ptr<Expression>& arg) { |
| return n + arg->type().slotCount(); |
| })); |
| // No-op compound constructors (containing a single argument of the same type) are eliminated. |
| // (Even though this is a "compound constructor," we let scalars pass through here; it's |
| // harmless to allow and simplifies call sites which need to narrow a vector and may sometimes |
| // end up with a scalar.) |
| if (args.size() == 1 && is_safe_to_eliminate(type, *args.front())) { |
| args.front()->fPosition = pos; |
| return std::move(args.front()); |
| } |
| // Beyond this point, the type must be a vector or matrix. |
| SkASSERT(type.isVector() || type.isMatrix()); |
| |
| if (context.fConfig->fSettings.fOptimize) { |
| // Find ConstructorCompounds embedded inside other ConstructorCompounds and flatten them. |
| // - float4(float2(1, 2), 3, 4) --> float4(1, 2, 3, 4) |
| // - float4(w, float3(sin(x), cos(y), tan(z))) --> float4(w, sin(x), cos(y), tan(z)) |
| // - mat2(float2(a, b), float2(c, d)) --> mat2(a, b, c, d) |
| |
| // See how many fields we would have if composite constructors were flattened out. |
| int fields = 0; |
| for (const std::unique_ptr<Expression>& arg : args) { |
| fields += arg->is<ConstructorCompound>() |
| ? arg->as<ConstructorCompound>().arguments().size() |
| : 1; |
| } |
| |
| // If we added up more fields than we're starting with, we found at least one input that can |
| // be flattened out. |
| if (fields > args.size()) { |
| ExpressionArray flattened; |
| flattened.reserve_back(fields); |
| for (std::unique_ptr<Expression>& arg : args) { |
| // For non-ConstructorCompound fields, move them over as-is. |
| if (!arg->is<ConstructorCompound>()) { |
| flattened.push_back(std::move(arg)); |
| continue; |
| } |
| // For ConstructorCompound fields, move over their inner arguments individually. |
| ConstructorCompound& compositeCtor = arg->as<ConstructorCompound>(); |
| for (std::unique_ptr<Expression>& innerArg : compositeCtor.arguments()) { |
| flattened.push_back(std::move(innerArg)); |
| } |
| } |
| args = std::move(flattened); |
| } |
| } |
| |
| // Replace constant variables with their corresponding values, so `float2(one, two)` can |
| // compile down to `float2(1.0, 2.0)` (the latter is a compile-time constant). |
| for (std::unique_ptr<Expression>& arg : args) { |
| arg = ConstantFolder::MakeConstantValueForVariable(pos, std::move(arg)); |
| } |
| |
| return std::make_unique<ConstructorCompound>(pos, type, std::move(args)); |
| } |
| |
| } // namespace SkSL |