Vulkan: Set varying location & xfb decorations in SPIR-V

The shader translator outputs arbitrary location indices.  Once compiled
by glslang, the SPIR-V transformer modifies these decorations.  If the
transform feedback extension is used, it will also add the relevant
decorations to the varyings that are captured.

Bug: angleproject:3394
Change-Id: I5ecafd0536408612a5d4b920dbabbfabe650657c
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2008468
Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
This commit is contained in:
Shahbaz Youssefi 2020-01-17 14:53:38 -05:00 коммит произвёл Commit Bot
Родитель 05e08edf70
Коммит f1f082e137
13 изменённых файлов: 778 добавлений и 365 удалений

Просмотреть файл

@ -765,6 +765,9 @@ extern const char kDriverUniformsVarName[];
// Interface block array variable name used for atomic counter emulation // Interface block array variable name used for atomic counter emulation
extern const char kAtomicCountersVarName[]; extern const char kAtomicCountersVarName[];
// Line raster emulation varying
extern const char kLineRasterEmulationPosition[];
} // namespace vk } // namespace vk
} // namespace sh } // namespace sh

Просмотреть файл

@ -88,7 +88,7 @@ class TOutputGLSLBase : public TIntermTraverser
} }
void declareStruct(const TStructure *structure); void declareStruct(const TStructure *structure);
virtual void writeQualifier(TQualifier qualifier, const TType &type, const TSymbol *symbol); void writeQualifier(TQualifier qualifier, const TType &type, const TSymbol *symbol);
bool structDeclared(const TStructure *structure) const; bool structDeclared(const TStructure *structure) const;
const char *mapQualifierToString(TQualifier qualifier); const char *mapQualifierToString(TQualifier qualifier);

Просмотреть файл

@ -46,15 +46,13 @@ void TOutputVulkanGLSL::writeLayoutQualifier(TIntermTyped *variable)
{ {
const TType &type = variable->getType(); const TType &type = variable->getType();
bool needsCustomLayout = IsVarying(type.getQualifier());
bool needsSetBinding = bool needsSetBinding =
IsSampler(type.getBasicType()) || type.isInterfaceBlock() || IsImage(type.getBasicType()); IsSampler(type.getBasicType()) || type.isInterfaceBlock() || IsImage(type.getBasicType());
bool needsLocation = type.getQualifier() == EvqAttribute || bool needsLocation = type.getQualifier() == EvqAttribute ||
type.getQualifier() == EvqVertexIn || type.getQualifier() == EvqVertexIn ||
type.getQualifier() == EvqFragmentOut; type.getQualifier() == EvqFragmentOut || IsVarying(type.getQualifier());
if (!NeedsToWriteLayoutQualifier(type) && !needsCustomLayout && !needsSetBinding && if (!NeedsToWriteLayoutQualifier(type) && !needsSetBinding && !needsLocation)
!needsLocation)
{ {
return; return;
} }
@ -103,33 +101,25 @@ void TOutputVulkanGLSL::writeLayoutQualifier(TIntermTyped *variable)
} }
const char *separator = ""; const char *separator = "";
if (needsCustomLayout) out << "layout(";
// If the resource declaration requires set & binding layout qualifiers, specify arbitrary
// ones.
if (needsSetBinding)
{ {
out << "@@ LAYOUT-" << name << "("; out << "set=0, binding=" << nextUnusedBinding();
separator = ", ";
} }
else
if (needsLocation)
{ {
out << "layout("; const unsigned int locationCount = CalculateVaryingLocationCount(symbol, getShaderType());
uint32_t location = IsShaderIn(type.getQualifier())
? nextUnusedInputLocation(locationCount)
: nextUnusedOutputLocation(locationCount);
// If the resource declaration requires set & binding layout qualifiers, specify arbitrary out << "location=" << location;
// ones. separator = ", ";
if (needsSetBinding)
{
out << "set=0, binding=" << nextUnusedBinding();
separator = ", ";
}
if (needsLocation)
{
const unsigned int locationCount =
CalculateVaryingLocationCount(symbol, getShaderType());
uint32_t location = IsShaderIn(type.getQualifier())
? nextUnusedInputLocation(locationCount)
: nextUnusedOutputLocation(locationCount);
out << "location=" << location;
separator = ", ";
}
} }
// Output the list of qualifiers already known at this stage, i.e. everything other than // Output the list of qualifiers already known at this stage, i.e. everything other than
@ -152,39 +142,6 @@ void TOutputVulkanGLSL::writeLayoutQualifier(TIntermTyped *variable)
} }
out << ") "; out << ") ";
if (needsCustomLayout)
{
out << "@@";
}
}
void TOutputVulkanGLSL::writeQualifier(TQualifier qualifier,
const TType &type,
const TSymbol *symbol)
{
// Only varyings need to output qualifiers through a @@ QUALIFIER macro. Glslang wrapper may
// decide to remove them if they are inactive and turn them into global variables. This is only
// necessary for varyings because they are the only shader interface variables that could be
// referenced in the shader source and still be inactive.
if (!sh::IsVarying(qualifier))
{
TOutputGLSLBase::writeQualifier(qualifier, type, symbol);
return;
}
if (symbol == nullptr)
{
return;
}
ImmutableString name = symbol->name();
// The in/out qualifiers are calculated here so glslang wrapper doesn't need to guess them.
ASSERT(IsShaderIn(qualifier) || IsShaderOut(qualifier));
const char *inOutQualifier = mapQualifierToString(qualifier);
TInfoSinkBase &out = objSink();
out << "@@ QUALIFIER-" << name.data() << "(" << inOutQualifier << ") @@ ";
} }
void TOutputVulkanGLSL::writeVariableType(const TType &type, void TOutputVulkanGLSL::writeVariableType(const TType &type,

Просмотреть файл

@ -45,7 +45,6 @@ class TOutputVulkanGLSL : public TOutputGLSL
protected: protected:
void writeLayoutQualifier(TIntermTyped *variable) override; void writeLayoutQualifier(TIntermTyped *variable) override;
void writeQualifier(TQualifier qualifier, const TType &type, const TSymbol *symbol) override;
void writeVariableType(const TType &type, void writeVariableType(const TType &type,
const TSymbol *symbol, const TSymbol *symbol,
bool isFunctionArgument) override; bool isFunctionArgument) override;

Просмотреть файл

@ -720,6 +720,8 @@ const char kDriverUniformsVarName[] = "ANGLEUniforms";
// Interface block array variable name used for atomic counter emulation // Interface block array variable name used for atomic counter emulation
const char kAtomicCountersVarName[] = "atomicCounters"; const char kAtomicCountersVarName[] = "atomicCounters";
const char kLineRasterEmulationPosition[] = "ANGLEPosition";
} // namespace vk } // namespace vk
} // namespace sh } // namespace sh

Просмотреть файл

@ -443,10 +443,11 @@ TVariable *AddANGLEPositionVaryingDeclaration(TIntermBlock *root,
TSymbolTable *symbolTable, TSymbolTable *symbolTable,
TQualifier qualifier) TQualifier qualifier)
{ {
// Define a driver varying vec2 "ANGLEPosition". // Define a vec2 driver varying to hold the line rasterization emulation position.
TType *varyingType = new TType(EbtFloat, EbpMedium, qualifier, 2); TType *varyingType = new TType(EbtFloat, EbpMedium, qualifier, 2);
TVariable *varyingVar = new TVariable(symbolTable, ImmutableString("ANGLEPosition"), TVariable *varyingVar =
varyingType, SymbolType::AngleInternal); new TVariable(symbolTable, ImmutableString(vk::kLineRasterEmulationPosition), varyingType,
SymbolType::AngleInternal);
TIntermSymbol *varyingDeclarator = new TIntermSymbol(varyingVar); TIntermSymbol *varyingDeclarator = new TIntermSymbol(varyingVar);
TIntermDeclaration *varyingDecl = new TIntermDeclaration; TIntermDeclaration *varyingDecl = new TIntermDeclaration;
varyingDecl->appendDeclarator(varyingDeclarator); varyingDecl->appendDeclarator(varyingDeclarator);

Просмотреть файл

@ -58,15 +58,17 @@ bool ComparePackedVarying(const PackedVarying &x, const PackedVarying &y)
// Implementation of PackedVarying // Implementation of PackedVarying
PackedVarying::PackedVarying(const sh::ShaderVariable &varyingIn, PackedVarying::PackedVarying(const sh::ShaderVariable &varyingIn,
sh::InterpolationType interpolationIn) sh::InterpolationType interpolationIn)
: PackedVarying(varyingIn, interpolationIn, "", false) : PackedVarying(varyingIn, interpolationIn, "", "", false)
{} {}
PackedVarying::PackedVarying(const sh::ShaderVariable &varyingIn, PackedVarying::PackedVarying(const sh::ShaderVariable &varyingIn,
sh::InterpolationType interpolationIn, sh::InterpolationType interpolationIn,
const std::string &parentStructNameIn, const std::string &parentStructNameIn,
const std::string &parentStructMappedNameIn,
GLuint fieldIndexIn) GLuint fieldIndexIn)
: varying(&varyingIn), : varying(&varyingIn),
interpolation(interpolationIn), interpolation(interpolationIn),
parentStructName(parentStructNameIn), parentStructName(parentStructNameIn),
parentStructMappedName(parentStructMappedNameIn),
arrayIndex(GL_INVALID_INDEX), arrayIndex(GL_INVALID_INDEX),
fieldIndex(fieldIndexIn) fieldIndex(fieldIndexIn)
{} {}
@ -84,6 +86,7 @@ PackedVarying &PackedVarying::operator=(PackedVarying &&other)
std::swap(shaderStages, other.shaderStages); std::swap(shaderStages, other.shaderStages);
std::swap(interpolation, other.interpolation); std::swap(interpolation, other.interpolation);
std::swap(parentStructName, other.parentStructName); std::swap(parentStructName, other.parentStructName);
std::swap(parentStructMappedName, other.parentStructMappedName);
std::swap(arrayIndex, other.arrayIndex); std::swap(arrayIndex, other.arrayIndex);
std::swap(fieldIndex, other.fieldIndex); std::swap(fieldIndex, other.fieldIndex);
@ -364,10 +367,11 @@ bool VaryingPacking::collectAndPackUserVaryings(gl::InfoLog &infoLog,
ASSERT(!field.isStruct() && !field.isArray()); ASSERT(!field.isStruct() && !field.isArray());
mPackedVaryings.emplace_back(field, interpolation, varying->name, mPackedVaryings.emplace_back(field, interpolation, varying->name,
fieldIndex); varying->mappedName, fieldIndex);
mPackedVaryings.back().shaderStages = shaderStages; mPackedVaryings.back().shaderStages = shaderStages;
uniqueFullNames.insert(mPackedVaryings.back().fullName()); uniqueFullNames.insert(mPackedVaryings.back().fullName());
} }
uniqueFullNames.insert(varying->name);
} }
else else
{ {
@ -382,7 +386,7 @@ bool VaryingPacking::collectAndPackUserVaryings(gl::InfoLog &infoLog,
// If the varying is not used in the input, we know it is inactive. // If the varying is not used in the input, we know it is inactive.
if (!input) if (!input)
{ {
mInactiveVaryingNames.push_back(ref.first); mInactiveVaryingMappedNames.push_back(output->mappedName);
continue; continue;
} }
@ -410,11 +414,12 @@ bool VaryingPacking::collectAndPackUserVaryings(gl::InfoLog &infoLog,
{ {
ASSERT(!field->isStruct() && !field->isArray()); ASSERT(!field->isStruct() && !field->isArray());
mPackedVaryings.emplace_back(*field, input->interpolation, input->name, mPackedVaryings.emplace_back(*field, input->interpolation, input->name,
fieldIndex); input->mappedName, fieldIndex);
mPackedVaryings.back().shaderStages.set(ShaderType::Vertex); mPackedVaryings.back().shaderStages.set(ShaderType::Vertex);
mPackedVaryings.back().arrayIndex = GL_INVALID_INDEX; mPackedVaryings.back().arrayIndex = GL_INVALID_INDEX;
uniqueFullNames.insert(tfVarying); uniqueFullNames.insert(tfVarying);
} }
uniqueFullNames.insert(input->name);
} }
// Array as a whole and array element conflict has already been checked in // Array as a whole and array element conflict has already been checked in
// linkValidateTransformFeedback. // linkValidateTransformFeedback.
@ -439,7 +444,7 @@ bool VaryingPacking::collectAndPackUserVaryings(gl::InfoLog &infoLog,
if (uniqueFullNames.count(ref.first) == 0) if (uniqueFullNames.count(ref.first) == 0)
{ {
mInactiveVaryingNames.push_back(ref.first); mInactiveVaryingMappedNames.push_back(input->mappedName);
} }
} }

Просмотреть файл

@ -33,6 +33,7 @@ struct PackedVarying : angle::NonCopyable
PackedVarying(const sh::ShaderVariable &varyingIn, PackedVarying(const sh::ShaderVariable &varyingIn,
sh::InterpolationType interpolationIn, sh::InterpolationType interpolationIn,
const std::string &parentStructNameIn, const std::string &parentStructNameIn,
const std::string &parentStructMappedNameIn,
GLuint fieldIndexIn); GLuint fieldIndexIn);
PackedVarying(PackedVarying &&other); PackedVarying(PackedVarying &&other);
~PackedVarying(); ~PackedVarying();
@ -76,6 +77,7 @@ struct PackedVarying : angle::NonCopyable
// Struct name // Struct name
std::string parentStructName; std::string parentStructName;
std::string parentStructMappedName;
GLuint arrayIndex; GLuint arrayIndex;
@ -180,9 +182,9 @@ class VaryingPacking final : angle::NonCopyable
return static_cast<unsigned int>(mRegisterList.size()); return static_cast<unsigned int>(mRegisterList.size());
} }
const std::vector<std::string> &getInactiveVaryingNames() const const std::vector<std::string> &getInactiveVaryingMappedNames() const
{ {
return mInactiveVaryingNames; return mInactiveVaryingMappedNames;
} }
const std::vector<sh::ShaderVariable> &getInputVaryings() const { return mInputVaryings; } const std::vector<sh::ShaderVariable> &getInputVaryings() const { return mInputVaryings; }
@ -201,7 +203,7 @@ class VaryingPacking final : angle::NonCopyable
std::vector<PackedVaryingRegister> mRegisterList; std::vector<PackedVaryingRegister> mRegisterList;
std::vector<sh::ShaderVariable> mInputVaryings; std::vector<sh::ShaderVariable> mInputVaryings;
std::vector<PackedVarying> mPackedVaryings; std::vector<PackedVarying> mPackedVaryings;
std::vector<std::string> mInactiveVaryingNames; std::vector<std::string> mInactiveVaryingMappedNames;
PackMode mPackMode; PackMode mPackMode;
}; };

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -38,6 +38,7 @@ struct GlslangSourceOptions
bool useOldRewriteStructSamplers = false; bool useOldRewriteStructSamplers = false;
bool supportsTransformFeedbackExtension = false; bool supportsTransformFeedbackExtension = false;
bool emulateTransformFeedback = false; bool emulateTransformFeedback = false;
bool emulateBresenhamLines = false;
}; };
using SpirvBlob = std::vector<uint32_t>; using SpirvBlob = std::vector<uint32_t>;
@ -59,10 +60,11 @@ struct ShaderInterfaceVariableInfo
// Used for vertex attributes, fragment shader outputs and varyings. There could be different // Used for vertex attributes, fragment shader outputs and varyings. There could be different
// variables that share the same name, such as a vertex attribute and a fragment output. They // variables that share the same name, such as a vertex attribute and a fragment output. They
// will share this object since they have the same name, but will find possibly different // will share this object since they have the same name, but will find possibly different
// locations in their respective slots. This is also used to indicate in which stages a varying // locations in their respective slots.
// is active, as the rest would contain kInvalid.
gl::ShaderMap<uint32_t> location; gl::ShaderMap<uint32_t> location;
gl::ShaderMap<uint32_t> component; gl::ShaderMap<uint32_t> component;
// The stages this shader interface variable is active.
gl::ShaderBitSet activeStages;
// Used for transform feedback extension to decorate vertex shader output. // Used for transform feedback extension to decorate vertex shader output.
uint32_t xfbBuffer = kInvalid; uint32_t xfbBuffer = kInvalid;
uint32_t xfbOffset = kInvalid; uint32_t xfbOffset = kInvalid;

Просмотреть файл

@ -33,6 +33,7 @@ GlslangSourceOptions CreateSourceOptions(const angle::FeaturesVk &features)
options.supportsTransformFeedbackExtension = options.supportsTransformFeedbackExtension =
features.supportsTransformFeedbackExtension.enabled; features.supportsTransformFeedbackExtension.enabled;
options.emulateTransformFeedback = features.emulateTransformFeedback.enabled; options.emulateTransformFeedback = features.emulateTransformFeedback.enabled;
options.emulateBresenhamLines = features.basicGLLineRasterization.enabled;
return options; return options;
} }
} // namespace } // namespace

Просмотреть файл

@ -455,6 +455,7 @@ angle::Result ProgramVk::loadSpirvBlob(ContextVk *contextVk, gl::BinaryInputStre
info.descriptorSet = stream->readInt<uint32_t>(); info.descriptorSet = stream->readInt<uint32_t>();
info.binding = stream->readInt<uint32_t>(); info.binding = stream->readInt<uint32_t>();
info.activeStages = gl::ShaderBitSet(static_cast<uint8_t>(stream->readInt<uint32_t>()));
info.xfbBuffer = stream->readInt<uint32_t>(); info.xfbBuffer = stream->readInt<uint32_t>();
info.xfbOffset = stream->readInt<uint32_t>(); info.xfbOffset = stream->readInt<uint32_t>();
info.xfbStride = stream->readInt<uint32_t>(); info.xfbStride = stream->readInt<uint32_t>();
@ -491,6 +492,7 @@ void ProgramVk::saveSpirvBlob(gl::BinaryOutputStream *stream)
stream->writeString(nameInfo.first); stream->writeString(nameInfo.first);
stream->writeIntOrNegOne(nameInfo.second.descriptorSet); stream->writeIntOrNegOne(nameInfo.second.descriptorSet);
stream->writeIntOrNegOne(nameInfo.second.binding); stream->writeIntOrNegOne(nameInfo.second.binding);
stream->writeIntOrNegOne(nameInfo.second.activeStages.bits());
stream->writeIntOrNegOne(nameInfo.second.xfbBuffer); stream->writeIntOrNegOne(nameInfo.second.xfbBuffer);
stream->writeIntOrNegOne(nameInfo.second.xfbOffset); stream->writeIntOrNegOne(nameInfo.second.xfbOffset);
stream->writeIntOrNegOne(nameInfo.second.xfbStride); stream->writeIntOrNegOne(nameInfo.second.xfbStride);

Просмотреть файл

@ -4712,6 +4712,36 @@ TEST_P(GLSLTest_ES3, ComplexVaryingStructsUsedInFragmentShader)
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
} }
// Test that an inactive varying array that doesn't get used in the fragment shader works.
TEST_P(GLSLTest_ES3, InactiveVaryingArrayUnusedInFragmentShader)
{
constexpr char kVS[] =
"#version 300 es\n"
"in vec4 inputAttribute;\n"
"out vec4 varArray[4];\n"
"void main()\n"
"{\n"
" gl_Position = inputAttribute;\n"
" varArray[0] = vec4(1.0, 0.0, 0.0, 1.0);\n"
" varArray[1] = vec4(0.0, 1.0, 0.0, 1.0);\n"
" varArray[2] = vec4(0.0, 0.0, 1.0, 1.0);\n"
" varArray[3] = vec4(1.0, 1.0, 0.0, 1.0);\n"
"}\n";
constexpr char kFS[] =
"#version 300 es\n"
"precision mediump float;\n"
"out vec4 col;\n"
"void main()\n"
"{\n"
" col = vec4(0.0, 0.0, 0.0, 1.0);\n"
"}\n";
ANGLE_GL_PROGRAM(program, kVS, kFS);
drawQuad(program.get(), "inputAttribute", 0.5f);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::black);
}
// Test that an inactive varying struct that doesn't get used in the fragment shader works. // Test that an inactive varying struct that doesn't get used in the fragment shader works.
TEST_P(GLSLTest_ES3, InactiveVaryingStructUnusedInFragmentShader) TEST_P(GLSLTest_ES3, InactiveVaryingStructUnusedInFragmentShader)
{ {