xref: /aosp_15_r20/external/skia/src/sksl/analysis/SkSLReturnsInputAlpha.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2023 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 "include/core/SkSpan.h"
9 #include "include/core/SkTypes.h"
10 #include "include/private/base/SkTArray.h"
11 #include "src/sksl/SkSLAnalysis.h"
12 #include "src/sksl/analysis/SkSLProgramUsage.h"
13 #include "src/sksl/analysis/SkSLProgramVisitor.h"
14 #include "src/sksl/ir/SkSLConstructor.h"
15 #include "src/sksl/ir/SkSLConstructorCompound.h"
16 #include "src/sksl/ir/SkSLConstructorCompoundCast.h"
17 #include "src/sksl/ir/SkSLConstructorSplat.h"
18 #include "src/sksl/ir/SkSLExpression.h"
19 #include "src/sksl/ir/SkSLFunctionDeclaration.h"
20 #include "src/sksl/ir/SkSLFunctionDefinition.h"
21 #include "src/sksl/ir/SkSLProgramElement.h"
22 #include "src/sksl/ir/SkSLReturnStatement.h"
23 #include "src/sksl/ir/SkSLStatement.h"
24 #include "src/sksl/ir/SkSLSwizzle.h"
25 #include "src/sksl/ir/SkSLTernaryExpression.h"
26 #include "src/sksl/ir/SkSLType.h"
27 #include "src/sksl/ir/SkSLVariable.h"
28 #include "src/sksl/ir/SkSLVariableReference.h"
29 
30 #include <memory>
31 
32 using namespace skia_private;
33 
34 namespace SkSL {
35 namespace {
36 
37 class ReturnsInputAlphaVisitor : public ProgramVisitor {
38 public:
ReturnsInputAlphaVisitor(const ProgramUsage & u)39     ReturnsInputAlphaVisitor(const ProgramUsage& u) : fUsage(u) {}
40 
visitProgramElement(const ProgramElement & pe)41     bool visitProgramElement(const ProgramElement& pe) override {
42         const FunctionDeclaration& decl = pe.as<FunctionDefinition>().declaration();
43         SkSpan<Variable* const> parameters = decl.parameters();
44 
45         // We expect a color filter to have a single half4 input.
46         if (parameters.size() != 1 ||
47             parameters[0]->type().columns() != 4 ||
48             !parameters[0]->type().componentType().isFloat()) {
49             // This doesn't look like a color filter.
50             return true;
51         }
52         fInputVar = parameters[0];
53 
54         // If the input variable has been written-to, then returning `input.a` isn't sufficient to
55         // guarantee that alpha is preserved.
56         ProgramUsage::VariableCounts counts = fUsage.get(*fInputVar);
57         if (counts.fWrite != 0) {
58             return true;
59         }
60 
61         return INHERITED::visitProgramElement(pe);
62     }
63 
isInputVar(const Expression & expr)64     bool isInputVar(const Expression& expr) {
65         return expr.is<VariableReference>() && expr.as<VariableReference>().variable() == fInputVar;
66     }
67 
isInputSwizzleEndingWithAlpha(const Expression & expr)68     bool isInputSwizzleEndingWithAlpha(const Expression& expr) {
69         if (!expr.is<Swizzle>()) {
70             return false;
71         }
72         const Swizzle& swizzle = expr.as<Swizzle>();
73         return this->isInputVar(*swizzle.base()) && swizzle.components().back() == 3;
74     }
75 
returnsInputAlpha(const Expression & expr)76     bool returnsInputAlpha(const Expression& expr) {
77         if (this->isInputVar(expr)) {
78             // This expression returns the input value as-is.
79             return true;
80         }
81         if (expr.is<Swizzle>()) {
82             // It's a swizzle: check for `input.___a`.
83             return this->isInputSwizzleEndingWithAlpha(expr);
84         }
85         if (expr.is<ConstructorSplat>() || expr.is<ConstructorCompound>()) {
86             // This is a splat or compound constructor; check for `input.a` as its final component.
87             const AnyConstructor& ctor = expr.asAnyConstructor();
88             return this->returnsInputAlpha(*ctor.argumentSpan().back());
89         }
90         if (expr.is<ConstructorCompoundCast>()) {
91             // Ignore typecasts between float and half.
92             const Expression& arg = *expr.as<ConstructorCompoundCast>().argument();
93             return arg.type().componentType().isFloat() && this->returnsInputAlpha(arg);
94         }
95         if (expr.is<TernaryExpression>()) {
96             // Both sides of the ternary must preserve input alpha.
97             const TernaryExpression& ternary = expr.as<TernaryExpression>();
98             return this->returnsInputAlpha(*ternary.ifTrue()) &&
99                    this->returnsInputAlpha(*ternary.ifFalse());
100         }
101         // We weren't able to pattern-match here.
102         return false;
103     }
104 
visitStatement(const Statement & s)105     bool visitStatement(const Statement& s) override {
106         if (s.is<ReturnStatement>()) {
107             return !this->returnsInputAlpha(*s.as<ReturnStatement>().expression());
108         }
109         return INHERITED::visitStatement(s);
110     }
111 
visitExpression(const Expression & e)112     bool visitExpression(const Expression& e) override {
113         // No need to recurse into expressions; these can never contain return statements.
114         return false;
115     }
116 
117 private:
118     const ProgramUsage& fUsage;
119     const Variable* fInputVar = nullptr;
120 
121     using INHERITED = ProgramVisitor;
122 };
123 
124 }  // namespace
125 
ReturnsInputAlpha(const FunctionDefinition & function,const ProgramUsage & usage)126 bool Analysis::ReturnsInputAlpha(const FunctionDefinition& function, const ProgramUsage& usage) {
127     ReturnsInputAlphaVisitor visitor{usage};
128     return !visitor.visitProgramElement(function);
129 }
130 
131 }  // namespace SkSL
132