Hash :
b1a0d60f
Author :
Date :
2025-01-08T15:10:41
WGSL: Fix accidentally overloaded functions Small-stride arrays in uniforms with the same element type, but different array sizes, would cause the WGSL generator to produce conversion functions with the same name but different array sizes. This CL puts the array size in the name of the function to avoid overloading, which is unsupported in WGSL. Bug: angleproject:376553328 Change-Id: I446e91ccb9da2872c88f1a4e05283aacc9d6f8b1 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/6160334 Commit-Queue: Matt Denton <mpdenton@google.com> Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org> Auto-Submit: Matthew Denton <mpdenton@chromium.org> Reviewed-by: Matt Denton <mpdenton@google.com> Reviewed-by: Liza Burakova <liza@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 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214
//
// 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/ImmutableStringBuilder.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 OutputUniformWrapperStructsAndConversions(
TInfoSinkBase &output,
const WGSLGenerationMetadataForUniforms &wgslGenerationMetadataForUniforms)
{
for (const TType &type : wgslGenerationMetadataForUniforms.arrayElementTypesInUniforms)
{
// Structs don't need wrapper structs.
ASSERT(type.getStruct() == nullptr);
// Multidimensional arrays not currently supported in uniforms
ASSERT(!type.isArray());
output << "struct " << MakeUniformWrapperStructName(&type) << "\n{\n";
output << " @align(16) " << kWrappedStructFieldName << " : ";
WriteWgslType(output, type, {});
output << "\n};\n";
}
for (const TType &type :
wgslGenerationMetadataForUniforms.arrayElementTypesThatNeedUnwrappingConversions)
{
// Should be a subset of the types that have had wrapper structs generated above, otherwise
// it's impossible to unwrap them!
TType innerType = type;
innerType.toArrayElementType();
ASSERT(wgslGenerationMetadataForUniforms.arrayElementTypesInUniforms.count(innerType) != 0);
// This could take ptr<uniform, typeName>, with the unrestricted_pointer_parameters
// extension. This is probably fine.
output << "fn " << MakeUnwrappingArrayConversionFunctionName(&type) << "(wrappedArr : ";
WriteWgslType(output, type, {WgslAddressSpace::Uniform});
output << ") -> ";
WriteWgslType(output, type, {WgslAddressSpace::NonUniform});
output << "\n{\n";
output << " var retVal : ";
WriteWgslType(output, type, {WgslAddressSpace::NonUniform});
output << ";\n";
output << " for (var i : u32 = 0; i < " << type.getOutermostArraySize() << "; i++) {;\n";
output << " retVal[i] = wrappedArr[i]." << kWrappedStructFieldName << ";\n";
output << " }\n";
output << " return retVal;\n";
output << "}\n";
}
return true;
}
ImmutableString MakeUnwrappingArrayConversionFunctionName(const TType *type)
{
ASSERT(type->getNumArraySizes() <= 1);
ImmutableString arrStr = type->isArray() ? BuildConcatenatedImmutableString(
"Array", type->getOutermostArraySize(), "_")
: kEmptyImmutableString;
return BuildConcatenatedImmutableString("ANGLE_Convert_", arrStr,
MakeUniformWrapperStructName(type), "_ElementsTo_",
type->getBuiltInTypeNameString(), "_Elements");
}
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(), {WgslAddressSpace::Uniform});
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