• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
34   private:
35 };
36 
37 // OpenGLES and Vulkan has different color component mapping for YUV. OpenGL spec maps R_gl=y,
38 // G_gl=u, B_gl=v, but Vulkan wants R_vulkan=v, G_vulkan=y, B_vulkan=u. We want all calculation to
39 // be in OpenGLES mapping during shader execution, but the actual buffer/image will be stored as
40 // vulkan mapping. This means when we sample from VkImage, we need to map from vulkan order back to
41 // 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,
42 // 2, 0, 3}. This function will check if the aggregate is a texture{proj|fetch}(samplerExternal,...)
43 // and if yes it will compose and return a swizzle node.
CheckTextureOpWithSamplerExternal2DY2YAndSwizzle(Visit visit,TIntermAggregate * node)44 TIntermSwizzle *CheckTextureOpWithSamplerExternal2DY2YAndSwizzle(Visit visit,
45                                                                  TIntermAggregate *node)
46 {
47     if (visit != Visit::PreVisit)
48     {
49         return nullptr;
50     }
51 
52     if (!BuiltInGroup::IsBuiltIn(node->getOp()))
53     {
54         return nullptr;
55     }
56 
57     TOperator op = node->getFunction()->getBuiltInOp();
58     if (op == EOpTexture || op == EOpTextureProj || op == EOpTexelFetch)
59     {
60         TIntermSequence *arguments = node->getSequence();
61         TType const &samplerType   = (*arguments)[0]->getAsTyped()->getType();
62         if (samplerType.getBasicType() != EbtSamplerExternal2DY2YEXT)
63         {
64             return nullptr;
65         }
66 
67         // texture(...).gbra
68         TIntermSwizzle *yuvSwizzle = new TIntermSwizzle(node, {1, 2, 0, 3});
69         return yuvSwizzle;
70     }
71 
72     return nullptr;
73 }
74 
visitAggregate(Visit visit,TIntermAggregate * node)75 bool ReswizzleYUVOpsTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
76 {
77     TIntermSwizzle *yuvSwizzle = CheckTextureOpWithSamplerExternal2DY2YAndSwizzle(visit, node);
78     if (yuvSwizzle != nullptr)
79     {
80         ASSERT(!getParentNode()->getAsSwizzleNode());
81         queueReplacement(yuvSwizzle, OriginalNode::BECOMES_CHILD);
82         return false;
83     }
84 
85     return true;
86 }
87 
visitSwizzle(Visit visit,TIntermSwizzle * node)88 bool ReswizzleYUVOpsTraverser::visitSwizzle(Visit visit, TIntermSwizzle *node)
89 {
90     TIntermAggregate *aggregate = node->getOperand()->getAsAggregate();
91     if (aggregate == nullptr)
92     {
93         return true;
94     }
95 
96     // There is swizzle on YUV texture sampler, and we need to apply YUV swizzle first and
97     // then followed by the original swizzle. Finally we fold the two swizzles into one.
98     TIntermSwizzle *yuvSwizzle = CheckTextureOpWithSamplerExternal2DY2YAndSwizzle(visit, aggregate);
99     if (yuvSwizzle != nullptr)
100     {
101         TIntermTyped *replacement = new TIntermSwizzle(yuvSwizzle, node->getSwizzleOffsets());
102         replacement               = replacement->fold(nullptr);
103         queueReplacement(replacement, OriginalNode::IS_DROPPED);
104         return false;
105     }
106 
107     return true;
108 }
109 }  // anonymous namespace
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}.
AdjustYUVOutput(TCompiler * compiler,TIntermBlock * root,TSymbolTable * symbolTable,const TIntermSymbol & yuvOutput)113 bool AdjustYUVOutput(TCompiler *compiler,
114                      TIntermBlock *root,
115                      TSymbolTable *symbolTable,
116                      const TIntermSymbol &yuvOutput)
117 {
118     TIntermBlock *block = new TIntermBlock;
119 
120     // output = output.brga
121     TVector<int> swizzle = {2, 0, 1, 3};
122     const int size       = yuvOutput.getType().getNominalSize();
123     if (size < 4)
124     {
125         swizzle.resize(size);
126     }
127 
128     TIntermTyped *assignment = new TIntermBinary(EOpAssign, yuvOutput.deepCopy(),
129                                                  new TIntermSwizzle(yuvOutput.deepCopy(), swizzle));
130     block->appendStatement(assignment);
131 
132     return RunAtTheEndOfShader(compiler, root, block, symbolTable);
133 }
134 
ReswizzleYUVTextureAccess(TCompiler * compiler,TIntermBlock * root,TSymbolTable * symbolTable)135 bool ReswizzleYUVTextureAccess(TCompiler *compiler, TIntermBlock *root, TSymbolTable *symbolTable)
136 {
137     ReswizzleYUVOpsTraverser traverser(symbolTable);
138     root->traverse(&traverser);
139 
140     if (!traverser.updateTree(compiler, root))
141     {
142         return false;
143     }
144 
145     return true;
146 }
147 }  // namespace sh
148