Commit 71e6afb1 by Shahbaz Youssefi Committed by Commit Bot

Vulkan: Set set/binding in SPIR-V

This change introduces a SPIR-V transformer that modifies shader interface variable decorations directly in SPIR-V instead of manipulating the input GLSL. Currently, descriptor set and binding indices are set by the transformer. The shader translator outputs arbitrary set and binding indices. Once compiled by glslang, the SPIR-V transformer modifies these decorations. The ultimate goal is to be able to modify the SPIR-V again when program pipeline objects decide a different set/binding is necessary. Bug: angleproject:3394 Change-Id: If358265a72bf1fe9f5676562b39a632cb2e05dc4 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2001477Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarShahbaz Youssefi <syoussefi@chromium.org> Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
parent 2bc9cc12
......@@ -613,6 +613,8 @@ if (angle_enable_vulkan || angle_enable_metal) {
":libANGLE_headers",
"${angle_glslang_dir}:glslang_default_resource_limits_sources",
"${angle_glslang_dir}:glslang_sources",
"${angle_spirv_headers_dir}:spv_headers",
"${angle_spirv_tools_dir}:spvtools_headers",
]
}
}
......
......@@ -15,4 +15,5 @@ angle_libjpeg_turbo_dir = "//third_party/libjpeg_turbo"
angle_jsoncpp_dir = "//third_party/jsoncpp"
angle_libpng_dir = "//third_party/libpng"
angle_spirv_cross_dir = "//third_party/spirv-cross/src"
angle_spirv_headers_dir = "//third_party/spirv-headers/src"
angle_spirv_tools_dir = "//third_party/spirv-tools/src"
......@@ -26,7 +26,7 @@
// Version number for shader translation API.
// It is incremented every time the API changes.
#define ANGLE_SH_VERSION 223
#define ANGLE_SH_VERSION 224
enum ShShaderSpec
{
......@@ -733,6 +733,11 @@ inline bool IsDesktopGLSpec(ShShaderSpec spec)
return spec == SH_GL_CORE_SPEC || spec == SH_GL_COMPATIBILITY_SPEC;
}
// Can't prefix with just _ because then we might introduce a double underscore, which is not safe
// in GLSL (ESSL 3.00.6 section 3.8: All identifiers containing a double underscore are reserved for
// use by the underlying implementation). u is short for user-defined.
extern const char kUserDefinedNamePrefix[];
namespace vk
{
......@@ -745,6 +750,21 @@ enum class SpecializationConstantId : uint32_t
EnumCount = 1,
};
// Interface block name containing the aggregate default uniforms
extern const char kDefaultUniformsNameVS[];
extern const char kDefaultUniformsNameTCS[];
extern const char kDefaultUniformsNameTES[];
extern const char kDefaultUniformsNameGS[];
extern const char kDefaultUniformsNameFS[];
extern const char kDefaultUniformsNameCS[];
// Interface block and variable name containing driver uniforms
extern const char kDriverUniformsBlockName[];
extern const char kDriverUniformsVarName[];
// Interface block array variable name used for atomic counter emulation
extern const char kAtomicCountersVarName[];
} // namespace vk
} // namespace sh
......
......@@ -183,6 +183,13 @@ std::string ToString(const T &value)
return o.str();
}
inline bool IsLittleEndian()
{
constexpr uint32_t kEndiannessTest = 1;
const bool isLittleEndian = *reinterpret_cast<const uint8_t *>(&kEndiannessTest) == 1;
return isLittleEndian;
}
// snprintf is not defined with MSVC prior to to msvc14
#if defined(_MSC_VER) && _MSC_VER < 1900
# define snprintf _snprintf
......
......@@ -18,11 +18,6 @@ namespace
{
constexpr const ImmutableString kHashedNamePrefix("webgl_");
// Can't prefix with just _ because then we might introduce a double underscore, which is not safe
// in GLSL (ESSL 3.00.6 section 3.8: All identifiers containing a double underscore are reserved for
// use by the underlying implementation). u is short for user-defined.
constexpr const ImmutableString kUnhashedNamePrefix("_u");
ImmutableString HashName(const ImmutableString &name, ShHashFunction64 hashFunction)
{
ASSERT(!name.empty());
......@@ -47,6 +42,8 @@ ImmutableString HashName(const ImmutableString &name,
ShHashFunction64 hashFunction,
NameMap *nameMap)
{
const ImmutableString kUnhashedNamePrefix(kUserDefinedNamePrefix);
if (hashFunction == nullptr)
{
if (name.length() + kUnhashedNamePrefix.length() > kESSLMaxIdentifierLength)
......
......@@ -35,20 +35,21 @@ TOutputVulkanGLSL::TOutputVulkanGLSL(TInfoSinkBase &objSink,
shaderType,
shaderVersion,
output,
compileOptions)
compileOptions),
mNextUnusedBinding(0)
{}
// 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()) ||
bool needsCustomLayout = type.getQualifier() == EvqAttribute ||
type.getQualifier() == EvqFragmentOut ||
type.getQualifier() == EvqVertexIn || IsVarying(type.getQualifier());
bool needsSetBinding =
IsSampler(type.getBasicType()) || type.isInterfaceBlock() || IsImage(type.getBasicType());
if (!NeedsToWriteLayoutQualifier(type) && !needsCustomLayout)
if (!NeedsToWriteLayoutQualifier(type) && !needsCustomLayout && !needsSetBinding)
{
return;
}
......@@ -96,6 +97,7 @@ void TOutputVulkanGLSL::writeLayoutQualifier(TIntermTyped *variable)
matrixPacking = getMatrixPackingString(layoutQualifier.matrixPacking);
}
const char *separator = "";
if (needsCustomLayout)
{
out << "@@ LAYOUT-" << name << "(";
......@@ -103,13 +105,20 @@ void TOutputVulkanGLSL::writeLayoutQualifier(TIntermTyped *variable)
else
{
out << "layout(";
// If the resource declaration requires set & binding layout qualifiers, specify arbitrary
// ones.
if (needsSetBinding)
{
out << "set=0, binding=" << nextUnusedBinding();
separator = ", ";
}
}
// 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;
......
......@@ -29,12 +29,19 @@ class TOutputVulkanGLSL : public TOutputGLSL
void writeStructType(const TStructure *structure);
uint32_t nextUnusedBinding() { return mNextUnusedBinding++; }
protected:
void writeLayoutQualifier(TIntermTyped *variable) override;
void writeQualifier(TQualifier qualifier, const TType &type, const TSymbol *symbol) override;
void writeVariableType(const TType &type,
const TSymbol *symbol,
bool isFunctionArgument) override;
// Every resource that requires set & binding layout qualifiers is assigned set 0 and an
// arbitrary binding when outputting GLSL. Glslang wrapper modifies set and binding decorations
// in SPIR-V directly.
uint32_t mNextUnusedBinding;
};
} // namespace sh
......@@ -698,4 +698,28 @@ unsigned int GetShaderSharedMemorySize(const ShHandle handle)
return sharedMemorySize;
}
// Can't prefix with just _ because then we might introduce a double underscore, which is not safe
// in GLSL (ESSL 3.00.6 section 3.8: All identifiers containing a double underscore are reserved for
// use by the underlying implementation). u is short for user-defined.
const char kUserDefinedNamePrefix[] = "_u";
namespace vk
{
// Interface block name containing the aggregate default uniforms
const char kDefaultUniformsNameVS[] = "defaultUniformsVS";
const char kDefaultUniformsNameTCS[] = "defaultUniformsTCS";
const char kDefaultUniformsNameTES[] = "defaultUniformsTES";
const char kDefaultUniformsNameGS[] = "defaultUniformsGS";
const char kDefaultUniformsNameFS[] = "defaultUniformsFS";
const char kDefaultUniformsNameCS[] = "defaultUniformsCS";
// Interface block and variable name containing driver uniforms
const char kDriverUniformsBlockName[] = "ANGLEUniformBlock";
const char kDriverUniformsVarName[] = "ANGLEUniforms";
// Interface block array variable name used for atomic counter emulation
const char kAtomicCountersVarName[] = "atomicCounters";
} // namespace vk
} // namespace sh
......@@ -12,6 +12,7 @@
#include "compiler/translator/TranslatorVulkan.h"
#include "angle_gl.h"
#include "common/PackedEnums.h"
#include "common/utilities.h"
#include "compiler/translator/BuiltinsWorkaroundGLSL.h"
#include "compiler/translator/ImmutableStringBuilder.h"
......@@ -158,8 +159,13 @@ class DeclareDefaultUniformsTraverser : public TIntermTraverser
constexpr ImmutableString kFlippedPointCoordName = ImmutableString("flippedPointCoord");
constexpr ImmutableString kFlippedFragCoordName = ImmutableString("flippedFragCoord");
constexpr ImmutableString kEmulatedDepthRangeParams = ImmutableString("ANGLEDepthRangeParams");
constexpr ImmutableString kUniformsBlockName = ImmutableString("ANGLEUniformBlock");
constexpr ImmutableString kUniformsVarName = ImmutableString("ANGLEUniforms");
constexpr gl::ShaderMap<const char *> kDefaultUniformNames = {
{gl::ShaderType::Vertex, vk::kDefaultUniformsNameVS},
{gl::ShaderType::Geometry, vk::kDefaultUniformsNameGS},
{gl::ShaderType::Fragment, vk::kDefaultUniformsNameFS},
{gl::ShaderType::Compute, vk::kDefaultUniformsNameCS},
};
// Specialization constant names
constexpr ImmutableString kLineRasterEmulationSpecConstVarName =
......@@ -396,9 +402,9 @@ const TVariable *AddGraphicsDriverUniformsToShader(TIntermBlock *root, TSymbolTa
}
// Define a driver uniform block "ANGLEUniformBlock" with instance name "ANGLEUniforms".
return DeclareInterfaceBlock(root, symbolTable, driverFieldList, EvqUniform,
TMemoryQualifier::Create(), 0, kUniformsBlockName,
kUniformsVarName);
return DeclareInterfaceBlock(
root, symbolTable, driverFieldList, EvqUniform, TMemoryQualifier::Create(), 0,
ImmutableString(vk::kDriverUniformsBlockName), ImmutableString(vk::kDriverUniformsVarName));
}
const TVariable *AddComputeDriverUniformsToShader(TIntermBlock *root, TSymbolTable *symbolTable)
......@@ -420,9 +426,9 @@ const TVariable *AddComputeDriverUniformsToShader(TIntermBlock *root, TSymbolTab
}
// Define a driver uniform block "ANGLEUniformBlock" with instance name "ANGLEUniforms".
return DeclareInterfaceBlock(root, symbolTable, driverFieldList, EvqUniform,
TMemoryQualifier::Create(), 0, kUniformsBlockName,
kUniformsVarName);
return DeclareInterfaceBlock(
root, symbolTable, driverFieldList, EvqUniform, TMemoryQualifier::Create(), 0,
ImmutableString(vk::kDriverUniformsBlockName), ImmutableString(vk::kDriverUniformsVarName));
}
TIntermSymbol *GenerateLineRasterSpecConstRef(TSymbolTable *symbolTable)
......@@ -791,7 +797,9 @@ bool TranslatorVulkan::translateImpl(TIntermBlock *root,
if (defaultUniformCount > 0)
{
sink << "\n@@ LAYOUT-defaultUniforms(std140) @@ uniform defaultUniforms\n{\n";
gl::ShaderType shaderType = gl::FromGLenum<gl::ShaderType>(getShaderType());
sink << "\nlayout(set=0, binding=" << outputGLSL->nextUnusedBinding()
<< ", std140) uniform " << kDefaultUniformNames[shaderType] << "\n{\n";
DeclareDefaultUniformsTraverser defaultTraverser(&sink, getHashFunction(), &getNameMap());
root->traverse(&defaultTraverser);
......
......@@ -22,7 +22,6 @@ namespace
{
constexpr ImmutableString kAtomicCounterTypeName = ImmutableString("ANGLE_atomic_uint");
constexpr ImmutableString kAtomicCounterBlockName = ImmutableString("ANGLEAtomicCounters");
constexpr ImmutableString kAtomicCounterVarName = ImmutableString("atomicCounters");
constexpr ImmutableString kAtomicCounterFieldName = ImmutableString("counters");
// DeclareAtomicCountersBuffer adds a storage buffer array that's used with atomic counters.
......@@ -48,7 +47,7 @@ const TVariable *DeclareAtomicCountersBuffers(TIntermBlock *root, TSymbolTable *
// Define a storage block "ANGLEAtomicCounters" with instance name "atomicCounters".
return DeclareInterfaceBlock(root, symbolTable, fieldList, EvqBuffer, coherentMemory,
kMaxAtomicCounterBuffers, kAtomicCounterBlockName,
kAtomicCounterVarName);
ImmutableString(vk::kAtomicCountersVarName));
}
TIntermConstantUnion *CreateUIntConstant(uint32_t value)
......
......@@ -360,7 +360,8 @@ class Traverser final : public TIntermTraverser, public ArrayTraverser
ASSERT(asSymbol);
const TVariable &variable = asSymbol->variable();
ASSERT(variable.symbolType() != SymbolType::Empty);
extractSampler(variable.name(), variable.getType(), newSequence, 0);
extractSampler(variable.name(), variable.symbolType(), variable.getType(), newSequence,
0);
mMultiReplacements.emplace_back(getParentNode()->getAsBlock(), decl, *newSequence);
}
......@@ -652,7 +653,7 @@ class Traverser final : public TIntermTraverser, public ArrayTraverser
if (fieldType.isSampler())
{
extractSampler(newPrefix, fieldType, newSequence, 0);
extractSampler(newPrefix, SymbolType::AngleInternal, fieldType, newSequence, 0);
}
else
{
......@@ -676,6 +677,7 @@ class Traverser final : public TIntermTraverser, public ArrayTraverser
// Extracts a sampler from a struct. Declares the new extracted sampler.
void extractSampler(const ImmutableString &newName,
SymbolType symbolType,
const TType &fieldType,
TIntermSequence *newSequence,
size_t arrayLevel)
......@@ -692,16 +694,27 @@ class Traverser final : public TIntermTraverser, public ArrayTraverser
newType->makeArray(static_cast<unsigned int>(mCumulativeArraySizeStack.back()));
}
newType->setQualifier(EvqUniform);
TVariable *newVariable =
new TVariable(mSymbolTable, newName, newType, SymbolType::AngleInternal);
TIntermSymbol *newRef = new TIntermSymbol(newVariable);
TVariable *newVariable = new TVariable(mSymbolTable, newName, newType, symbolType);
TIntermSymbol *newRef = new TIntermSymbol(newVariable);
TIntermDeclaration *samplerDecl = new TIntermDeclaration;
samplerDecl->appendDeclarator(newRef);
newSequence->push_back(samplerDecl);
mSymbolTable->declareInternal(newVariable);
// TODO(syoussefi): Use a SymbolType::Empty name instead of generating a name as currently
// done. There is no guarantee that these generated names cannot clash. Create a mapping
// from the previous name to the name assigned to the SymbolType::Empty variable so
// ShaderVariable::mappedName can be updated post-transformation.
// http://anglebug.com/4301
if (symbolType == SymbolType::AngleInternal)
{
mSymbolTable->declareInternal(newVariable);
}
else
{
mSymbolTable->declare(newVariable);
}
GenerateArrayStrides(mArraySizeStack, &mVariableExtraData.arrayStrideMap[newVariable]);
......
......@@ -22,6 +22,12 @@ ANGLE_DISABLE_SHADOWING_WARNING
ANGLE_REENABLE_SHADOWING_WARNING
ANGLE_REENABLE_EXTRA_SEMI_WARNING
// SPIR-V headers include for AST transformation.
#include <spirv/unified1/spirv.hpp>
// SPIR-V tools include for AST validation.
#include <spirv-tools/libspirv.hpp>
#include <array>
#include <numeric>
......@@ -56,6 +62,13 @@ constexpr char kParamsEnd = ')';
constexpr uint32_t kANGLEPositionLocationOffset = 1;
constexpr uint32_t kXfbANGLEPositionLocationOffset = 2;
constexpr gl::ShaderMap<const char *> kDefaultUniformNames = {
{gl::ShaderType::Vertex, sh::vk::kDefaultUniformsNameVS},
{gl::ShaderType::Geometry, sh::vk::kDefaultUniformsNameGS},
{gl::ShaderType::Fragment, sh::vk::kDefaultUniformsNameFS},
{gl::ShaderType::Compute, sh::vk::kDefaultUniformsNameCS},
};
template <size_t N>
constexpr size_t ConstStrLen(const char (&)[N])
{
......@@ -388,6 +401,71 @@ std::string IntermediateShaderSource::getShaderSource()
return shaderSource;
}
// Test if there are non-zero indices in the uniform name, returning false in that case. This
// happens for multi-dimensional arrays, where a uniform is created for every possible index of the
// array (except for the innermost dimension). When assigning decorations (set/binding/etc), only
// the indices corresponding to the first element of the array should be specified. This function
// is used to skip the other indices.
//
// If useOldRewriteStructSamplers, there are multiple samplers extracted out of struct arrays
// though, so the above only applies to the sampler array defined in the struct.
bool UniformNameIsIndexZero(const std::string &name, bool excludeCheckForOwningStructArrays)
{
size_t lastBracketClose = 0;
if (excludeCheckForOwningStructArrays)
{
size_t lastDot = name.find_last_of('.');
if (lastDot != std::string::npos)
{
lastBracketClose = lastDot;
}
}
while (true)
{
size_t openBracket = name.find('[', lastBracketClose);
if (openBracket == std::string::npos)
{
break;
}
size_t closeBracket = name.find(']', openBracket);
// If the index between the brackets is not zero, ignore this uniform.
if (name.substr(openBracket + 1, closeBracket - openBracket - 1) != "0")
{
return false;
}
lastBracketClose = closeBracket;
}
return true;
}
// Strip indices from the name. If there are non-zero indices, return false to indicate that this
// image uniform doesn't require set/binding. That is done on index 0.
bool GetImageNameWithoutIndices(std::string *name)
{
if (name->back() != ']')
{
return true;
}
if (!UniformNameIsIndexZero(*name, false))
{
return false;
}
// Strip all indices
*name = name->substr(0, name->find('['));
return true;
}
bool MappedSamplerNameNeedsUserDefinedPrefix(const std::string &originalName)
{
return originalName.find('.') == std::string::npos;
}
std::string GetMappedSamplerNameOld(const std::string &originalName)
{
std::string samplerName = gl::ParseResourceName(originalName, nullptr);
......@@ -398,6 +476,12 @@ std::string GetMappedSamplerNameOld(const std::string &originalName)
// Samplers in arrays of structs are also extracted.
std::replace(samplerName.begin(), samplerName.end(), '[', '_');
samplerName.erase(std::remove(samplerName.begin(), samplerName.end(), ']'), samplerName.end());
if (MappedSamplerNameNeedsUserDefinedPrefix(originalName))
{
samplerName = sh::kUserDefinedNamePrefix + samplerName;
}
return samplerName;
}
......@@ -415,6 +499,24 @@ uint32_t CountExplicitOutputs(OutputIter outputsBegin,
return std::accumulate(outputsBegin, outputsEnd, 0, reduce);
}
ShaderInterfaceVariableInfo *AddShaderInterfaceVariable(ShaderInterfaceVariableInfoMap *infoMap,
const std::string &varName)
{
ASSERT(infoMap->find(varName) == infoMap->end());
return &(*infoMap)[varName];
}
ShaderInterfaceVariableInfo *AddResourceInfo(ShaderInterfaceVariableInfoMap *infoMap,
const std::string &varName,
uint32_t descriptorSet,
uint32_t binding)
{
ShaderInterfaceVariableInfo *info = AddShaderInterfaceVariable(infoMap, varName);
info->descriptorSet = descriptorSet;
info->binding = binding;
return info;
}
std::string GenerateTransformFeedbackVaryingOutput(const gl::TransformFeedbackVarying &varying,
const gl::UniformTypeInfo &info,
size_t strideBytes,
......@@ -435,11 +537,11 @@ std::string GenerateTransformFeedbackVaryingOutput(const gl::TransformFeedbackVa
{
for (int row = 0; row < info.rowCount; ++row)
{
result << "xfbOut" << bufferIndex << "[ANGLEUniforms.xfbBufferOffsets["
<< bufferIndex
result << "xfbOut" << bufferIndex << "[" << sh::vk::kDriverUniformsVarName
<< ".xfbBufferOffsets[" << bufferIndex
<< "] + (gl_VertexIndex + gl_InstanceIndex * "
"ANGLEUniforms.xfbVerticesPerDraw) * "
<< stride << " + " << offset << "] = " << info.glslAsFloat << "("
<< sh::vk::kDriverUniformsVarName << ".xfbVerticesPerDraw) * " << stride
<< " + " << offset << "] = " << info.glslAsFloat << "("
<< varying.mappedName;
if (varying.isArray())
......@@ -468,7 +570,8 @@ std::string GenerateTransformFeedbackVaryingOutput(const gl::TransformFeedbackVa
void GenerateTransformFeedbackEmulationOutputs(const GlslangSourceOptions &options,
const gl::ProgramState &programState,
IntermediateShaderSource *vertexShader)
IntermediateShaderSource *vertexShader,
ShaderInterfaceVariableInfoMap *variableInfoMapOut)
{
const std::vector<gl::TransformFeedbackVarying> &varyings =
programState.getLinkedTransformFeedbackVaryings();
......@@ -487,12 +590,19 @@ void GenerateTransformFeedbackEmulationOutputs(const GlslangSourceOptions &optio
const std::string xfbBinding = Str(options.xfbBindingIndexStart + bufferIndex);
xfbIndices[bufferIndex] = Str(bufferIndex);
xfbDecl += "layout(set = " + xfbSet + ", binding = " + xfbBinding + ") buffer xfbBuffer" +
xfbIndices[bufferIndex] + " { float xfbOut" + xfbIndices[bufferIndex] +
"[]; };\n";
std::string bufferName = "xfbBuffer" + xfbIndices[bufferIndex];
xfbDecl += "layout(set = " + xfbSet + ", binding = " + xfbBinding + ") buffer " +
bufferName + " { float xfbOut" + xfbIndices[bufferIndex] + "[]; };\n";
// Add this entry to the info map, so we can easily assert that every resource has an entry
// in this map.
AddResourceInfo(variableInfoMapOut, bufferName, options.uniformsAndXfbDescriptorSetIndex,
options.xfbBindingIndexStart + bufferIndex);
}
std::string xfbOut = "if (ANGLEUniforms.xfbActiveUnpaused != 0)\n{\n";
std::string xfbOut =
"if (" + std::string(sh::vk::kDriverUniformsVarName) + ".xfbActiveUnpaused != 0)\n{\n";
size_t outputOffset = 0;
for (size_t varyingIndex = 0; varyingIndex < varyings.size(); ++varyingIndex)
{
......@@ -808,71 +918,40 @@ void AssignVaryingLocations(const gl::ProgramState &programState,
}
void AssignUniformBindings(const GlslangSourceOptions &options,
gl::ShaderMap<IntermediateShaderSource> *shaderSources)
{
// Bind the default uniforms for vertex and fragment shaders.
// See corresponding code in OutputVulkanGLSL.cpp.
const std::string uniformsDescriptorSet =
"set = " + Str(options.uniformsAndXfbDescriptorSetIndex);
constexpr char kDefaultUniformsBlockName[] = "defaultUniforms";
uint32_t bindingIndex = 0;
for (IntermediateShaderSource &shaderSource : *shaderSources)
{
if (!shaderSource.empty())
{
std::string defaultUniformsBinding =
uniformsDescriptorSet + ", binding = " + Str(bindingIndex++);
shaderSource.insertLayoutSpecifier(kDefaultUniformsBlockName, defaultUniformsBinding);
}
}
// Substitute layout string for the driver uniforms block.
const std::string driverBlockLayoutString =
"set = " + Str(options.driverUniformsDescriptorSetIndex) + ", binding = 0";
constexpr char kDriverBlockName[] = "ANGLEUniformBlock";
for (IntermediateShaderSource &shaderSource : *shaderSources)
{
shaderSource.insertLayoutSpecifier(kDriverBlockName, driverBlockLayoutString);
}
}
// Helper to go through shader stages and substitute layout macro. The translator must have already
// output the qualifiers.
void AssignResourceBinding(gl::ShaderBitSet activeShaders,
const std::string &name,
const std::string &bindingString,
gl::ShaderMap<IntermediateShaderSource> *shaderSources)
gl::ShaderMap<IntermediateShaderSource> *shaderSources,
ShaderInterfaceVariableInfoMap *variableInfoMapOut)
{
// Assign binding to the default uniforms block of each shader stage.
uint32_t bindingIndex = 0;
for (const gl::ShaderType shaderType : gl::AllShaderTypes())
{
IntermediateShaderSource &shaderSource = (*shaderSources)[shaderType];
if (activeShaders[shaderType] && !shaderSource.empty())
if (!shaderSource.empty())
{
shaderSource.insertLayoutSpecifier(name, bindingString);
AddResourceInfo(variableInfoMapOut, kDefaultUniformNames[shaderType],
options.uniformsAndXfbDescriptorSetIndex, bindingIndex);
++bindingIndex;
}
}
// Assign binding to the driver uniforms block
AddResourceInfo(variableInfoMapOut, sh::vk::kDriverUniformsVarName,
options.driverUniformsDescriptorSetIndex, 0);
}
uint32_t AssignInterfaceBlockBindings(const GlslangSourceOptions &options,
const std::vector<gl::InterfaceBlock> &blocks,
uint32_t bindingStart,
gl::ShaderMap<IntermediateShaderSource> *shaderSources)
ShaderInterfaceVariableInfoMap *variableInfoMapOut)
{
const std::string resourcesDescriptorSet =
"set = " + Str(options.shaderResourceDescriptorSetIndex);
uint32_t bindingIndex = bindingStart;
for (const gl::InterfaceBlock &block : blocks)
{
if (!block.isArray || block.arrayElement == 0)
{
const std::string bindingString =
resourcesDescriptorSet + ", binding = " + Str(bindingIndex++);
AssignResourceBinding(block.activeShaders(), block.name, bindingString, shaderSources);
AddResourceInfo(variableInfoMapOut, block.mappedName,
options.shaderResourceDescriptorSetIndex, bindingIndex);
++bindingIndex;
}
}
......@@ -882,26 +961,15 @@ uint32_t AssignInterfaceBlockBindings(const GlslangSourceOptions &options,
uint32_t AssignAtomicCounterBufferBindings(const GlslangSourceOptions &options,
const std::vector<gl::AtomicCounterBuffer> &buffers,
uint32_t bindingStart,
gl::ShaderMap<IntermediateShaderSource> *shaderSources)
ShaderInterfaceVariableInfoMap *variableInfoMapOut)
{
if (buffers.size() == 0)
{
return bindingStart;
}
constexpr char kAtomicCounterBlockName[] = "ANGLEAtomicCounters";
const std::string bindingString = "set = " + Str(options.shaderResourceDescriptorSetIndex) +
", binding = " + Str(bindingStart);
for (const gl::ShaderType shaderType : gl::AllShaderTypes())
{
IntermediateShaderSource &shaderSource = (*shaderSources)[shaderType];
if (!shaderSource.empty())
{
// All atomic counter buffers are placed under one binding shared between all stages.
shaderSource.insertLayoutSpecifier(kAtomicCounterBlockName, bindingString);
}
}
AddResourceInfo(variableInfoMapOut, sh::vk::kAtomicCountersVarName,
options.shaderResourceDescriptorSetIndex, bindingStart);
return bindingStart + 1;
}
......@@ -910,25 +978,20 @@ uint32_t AssignImageBindings(const GlslangSourceOptions &options,
const std::vector<gl::LinkedUniform> &uniforms,
const gl::RangeUI &imageUniformRange,
uint32_t bindingStart,
gl::ShaderMap<IntermediateShaderSource> *shaderSources)
ShaderInterfaceVariableInfoMap *variableInfoMapOut)
{
const std::string resourcesDescriptorSet =
"set = " + Str(options.shaderResourceDescriptorSetIndex);
uint32_t bindingIndex = bindingStart;
for (unsigned int uniformIndex : imageUniformRange)
{
const gl::LinkedUniform &imageUniform = uniforms[uniformIndex];
const std::string bindingString =
resourcesDescriptorSet + ", binding = " + Str(bindingIndex++);
std::string name = imageUniform.name;
if (name.back() == ']')
std::string name = imageUniform.mappedName;
if (GetImageNameWithoutIndices(&name))
{
name = name.substr(0, name.find('['));
AddResourceInfo(variableInfoMapOut, name, options.shaderResourceDescriptorSetIndex,
bindingIndex);
}
AssignResourceBinding(imageUniform.activeShaders(), name, bindingString, shaderSources);
++bindingIndex;
}
return bindingIndex;
......@@ -936,35 +999,33 @@ uint32_t AssignImageBindings(const GlslangSourceOptions &options,
void AssignNonTextureBindings(const GlslangSourceOptions &options,
const gl::ProgramState &programState,
gl::ShaderMap<IntermediateShaderSource> *shaderSources)
ShaderInterfaceVariableInfoMap *variableInfoMapOut)
{
uint32_t bindingStart = 0;
const std::vector<gl::InterfaceBlock> &uniformBlocks = programState.getUniformBlocks();
bindingStart =
AssignInterfaceBlockBindings(options, uniformBlocks, bindingStart, shaderSources);
AssignInterfaceBlockBindings(options, uniformBlocks, bindingStart, variableInfoMapOut);
const std::vector<gl::InterfaceBlock> &storageBlocks = programState.getShaderStorageBlocks();
bindingStart =
AssignInterfaceBlockBindings(options, storageBlocks, bindingStart, shaderSources);
AssignInterfaceBlockBindings(options, storageBlocks, bindingStart, variableInfoMapOut);
const std::vector<gl::AtomicCounterBuffer> &atomicCounterBuffers =
programState.getAtomicCounterBuffers();
bindingStart = AssignAtomicCounterBufferBindings(options, atomicCounterBuffers, bindingStart,
shaderSources);
variableInfoMapOut);
const std::vector<gl::LinkedUniform> &uniforms = programState.getUniforms();
const gl::RangeUI &imageUniformRange = programState.getImageUniformRange();
bindingStart =
AssignImageBindings(options, uniforms, imageUniformRange, bindingStart, shaderSources);
AssignImageBindings(options, uniforms, imageUniformRange, bindingStart, variableInfoMapOut);
}
void AssignTextureBindings(const GlslangSourceOptions &options,
const gl::ProgramState &programState,
gl::ShaderMap<IntermediateShaderSource> *shaderSources)
ShaderInterfaceVariableInfoMap *variableInfoMapOut)
{
const std::string texturesDescriptorSet = "set = " + Str(options.textureDescriptorSetIndex);
// Assign textures to a descriptor set and binding.
uint32_t bindingIndex = 0;
const std::vector<gl::LinkedUniform> &uniforms = programState.getUniforms();
......@@ -979,16 +1040,18 @@ void AssignTextureBindings(const GlslangSourceOptions &options,
continue;
}
const std::string bindingString =
texturesDescriptorSet + ", binding = " + Str(bindingIndex++);
if (UniformNameIsIndexZero(samplerUniform.name, options.useOldRewriteStructSamplers))
{
// Samplers in structs are extracted and renamed.
const std::string samplerName = options.useOldRewriteStructSamplers
? GetMappedSamplerNameOld(samplerUniform.name)
: GlslangGetMappedSamplerName(samplerUniform.name);
// Samplers in structs are extracted and renamed.
const std::string samplerName = options.useOldRewriteStructSamplers
? GetMappedSamplerNameOld(samplerUniform.name)
: GlslangGetMappedSamplerName(samplerUniform.name);
AddResourceInfo(variableInfoMapOut, samplerName, options.textureDescriptorSetIndex,
bindingIndex);
}
AssignResourceBinding(samplerUniform.activeShaders(), samplerName, bindingString,
shaderSources);
++bindingIndex;
}
}
......@@ -1017,7 +1080,7 @@ constexpr gl::ShaderMap<EShLanguage> kShLanguageMap = {
angle::Result GetShaderSpirvCode(GlslangErrorCallback callback,
const gl::Caps &glCaps,
const gl::ShaderMap<std::string> &shaderSources,
gl::ShaderMap<SpirvBlob> *spirvBlobsOut)
gl::ShaderMap<std::vector<uint32_t>> *spirvBlobsOut)
{
// Enable SPIR-V and Vulkan rules when parsing GLSL
EShMessages messages = static_cast<EShMessages>(EShMsgSpvRules | EShMsgVulkanRules);
......@@ -1084,6 +1147,391 @@ angle::Result GetShaderSpirvCode(GlslangErrorCallback callback,
return angle::Result::Continue;
}
void ValidateSpirvMessage(spv_message_level_t level,
const char *source,
const spv_position_t &position,
const char *message)
{
WARN() << "Level" << level << ": " << message;
}
bool ValidateSpirv(const std::vector<uint32_t> &spirvBlob)
{
spvtools::SpirvTools spirvTools(SPV_ENV_VULKAN_1_1);
spirvTools.SetMessageConsumer(ValidateSpirvMessage);
bool result = spirvTools.Validate(spirvBlob);
if (!result)
{
std::string readableSpirv;
result = spirvTools.Disassemble(spirvBlob, &readableSpirv,
SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES);
WARN() << "Invalid SPIR-V:\n" << readableSpirv;
}
return result;
}
// A SPIR-V transformer. It walks the instructions and modifies them as necessary, for example to
// assign bindings or locations.
class SpirvTransformer final : angle::NonCopyable
{
public:
SpirvTransformer(const std::vector<uint32_t> &spirvBlobIn,
const ShaderInterfaceVariableInfoMap &variableInfoMap,
gl::ShaderType shaderType,
SpirvBlob *spirvBlobOut)
: mSpirvBlobIn(spirvBlobIn),
mShaderType(shaderType),
mVariableInfoMap(variableInfoMap),
mSpirvBlobOut(spirvBlobOut)
{}
bool transform();
private:
// SPIR-V 1.0 Table 1: First Words of Physical Layout
enum HeaderIndex
{
kHeaderIndexMagic = 0,
kHeaderIndexVersion = 1,
kHeaderIndexGenerator = 2,
kHeaderIndexIndexBound = 3,
kHeaderIndexSchema = 4,
kHeaderIndexInstructions = 5,
};
// A prepass to resolve interesting ids:
void resolveVariableIds();
// Transform instructions:
void transformInstruction();
// Instructions that are purely informational:
void visitName(const uint32_t *instruction);
void visitTypeHelper(const uint32_t *instruction, size_t idIndex, size_t typeIdIndex);
void visitTypeArray(const uint32_t *instruction);
void visitTypePointer(const uint32_t *instruction);
void visitVariable(const uint32_t *instruction);
// Instructions that potentially need transformation. They return true if the instruction is
// transformed. If false is returned, the instruction should be copied as-is.
bool transformDecorate(const uint32_t *instruction, size_t wordCount);
// Any other instructions:
void copyInstruction(const uint32_t *instruction, size_t wordCount);
size_t getCurrentOutputOffset() const;
// SPIR-V to transform:
const std::vector<uint32_t> &mSpirvBlobIn;
const gl::ShaderType mShaderType;
// Input shader variable info map:
const ShaderInterfaceVariableInfoMap &mVariableInfoMap;
// Transformed SPIR-V:
SpirvBlob *mSpirvBlobOut;
// Traversal state:
size_t mCurrentWord = 0;
// Transformation state:
// Shader variable info per id, if id is a shader variable.
std::vector<const ShaderInterfaceVariableInfo *> mVariableInfoById;
};
bool SpirvTransformer::transform()
{
// Glslang succeeded in outputting SPIR-V, so we assume it's valid.
ASSERT(mSpirvBlobIn.size() >= kHeaderIndexInstructions);
// Since SPIR-V comes from a local call to glslang, it necessarily has the same endianness as
// the running architecture, so no byte-swapping is necessary.
ASSERT(mSpirvBlobIn[kHeaderIndexMagic] == spv::MagicNumber);
// Make sure the transformer is not reused to avoid having to reinitialize it here.
ASSERT(mCurrentWord == 0);
// Make sure the SpirvBlob is not reused.
ASSERT(mSpirvBlobOut->empty());
// First, find all necessary ids and associate them with the information required to transform
// their decorations.
resolveVariableIds();
// Copy the header to SpirvBlob
mSpirvBlobOut->assign(mSpirvBlobIn.begin(), mSpirvBlobIn.begin() + kHeaderIndexInstructions);
mCurrentWord = kHeaderIndexInstructions;
while (mCurrentWord < mSpirvBlobIn.size())
{
transformInstruction();
}
return true;
}
// SPIR-V 1.0 Table 2: Instruction Physical Layout
uint32_t GetSpirvInstructionLength(const uint32_t *instruction)
{
return instruction[0] >> 16;
}
uint32_t GetSpirvInstructionOp(const uint32_t *instruction)
{
constexpr uint32_t kOpMask = 0xFFFFu;
return instruction[0] & kOpMask;
}
void SpirvTransformer::resolveVariableIds()
{
// Allocate storage for id-to-info map. If %i is the id of a name in mVariableInfoMap, index i
// in this vector will hold a pointer to the ShaderInterfaceVariableInfo object associated with
// that name in mVariableInfoMap.
mVariableInfoById.resize(mSpirvBlobIn[kHeaderIndexIndexBound] + 1, nullptr);
size_t currentWord = kHeaderIndexInstructions;
while (currentWord < mSpirvBlobIn.size())
{
const uint32_t *instruction = &mSpirvBlobIn[currentWord];
const uint32_t wordCount = GetSpirvInstructionLength(instruction);
const uint32_t opCode = GetSpirvInstructionOp(instruction);
switch (opCode)
{
case spv::OpName:
visitName(instruction);
break;
case spv::OpTypeArray:
visitTypeArray(instruction);
break;
case spv::OpTypePointer:
visitTypePointer(instruction);
break;
case spv::OpVariable:
visitVariable(instruction);
break;
case spv::OpFunction:
// SPIR-V is structured in sections. Names appear before decorations, which are
// followed by type+variables and finally functions. We are only interested in name
// and variable declarations (as well as type declarations for the sake of nameless
// interface blocks). Early out when the function declaration section is met.
return;
default:
break;
}
currentWord += wordCount;
}
}
void SpirvTransformer::transformInstruction()
{
const uint32_t *instruction = &mSpirvBlobIn[mCurrentWord];
const uint32_t wordCount = GetSpirvInstructionLength(instruction);
const uint32_t opCode = GetSpirvInstructionOp(instruction);
// Since glslang succeeded in producing SPIR-V, we assume it to be valid.
const size_t spirvBlobInSize = mSpirvBlobIn.size();
ASSERT(mCurrentWord + wordCount <= spirvBlobInSize);
// Only look at interesting instructions.
bool transformed = false;
switch (opCode)
{
case spv::OpDecorate:
transformed = transformDecorate(instruction, wordCount);
break;
case spv::OpFunction:
// SPIR-V is structured in sections. Function declarations come last. This
// transformation will not modify functions, so we can copy the rest of the spir-v
// as-is.
copyInstruction(instruction, spirvBlobInSize - mCurrentWord);
mCurrentWord = spirvBlobInSize;
return;
default:
break;
}
// If the instruction was not transformed, copy it to output as is.
if (!transformed)
{
copyInstruction(instruction, wordCount);
}
// Advance to next instruction.
mCurrentWord += wordCount;
}
void SpirvTransformer::visitName(const uint32_t *instruction)
{
// We currently don't have any big-endian devices in the list of supported platforms. Literal
// strings in SPIR-V are stored little-endian (SPIR-V 1.0 Section 2.2.1, Literal String), so if
// a big-endian device is to be supported, the string matching here should be specialized.
ASSERT(IsLittleEndian());
// SPIR-V 1.0 Section 3.32 Instructions, OpName
constexpr size_t kIdIndex = 1;
constexpr size_t kNameIndex = 2;
const uint32_t id = instruction[kIdIndex];
const char *name = reinterpret_cast<const char *>(&instruction[kNameIndex]);
auto infoIter = mVariableInfoMap.find(name);
if (infoIter == mVariableInfoMap.end())
{
return;
}
// The names and ids are unique
ASSERT(id < mVariableInfoById.size());
ASSERT(mVariableInfoById[id] == nullptr);
// Associate the id of this name with its info.
mVariableInfoById[id] = &infoIter->second;
}
void SpirvTransformer::visitTypeHelper(const uint32_t *instruction,
const size_t idIndex,
const size_t typeIdIndex)
{
const uint32_t id = instruction[idIndex];
const uint32_t typeId = instruction[typeIdIndex];
// Every type id is declared only once.
ASSERT(typeId < mVariableInfoById.size());
if (mVariableInfoById[typeId] != nullptr)
{
// Carry the info forward from the base type. This is only necessary for interface blocks,
// as the variable info is associated with the block name instead of the variable name (to
// support nameless interface blocks). In that case, the variable itself doesn't yet have
// an associated info.
ASSERT(id < mVariableInfoById.size());
ASSERT(mVariableInfoById[id] == nullptr);
mVariableInfoById[id] = mVariableInfoById[typeId];
}
}
void SpirvTransformer::visitTypeArray(const uint32_t *instruction)
{
// SPIR-V 1.0 Section 3.32 Instructions, OpTypeArray
constexpr size_t kIdIndex = 1;
constexpr size_t kElementTypeIdIndex = 2;
visitTypeHelper(instruction, kIdIndex, kElementTypeIdIndex);
}
void SpirvTransformer::visitTypePointer(const uint32_t *instruction)
{
// SPIR-V 1.0 Section 3.32 Instructions, OpTypePointer
constexpr size_t kIdIndex = 1;
constexpr size_t kTypeIdIndex = 3;
visitTypeHelper(instruction, kIdIndex, kTypeIdIndex);
}
void SpirvTransformer::visitVariable(const uint32_t *instruction)
{
// SPIR-V 1.0 Section 3.32 Instructions, OpVariable
constexpr size_t kTypeIdIndex = 1;
constexpr size_t kIdIndex = 2;
constexpr size_t kStorageClassIndex = 3;
visitTypeHelper(instruction, kIdIndex, kTypeIdIndex);
// All resources that take set/binding should be transformed.
const uint32_t id = instruction[kIdIndex];
const uint32_t storageClass = instruction[kStorageClassIndex];
ASSERT((storageClass != spv::StorageClassUniform && storageClass != spv::StorageClassImage &&
storageClass != spv::StorageClassStorageBuffer) ||
mVariableInfoById[id] != nullptr);
}
bool SpirvTransformer::transformDecorate(const uint32_t *instruction, size_t wordCount)
{
// SPIR-V 1.0 Section 3.32 Instructions, OpDecorate
constexpr size_t kIdIndex = 1;
constexpr size_t kDecorationIndex = 2;
constexpr size_t kDecorationValueIndex = 3;
uint32_t id = instruction[kIdIndex];
uint32_t decoration = instruction[kDecorationIndex];
const ShaderInterfaceVariableInfo *info = mVariableInfoById[id];
// If variable is not a shader interface variable that needs modification, there's nothing to
// do.
if (info == nullptr)
{
return false;
}
uint32_t newDecorationValue = ShaderInterfaceVariableInfo::kInvalid;
switch (decoration)
{
case spv::DecorationLocation:
if (info->activeStages[mShaderType])
{
newDecorationValue = info->location;
}
break;
case spv::DecorationComponent:
if (info->activeStages[mShaderType])
{
newDecorationValue = info->component;
}
break;
case spv::DecorationBinding:
newDecorationValue = info->binding;
break;
case spv::DecorationDescriptorSet:
newDecorationValue = info->descriptorSet;
break;
case spv::DecorationOffset:
newDecorationValue = info->xfbOffset;
break;
case spv::DecorationXfbBuffer:
newDecorationValue = info->xfbBuffer;
break;
case spv::DecorationXfbStride:
newDecorationValue = info->xfbStride;
break;
default:
break;
}
// If the decoration is not something we care about modifying, there's nothing to do.
if (newDecorationValue == ShaderInterfaceVariableInfo::kInvalid)
{
return false;
}
// Copy the decoration declaration and modify it.
const size_t currentOutputOffset = getCurrentOutputOffset();
copyInstruction(instruction, wordCount);
(*mSpirvBlobOut)[currentOutputOffset + kDecorationValueIndex] = newDecorationValue;
return true;
}
void SpirvTransformer::copyInstruction(const uint32_t *instruction, size_t wordCount)
{
mSpirvBlobOut->insert(mSpirvBlobOut->end(), instruction, instruction + wordCount);
}
size_t SpirvTransformer::getCurrentOutputOffset() const
{
return mSpirvBlobOut->size();
}
} // anonymous namespace
void GlslangInitialize()
......@@ -1125,14 +1573,22 @@ std::string GlslangGetMappedSamplerName(const std::string &originalName)
samplerName.erase(out, samplerName.end());
if (MappedSamplerNameNeedsUserDefinedPrefix(originalName))
{
samplerName = sh::kUserDefinedNamePrefix + samplerName;
}
return samplerName;
}
void GlslangGetShaderSource(const GlslangSourceOptions &options,
const gl::ProgramState &programState,
const gl::ProgramLinkedResources &resources,
gl::ShaderMap<std::string> *shaderSourcesOut)
gl::ShaderMap<std::string> *shaderSourcesOut,
ShaderInterfaceVariableInfoMap *variableInfoMapOut)
{
variableInfoMapOut->clear();
gl::ShaderMap<IntermediateShaderSource> intermediateSources;
for (const gl::ShaderType shaderType : gl::AllShaderTypes())
......@@ -1167,7 +1623,8 @@ void GlslangGetShaderSource(const GlslangSourceOptions &options,
}
else if (options.emulateTransformFeedback)
{
GenerateTransformFeedbackEmulationOutputs(options, programState, vertexSource);
GenerateTransformFeedbackEmulationOutputs(options, programState, vertexSource,
variableInfoMapOut);
}
}
}
......@@ -1190,9 +1647,9 @@ void GlslangGetShaderSource(const GlslangSourceOptions &options,
AssignVaryingLocations(programState, resources, &intermediateSources, &xfbBufferMap);
}
AssignUniformBindings(options, &intermediateSources);
AssignTextureBindings(options, programState, &intermediateSources);
AssignNonTextureBindings(options, programState, &intermediateSources);
AssignUniformBindings(options, &intermediateSources, variableInfoMapOut);
AssignTextureBindings(options, programState, variableInfoMapOut);
AssignNonTextureBindings(options, programState, variableInfoMapOut);
CleanupUnusedEntities(options.useOldRewriteStructSamplers, programState, resources,
&intermediateSources);
......@@ -1206,8 +1663,30 @@ void GlslangGetShaderSource(const GlslangSourceOptions &options,
angle::Result GlslangGetShaderSpirvCode(GlslangErrorCallback callback,
const gl::Caps &glCaps,
const gl::ShaderMap<std::string> &shaderSources,
const ShaderInterfaceVariableInfoMap &variableInfoMap,
gl::ShaderMap<SpirvBlob> *spirvBlobsOut)
{
return GetShaderSpirvCode(callback, glCaps, shaderSources, spirvBlobsOut);
gl::ShaderMap<std::vector<uint32_t>> initialSpirvBlobs;
ANGLE_TRY(GetShaderSpirvCode(callback, glCaps, shaderSources, &initialSpirvBlobs));
// Transform the SPIR-V code by assigning location/set/binding values.
for (const gl::ShaderType shaderType : gl::AllShaderTypes())
{
const std::vector<uint32_t> initialSpirvBlob = initialSpirvBlobs[shaderType];
if (initialSpirvBlob.empty())
{
continue;
}
SpirvBlob *spirvBlob = &(*spirvBlobsOut)[shaderType];
SpirvTransformer transformer(initialSpirvBlob, variableInfoMap, shaderType, spirvBlob);
ANGLE_GLSLANG_CHECK(callback, transformer.transform(), GlslangError::InvalidSpirv);
ASSERT(ValidateSpirv(*spirvBlob));
}
return angle::Result::Continue;
}
} // namespace rx
......@@ -18,6 +18,7 @@ namespace rx
enum class GlslangError
{
InvalidShader,
InvalidSpirv,
};
struct GlslangSourceOptions
......@@ -43,22 +44,49 @@ using SpirvBlob = std::vector<uint32_t>;
using GlslangErrorCallback = std::function<angle::Result(GlslangError)>;
// Information for each shader interface variable. Not all fields are relevant to each shader
// interface variable. For example opaque uniforms require a set and binding index, while vertex
// attributes require a location.
struct ShaderInterfaceVariableInfo
{
static constexpr uint32_t kInvalid = std::numeric_limits<uint32_t>::max();
// Used for interface blocks and opaque uniforms.
uint32_t descriptorSet = kInvalid;
uint32_t binding = kInvalid;
// Used for vertex attributes, fragment shader outputs and varyings.
uint32_t location = kInvalid;
uint32_t component = kInvalid;
// Used for varyings.
gl::ShaderBitSet activeStages;
// Used for transform feedback extension to decorate vertex shader output.
uint32_t xfbBuffer = kInvalid;
uint32_t xfbOffset = kInvalid;
uint32_t xfbStride = kInvalid;
};
using ShaderInterfaceVariableInfoMap = std::unordered_map<std::string, ShaderInterfaceVariableInfo>;
void GlslangInitialize();
void GlslangRelease();
// Get the mapped sampler name after the soure is transformed by GlslangGetShaderSource()
std::string GlslangGetMappedSamplerName(const std::string &originalName);
// Transform the source to include actual binding points for various shader
// resources (textures, buffers, xfb, etc)
// Transform the source to include actual binding points for various shader resources (textures,
// buffers, xfb, etc). For some variables, these values are instead output to the variableInfoMap
// to be set during a SPIR-V transformation. This is a transitory step towards moving all variables
// to this map, at which point GlslangGetShaderSpirvCode will also be called by this function.
void GlslangGetShaderSource(const GlslangSourceOptions &options,
const gl::ProgramState &programState,
const gl::ProgramLinkedResources &resources,
gl::ShaderMap<std::string> *shaderSourcesOut);
gl::ShaderMap<std::string> *shaderSourcesOut,
ShaderInterfaceVariableInfoMap *variableInfoMapOut);
angle::Result GlslangGetShaderSpirvCode(GlslangErrorCallback callback,
const gl::Caps &glCaps,
const gl::ShaderMap<std::string> &shaderSources,
const ShaderInterfaceVariableInfoMap &variableInfoMap,
gl::ShaderMap<SpirvBlob> *spirvBlobsOut);
} // namespace rx
......
......@@ -17,6 +17,7 @@
#include "common/Optional.h"
#include "common/utilities.h"
#include "libANGLE/renderer/ProgramImpl.h"
#include "libANGLE/renderer/glslang_wrapper_utils.h"
#include "libANGLE/renderer/metal/mtl_command_buffer.h"
#include "libANGLE/renderer/metal/mtl_resources.h"
#include "libANGLE/renderer/metal/mtl_state_cache.h"
......@@ -162,6 +163,7 @@ class ProgramMtl : public ProgramImpl
// We keep the translated linked shader sources to use with shader draw call patching.
gl::ShaderMap<std::string> mShaderSource;
ShaderInterfaceVariableInfoMap mVariableInfoMap;
mtl::RenderPipelineCache mMetalRenderPipelineCache;
};
......
......@@ -295,7 +295,7 @@ std::unique_ptr<LinkEvent> ProgramMtl::link(const gl::Context *context,
// assignment done in that function.
linkResources(resources);
mtl::GlslangGetShaderSource(mState, resources, &mShaderSource);
mtl::GlslangGetShaderSource(mState, resources, &mShaderSource, &mVariableInfoMap);
// NOTE(hqle): Parallelize linking.
return std::make_unique<LinkEventDone>(linkImpl(context, infoLog));
......@@ -313,7 +313,7 @@ angle::Result ProgramMtl::linkImpl(const gl::Context *glContext, gl::InfoLog &in
// Convert GLSL to spirv code
gl::ShaderMap<std::vector<uint32_t>> shaderCodes;
ANGLE_TRY(mtl::GlslangGetShaderSpirvCode(contextMtl, contextMtl->getCaps(), mShaderSource,
&shaderCodes));
mVariableInfoMap, &shaderCodes));
// Convert spirv code to MSL
ANGLE_TRY(convertToMsl(glContext, gl::ShaderType::Vertex, infoLog,
......
......@@ -12,6 +12,7 @@
#include "libANGLE/Caps.h"
#include "libANGLE/Context.h"
#include "libANGLE/renderer/ProgramImpl.h"
#include "libANGLE/renderer/glslang_wrapper_utils.h"
#include "libANGLE/renderer/metal/mtl_common.h"
namespace rx
......@@ -20,11 +21,13 @@ namespace mtl
{
void GlslangGetShaderSource(const gl::ProgramState &programState,
const gl::ProgramLinkedResources &resources,
gl::ShaderMap<std::string> *shaderSourcesOut);
gl::ShaderMap<std::string> *shaderSourcesOut,
ShaderInterfaceVariableInfoMap *variableInfoMapOut);
angle::Result GlslangGetShaderSpirvCode(ErrorHandler *context,
const gl::Caps &glCaps,
const gl::ShaderMap<std::string> &shaderSources,
const ShaderInterfaceVariableInfoMap &variableInfoMap,
gl::ShaderMap<std::vector<uint32_t>> *shaderCodeOut);
} // namespace mtl
} // namespace rx
......
......@@ -43,19 +43,22 @@ GlslangSourceOptions CreateSourceOptions()
void GlslangGetShaderSource(const gl::ProgramState &programState,
const gl::ProgramLinkedResources &resources,
gl::ShaderMap<std::string> *shaderSourcesOut)
gl::ShaderMap<std::string> *shaderSourcesOut,
ShaderInterfaceVariableInfoMap *variableInfoMapOut)
{
rx::GlslangGetShaderSource(CreateSourceOptions(), programState, resources, shaderSourcesOut);
rx::GlslangGetShaderSource(CreateSourceOptions(), programState, resources, shaderSourcesOut,
variableInfoMapOut);
}
angle::Result GlslangGetShaderSpirvCode(ErrorHandler *context,
const gl::Caps &glCaps,
const gl::ShaderMap<std::string> &shaderSources,
const ShaderInterfaceVariableInfoMap &variableInfoMap,
gl::ShaderMap<std::vector<uint32_t>> *shaderCodeOut)
{
return rx::GlslangGetShaderSpirvCode(
[context](GlslangError error) { return HandleError(context, error); }, glCaps,
shaderSources, shaderCodeOut);
shaderSources, variableInfoMap, shaderCodeOut);
}
} // namespace mtl
} // namespace rx
......@@ -8,7 +8,6 @@
#include "libANGLE/renderer/vulkan/GlslangWrapperVk.h"
#include "libANGLE/renderer/glslang_wrapper_utils.h"
#include "libANGLE/renderer/vulkan/ContextVk.h"
#include "libANGLE/renderer/vulkan/vk_cache_utils.h"
......@@ -42,20 +41,22 @@ GlslangSourceOptions CreateSourceOptions(const angle::FeaturesVk &features)
void GlslangWrapperVk::GetShaderSource(const angle::FeaturesVk &features,
const gl::ProgramState &programState,
const gl::ProgramLinkedResources &resources,
gl::ShaderMap<std::string> *shaderSourcesOut)
gl::ShaderMap<std::string> *shaderSourcesOut,
ShaderInterfaceVariableInfoMap *variableInfoMapOut)
{
GlslangGetShaderSource(CreateSourceOptions(features), programState, resources,
shaderSourcesOut);
GlslangGetShaderSource(CreateSourceOptions(features), programState, resources, shaderSourcesOut,
variableInfoMapOut);
}
// static
angle::Result GlslangWrapperVk::GetShaderCode(vk::Context *context,
const gl::Caps &glCaps,
const gl::ShaderMap<std::string> &shaderSources,
const ShaderInterfaceVariableInfoMap &variableInfoMap,
gl::ShaderMap<std::vector<uint32_t>> *shaderCodeOut)
{
return GlslangGetShaderSpirvCode(
[context](GlslangError error) { return ErrorHandler(context, error); }, glCaps,
shaderSources, shaderCodeOut);
shaderSources, variableInfoMap, shaderCodeOut);
}
} // namespace rx
......@@ -10,6 +10,7 @@
#define LIBANGLE_RENDERER_VULKAN_GLSLANG_WRAPPER_H_
#include "libANGLE/renderer/ProgramImpl.h"
#include "libANGLE/renderer/glslang_wrapper_utils.h"
#include "libANGLE/renderer/vulkan/vk_utils.h"
namespace angle
......@@ -27,11 +28,13 @@ class GlslangWrapperVk
static void GetShaderSource(const angle::FeaturesVk &features,
const gl::ProgramState &programState,
const gl::ProgramLinkedResources &resources,
gl::ShaderMap<std::string> *shaderSourcesOut);
gl::ShaderMap<std::string> *shaderSourcesOut,
ShaderInterfaceVariableInfoMap *variableInfoMapOut);
static angle::Result GetShaderCode(vk::Context *context,
const gl::Caps &glCaps,
const gl::ShaderMap<std::string> &shaderSources,
const ShaderInterfaceVariableInfoMap &variableInfoMap,
gl::ShaderMap<std::vector<uint32_t>> *shaderCodesOut);
};
} // namespace rx
......
......@@ -364,14 +364,16 @@ ProgramVk::ShaderInfo::ShaderInfo() {}
ProgramVk::ShaderInfo::~ShaderInfo() = default;
angle::Result ProgramVk::ShaderInfo::initShaders(ContextVk *contextVk,
const gl::ShaderMap<std::string> &shaderSources,
gl::ShaderMap<SpirvBlob> *spirvBlobsOut)
angle::Result ProgramVk::ShaderInfo::initShaders(
ContextVk *contextVk,
const gl::ShaderMap<std::string> &shaderSources,
const ShaderInterfaceVariableInfoMap &variableInfoMap,
gl::ShaderMap<SpirvBlob> *spirvBlobsOut)
{
ASSERT(!valid());
ANGLE_TRY(GlslangWrapperVk::GetShaderCode(contextVk, contextVk->getCaps(), shaderSources,
spirvBlobsOut));
variableInfoMap, spirvBlobsOut));
mIsInitialized = true;
return angle::Result::Continue;
......@@ -438,6 +440,25 @@ angle::Result ProgramVk::loadSpirvBlob(ContextVk *contextVk, gl::BinaryInputStre
// Read the shader source
mShaderSources[shaderType] = stream->readString();
// Read the expected bindings
size_t infoCount = stream->readInt<size_t>();
for (size_t i = 0; i < infoCount; ++i)
{
std::string varName = stream->readString();
ShaderInterfaceVariableInfo info;
info.descriptorSet = stream->readInt<uint32_t>();
info.binding = stream->readInt<uint32_t>();
info.location = stream->readInt<uint32_t>();
info.component = stream->readInt<uint32_t>();
info.activeStages = gl::ShaderBitSet(static_cast<uint8_t>(stream->readInt<uint32_t>()));
info.xfbBuffer = stream->readInt<uint32_t>();
info.xfbOffset = stream->readInt<uint32_t>();
info.xfbStride = stream->readInt<uint32_t>();
mVariableInfoMap[varName] = info;
}
SpirvBlob *spirvBlob = &mShaderInfo.getSpirvBlobs()[shaderType];
// Read the SPIR-V
......@@ -455,6 +476,21 @@ void ProgramVk::saveSpirvBlob(gl::BinaryOutputStream *stream)
// Write the shader source
stream->writeString(mShaderSources[shaderType]);
// Write the expected bindings
stream->writeInt(mVariableInfoMap.size());
for (const auto &nameInfo : mVariableInfoMap)
{
stream->writeString(nameInfo.first);
stream->writeIntOrNegOne(nameInfo.second.descriptorSet);
stream->writeIntOrNegOne(nameInfo.second.binding);
stream->writeIntOrNegOne(nameInfo.second.location);
stream->writeIntOrNegOne(nameInfo.second.component);
stream->writeIntOrNegOne(nameInfo.second.activeStages.bits());
stream->writeIntOrNegOne(nameInfo.second.xfbBuffer);
stream->writeIntOrNegOne(nameInfo.second.xfbOffset);
stream->writeIntOrNegOne(nameInfo.second.xfbStride);
}
const SpirvBlob &spirvBlob = mShaderInfo.getSpirvBlobs()[shaderType];
// Write the SPIR-V
......@@ -611,7 +647,7 @@ std::unique_ptr<LinkEvent> ProgramVk::link(const gl::Context *context,
linkResources(resources);
GlslangWrapperVk::GetShaderSource(contextVk->getRenderer()->getFeatures(), mState, resources,
&mShaderSources);
&mShaderSources, &mVariableInfoMap);
reset(contextVk);
......
......@@ -223,8 +223,8 @@ class ProgramVk : public ProgramImpl
// constants.
if (!mShaderInfo.valid())
{
ANGLE_TRY(
mShaderInfo.initShaders(contextVk, mShaderSources, &mShaderInfo.getSpirvBlobs()));
ANGLE_TRY(mShaderInfo.initShaders(contextVk, mShaderSources, mVariableInfoMap,
&mShaderInfo.getSpirvBlobs()));
}
ASSERT(mShaderInfo.valid());
......@@ -314,6 +314,7 @@ class ProgramVk : public ProgramImpl
angle::Result initShaders(ContextVk *contextVk,
const gl::ShaderMap<std::string> &shaderSources,
const ShaderInterfaceVariableInfoMap &variableInfoMap,
gl::ShaderMap<SpirvBlob> *spirvBlobsOut);
void release(ContextVk *contextVk);
......@@ -350,10 +351,12 @@ class ProgramVk : public ProgramImpl
ProgramInfo mDefaultProgramInfo;
ProgramInfo mLineRasterProgramInfo;
// We keep the translated linked shader sources to use with shader draw call compilation.
// We keep the translated linked shader sources and the expected location/set/binding mapping to
// use with shader draw call compilation.
// TODO(syoussefi): Remove when shader compilation is done at link time.
// http://anglebug.com/3394
gl::ShaderMap<std::string> mShaderSources;
ShaderInterfaceVariableInfoMap mVariableInfoMap;
// We keep the SPIR-V code to use for draw call pipeline creation.
ShaderInfo mShaderInfo;
......
......@@ -563,6 +563,11 @@ RendererVk::RendererVk()
{
VkFormatProperties invalid = {0, 0, kInvalidFormatFeatureFlags};
mFormatProperties.fill(invalid);
// We currently don't have any big-endian devices in the list of supported platforms. There are
// a number of places in the Vulkan backend that make this assumption. This assertion is made
// early to fail immediately on big-endian platforms.
ASSERT(IsLittleEndian());
}
RendererVk::~RendererVk()
......
......@@ -108,9 +108,7 @@ uint32_t GetConvertVertexFlags(const UtilsVk::ConvertVertexParameters &params)
// is not added to the build configuration file (to reduce binary size). If necessary, add
// IsBigEndian to ConvertVertex.comp.json and select the appropriate flag based on the
// endian-ness test here.
uint32_t endiannessTest = 0;
*reinterpret_cast<uint8_t *>(&endiannessTest) = 1;
ASSERT(endiannessTest == 1);
ASSERT(IsLittleEndian());
uint32_t flags = 0;
......
......@@ -102,7 +102,7 @@ TEST_F(EmulateGLBaseVertexBaseInstanceTest, EmulatesUniform)
#ifdef ANGLE_ENABLE_VULKAN
EXPECT_TRUE(foundInCode(
SH_GLSL_VULKAN_OUTPUT,
"uniform defaultUniforms\n{\n int angle_BaseInstance;\n int angle_BaseVertex;"));
"uniform defaultUniformsVS\n{\n int angle_BaseInstance;\n int angle_BaseVertex;"));
#endif
#ifdef ANGLE_ENABLE_HLSL
EXPECT_TRUE(foundInCode(SH_HLSL_3_0_OUTPUT, "uniform int angle_BaseVertex : register"));
......
......@@ -97,7 +97,7 @@ TEST_F(EmulateGLDrawIDTest, EmulatesUniform)
#ifdef ANGLE_ENABLE_VULKAN
EXPECT_TRUE(
foundInCode(SH_GLSL_VULKAN_OUTPUT, "uniform defaultUniforms\n{\n int angle_DrawID;"));
foundInCode(SH_GLSL_VULKAN_OUTPUT, "uniform defaultUniformsVS\n{\n int angle_DrawID;"));
#endif
#ifdef ANGLE_ENABLE_HLSL
EXPECT_TRUE(foundInCode(SH_HLSL_3_0_OUTPUT, "uniform int angle_DrawID : register"));
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment