| // |
| // Copyright 2016 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. |
| // |
| // OutputVulkanGLSL: |
| // Code that outputs shaders that fit GL_KHR_vulkan_glsl. |
| // The shaders are then fed into glslang to spit out SPIR-V (libANGLE-side). |
| // See: https://www.khronos.org/registry/vulkan/specs/misc/GL_KHR_vulkan_glsl.txt |
| // |
| |
| #include "compiler/translator/OutputVulkanGLSL.h" |
| |
| #include "compiler/translator/BaseTypes.h" |
| #include "compiler/translator/Symbol.h" |
| #include "compiler/translator/util.h" |
| |
| namespace sh |
| { |
| |
| TOutputVulkanGLSL::TOutputVulkanGLSL(TInfoSinkBase &objSink, |
| ShArrayIndexClampingStrategy clampingStrategy, |
| ShHashFunction64 hashFunction, |
| NameMap &nameMap, |
| TSymbolTable *symbolTable, |
| sh::GLenum shaderType, |
| int shaderVersion, |
| ShShaderOutput output, |
| ShCompileOptions compileOptions) |
| : TOutputGLSL(objSink, |
| clampingStrategy, |
| hashFunction, |
| nameMap, |
| symbolTable, |
| shaderType, |
| shaderVersion, |
| output, |
| compileOptions) |
| {} |
| |
| // TODO(jmadill): This is not complete. |
| void TOutputVulkanGLSL::writeLayoutQualifier(TIntermTyped *variable) |
| { |
| const TType &type = variable->getType(); |
| |
| bool needsCustomLayout = |
| type.getQualifier() == EvqAttribute || type.getQualifier() == EvqFragmentOut || |
| type.getQualifier() == EvqVertexIn || IsVarying(type.getQualifier()) || |
| IsSampler(type.getBasicType()) || type.isInterfaceBlock() || IsImage(type.getBasicType()); |
| |
| if (!NeedsToWriteLayoutQualifier(type) && !needsCustomLayout) |
| { |
| return; |
| } |
| |
| TInfoSinkBase &out = objSink(); |
| |
| // This isn't super clean, but it gets the job done. |
| // See corresponding code in glslang_wrapper_utils.cpp. |
| TIntermSymbol *symbol = variable->getAsSymbolNode(); |
| ASSERT(symbol); |
| |
| ImmutableString name = symbol->getName(); |
| const char *blockStorage = nullptr; |
| const char *matrixPacking = nullptr; |
| |
| // For interface blocks, use the block name instead. When the layout qualifier is being |
| // replaced in the backend, that would be the name that's available. |
| if (type.isInterfaceBlock()) |
| { |
| const TInterfaceBlock *interfaceBlock = type.getInterfaceBlock(); |
| name = interfaceBlock->name(); |
| TLayoutBlockStorage storage = interfaceBlock->blockStorage(); |
| |
| // Make sure block storage format is specified. |
| if (storage != EbsStd430) |
| { |
| // Change interface block layout qualifiers to std140 for any layout that is not |
| // explicitly set to std430. This is to comply with GL_KHR_vulkan_glsl where shared and |
| // packed are not allowed (and std140 could be used instead) and unspecified layouts can |
| // assume either std140 or std430 (and we choose std140 as std430 is not yet universally |
| // supported). |
| storage = EbsStd140; |
| } |
| |
| if (interfaceBlock->blockStorage() != EbsUnspecified) |
| { |
| blockStorage = getBlockStorageString(storage); |
| } |
| |
| // We expect all interface blocks to have been transformed to column major, so we don't |
| // specify the packing. Any remaining interface block qualified with row_major shouldn't |
| // have any matrices inside. |
| ASSERT(type.getLayoutQualifier().matrixPacking != EmpRowMajor || |
| !interfaceBlock->containsMatrices()); |
| } |
| |
| if (needsCustomLayout) |
| { |
| out << "@@ LAYOUT-" << name << "("; |
| } |
| else |
| { |
| out << "layout("; |
| } |
| |
| // Output the list of qualifiers already known at this stage, i.e. everything other than |
| // `location` and `set`/`binding`. |
| std::string otherQualifiers = getCommonLayoutQualifiers(variable); |
| |
| const char *separator = ""; |
| if (blockStorage) |
| { |
| out << separator << blockStorage; |
| separator = ", "; |
| } |
| if (matrixPacking) |
| { |
| out << separator << matrixPacking; |
| separator = ", "; |
| } |
| if (!otherQualifiers.empty()) |
| { |
| out << separator << otherQualifiers; |
| } |
| |
| out << ") "; |
| if (needsCustomLayout) |
| { |
| out << "@@"; |
| } |
| } |
| |
| void TOutputVulkanGLSL::writeFieldLayoutQualifier(const TField *field) |
| { |
| // We expect all interface blocks to have been transformed to column major, as Vulkan GLSL |
| // doesn't allow layout qualifiers on interface block fields. Any remaining interface block |
| // qualified with row_major shouldn't have any matrices inside, so the qualifier can be |
| // dropped. |
| } |
| |
| void TOutputVulkanGLSL::writeQualifier(TQualifier qualifier, |
| const TType &type, |
| const TSymbol *symbol) |
| { |
| if (qualifier != EvqUniform && qualifier != EvqBuffer && qualifier != EvqAttribute && |
| qualifier != EvqVertexIn && !sh::IsVarying(qualifier)) |
| { |
| TOutputGLSLBase::writeQualifier(qualifier, type, symbol); |
| return; |
| } |
| |
| if (symbol == nullptr) |
| { |
| return; |
| } |
| |
| ImmutableString name = symbol->name(); |
| |
| // For interface blocks, use the block name instead. When the qualifier is being replaced in |
| // the backend, that would be the name that's available. |
| if (type.isInterfaceBlock()) |
| { |
| name = type.getInterfaceBlock()->name(); |
| } |
| |
| TInfoSinkBase &out = objSink(); |
| out << "@@ QUALIFIER-" << name.data() << "(" << getMemoryQualifiers(type) << ") @@ "; |
| } |
| |
| void TOutputVulkanGLSL::writeVariableType(const TType &type, const TSymbol *symbol) |
| { |
| TType overrideType(type); |
| |
| // External textures are treated as 2D textures in the vulkan back-end |
| if (type.getBasicType() == EbtSamplerExternalOES) |
| { |
| overrideType.setBasicType(EbtSampler2D); |
| } |
| |
| TOutputGLSL::writeVariableType(overrideType, symbol); |
| } |
| |
| void TOutputVulkanGLSL::writeStructType(const TStructure *structure) |
| { |
| if (!structDeclared(structure)) |
| { |
| declareStruct(structure); |
| objSink() << ";\n"; |
| } |
| } |
| |
| } // namespace sh |