Hash :
473798bf
Author :
Date :
2024-11-28T00:38:36
WGSL: @align appropriate struct members in uniforms. Structs used in the uniform address space need to have certain members aligned according to the uniform address space layout constraints (substantially similar to std140). This CL adds @align annotations where necessary, in structs used in the uniform address space. Strictly speaking, it's okay to apply @align annotations to all structs used in the WGSL program, but this CL uses a pre-pass AST traverser to records all the structs used in the uniform address space. This is to avoid more unreadable generated code, and when more transformations are applied to these structs in future CLs, less generated code overall. After this, the only types that can't yet be used in a uniform are matCx2, arrays with stride not divisble by 16 (except when the array element type is a struct), and bools. This is #1 in struct translation in https://docs.google.com/document/d/17Qku1QEbLDhvJS-JJ9lPQAbnuZtLxWhG-ha5eCUhtEY/edit?tab=t.0#bookmark=id.rudfrn2o6jv1 Bug: angleproject:376553328 Change-Id: Ibff3414043a6ecb4a01ef8e3e71dad9c1066ddfd Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/6056951 Commit-Queue: Matthew Denton <mpdenton@chromium.org> Reviewed-by: Liza Burakova <liza@chromium.org> Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
//
// Copyright 2024 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
#include "compiler/translator/wgsl/OutputUniformBlocks.h"
#include "angle_gl.h"
#include "common/mathutil.h"
#include "common/utilities.h"
#include "compiler/translator/BaseTypes.h"
#include "compiler/translator/Compiler.h"
#include "compiler/translator/InfoSink.h"
#include "compiler/translator/IntermNode.h"
#include "compiler/translator/SymbolUniqueId.h"
#include "compiler/translator/tree_util/IntermTraverse.h"
#include "compiler/translator/util.h"
#include "compiler/translator/wgsl/Utils.h"
namespace sh
{
namespace
{
// Traverses the AST and finds all structs that are used in the uniform address space (see the
// UniformBlockMetadata struct).
class FindUniformAddressSpaceStructs : public TIntermTraverser
{
public:
FindUniformAddressSpaceStructs(UniformBlockMetadata *uniformBlockMetadata)
: TIntermTraverser(true, false, false), mUniformBlockMetadata(uniformBlockMetadata)
{}
~FindUniformAddressSpaceStructs() override = default;
bool visitDeclaration(Visit visit, TIntermDeclaration *node) override
{
const TIntermSequence &sequence = *(node->getSequence());
TIntermTyped *variable = sequence.front()->getAsTyped();
const TType &type = variable->getType();
// TODO(anglebug.com/376553328): should eventually ASSERT that there are no default uniforms
// here.
if (type.getQualifier() == EvqUniform)
{
recordTypesUsedInUniformAddressSpace(&type);
}
return true;
}
private:
// Recurses through the tree of types referred to be `type` (which is used in the uniform
// address space) and fills in the `mUniformBlockMetadata` struct appropriately.
void recordTypesUsedInUniformAddressSpace(const TType *type)
{
if (type->isArray())
{
TType innerType = *type;
innerType.toArrayBaseType();
recordTypesUsedInUniformAddressSpace(&innerType);
}
else if (type->getStruct() != nullptr)
{
mUniformBlockMetadata->structsInUniformAddressSpace.insert(
type->getStruct()->uniqueId().get());
// Recurse into the types of the fields of this struct type.
for (TField *const field : type->getStruct()->fields())
{
recordTypesUsedInUniformAddressSpace(field->type());
}
}
}
UniformBlockMetadata *const mUniformBlockMetadata;
};
} // namespace
bool RecordUniformBlockMetadata(TIntermBlock *root, UniformBlockMetadata &outMetadata)
{
FindUniformAddressSpaceStructs traverser(&outMetadata);
root->traverse(&traverser);
return true;
}
bool OutputUniformBlocks(TCompiler *compiler, TIntermBlock *root)
{
// TODO(anglebug.com/42267100): This should eventually just be handled the same way as a regular
// UBO, like in Vulkan which create a block out of the default uniforms with a traverser:
// https://source.chromium.org/chromium/chromium/src/+/main:third_party/angle/src/compiler/translator/spirv/TranslatorSPIRV.cpp;l=70;drc=451093bbaf7fe812bf67d27d760f3bb64c92830b
const std::vector<ShaderVariable> &basicUniforms = compiler->getUniforms();
TInfoSinkBase &output = compiler->getInfoSink().obj;
GlobalVars globalVars = FindGlobalVars(root);
// Only output a struct at all if there are going to be members.
bool outputStructHeader = false;
for (const ShaderVariable &shaderVar : basicUniforms)
{
if (gl::IsOpaqueType(shaderVar.type))
{
continue;
}
if (shaderVar.isBuiltIn())
{
// gl_DepthRange and also the GLSL 4.2 gl_NumSamples are uniforms.
// TODO(anglebug.com/42267100): put gl_DepthRange into default uniform block.
continue;
}
if (!outputStructHeader)
{
output << "struct ANGLE_DefaultUniformBlock {\n";
outputStructHeader = true;
}
output << " ";
// TODO(anglebug.com/42267100): some types will NOT match std140 layout here, namely matCx2,
// bool, and arrays with stride less than 16.
// (this check does not cover the unsupported case where there is an array of structs of
// size < 16).
if (gl::VariableRowCount(shaderVar.type) == 2 || shaderVar.type == GL_BOOL ||
(shaderVar.isArray() && !shaderVar.isStruct() &&
gl::VariableComponentCount(shaderVar.type) < 3))
{
return false;
}
output << shaderVar.name << " : ";
TIntermDeclaration *declNode = globalVars.find(shaderVar.name)->second;
const TVariable *astVar = &ViewDeclaration(*declNode).symbol.variable();
WriteWgslType(output, astVar->getType());
output << ",\n";
}
// TODO(anglebug.com/42267100): might need string replacement for @group(0) and @binding(0)
// annotations. All WGSL resources available to shaders share the same (group, binding) ID
// space.
if (outputStructHeader)
{
ASSERT(compiler->getShaderType() == GL_VERTEX_SHADER ||
compiler->getShaderType() == GL_FRAGMENT_SHADER);
const uint32_t bindingIndex = compiler->getShaderType() == GL_VERTEX_SHADER
? kDefaultVertexUniformBlockBinding
: kDefaultFragmentUniformBlockBinding;
output << "};\n\n"
<< "@group(" << kDefaultUniformBlockBindGroup << ") @binding(" << bindingIndex
<< ") var<uniform> " << kDefaultUniformBlockVarName << " : "
<< kDefaultUniformBlockVarType << ";\n";
}
return true;
}
} // namespace sh