blob: 740943e978c986fcf804a14837dadadf76ad0a89 [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.
// 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