xref: /aosp_15_r20/external/angle/src/compiler/translator/tree_ops/spirv/ReswizzleYUVOps.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1 //
2 // Copyright 2023 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // ReswizzleYUVOps: Adjusts swizzles for YUV channel order difference between
7 //   GLES and Vulkan
8 //
9 //
10 
11 #include "compiler/translator/tree_ops/spirv/EmulateYUVBuiltIns.h"
12 
13 #include "compiler/translator/StaticType.h"
14 #include "compiler/translator/SymbolTable.h"
15 #include "compiler/translator/tree_util/IntermNode_util.h"
16 #include "compiler/translator/tree_util/IntermTraverse.h"
17 #include "compiler/translator/tree_util/RunAtTheEndOfShader.h"
18 
19 namespace sh
20 {
21 namespace
22 {
23 // A traverser that adjusts channel order for various yuv ops.
24 class ReswizzleYUVOpsTraverser : public TIntermTraverser
25 {
26   public:
ReswizzleYUVOpsTraverser(TSymbolTable * symbolTable)27     ReswizzleYUVOpsTraverser(TSymbolTable *symbolTable)
28         : TIntermTraverser(true, false, false, symbolTable)
29     {}
30 
31     bool visitAggregate(Visit visit, TIntermAggregate *node) override;
32     bool visitSwizzle(Visit visit, TIntermSwizzle *node) override;
33     bool adjustOutput(TCompiler *compiler, TIntermBlock *root, const TIntermSymbol &yuvOutput);
34 
35   private:
36 };
37 
38 // OpenGLES and Vulkan has different color component mapping for YUV. OpenGL spec maps R_gl=y,
39 // G_gl=u, B_gl=v, but Vulkan wants R_vulkan=v, G_vulkan=y, B_vulkan=u. We want all calculation to
40 // be in OpenGLES mapping during shader execution, but the actual buffer/image will be stored as
41 // vulkan mapping. This means when we sample from VkImage, we need to map from vulkan order back to
42 // GL order, which comes out to be R_gl=y=G_vulkan=1, G_gl=u=B_vulkan=2, B_gl=v=R_vulkan=0. i.e, {1,
43 // 2, 0, 3}. This function will check if the aggregate is a texture{proj|fetch}(samplerExternal,...)
44 // and if yes it will compose and return a swizzle node.
CheckTextureOpWithSamplerExternal2DY2YAndSwizzle(Visit visit,TIntermAggregate * node)45 TIntermSwizzle *CheckTextureOpWithSamplerExternal2DY2YAndSwizzle(Visit visit,
46                                                                  TIntermAggregate *node)
47 {
48     if (visit != Visit::PreVisit)
49     {
50         return nullptr;
51     }
52 
53     if (!BuiltInGroup::IsBuiltIn(node->getOp()))
54     {
55         return nullptr;
56     }
57 
58     TOperator op = node->getFunction()->getBuiltInOp();
59     if (op == EOpTexture || op == EOpTextureProj || op == EOpTexelFetch)
60     {
61         TIntermSequence *arguments = node->getSequence();
62         TType const &samplerType   = (*arguments)[0]->getAsTyped()->getType();
63         if (samplerType.getBasicType() != EbtSamplerExternal2DY2YEXT)
64         {
65             return nullptr;
66         }
67 
68         // texture(...).gbra
69         TIntermSwizzle *yuvSwizzle = new TIntermSwizzle(node, {1, 2, 0, 3});
70         return yuvSwizzle;
71     }
72 
73     return nullptr;
74 }
75 
visitAggregate(Visit visit,TIntermAggregate * node)76 bool ReswizzleYUVOpsTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
77 {
78     TIntermSwizzle *yuvSwizzle = CheckTextureOpWithSamplerExternal2DY2YAndSwizzle(visit, node);
79     if (yuvSwizzle != nullptr)
80     {
81         ASSERT(!getParentNode()->getAsSwizzleNode());
82         queueReplacement(yuvSwizzle, OriginalNode::BECOMES_CHILD);
83         return false;
84     }
85 
86     return true;
87 }
88 
visitSwizzle(Visit visit,TIntermSwizzle * node)89 bool ReswizzleYUVOpsTraverser::visitSwizzle(Visit visit, TIntermSwizzle *node)
90 {
91     TIntermAggregate *aggregate = node->getOperand()->getAsAggregate();
92     if (aggregate == nullptr)
93     {
94         return true;
95     }
96 
97     // There is swizzle on YUV texture sampler, and we need to apply YUV swizzle first and
98     // then followed by the original swizzle. Finally we fold the two swizzles into one.
99     TIntermSwizzle *yuvSwizzle = CheckTextureOpWithSamplerExternal2DY2YAndSwizzle(visit, aggregate);
100     if (yuvSwizzle != nullptr)
101     {
102         TIntermTyped *replacement = new TIntermSwizzle(yuvSwizzle, node->getSwizzleOffsets());
103         replacement               = replacement->fold(nullptr);
104         queueReplacement(replacement, OriginalNode::IS_DROPPED);
105         return false;
106     }
107 
108     return true;
109 }
110 
111 // OpenGLES and Vulkan has different color component mapping for YUV. When we write YUV data, we
112 // need to convert OpenGL mapping to vulkan's mapping, which comes out to be {2, 0, 1, 3}.
adjustOutput(TCompiler * compiler,TIntermBlock * root,const TIntermSymbol & yuvOutput)113 bool ReswizzleYUVOpsTraverser::adjustOutput(TCompiler *compiler,
114                                             TIntermBlock *root,
115                                             const TIntermSymbol &yuvOutput)
116 {
117     TIntermBlock *block = new TIntermBlock;
118 
119     // output = output.brga
120     TVector<int> swizzle = {2, 0, 1, 3};
121     const int size       = yuvOutput.getType().getNominalSize();
122     if (size < 4)
123     {
124         swizzle.resize(size);
125     }
126 
127     TIntermTyped *assignment = new TIntermBinary(EOpAssign, yuvOutput.deepCopy(),
128                                                  new TIntermSwizzle(yuvOutput.deepCopy(), swizzle));
129     block->appendStatement(assignment);
130 
131     return RunAtTheEndOfShader(compiler, root, block, mSymbolTable);
132 }
133 }  // anonymous namespace
134 
ReswizzleYUVOps(TCompiler * compiler,TIntermBlock * root,TSymbolTable * symbolTable,const TIntermSymbol * yuvOutput)135 bool ReswizzleYUVOps(TCompiler *compiler,
136                      TIntermBlock *root,
137                      TSymbolTable *symbolTable,
138                      const TIntermSymbol *yuvOutput)
139 {
140     ReswizzleYUVOpsTraverser traverser(symbolTable);
141     root->traverse(&traverser);
142 
143     if (!traverser.updateTree(compiler, root))
144     {
145         return false;
146     }
147 
148     if (yuvOutput != nullptr && !traverser.adjustOutput(compiler, root, *yuvOutput))
149     {
150         return false;
151     }
152     return true;
153 }
154 }  // namespace sh
155