// // 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, to be fed to glslang to generate // SPIR-V. // 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/ValidateVaryingLocations.h" #include "compiler/translator/util.h" namespace sh { TOutputVulkanGLSL::TOutputVulkanGLSL(TCompiler *compiler, TInfoSinkBase &objSink, bool enablePrecision, ShCompileOptions compileOptions) : TOutputGLSL(compiler, objSink, compileOptions), mNextUnusedBinding(0), mNextUnusedInputLocation(0), mNextUnusedOutputLocation(0), mEnablePrecision(enablePrecision) {} void TOutputVulkanGLSL::writeLayoutQualifier(TIntermSymbol *symbol) { const TType &type = symbol->getType(); bool needsSetBinding = IsSampler(type.getBasicType()) || (type.isInterfaceBlock() && (type.getQualifier() == EvqUniform || type.getQualifier() == EvqBuffer)) || IsImage(type.getBasicType()) || IsSubpassInputType(type.getBasicType()); bool needsLocation = type.getQualifier() == EvqAttribute || type.getQualifier() == EvqVertexIn || type.getQualifier() == EvqFragmentOut || IsVarying(type.getQualifier()); bool needsInputAttachmentIndex = IsSubpassInputType(type.getBasicType()); bool needsSpecConstId = type.getQualifier() == EvqSpecConst; if (!NeedsToWriteLayoutQualifier(type) && !needsSetBinding && !needsLocation && !needsInputAttachmentIndex && !needsSpecConstId) { return; } TInfoSinkBase &out = objSink(); const TLayoutQualifier &layoutQualifier = type.getLayoutQualifier(); // This isn't super clean, but it gets the job done. // See corresponding code in glslang_wrapper_utils.cpp. const char *blockStorage = nullptr; const char *matrixPacking = nullptr; if (type.isInterfaceBlock()) { const TInterfaceBlock *interfaceBlock = type.getInterfaceBlock(); 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); } } // Specify matrix packing if necessary. if (layoutQualifier.matrixPacking != EmpUnspecified) { matrixPacking = getMatrixPackingString(layoutQualifier.matrixPacking); } const char *kCommaSeparator = ", "; const char *separator = ""; out << "layout("; // If the resource declaration is about input attachment, need to specify input_attachment_index if (needsInputAttachmentIndex) { out << "input_attachment_index=" << layoutQualifier.inputAttachmentIndex; separator = kCommaSeparator; } // If it's a specialization constant, add that constant_id qualifier. if (needsSpecConstId) { out << separator << "constant_id=" << layoutQualifier.location; } // If the resource declaration requires set & binding layout qualifiers, specify arbitrary // ones. if (needsSetBinding) { out << separator << "set=0, binding=" << nextUnusedBinding(); separator = kCommaSeparator; } if (needsLocation) { uint32_t location = 0; if (layoutQualifier.index <= 0) { // Note: for index == 1 (dual source blending), don't count locations as they are // expected to alias the color output locations. Only one dual-source output is // supported, so location will be always 0. const unsigned int locationCount = CalculateVaryingLocationCount(symbol->getType(), getShaderType()); location = IsShaderIn(type.getQualifier()) ? nextUnusedInputLocation(locationCount) : nextUnusedOutputLocation(locationCount); } out << separator << "location=" << location; separator = kCommaSeparator; } // Output the list of qualifiers already known at this stage, i.e. everything other than // `location` and `set`/`binding`. std::string otherQualifiers = getCommonLayoutQualifiers(symbol); if (blockStorage) { out << separator << blockStorage; separator = kCommaSeparator; } if (matrixPacking) { out << separator << matrixPacking; separator = kCommaSeparator; } if (!otherQualifiers.empty()) { out << separator << otherQualifiers; } out << ") "; } void TOutputVulkanGLSL::writeVariableType(const TType &type, const TSymbol *symbol, bool isFunctionArgument) { 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, isFunctionArgument); } bool TOutputVulkanGLSL::writeVariablePrecision(TPrecision precision) { if ((precision == EbpUndefined) || !mEnablePrecision) return false; TInfoSinkBase &out = objSink(); out << getPrecisionString(precision); return true; } } // namespace sh