xref: /aosp_15_r20/external/angle/src/compiler/translator/wgsl/OutputUniformBlocks.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1 //
2 // Copyright 2024 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 
7 #include "compiler/translator/wgsl/OutputUniformBlocks.h"
8 
9 #include "angle_gl.h"
10 #include "common/mathutil.h"
11 #include "common/utilities.h"
12 #include "compiler/translator/BaseTypes.h"
13 #include "compiler/translator/Compiler.h"
14 #include "compiler/translator/InfoSink.h"
15 #include "compiler/translator/IntermNode.h"
16 #include "compiler/translator/SymbolUniqueId.h"
17 #include "compiler/translator/tree_util/IntermTraverse.h"
18 #include "compiler/translator/util.h"
19 #include "compiler/translator/wgsl/Utils.h"
20 
21 namespace sh
22 {
23 
24 namespace
25 {
26 
27 // Traverses the AST and finds all structs that are used in the uniform address space (see the
28 // UniformBlockMetadata struct).
29 class FindUniformAddressSpaceStructs : public TIntermTraverser
30 {
31   public:
FindUniformAddressSpaceStructs(UniformBlockMetadata * uniformBlockMetadata)32     FindUniformAddressSpaceStructs(UniformBlockMetadata *uniformBlockMetadata)
33         : TIntermTraverser(true, false, false), mUniformBlockMetadata(uniformBlockMetadata)
34     {}
35 
36     ~FindUniformAddressSpaceStructs() override = default;
37 
visitDeclaration(Visit visit,TIntermDeclaration * node)38     bool visitDeclaration(Visit visit, TIntermDeclaration *node) override
39     {
40         const TIntermSequence &sequence = *(node->getSequence());
41 
42         TIntermTyped *variable = sequence.front()->getAsTyped();
43         const TType &type      = variable->getType();
44 
45         // TODO(anglebug.com/376553328): should eventually ASSERT that there are no default uniforms
46         // here.
47         if (type.getQualifier() == EvqUniform)
48         {
49             recordTypesUsedInUniformAddressSpace(&type);
50         }
51 
52         return true;
53     }
54 
55   private:
56     // Recurses through the tree of types referred to be `type` (which is used in the uniform
57     // address space) and fills in the `mUniformBlockMetadata` struct appropriately.
recordTypesUsedInUniformAddressSpace(const TType * type)58     void recordTypesUsedInUniformAddressSpace(const TType *type)
59     {
60         if (type->isArray())
61         {
62             TType innerType = *type;
63             innerType.toArrayBaseType();
64             recordTypesUsedInUniformAddressSpace(&innerType);
65         }
66         else if (type->getStruct() != nullptr)
67         {
68             mUniformBlockMetadata->structsInUniformAddressSpace.insert(
69                 type->getStruct()->uniqueId().get());
70             // Recurse into the types of the fields of this struct type.
71             for (TField *const field : type->getStruct()->fields())
72             {
73                 recordTypesUsedInUniformAddressSpace(field->type());
74             }
75         }
76     }
77 
78     UniformBlockMetadata *const mUniformBlockMetadata;
79 };
80 
81 }  // namespace
82 
RecordUniformBlockMetadata(TIntermBlock * root,UniformBlockMetadata & outMetadata)83 bool RecordUniformBlockMetadata(TIntermBlock *root, UniformBlockMetadata &outMetadata)
84 {
85     FindUniformAddressSpaceStructs traverser(&outMetadata);
86     root->traverse(&traverser);
87     return true;
88 }
89 
OutputUniformBlocks(TCompiler * compiler,TIntermBlock * root)90 bool OutputUniformBlocks(TCompiler *compiler, TIntermBlock *root)
91 {
92     // TODO(anglebug.com/42267100): This should eventually just be handled the same way as a regular
93     // UBO, like in Vulkan which create a block out of the default uniforms with a traverser:
94     // https://source.chromium.org/chromium/chromium/src/+/main:third_party/angle/src/compiler/translator/spirv/TranslatorSPIRV.cpp;l=70;drc=451093bbaf7fe812bf67d27d760f3bb64c92830b
95     const std::vector<ShaderVariable> &basicUniforms = compiler->getUniforms();
96     TInfoSinkBase &output                            = compiler->getInfoSink().obj;
97     GlobalVars globalVars                            = FindGlobalVars(root);
98 
99     // Only output a struct at all if there are going to be members.
100     bool outputStructHeader = false;
101     for (const ShaderVariable &shaderVar : basicUniforms)
102     {
103         if (gl::IsOpaqueType(shaderVar.type))
104         {
105             continue;
106         }
107         if (shaderVar.isBuiltIn())
108         {
109             // gl_DepthRange and also the GLSL 4.2 gl_NumSamples are uniforms.
110             // TODO(anglebug.com/42267100): put gl_DepthRange into default uniform block.
111             continue;
112         }
113         if (!outputStructHeader)
114         {
115             output << "struct ANGLE_DefaultUniformBlock {\n";
116             outputStructHeader = true;
117         }
118         output << "  ";
119         // TODO(anglebug.com/42267100): some types will NOT match std140 layout here, namely matCx2,
120         // bool, and arrays with stride less than 16.
121         // (this check does not cover the unsupported case where there is an array of structs of
122         // size < 16).
123         if (gl::VariableRowCount(shaderVar.type) == 2 || shaderVar.type == GL_BOOL ||
124             (shaderVar.isArray() && !shaderVar.isStruct() &&
125              gl::VariableComponentCount(shaderVar.type) < 3))
126         {
127             return false;
128         }
129         output << shaderVar.name << " : ";
130 
131         TIntermDeclaration *declNode = globalVars.find(shaderVar.name)->second;
132         const TVariable *astVar      = &ViewDeclaration(*declNode).symbol.variable();
133         WriteWgslType(output, astVar->getType());
134 
135         output << ",\n";
136     }
137     // TODO(anglebug.com/42267100): might need string replacement for @group(0) and @binding(0)
138     // annotations. All WGSL resources available to shaders share the same (group, binding) ID
139     // space.
140     if (outputStructHeader)
141     {
142         ASSERT(compiler->getShaderType() == GL_VERTEX_SHADER ||
143                compiler->getShaderType() == GL_FRAGMENT_SHADER);
144         const uint32_t bindingIndex = compiler->getShaderType() == GL_VERTEX_SHADER
145                                           ? kDefaultVertexUniformBlockBinding
146                                           : kDefaultFragmentUniformBlockBinding;
147         output << "};\n\n"
148                << "@group(" << kDefaultUniformBlockBindGroup << ") @binding(" << bindingIndex
149                << ") var<uniform> " << kDefaultUniformBlockVarName << " : "
150                << kDefaultUniformBlockVarType << ";\n";
151     }
152 
153     return true;
154 }
155 
156 }  // namespace sh
157