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