blob: 2a1ebc21f4c421beb24869422e41a27858d94123 [file] [log] [blame]
//
// 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