// // 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/Utils.h" #include "common/log_utils.h" #include "compiler/translator/BaseTypes.h" #include "compiler/translator/Common.h" #include "compiler/translator/ImmutableStringBuilder.h" #include "compiler/translator/Symbol.h" #include "compiler/translator/Types.h" #include "compiler/translator/util.h" namespace sh { namespace { const char kWrappedPrefix[] = "ANGLE_wrapped_"; } template void WriteWgslBareTypeName(StringStreamType &output, const TType &type, const EmitTypeConfig &config) { const TBasicType basicType = type.getBasicType(); switch (basicType) { case TBasicType::EbtVoid: case TBasicType::EbtBool: output << type.getBasicString(); break; // TODO(anglebug.com/42267100): is there double precision (f64) in GLSL? It doesn't really // exist in WGSL (i.e. f64 does not exist but AbstractFloat can handle 64 bits???) Metal // does not have 64 bit double precision types. It's being implemented in WGPU: // https://github.com/gpuweb/gpuweb/issues/2805 case TBasicType::EbtFloat: output << "f32"; break; case TBasicType::EbtInt: output << "i32"; break; case TBasicType::EbtUInt: output << "u32"; break; case TBasicType::EbtStruct: WriteNameOf(output, *type.getStruct()); break; case TBasicType::EbtInterfaceBlock: WriteNameOf(output, *type.getInterfaceBlock()); break; default: if (IsSampler(basicType)) { // Variables of sampler type should be written elsewhere since they require special // handling; they are split into two different variables in WGSL. // TODO(anglebug.com/389145696): this is reachable if a sampler is passed as a // function parameter. They should be monomorphized. UNIMPLEMENTED(); } else if (IsImage(basicType)) { // GLSL's image types are not implemented in this backend. UNIMPLEMENTED(); output << "texture_storage_2d<"; switch (type.getBasicType()) { case EbtImage2D: output << "f32"; break; case EbtIImage2D: output << "i32"; break; case EbtUImage2D: output << "u32"; break; default: UNIMPLEMENTED(); break; } if (type.getMemoryQualifier().readonly || type.getMemoryQualifier().writeonly) { UNIMPLEMENTED(); } output << ">"; } else { UNREACHABLE(); } break; } } template void WriteNameOf(StringStreamType &output, SymbolType symbolType, const ImmutableString &name) { switch (symbolType) { case SymbolType::BuiltIn: output << name; break; case SymbolType::UserDefined: output << kUserDefinedNamePrefix << name; break; case SymbolType::AngleInternal: output << name; break; case SymbolType::Empty: // TODO(anglebug.com/42267100): support this if necessary UNREACHABLE(); } } template void WriteWgslType(StringStreamType &output, const TType &type, const EmitTypeConfig &config) { if (type.isArray()) { // WGSL does not support samplers anywhere inside structs or arrays. ASSERT(!type.isSampler() && !type.isStructureContainingSamplers()); // Examples: // array // array, 10> output << "array<"; TType innerType = type; innerType.toArrayElementType(); if (ElementTypeNeedsUniformWrapperStruct(config.addressSpace == WgslAddressSpace::Uniform, &type)) { // Multidimensional arrays not currently supported in uniforms in the WebGPU backend ASSERT(!innerType.isArray()); // Due to uniform address space layout constraints, certain array element types must // be wrapped in a wrapper struct. // Example: array output << MakeUniformWrapperStructName(&innerType); } else { WriteWgslType(output, innerType, config); } output << ", " << type.getOutermostArraySize() << ">"; } else if (type.isVector()) { output << "vec" << static_cast(type.getNominalSize()) << "<"; WriteWgslBareTypeName(output, type, config); output << ">"; } else if (type.isMatrix()) { if (config.addressSpace == WgslAddressSpace::Uniform && type.getRows() == 2) { // matCx2 in the uniform address space is too packed for std140, and so they will be // represented by an array. output << "array<" << kWrappedPrefix << "vec2, " << static_cast(type.getCols()) << ">"; } else { output << "mat" << static_cast(type.getCols()) << "x" << static_cast(type.getRows()) << "<"; WriteWgslBareTypeName(output, type, config); output << ">"; } } else { // This type has no dimensions and is equivalent to its bare type. WriteWgslBareTypeName(output, type, config); } } template void WriteWgslBareTypeName(TInfoSinkBase &output, const TType &type, const EmitTypeConfig &config); template void WriteNameOf(TInfoSinkBase &output, SymbolType symbolType, const ImmutableString &name); template void WriteWgslType(TInfoSinkBase &output, const TType &type, const EmitTypeConfig &config); template void WriteWgslBareTypeName(TStringStream &output, const TType &type, const EmitTypeConfig &config); template void WriteNameOf(TStringStream &output, SymbolType symbolType, const ImmutableString &name); template void WriteWgslType(TStringStream &output, const TType &type, const EmitTypeConfig &config); template void WriteWgslSamplerType(StringStreamType &output, const TType &type, WgslSamplerTypeConfig samplerType) { ASSERT(type.isSampler()); if (samplerType == WgslSamplerTypeConfig::Texture) { output << "texture"; if (IsShadowSampler(type.getBasicType())) { output << "_depth"; } if (IsSamplerMS(type.getBasicType())) { output << "_multisampled"; ASSERT(IsSampler2D(type.getBasicType())); // Unsupported in wGSL, it seems. ASSERT(!IsSampler2DMSArray(type.getBasicType())); } if (IsSampler2D(type.getBasicType()) || IsSampler2DArray(type.getBasicType())) { output << "_2d"; } else if (IsSampler3D(type.getBasicType())) { output << "_3d"; } else if (IsSamplerCube(type.getBasicType())) { output << "_cube"; } if (IsSamplerArray(type.getBasicType())) { ASSERT(!IsSampler3D(type.getBasicType())); output << "_array"; } // Shadow samplers are always floating point in both GLSL and WGSL and don't need to be // parameterized. if (!IsShadowSampler(type.getBasicType())) { output << "<"; if (!IsIntegerSampler(type.getBasicType())) { output << "f32"; } else if (!IsIntegerSamplerUnsigned(type.getBasicType())) { output << "i32"; } else { output << "u32"; } output << ">"; } if (type.getMemoryQualifier().readonly || type.getMemoryQualifier().writeonly) { // TODO(anglebug.com/42267100): implement memory qualifiers. UNIMPLEMENTED(); } } else { ASSERT(samplerType == WgslSamplerTypeConfig::Sampler); // sampler or sampler_comparison. if (IsShadowSampler(type.getBasicType())) { output << "sampler_comparison"; } else { output << "sampler"; } } } template void WriteWgslSamplerType(TInfoSinkBase &output, const TType &type, WgslSamplerTypeConfig samplerType); template void WriteWgslSamplerType(TStringStream &output, const TType &type, WgslSamplerTypeConfig samplerType); ImmutableString MakeUniformWrapperStructName(const TType *type) { return BuildConcatenatedImmutableString(kWrappedPrefix, type->getBuiltInTypeNameString()); } bool ElementTypeNeedsUniformWrapperStruct(bool inUniformAddressSpace, const TType *type) { // Only types that are used as array element types in the uniform address space need wrapper // structs. If the array element type is a struct it does not need to be wrapped in another // layer of struct. if (!inUniformAddressSpace || !type->isArray() || type->getStruct()) { return false; } TType elementType = *type; elementType.toArrayElementType(); // If the array element type's stride is already a multiple of 16, it does not need a wrapper // struct. // // The remaining possible element types are scalars, vectors, matrices, and other arrays. // - Scalars need to be aligned to 16. // - vec3 and vec4 are already aligned to 16, but vec2 needs to be aligned. // - Matrices are aligned to 16 automatically, except matCx2 which already needs to be handled // by specialized code anyway. // TODO(anglebug.com/376553328): re-enable this ASSERT once matCx2 are handled. // ASSERT(!type->isMatrix() || type->getRows() != 2); // - WebGL2 doesn't support nested arrays so this won't either, though support wouldn't be hard. ASSERT(!elementType.isArray()); return elementType.isScalar() || (elementType.isVector() && elementType.getNominalSize() == 2); } GlobalVars FindGlobalVars(TIntermBlock *root) { GlobalVars globals; for (TIntermNode *node : *root->getSequence()) { if (TIntermDeclaration *declNode = node->getAsDeclarationNode()) { Declaration decl = ViewDeclaration(*declNode); globals.insert({decl.symbol.variable().name(), declNode}); } } return globals; } } // namespace sh