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