Commit f1f082e1 by Shahbaz Youssefi Committed by Commit Bot

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: 's avatarJamie Madill <jmadill@chromium.org>
parent 05e08edf
...@@ -765,6 +765,9 @@ extern const char kDriverUniformsVarName[]; ...@@ -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 ...@@ -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) ...@@ -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) ...@@ -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
{
out << "layout(";
// If the resource declaration requires set & binding layout qualifiers, specify arbitrary if (needsLocation)
// ones. {
if (needsSetBinding) const unsigned int locationCount = CalculateVaryingLocationCount(symbol, getShaderType());
{ uint32_t location = IsShaderIn(type.getQualifier())
out << "set=0, binding=" << nextUnusedBinding(); ? nextUnusedInputLocation(locationCount)
separator = ", "; : nextUnusedOutputLocation(locationCount);
}
if (needsLocation) out << "location=" << location;
{ separator = ", ";
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) ...@@ -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 ...@@ -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"; ...@@ -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, ...@@ -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) ...@@ -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) ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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;
}; };
......
...@@ -51,16 +51,15 @@ namespace rx ...@@ -51,16 +51,15 @@ namespace rx
{ {
namespace namespace
{ {
constexpr char kMarkerStart[] = "@@ "; constexpr char kMarkerStart[] = "@@ ";
constexpr char kQualifierMarkerBegin[] = "@@ QUALIFIER-"; constexpr char kQualifierMarkerBegin[] = "@@ QUALIFIER-";
constexpr char kLayoutMarkerBegin[] = "@@ LAYOUT-"; constexpr char kLayoutMarkerBegin[] = "@@ LAYOUT-";
constexpr char kXfbDeclMarkerBegin[] = "@@ XFB-DECL"; constexpr char kXfbDeclMarkerBegin[] = "@@ XFB-DECL";
constexpr char kXfbOutMarkerBegin[] = "@@ XFB-OUT"; constexpr char kXfbOutMarkerBegin[] = "@@ XFB-OUT";
constexpr char kMarkerEnd[] = " @@"; constexpr char kMarkerEnd[] = " @@";
constexpr char kParamsBegin = '('; constexpr char kParamsBegin = '(';
constexpr char kParamsEnd = ')'; constexpr char kParamsEnd = ')';
constexpr uint32_t kANGLEPositionLocationOffset = 1; constexpr char kXfbBuiltInPrefix[] = "xfbANGLE";
constexpr uint32_t kXfbANGLEPositionLocationOffset = 2;
constexpr gl::ShaderMap<const char *> kDefaultUniformNames = { constexpr gl::ShaderMap<const char *> kDefaultUniformNames = {
{gl::ShaderType::Vertex, sh::vk::kDefaultUniformsNameVS}, {gl::ShaderType::Vertex, sh::vk::kDefaultUniformsNameVS},
...@@ -112,14 +111,6 @@ void GetBuiltInResourcesFromCaps(const gl::Caps &caps, TBuiltInResource *outBuil ...@@ -112,14 +111,6 @@ void GetBuiltInResourcesFromCaps(const gl::Caps &caps, TBuiltInResource *outBuil
outBuiltInResources->maxVertexUniformVectors = caps.maxVertexUniformVectors; outBuiltInResources->maxVertexUniformVectors = caps.maxVertexUniformVectors;
} }
// Information used for Xfb layout qualifier
struct XFBBufferInfo
{
GLuint index;
GLuint offset;
GLuint stride;
};
struct VaryingNameEquals struct VaryingNameEquals
{ {
VaryingNameEquals(const std::string &name_) : name(name_) {} VaryingNameEquals(const std::string &name_) : name(name_) {}
...@@ -128,8 +119,6 @@ struct VaryingNameEquals ...@@ -128,8 +119,6 @@ struct VaryingNameEquals
std::string name; std::string name;
}; };
using XfbBufferMap = std::map<std::string, XFBBufferInfo>;
class IntermediateShaderSource final : angle::NonCopyable class IntermediateShaderSource final : angle::NonCopyable
{ {
public: public:
...@@ -506,35 +495,70 @@ ShaderInterfaceVariableInfo *AddShaderInterfaceVariable(ShaderInterfaceVariableI ...@@ -506,35 +495,70 @@ ShaderInterfaceVariableInfo *AddShaderInterfaceVariable(ShaderInterfaceVariableI
return &(*infoMap)[varName]; return &(*infoMap)[varName];
} }
ShaderInterfaceVariableInfo *GetShaderInterfaceVariable(ShaderInterfaceVariableInfoMap *infoMap,
const std::string &varName)
{
ASSERT(infoMap->find(varName) != infoMap->end());
return &(*infoMap)[varName];
}
ShaderInterfaceVariableInfo *AddResourceInfo(ShaderInterfaceVariableInfoMap *infoMap, ShaderInterfaceVariableInfo *AddResourceInfo(ShaderInterfaceVariableInfoMap *infoMap,
const std::string &varName, const std::string &varName,
uint32_t descriptorSet, uint32_t descriptorSet,
uint32_t binding) uint32_t binding)
{ {
gl::ShaderBitSet allStages;
allStages.set();
ShaderInterfaceVariableInfo *info = AddShaderInterfaceVariable(infoMap, varName); ShaderInterfaceVariableInfo *info = AddShaderInterfaceVariable(infoMap, varName);
info->descriptorSet = descriptorSet; info->descriptorSet = descriptorSet;
info->binding = binding; info->binding = binding;
info->activeStages = allStages;
return info; return info;
} }
// Add location information for an in/out variable. // Add location information for an in/out variable.
ShaderInterfaceVariableInfo *AddLocationInfo(ShaderInterfaceVariableInfoMap *infoMap, ShaderInterfaceVariableInfo *AddLocationInfo(ShaderInterfaceVariableInfoMap *infoMap,
const std::string &varName, const std::string &varName,
gl::ShaderType shaderType,
uint32_t location, uint32_t location,
uint32_t component) uint32_t component,
gl::ShaderBitSet activeStages)
{ {
// The info map for this name may or may not exist already. This function merges the // The info map for this name may or may not exist already. This function merges the
// location/component information. // location/component information.
ShaderInterfaceVariableInfo *info = &(*infoMap)[varName]; ShaderInterfaceVariableInfo *info = &(*infoMap)[varName];
ASSERT(info->descriptorSet == ShaderInterfaceVariableInfo::kInvalid); for (const gl::ShaderType shaderType : activeStages)
ASSERT(info->binding == ShaderInterfaceVariableInfo::kInvalid); {
ASSERT(info->location[shaderType] == ShaderInterfaceVariableInfo::kInvalid); ASSERT(info->descriptorSet == ShaderInterfaceVariableInfo::kInvalid);
ASSERT(info->component[shaderType] == ShaderInterfaceVariableInfo::kInvalid); ASSERT(info->binding == ShaderInterfaceVariableInfo::kInvalid);
ASSERT(info->location[shaderType] == ShaderInterfaceVariableInfo::kInvalid);
ASSERT(info->component[shaderType] == ShaderInterfaceVariableInfo::kInvalid);
info->location[shaderType] = location;
info->component[shaderType] = component;
}
info->activeStages |= activeStages;
info->location[shaderType] = location; return info;
info->component[shaderType] = component; }
// Modify an existing out variable and add transform feedback information.
ShaderInterfaceVariableInfo *SetXfbInfo(ShaderInterfaceVariableInfoMap *infoMap,
const std::string &varName,
uint32_t xfbBuffer,
uint32_t xfbOffset,
uint32_t xfbStride)
{
ShaderInterfaceVariableInfo *info = GetShaderInterfaceVariable(infoMap, varName);
ASSERT(info->xfbBuffer == ShaderInterfaceVariableInfo::kInvalid);
ASSERT(info->xfbOffset == ShaderInterfaceVariableInfo::kInvalid);
ASSERT(info->xfbStride == ShaderInterfaceVariableInfo::kInvalid);
info->xfbBuffer = xfbBuffer;
info->xfbOffset = xfbOffset;
info->xfbStride = xfbStride;
return info; return info;
} }
...@@ -647,152 +671,90 @@ void GenerateTransformFeedbackEmulationOutputs(const GlslangSourceOptions &optio ...@@ -647,152 +671,90 @@ void GenerateTransformFeedbackEmulationOutputs(const GlslangSourceOptions &optio
vertexShader->insertTransformFeedbackOutput(std::move(xfbOut)); vertexShader->insertTransformFeedbackOutput(std::move(xfbOut));
} }
// Calculates XFB layout qualifier arguments for each tranform feedback varying, inserts bool IsFirstRegisterOfVarying(const gl::PackedVaryingRegister &varyingReg)
// layout quailifier for built-in varyings here and gathers calculated arguments for non built-in {
// varyings for later use. const gl::PackedVarying &varying = *varyingReg.packedVarying;
// In Vulkan GLSL, struct fields are not allowed to have location assignments. The varying of a
// struct type is thus given a location equal to the one assigned to its first field.
if (varying.isStructField() && varying.fieldIndex > 0)
{
return false;
}
// Similarly, assign array varying locations to the assigned location of the first element.
if (varyingReg.varyingArrayIndex != 0 || (varying.isArrayElement() && varying.arrayIndex != 0))
{
return false;
}
// Similarly, assign matrix varying locations to the assigned location of the first row.
if (varyingReg.varyingRowIndex != 0)
{
return false;
}
return true;
}
// Calculates XFB layout qualifier arguments for each tranform feedback varying. Stores calculated
// values for the SPIR-V transformation.
void GenerateTransformFeedbackExtensionOutputs(const gl::ProgramState &programState, void GenerateTransformFeedbackExtensionOutputs(const gl::ProgramState &programState,
IntermediateShaderSource *vertexShader, IntermediateShaderSource *vertexShader,
XfbBufferMap *xfbBufferMap, const gl::ProgramLinkedResources &resources,
const gl::ProgramLinkedResources &resources) ShaderInterfaceVariableInfoMap *variableInfoMapOut,
uint32_t *locationsUsedForXfbExtensionOut)
{ {
const std::vector<gl::TransformFeedbackVarying> &tfVaryings = const std::vector<gl::TransformFeedbackVarying> &tfVaryings =
programState.getLinkedTransformFeedbackVaryings(); programState.getLinkedTransformFeedbackVaryings();
const std::vector<GLsizei> &varyingStrides = programState.getTransformFeedbackStrides();
const bool isInterleaved =
programState.getTransformFeedbackBufferMode() == GL_INTERLEAVED_ATTRIBS;
std::string xfbDecl; std::string xfbDecl;
bool hasBuiltInVaryings = false; std::string xfbOut;
bool replacePositionVarying = false;
uint32_t currentOffset = 0;
uint32_t currentStride = 0;
uint32_t bufferIndex = 0;
std::string varyingType;
std::string xfbIndices;
std::string xfbOffsets;
std::string xfbStrides;
std::string replacedPositionLayout;
for (uint32_t varyingIndex = 0; varyingIndex < tfVaryings.size(); ++varyingIndex) for (uint32_t varyingIndex = 0; varyingIndex < tfVaryings.size(); ++varyingIndex)
{ {
if (isInterleaved) const gl::TransformFeedbackVarying &tfVarying = tfVaryings[varyingIndex];
{ const std::string &tfVaryingName = tfVarying.mappedName;
bufferIndex = 0;
if (varyingIndex > 0)
{
const gl::TransformFeedbackVarying &prev = tfVaryings[varyingIndex - 1];
currentOffset += prev.size() * gl::VariableExternalSize(prev.type);
}
currentStride = varyingStrides[0];
}
else
{
bufferIndex = varyingIndex;
currentOffset = 0;
currentStride = varyingStrides[varyingIndex];
}
if (tfVaryings[varyingIndex].isBuiltIn())
{
xfbIndices = Str(bufferIndex);
xfbOffsets = Str(currentOffset);
xfbStrides = Str(currentStride);
varyingType = gl::GetGLSLTypeString(tfVaryings[varyingIndex].type);
if (tfVaryings[varyingIndex].name.compare("gl_Position") == 0)
{
replacePositionVarying = true;
std::string xfbReplacedPositionLocation =
Str(resources.varyingPacking.getMaxSemanticIndex() +
kXfbANGLEPositionLocationOffset);
replacedPositionLayout = "layout(location = " + xfbReplacedPositionLocation +
", xfb_buffer = " + xfbIndices +
", xfb_offset = " + xfbOffsets +
", xfb_stride = " + xfbStrides + ") out " + varyingType +
" xfbANGLEPosition;\n";
}
else
{
// Since built-in varyings are not in RegisterList, we can add layout qualifier
// here.
if (!hasBuiltInVaryings)
{
hasBuiltInVaryings = true;
xfbDecl += "out gl_PerVertex\n{\n";
for (uint32_t index = 0; index < tfVaryings.size(); ++index)
{
// need to add gl_Position to gl_Pervertex because we declared layout for
// replaced xfbANGLEPosition instead
if (tfVaryings[index].name.compare("gl_Position") == 0)
{
xfbDecl += "vec4 gl_Position;\n";
break;
}
}
}
xfbDecl += "layout(xfb_buffer = " + xfbIndices + ", xfb_offset = " + xfbOffsets +
", xfb_stride = " + xfbStrides + ") " + varyingType + " " +
tfVaryings[varyingIndex].name + ";\n";
}
}
else
{
// Layout qualifier for non built-in varying will be written later, so we just save
// Xfb layout qualifier information into the xfbBufferMap.
XFBBufferInfo bufferInfo;
bufferInfo.index = bufferIndex;
bufferInfo.offset = currentOffset;
bufferInfo.stride = currentStride;
xfbBufferMap->insert(make_pair(tfVaryings[varyingIndex].name, bufferInfo));
}
}
if (hasBuiltInVaryings) if (tfVarying.isBuiltIn())
{
// We should add non transform feedback built-in varyings to gl_PerVertex because once
// we declare gl_PerVertex, all built-in varyings used in shaders should be included
// in gl_PerVertex struct.
for (const sh::ShaderVariable &varying : resources.varyingPacking.getInputVaryings())
{ {
if (varying.isBuiltIn()) // For simplicity, create a copy of every builtin that's captured so xfb qualifiers
{ // could be added to that instead. This allows the SPIR-V transformation to ignore
auto iter = std::find_if(tfVaryings.begin(), tfVaryings.end(), // OpMemberName and OpMemberDecorate instructions. Note that capturing gl_Position
VaryingNameEquals(varying.name)); // already requires such a copy, since the translator modifies this value at the end of
if (iter == tfVaryings.end()) // main. Capturing the rest of the built-ins are niche enough that the inefficiency
{ // involved in doing this is not a concern.
xfbDecl += gl::GetGLSLTypeString(varying.type) + " " + varying.name + ";\n";
} uint32_t xfbVaryingLocation = resources.varyingPacking.getMaxSemanticIndex() +
} ++(*locationsUsedForXfbExtensionOut);
std::string xfbVaryingName = kXfbBuiltInPrefix + tfVaryingName;
// Add declaration and initialization code for the new varying.
std::string varyingType = gl::GetGLSLTypeString(tfVarying.type);
xfbDecl += "layout(location = " + Str(xfbVaryingLocation) + ") out " + varyingType +
" " + xfbVaryingName + ";\n";
xfbOut += xfbVaryingName + " = " + tfVaryingName + ";\n";
} }
xfbDecl += "\n};\n";
} }
xfbDecl += replacedPositionLayout;
vertexShader->insertTransformFeedbackDeclaration(std::move(xfbDecl)); vertexShader->insertTransformFeedbackDeclaration(std::move(xfbDecl));
std::string xfbOut;
if (replacePositionVarying)
{
xfbOut += "xfbANGLEPosition = gl_Position;\n";
}
vertexShader->insertTransformFeedbackOutput(std::move(xfbOut)); vertexShader->insertTransformFeedbackOutput(std::move(xfbOut));
} }
void AssignAttributeLocations(const gl::ProgramState &programState, void AssignAttributeLocations(const gl::ProgramState &programState,
ShaderInterfaceVariableInfoMap *variableInfoMapOut) ShaderInterfaceVariableInfoMap *variableInfoMapOut)
{ {
gl::ShaderBitSet vertexOnly;
vertexOnly.set(gl::ShaderType::Vertex);
// Assign attribute locations for the vertex shader. // Assign attribute locations for the vertex shader.
for (const sh::ShaderVariable &attribute : programState.getProgramInputs()) for (const sh::ShaderVariable &attribute : programState.getProgramInputs())
{ {
ASSERT(attribute.active); ASSERT(attribute.active);
AddLocationInfo(variableInfoMapOut, attribute.mappedName, gl::ShaderType::Vertex, AddLocationInfo(variableInfoMapOut, attribute.mappedName, attribute.location,
attribute.location, ShaderInterfaceVariableInfo::kInvalid); ShaderInterfaceVariableInfo::kInvalid, vertexOnly);
} }
} }
...@@ -805,6 +767,10 @@ void AssignOutputLocations(const gl::ProgramState &programState, ...@@ -805,6 +767,10 @@ void AssignOutputLocations(const gl::ProgramState &programState,
const auto &outputVariables = programState.getOutputVariables(); const auto &outputVariables = programState.getOutputVariables();
const std::array<std::string, 3> implicitOutputs = {"gl_FragDepth", "gl_SampleMask", const std::array<std::string, 3> implicitOutputs = {"gl_FragDepth", "gl_SampleMask",
"gl_FragStencilRefARB"}; "gl_FragStencilRefARB"};
gl::ShaderBitSet fragmentOnly;
fragmentOnly.set(gl::ShaderType::Fragment);
for (const gl::VariableLocation &outputLocation : outputLocations) for (const gl::VariableLocation &outputLocation : outputLocations)
{ {
if (outputLocation.arrayIndex == 0 && outputLocation.used() && !outputLocation.ignored) if (outputLocation.arrayIndex == 0 && outputLocation.used() && !outputLocation.ignored)
...@@ -825,111 +791,193 @@ void AssignOutputLocations(const gl::ProgramState &programState, ...@@ -825,111 +791,193 @@ void AssignOutputLocations(const gl::ProgramState &programState,
implicitOutputs.begin(), implicitOutputs.end()) == 1); implicitOutputs.begin(), implicitOutputs.end()) == 1);
} }
AddLocationInfo(variableInfoMapOut, outputVar.mappedName, gl::ShaderType::Fragment, AddLocationInfo(variableInfoMapOut, outputVar.mappedName, location,
location, ShaderInterfaceVariableInfo::kInvalid); ShaderInterfaceVariableInfo::kInvalid, fragmentOnly);
} }
} }
// When no fragment output is specified by the shader, the translator outputs webgl_FragColor or
// webgl_FragData. Add an entry for these. Even though the translator is already assigning
// location 0 to these entries, adding an entry for them here allows us to ASSERT that every
// shader interface variable is processed during the SPIR-V transformation. This is done when
// iterating the ids provided by OpEntryPoint.
AddLocationInfo(variableInfoMapOut, "webgl_FragColor", 0, 0, fragmentOnly);
AddLocationInfo(variableInfoMapOut, "webgl_FragData", 0, 0, fragmentOnly);
} }
void AssignVaryingLocations(const gl::ProgramState &programState, void AssignVaryingLocations(const GlslangSourceOptions &options,
const gl::ProgramState &programState,
const gl::ProgramLinkedResources &resources, const gl::ProgramLinkedResources &resources,
gl::ShaderMap<IntermediateShaderSource> *shaderSources, uint32_t locationsUsedForXfbExtension,
XfbBufferMap *xfbBufferMap) ShaderInterfaceVariableInfoMap *variableInfoMapOut)
{ {
uint32_t locationsUsedForEmulation = locationsUsedForXfbExtension;
// Substitute layout and qualifier strings for the position varying added for line raster
// emulation.
if (options.emulateBresenhamLines)
{
uint32_t lineRasterEmulationPositionLocation = locationsUsedForEmulation++;
gl::ShaderBitSet allActiveStages;
allActiveStages.set();
AddLocationInfo(variableInfoMapOut, sh::vk::kLineRasterEmulationPosition,
lineRasterEmulationPositionLocation, ShaderInterfaceVariableInfo::kInvalid,
allActiveStages);
}
// Assign varying locations. // Assign varying locations.
for (const gl::PackedVaryingRegister &varyingReg : resources.varyingPacking.getRegisterList()) for (const gl::PackedVaryingRegister &varyingReg : resources.varyingPacking.getRegisterList())
{ {
const auto &varying = *varyingReg.packedVarying; if (!IsFirstRegisterOfVarying(varyingReg))
// In Vulkan GLSL, struct fields are not allowed to have location assignments. The varying
// of a struct type is thus given a location equal to the one assigned to its first field.
if (varying.isStructField() && varying.fieldIndex > 0)
{ {
continue; continue;
} }
// Similarly, assign array varying locations to the assigned location of the first element. const gl::PackedVarying &varying = *varyingReg.packedVarying;
if (varying.isArrayElement() && varying.arrayIndex != 0)
{
continue;
}
// In the following: // In the following:
// //
// struct S { vec4 field; }; // struct S { vec4 field; };
// out S varStruct; // out S varStruct;
// //
// "varStruct" is found through |parentStructName|, with |varying->name| being "field". In // "_uvarStruct" is found through |parentStructMappedName|, with |varying->mappedName|
// such a case, use |parentStructName|. // being "_ufield". In such a case, use |parentStructMappedName|.
const std::string &name = const std::string &name =
varying.isStructField() ? varying.parentStructName : varying.varying->name; varying.isStructField() ? varying.parentStructMappedName : varying.varying->mappedName;
std::string locationString = "location = " + Str(varyingReg.registerRow); uint32_t location = varyingReg.registerRow + locationsUsedForEmulation;
uint32_t component = ShaderInterfaceVariableInfo::kInvalid;
if (varyingReg.registerColumn > 0) if (varyingReg.registerColumn > 0)
{ {
ASSERT(!varying.varying->isStruct()); ASSERT(!varying.varying->isStruct());
ASSERT(!gl::IsMatrixType(varying.varying->type)); ASSERT(!gl::IsMatrixType(varying.varying->type));
locationString += ", component = " + Str(varyingReg.registerColumn); component = varyingReg.registerColumn;
} }
std::string *layoutSpecifier = &locationString; AddLocationInfo(variableInfoMapOut, name, location, component, varying.shaderStages);
}
std::string xfbSpecifier; // Add an entry for inactive varyings.
XfbBufferMap::iterator iter; for (const std::string &varyingName : resources.varyingPacking.getInactiveVaryingMappedNames())
iter = xfbBufferMap->find(name); {
if (iter != xfbBufferMap->end()) bool isBuiltin = angle::BeginsWith(varyingName, "gl_");
if (isBuiltin)
{ {
XFBBufferInfo item = iter->second; continue;
xfbSpecifier = "xfb_buffer = " + Str(item.index) +
", xfb_offset = " + Str(item.offset) +
", xfb_stride = " + Str(item.stride) + ", " + locationString;
layoutSpecifier = &xfbSpecifier;
} }
for (const gl::ShaderType stage : gl::kAllGraphicsShaderTypes) // TODO(syoussefi): inactive varying names should be unique. However, due to mishandling of
// partially captured arrays, a varying name can end up in both the active and inactive
// lists. The test below should be removed once that issue is resolved.
// http://anglebug.com/4140
if (variableInfoMapOut->find(varyingName) != variableInfoMapOut->end())
{ {
IntermediateShaderSource *shaderSource = &(*shaderSources)[stage]; continue;
if (shaderSource->empty()) }
{
ASSERT(!varying.shaderStages[stage]); AddLocationInfo(variableInfoMapOut, varyingName, ShaderInterfaceVariableInfo::kInvalid,
continue; ShaderInterfaceVariableInfo::kInvalid, gl::ShaderBitSet());
} }
}
// Calculates XFB layout qualifier arguments for each tranform feedback varying. Stores calculated
// values for the SPIR-V transformation.
void AssignTransformFeedbackExtensionQualifiers(const gl::ProgramState &programState,
const gl::ProgramLinkedResources &resources,
uint32_t locationsUsedForXfbExtension,
ShaderInterfaceVariableInfoMap *variableInfoMapOut)
{
const std::vector<gl::TransformFeedbackVarying> &tfVaryings =
programState.getLinkedTransformFeedbackVaryings();
const std::vector<GLsizei> &varyingStrides = programState.getTransformFeedbackStrides();
const bool isInterleaved =
programState.getTransformFeedbackBufferMode() == GL_INTERLEAVED_ATTRIBS;
if (!varying.shaderStages[stage]) std::string xfbDecl;
std::string xfbOut;
uint32_t currentOffset = 0;
uint32_t currentStride = 0;
uint32_t bufferIndex = 0;
uint32_t currentBuiltinLocation = 0;
for (uint32_t varyingIndex = 0; varyingIndex < tfVaryings.size(); ++varyingIndex)
{
if (isInterleaved)
{
bufferIndex = 0;
if (varyingIndex > 0)
{ {
// If not active in this stage, remove the varying declaration. Imagine the const gl::TransformFeedbackVarying &prev = tfVaryings[varyingIndex - 1];
// following scenario: currentOffset += prev.size() * gl::VariableExternalSize(prev.type);
//
// - VS: declare out varying used for transform feedback
// - FS: declare corresponding in varying which is not active
//
// Then varying.shaderStages would only contain Vertex, but the varying is not
// present in the list of inactive varyings since it _is_ active in some stages.
// As a result, we remove the varying from any stage that's not active.
// CleanupUnusedEntities will remove the varyings that are inactive in all stages.
shaderSource->eraseLayoutAndQualifierSpecifiers(name, "");
continue;
} }
currentStride = varyingStrides[0];
}
else
{
bufferIndex = varyingIndex;
currentOffset = 0;
currentStride = varyingStrides[varyingIndex];
}
const gl::TransformFeedbackVarying &tfVarying = tfVaryings[varyingIndex];
const std::string &tfVaryingName = tfVarying.mappedName;
if (tfVarying.isBuiltIn())
{
uint32_t xfbVaryingLocation = currentBuiltinLocation++;
std::string xfbVaryingName = kXfbBuiltInPrefix + tfVaryingName;
ASSERT(xfbVaryingLocation < locationsUsedForXfbExtension);
gl::ShaderBitSet vertexOnly;
vertexOnly.set(gl::ShaderType::Vertex);
shaderSource->insertLayoutSpecifier( AddLocationInfo(variableInfoMapOut, xfbVaryingName, xfbVaryingLocation,
name, stage == gl::ShaderType::Fragment ? locationString : *layoutSpecifier); ShaderInterfaceVariableInfo::kInvalid, vertexOnly);
shaderSource->insertQualifierSpecifier(name, ""); SetXfbInfo(variableInfoMapOut, xfbVaryingName, bufferIndex, currentOffset,
currentStride);
} }
} else if (!tfVarying.isArray() || tfVarying.arrayIndex == 0)
{
// Note: capturing individual array elements using the Vulkan transform feedback
// extension is not supported, and it unlikely to be ever supported (on the contrary, it
// may be removed from the GLES spec). http://anglebug.com/4140
// Find the varying with this name. If a struct is captured, we would be iterating over
// its fields, and the name of the varying is found through parentStructMappedName. Not
// only that, but also we should only do this for the first field of the struct.
const gl::PackedVarying *originalVarying = nullptr;
for (const gl::PackedVaryingRegister &varyingReg :
resources.varyingPacking.getRegisterList())
{
if (!IsFirstRegisterOfVarying(varyingReg))
{
continue;
}
// Substitute layout and qualifier strings for the position varying. Use the first free const gl::PackedVarying *varying = varyingReg.packedVarying;
// varying register after the packed varyings.
constexpr char kVaryingName[] = "ANGLEPosition";
std::stringstream layoutStream;
layoutStream << "location = "
<< (resources.varyingPacking.getMaxSemanticIndex() + kANGLEPositionLocationOffset);
const std::string layout = layoutStream.str();
for (IntermediateShaderSource &shaderSource : *shaderSources) if (varying->varying->name == tfVarying.name)
{ {
shaderSource.insertLayoutSpecifier(kVaryingName, layout); originalVarying = varying;
shaderSource.insertQualifierSpecifier(kVaryingName, ""); break;
}
}
if (originalVarying)
{
const std::string &mappedName = originalVarying->isStructField()
? originalVarying->parentStructMappedName
: originalVarying->varying->mappedName;
// Set xfb info for this varying. AssignVaryingLocations should have already added
// location information for these varyings.
SetXfbInfo(variableInfoMapOut, mappedName, bufferIndex, currentOffset,
currentStride);
}
}
} }
} }
...@@ -1071,21 +1119,6 @@ void AssignTextureBindings(const GlslangSourceOptions &options, ...@@ -1071,21 +1119,6 @@ void AssignTextureBindings(const GlslangSourceOptions &options,
} }
} }
void CleanupUnusedEntities(bool useOldRewriteStructSamplers,
const gl::ProgramState &programState,
const gl::ProgramLinkedResources &resources,
gl::ShaderMap<IntermediateShaderSource> *shaderSources)
{
// Remove all the markers for unused varyings.
for (const std::string &varyingName : resources.varyingPacking.getInactiveVaryingNames())
{
for (IntermediateShaderSource &shaderSource : *shaderSources)
{
shaderSource.eraseLayoutAndQualifierSpecifiers(varyingName, "");
}
}
}
constexpr gl::ShaderMap<EShLanguage> kShLanguageMap = { constexpr gl::ShaderMap<EShLanguage> kShLanguageMap = {
{gl::ShaderType::Vertex, EShLangVertex}, {gl::ShaderType::Vertex, EShLangVertex},
{gl::ShaderType::Geometry, EShLangGeometry}, {gl::ShaderType::Geometry, EShLangGeometry},
...@@ -1201,9 +1234,15 @@ class SpirvTransformer final : angle::NonCopyable ...@@ -1201,9 +1234,15 @@ class SpirvTransformer final : angle::NonCopyable
SpirvBlob *spirvBlobOut) SpirvBlob *spirvBlobOut)
: mSpirvBlobIn(spirvBlobIn), : mSpirvBlobIn(spirvBlobIn),
mShaderType(shaderType), mShaderType(shaderType),
mHasTransformFeedbackOutput(false),
mVariableInfoMap(variableInfoMap), mVariableInfoMap(variableInfoMap),
mSpirvBlobOut(spirvBlobOut) mSpirvBlobOut(spirvBlobOut)
{} {
gl::ShaderBitSet allStages;
allStages.set();
mBuiltinVariableInfo.activeStages = allStages;
}
bool transform(); bool transform();
...@@ -1234,29 +1273,43 @@ class SpirvTransformer final : angle::NonCopyable ...@@ -1234,29 +1273,43 @@ class SpirvTransformer final : angle::NonCopyable
// Instructions that potentially need transformation. They return true if the instruction is // Instructions that potentially need transformation. They return true if the instruction is
// transformed. If false is returned, the instruction should be copied as-is. // transformed. If false is returned, the instruction should be copied as-is.
bool transformAccessChain(const uint32_t *instruction, size_t wordCount);
bool transformCapability(const uint32_t *instruction, size_t wordCount);
bool transformEntryPoint(const uint32_t *instruction, size_t wordCount);
bool transformDecorate(const uint32_t *instruction, size_t wordCount); bool transformDecorate(const uint32_t *instruction, size_t wordCount);
bool transformTypePointer(const uint32_t *instruction, size_t wordCount);
bool transformVariable(const uint32_t *instruction, size_t wordCount);
// Any other instructions: // Any other instructions:
void copyInstruction(const uint32_t *instruction, size_t wordCount); size_t copyInstruction(const uint32_t *instruction, size_t wordCount);
size_t getCurrentOutputOffset() const; uint32_t getNewId();
// SPIR-V to transform: // SPIR-V to transform:
const std::vector<uint32_t> &mSpirvBlobIn; const std::vector<uint32_t> &mSpirvBlobIn;
const gl::ShaderType mShaderType; const gl::ShaderType mShaderType;
bool mHasTransformFeedbackOutput;
// Input shader variable info map: // Input shader variable info map:
const ShaderInterfaceVariableInfoMap &mVariableInfoMap; const ShaderInterfaceVariableInfoMap &mVariableInfoMap;
ShaderInterfaceVariableInfo mBuiltinVariableInfo;
// Transformed SPIR-V: // Transformed SPIR-V:
SpirvBlob *mSpirvBlobOut; SpirvBlob *mSpirvBlobOut;
// Traversal state: // Traversal state:
size_t mCurrentWord = 0; size_t mCurrentWord = 0;
bool mIsInFunctionSection = false;
// Transformation state: // Transformation state:
// Shader variable info per id, if id is a shader variable. // Shader variable info per id, if id is a shader variable.
std::vector<const ShaderInterfaceVariableInfo *> mVariableInfoById; std::vector<const ShaderInterfaceVariableInfo *> mVariableInfoById;
// Each OpTypePointer instruction that defines a type with the Output storage class is
// duplicated with a similar instruction but which defines a type with the Private storage
// class. If inactive varyings are encountered, its type is changed to the Private one. The
// following vector maps the Output type id to the corresponding Private one.
std::vector<uint32_t> mTypePointerTransformedId;
}; };
bool SpirvTransformer::transform() bool SpirvTransformer::transform()
...@@ -1269,6 +1322,7 @@ bool SpirvTransformer::transform() ...@@ -1269,6 +1322,7 @@ bool SpirvTransformer::transform()
// Make sure the transformer is not reused to avoid having to reinitialize it here. // Make sure the transformer is not reused to avoid having to reinitialize it here.
ASSERT(mCurrentWord == 0); ASSERT(mCurrentWord == 0);
ASSERT(mIsInFunctionSection == false);
// Make sure the SpirvBlob is not reused. // Make sure the SpirvBlob is not reused.
ASSERT(mSpirvBlobOut->empty()); ASSERT(mSpirvBlobOut->empty());
...@@ -1301,12 +1355,34 @@ uint32_t GetSpirvInstructionOp(const uint32_t *instruction) ...@@ -1301,12 +1355,34 @@ uint32_t GetSpirvInstructionOp(const uint32_t *instruction)
return instruction[0] & kOpMask; return instruction[0] & kOpMask;
} }
void SetSpirvInstructionLength(uint32_t *instruction, size_t length)
{
ASSERT(length < 0xFFFFu);
constexpr uint32_t kLengthMask = 0xFFFF0000u;
instruction[0] &= ~kLengthMask;
instruction[0] |= length << 16;
}
void SetSpirvInstructionOp(uint32_t *instruction, uint32_t op)
{
constexpr uint32_t kOpMask = 0xFFFFu;
instruction[0] &= ~kOpMask;
instruction[0] |= op;
}
void SpirvTransformer::resolveVariableIds() void SpirvTransformer::resolveVariableIds()
{ {
size_t indexBound = mSpirvBlobIn[kHeaderIndexIndexBound];
// Allocate storage for id-to-info map. If %i is the id of a name in mVariableInfoMap, index i // 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 // in this vector will hold a pointer to the ShaderInterfaceVariableInfo object associated with
// that name in mVariableInfoMap. // that name in mVariableInfoMap.
mVariableInfoById.resize(mSpirvBlobIn[kHeaderIndexIndexBound] + 1, nullptr); mVariableInfoById.resize(indexBound + 1, nullptr);
// Allocate storage for Output type pointer map. At index i, this vector holds the identical
// type as %i except for its storage class turned to Private.
mTypePointerTransformedId.resize(indexBound + 1, 0);
size_t currentWord = kHeaderIndexInstructions; size_t currentWord = kHeaderIndexInstructions;
...@@ -1332,10 +1408,11 @@ void SpirvTransformer::resolveVariableIds() ...@@ -1332,10 +1408,11 @@ void SpirvTransformer::resolveVariableIds()
visitVariable(instruction); visitVariable(instruction);
break; break;
case spv::OpFunction: case spv::OpFunction:
// SPIR-V is structured in sections. Names appear before decorations, which are // SPIR-V is structured in sections (SPIR-V 1.0 Section 2.4 Logical Layout of a
// followed by type+variables and finally functions. We are only interested in name // Module). Names appear before decorations, which are followed by type+variables
// and variable declarations (as well as type declarations for the sake of nameless // and finally functions. We are only interested in name and variable declarations
// interface blocks). Early out when the function declaration section is met. // (as well as type declarations for the sake of nameless interface blocks). Early
// out when the function declaration section is met.
return; return;
default: default:
break; break;
...@@ -1353,25 +1430,56 @@ void SpirvTransformer::transformInstruction() ...@@ -1353,25 +1430,56 @@ void SpirvTransformer::transformInstruction()
const uint32_t opCode = GetSpirvInstructionOp(instruction); const uint32_t opCode = GetSpirvInstructionOp(instruction);
// Since glslang succeeded in producing SPIR-V, we assume it to be valid. // Since glslang succeeded in producing SPIR-V, we assume it to be valid.
const size_t spirvBlobInSize = mSpirvBlobIn.size(); ASSERT(mCurrentWord + wordCount <= mSpirvBlobIn.size());
ASSERT(mCurrentWord + wordCount <= spirvBlobInSize);
if (opCode == spv::OpFunction)
{
// SPIR-V is structured in sections. Function declarations come last. Only Op*Access*
// opcodes inside functions need to be inspected.
mIsInFunctionSection = true;
}
// Only look at interesting instructions. // Only look at interesting instructions.
bool transformed = false; bool transformed = false;
switch (opCode)
if (mIsInFunctionSection)
{ {
case spv::OpDecorate: // Look at in-function opcodes.
transformed = transformDecorate(instruction, wordCount); switch (opCode)
break; {
case spv::OpFunction: case spv::OpAccessChain:
// SPIR-V is structured in sections. Function declarations come last. This case spv::OpInBoundsAccessChain:
// transformation will not modify functions, so we can copy the rest of the spir-v case spv::OpPtrAccessChain:
// as-is. case spv::OpInBoundsPtrAccessChain:
copyInstruction(instruction, spirvBlobInSize - mCurrentWord); transformed = transformAccessChain(instruction, wordCount);
mCurrentWord = spirvBlobInSize; break;
return; default:
default: break;
break; }
}
else
{
// Look at global declaration opcodes.
switch (opCode)
{
case spv::OpCapability:
transformed = transformCapability(instruction, wordCount);
break;
case spv::OpEntryPoint:
transformed = transformEntryPoint(instruction, wordCount);
break;
case spv::OpDecorate:
transformed = transformDecorate(instruction, wordCount);
break;
case spv::OpTypePointer:
transformed = transformTypePointer(instruction, wordCount);
break;
case spv::OpVariable:
transformed = transformVariable(instruction, wordCount);
break;
default:
break;
}
} }
// If the instruction was not transformed, copy it to output as is. // If the instruction was not transformed, copy it to output as is.
...@@ -1398,18 +1506,38 @@ void SpirvTransformer::visitName(const uint32_t *instruction) ...@@ -1398,18 +1506,38 @@ void SpirvTransformer::visitName(const uint32_t *instruction)
const uint32_t id = instruction[kIdIndex]; const uint32_t id = instruction[kIdIndex];
const char *name = reinterpret_cast<const char *>(&instruction[kNameIndex]); const char *name = reinterpret_cast<const char *>(&instruction[kNameIndex]);
// The names and ids are unique
ASSERT(id < mVariableInfoById.size());
ASSERT(mVariableInfoById[id] == nullptr);
bool isBuiltin = angle::BeginsWith(name, "gl_");
if (isBuiltin)
{
// Make all builtins point to this no-op info. Adding this entry allows us to ASSERT that
// every shader interface variable is processed during the SPIR-V transformation. This is
// done when iterating the ids provided by OpEntryPoint.
mVariableInfoById[id] = &mBuiltinVariableInfo;
return;
}
auto infoIter = mVariableInfoMap.find(name); auto infoIter = mVariableInfoMap.find(name);
if (infoIter == mVariableInfoMap.end()) if (infoIter == mVariableInfoMap.end())
{ {
return; return;
} }
// The names and ids are unique const ShaderInterfaceVariableInfo *info = &infoIter->second;
ASSERT(id < mVariableInfoById.size());
ASSERT(mVariableInfoById[id] == nullptr);
// Associate the id of this name with its info. // Associate the id of this name with its info.
mVariableInfoById[id] = &infoIter->second; mVariableInfoById[id] = info;
// Note if the variable is captured by transform feedback. In that case, the TransformFeedback
// capability needs to be added.
if (mShaderType != gl::ShaderType::Fragment &&
info->xfbBuffer != ShaderInterfaceVariableInfo::kInvalid && info->activeStages[mShaderType])
{
mHasTransformFeedbackOutput = true;
}
} }
void SpirvTransformer::visitTypeHelper(const uint32_t *instruction, void SpirvTransformer::visitTypeHelper(const uint32_t *instruction,
...@@ -1490,6 +1618,12 @@ bool SpirvTransformer::transformDecorate(const uint32_t *instruction, size_t wor ...@@ -1490,6 +1618,12 @@ bool SpirvTransformer::transformDecorate(const uint32_t *instruction, size_t wor
return false; return false;
} }
// If it's an inactive varying, remove the decoration altogether.
if (!info->activeStages[mShaderType])
{
return true;
}
uint32_t newDecorationValue = ShaderInterfaceVariableInfo::kInvalid; uint32_t newDecorationValue = ShaderInterfaceVariableInfo::kInvalid;
switch (decoration) switch (decoration)
...@@ -1497,24 +1631,12 @@ bool SpirvTransformer::transformDecorate(const uint32_t *instruction, size_t wor ...@@ -1497,24 +1631,12 @@ bool SpirvTransformer::transformDecorate(const uint32_t *instruction, size_t wor
case spv::DecorationLocation: case spv::DecorationLocation:
newDecorationValue = info->location[mShaderType]; newDecorationValue = info->location[mShaderType];
break; break;
case spv::DecorationComponent:
newDecorationValue = info->component[mShaderType];
break;
case spv::DecorationBinding: case spv::DecorationBinding:
newDecorationValue = info->binding; newDecorationValue = info->binding;
break; break;
case spv::DecorationDescriptorSet: case spv::DecorationDescriptorSet:
newDecorationValue = info->descriptorSet; newDecorationValue = info->descriptorSet;
break; break;
case spv::DecorationOffset:
newDecorationValue = info->xfbOffset;
break;
case spv::DecorationXfbBuffer:
newDecorationValue = info->xfbBuffer;
break;
case spv::DecorationXfbStride:
newDecorationValue = info->xfbStride;
break;
default: default:
break; break;
} }
...@@ -1526,21 +1648,303 @@ bool SpirvTransformer::transformDecorate(const uint32_t *instruction, size_t wor ...@@ -1526,21 +1648,303 @@ bool SpirvTransformer::transformDecorate(const uint32_t *instruction, size_t wor
} }
// Copy the decoration declaration and modify it. // Copy the decoration declaration and modify it.
const size_t currentOutputOffset = getCurrentOutputOffset(); const size_t instructionOffset = copyInstruction(instruction, wordCount);
(*mSpirvBlobOut)[instructionOffset + kDecorationValueIndex] = newDecorationValue;
// If there are decorations to be added, add them right after the Location decoration is
// encountered.
if (decoration != spv::DecorationLocation)
{
return true;
}
// Add component decoration, if any.
if (info->component[mShaderType] != ShaderInterfaceVariableInfo::kInvalid)
{
// Copy the location decoration declaration and modify it to contain the Component
// decoration.
const size_t instOffset = copyInstruction(instruction, wordCount);
(*mSpirvBlobOut)[instOffset + kDecorationIndex] = spv::DecorationComponent;
(*mSpirvBlobOut)[instOffset + kDecorationValueIndex] = info->component[mShaderType];
}
// Add Xfb decorations, if any.
if (mShaderType != gl::ShaderType::Fragment &&
info->xfbBuffer != ShaderInterfaceVariableInfo::kInvalid)
{
ASSERT(info->xfbStride != ShaderInterfaceVariableInfo::kInvalid);
ASSERT(info->xfbOffset != ShaderInterfaceVariableInfo::kInvalid);
constexpr size_t kXfbDecorationCount = 3;
constexpr uint32_t xfbDecorations[kXfbDecorationCount] = {
spv::DecorationXfbBuffer,
spv::DecorationXfbStride,
spv::DecorationOffset,
};
const uint32_t xfbDecorationValues[kXfbDecorationCount] = {
info->xfbBuffer,
info->xfbStride,
info->xfbOffset,
};
// Copy the location decoration declaration three times, and modify them to contain the
// XfbBuffer, XfbStride and Offset decorations.
for (size_t i = 0; i < kXfbDecorationCount; ++i)
{
const size_t xfbInstructionOffset = copyInstruction(instruction, wordCount);
(*mSpirvBlobOut)[xfbInstructionOffset + kDecorationIndex] = xfbDecorations[i];
(*mSpirvBlobOut)[xfbInstructionOffset + kDecorationValueIndex] = xfbDecorationValues[i];
}
}
return true;
}
bool SpirvTransformer::transformCapability(const uint32_t *instruction, size_t wordCount)
{
if (!mHasTransformFeedbackOutput)
{
return false;
}
// SPIR-V 1.0 Section 3.32 Instructions, OpCapability
constexpr size_t kCapabilityIndex = 1;
uint32_t capability = instruction[kCapabilityIndex];
// Transform feedback capability shouldn't have already been specified.
ASSERT(capability != spv::CapabilityTransformFeedback);
// Vulkan shaders have either Shader, Geometry or Tessellation capability. We find this
// capability, and add the TransformFeedback capability after it.
if (capability != spv::CapabilityShader && capability != spv::CapabilityGeometry &&
capability != spv::CapabilityTessellation)
{
return false;
}
// Copy the original capability declaration.
copyInstruction(instruction, wordCount); copyInstruction(instruction, wordCount);
(*mSpirvBlobOut)[currentOutputOffset + kDecorationValueIndex] = newDecorationValue;
// Create the TransformFeedback capability declaration.
// SPIR-V 1.0 Section 3.32 Instructions, OpCapability
constexpr size_t kCapabilityInstructionLength = 2;
std::array<uint32_t, kCapabilityInstructionLength> newCapabilityDeclaration = {
instruction[0], // length+opcode is identical
};
// Fill the fields.
newCapabilityDeclaration[kCapabilityIndex] = spv::CapabilityTransformFeedback;
copyInstruction(newCapabilityDeclaration.data(), kCapabilityInstructionLength);
return true;
}
bool SpirvTransformer::transformEntryPoint(const uint32_t *instruction, size_t wordCount)
{
// Remove inactive varyings from the shader interface declaration.
// SPIR-V 1.0 Section 3.32 Instructions, OpEntryPoint
constexpr size_t kNameIndex = 3;
// Calculate the length of entry point name in words. Note that endianness of the string
// doesn't matter, since we are looking for the '\0' character and rounding up to the word size.
// This calculates (strlen(name)+1+3) / 4, which is equal to strlen(name)/4+1.
const size_t nameLength =
strlen(reinterpret_cast<const char *>(&instruction[kNameIndex])) / 4 + 1;
const uint32_t instructionLength = GetSpirvInstructionLength(instruction);
const size_t interfaceStart = kNameIndex + nameLength;
const size_t interfaceCount = instructionLength - interfaceStart;
// Create a copy of the entry point for modification.
std::vector<uint32_t> filteredEntryPoint(instruction, instruction + wordCount);
// Filter out inactive varyings from entry point interface declaration.
size_t writeIndex = interfaceStart;
for (size_t index = 0; index < interfaceCount; ++index)
{
uint32_t id = instruction[interfaceStart + index];
const ShaderInterfaceVariableInfo *info = mVariableInfoById[id];
ASSERT(info);
if (!info->activeStages[mShaderType])
{
continue;
}
filteredEntryPoint[writeIndex] = id;
++writeIndex;
}
// Update the length of the instruction.
const size_t newLength = writeIndex;
SetSpirvInstructionLength(filteredEntryPoint.data(), newLength);
// Copy to output.
copyInstruction(filteredEntryPoint.data(), newLength);
// Add an OpExecutionMode Xfb instruction if necessary.
if (!mHasTransformFeedbackOutput)
{
return true;
}
// SPIR-V 1.0 Section 3.32 Instructions, OpEntryPoint
constexpr size_t kEntryPointIdIndex = 2;
// SPIR-V 1.0 Section 3.32 Instructions, OpExecutionMode
constexpr size_t kExecutionModeInstructionLength = 3;
constexpr size_t kExecutionModeIdIndex = 1;
constexpr size_t kExecutionModeExecutionModeIndex = 2;
std::array<uint32_t, kExecutionModeInstructionLength> newExecutionModeDeclaration = {};
// Fill the fields.
SetSpirvInstructionOp(newExecutionModeDeclaration.data(), spv::OpExecutionMode);
SetSpirvInstructionLength(newExecutionModeDeclaration.data(), kExecutionModeInstructionLength);
newExecutionModeDeclaration[kExecutionModeIdIndex] = instruction[kEntryPointIdIndex];
newExecutionModeDeclaration[kExecutionModeExecutionModeIndex] = spv::ExecutionModeXfb;
copyInstruction(newExecutionModeDeclaration.data(), kExecutionModeInstructionLength);
return true;
}
bool SpirvTransformer::transformTypePointer(const uint32_t *instruction, size_t wordCount)
{
// SPIR-V 1.0 Section 3.32 Instructions, OpTypePointer
constexpr size_t kIdIndex = 1;
constexpr size_t kStorageClassIndex = 2;
const uint32_t id = instruction[kIdIndex];
const uint32_t storageClass = instruction[kStorageClassIndex];
// If the storage class is output, this may be used to create a variable corresponding to an
// inactive varying, or if that varying is a struct, an Op*AccessChain retrieving a field of
// that inactive varying.
//
// Unfortunately, SPIR-V specifies the storage class both on the type and the variable
// declaration. Otherwise it would have been sufficient to modify the OpVariable instruction.
// For simplicty, copy every "OpTypePointer Output" instruction except with the Private storage
// class, in case it may be necessary later.
if (storageClass != spv::StorageClassOutput)
{
return false;
}
// Cannot create a Private type declaration from the builtin gl_PerVertex. Note that
// mVariableInfoById is only ever set for variables, except for nameless interface blocks and
// the builtin gl_PerVertex. Since the storage class is Output, if mVariableInfoById for this
// type is not nullptr, this must be a builtin.
const ShaderInterfaceVariableInfo *info = mVariableInfoById[id];
if (info)
{
ASSERT(info == &mBuiltinVariableInfo);
return false;
}
// Copy the type declaration for modification.
const size_t instructionOffset = copyInstruction(instruction, wordCount);
const uint32_t newTypeId = getNewId();
(*mSpirvBlobOut)[instructionOffset + kIdIndex] = newTypeId;
(*mSpirvBlobOut)[instructionOffset + kStorageClassIndex] = spv::StorageClassPrivate;
// Remember the id of the replacement.
ASSERT(id < mTypePointerTransformedId.size());
mTypePointerTransformedId[id] = newTypeId;
// The original instruction should still be present as well. At this point, we don't know
// whether we will need the Output or Private type.
return false;
}
bool SpirvTransformer::transformVariable(const uint32_t *instruction, size_t wordCount)
{
// SPIR-V 1.0 Section 3.32 Instructions, OpVariable
constexpr size_t kTypeIdIndex = 1;
constexpr size_t kIdIndex = 2;
constexpr size_t kStorageClassIndex = 3;
const uint32_t id = instruction[kIdIndex];
const uint32_t typeId = instruction[kTypeIdIndex];
const uint32_t storageClass = instruction[kStorageClassIndex];
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;
}
// Furthermore, if it's not an inactive varying output, there's nothing to do. Note that
// inactive varying inputs are already pruned by the translator.
ASSERT(storageClass != spv::StorageClassInput || info->activeStages[mShaderType]);
if (info->activeStages[mShaderType])
{
return false;
}
ASSERT(storageClass == spv::StorageClassOutput);
// Copy the variable declaration for modification. Change its type to the corresponding type
// with the Private storage class, as well as changing the storage class respecified in this
// instruction.
const size_t instructionOffset = copyInstruction(instruction, wordCount);
ASSERT(typeId < mTypePointerTransformedId.size());
ASSERT(mTypePointerTransformedId[typeId] != 0);
(*mSpirvBlobOut)[instructionOffset + kTypeIdIndex] = mTypePointerTransformedId[typeId];
(*mSpirvBlobOut)[instructionOffset + kStorageClassIndex] = spv::StorageClassPrivate;
return true;
}
bool SpirvTransformer::transformAccessChain(const uint32_t *instruction, size_t wordCount)
{
// SPIR-V 1.0 Section 3.32 Instructions, OpAccessChain, OpInBoundsAccessChain, OpPtrAccessChain,
// OpInBoundsPtrAccessChain
constexpr size_t kTypeIdIndex = 1;
constexpr size_t kBaseIdIndex = 3;
const uint32_t typeId = instruction[kTypeIdIndex];
const uint32_t baseId = instruction[kBaseIdIndex];
// If not accessing an inactive output varying, nothing to do.
const ShaderInterfaceVariableInfo *info = mVariableInfoById[baseId];
if (info == nullptr || info->activeStages[mShaderType])
{
return false;
}
// Copy the instruction for modification.
const size_t instructionOffset = copyInstruction(instruction, wordCount);
ASSERT(typeId < mTypePointerTransformedId.size());
ASSERT(mTypePointerTransformedId[typeId] != 0);
(*mSpirvBlobOut)[instructionOffset + kTypeIdIndex] = mTypePointerTransformedId[typeId];
return true; return true;
} }
void SpirvTransformer::copyInstruction(const uint32_t *instruction, size_t wordCount) size_t SpirvTransformer::copyInstruction(const uint32_t *instruction, size_t wordCount)
{ {
size_t instructionOffset = mSpirvBlobOut->size();
mSpirvBlobOut->insert(mSpirvBlobOut->end(), instruction, instruction + wordCount); mSpirvBlobOut->insert(mSpirvBlobOut->end(), instruction, instruction + wordCount);
return instructionOffset;
} }
size_t SpirvTransformer::getCurrentOutputOffset() const uint32_t SpirvTransformer::getNewId()
{ {
return mSpirvBlobOut->size(); return (*mSpirvBlobOut)[kHeaderIndexIndexBound]++;
} }
} // anonymous namespace } // anonymous namespace
...@@ -1608,6 +2012,7 @@ void GlslangGetShaderSource(const GlslangSourceOptions &options, ...@@ -1608,6 +2012,7 @@ void GlslangGetShaderSource(const GlslangSourceOptions &options,
variableInfoMapOut->clear(); variableInfoMapOut->clear();
gl::ShaderMap<IntermediateShaderSource> intermediateSources; gl::ShaderMap<IntermediateShaderSource> intermediateSources;
uint32_t locationsUsedForXfbExtension = 0;
for (const gl::ShaderType shaderType : gl::AllShaderTypes()) for (const gl::ShaderType shaderType : gl::AllShaderTypes())
{ {
...@@ -1622,8 +2027,6 @@ void GlslangGetShaderSource(const GlslangSourceOptions &options, ...@@ -1622,8 +2027,6 @@ void GlslangGetShaderSource(const GlslangSourceOptions &options,
IntermediateShaderSource *fragmentSource = &intermediateSources[gl::ShaderType::Fragment]; IntermediateShaderSource *fragmentSource = &intermediateSources[gl::ShaderType::Fragment];
IntermediateShaderSource *computeSource = &intermediateSources[gl::ShaderType::Compute]; IntermediateShaderSource *computeSource = &intermediateSources[gl::ShaderType::Compute];
XfbBufferMap xfbBufferMap;
// Write transform feedback output code. // Write transform feedback output code.
if (!vertexSource->empty()) if (!vertexSource->empty())
{ {
...@@ -1636,8 +2039,9 @@ void GlslangGetShaderSource(const GlslangSourceOptions &options, ...@@ -1636,8 +2039,9 @@ void GlslangGetShaderSource(const GlslangSourceOptions &options,
{ {
if (options.supportsTransformFeedbackExtension) if (options.supportsTransformFeedbackExtension)
{ {
GenerateTransformFeedbackExtensionOutputs(programState, vertexSource, &xfbBufferMap, GenerateTransformFeedbackExtensionOutputs(programState, vertexSource, resources,
resources); variableInfoMapOut,
&locationsUsedForXfbExtension);
} }
else if (options.emulateTransformFeedback) else if (options.emulateTransformFeedback)
{ {
...@@ -1662,16 +2066,21 @@ void GlslangGetShaderSource(const GlslangSourceOptions &options, ...@@ -1662,16 +2066,21 @@ void GlslangGetShaderSource(const GlslangSourceOptions &options,
if (computeSource->empty()) if (computeSource->empty())
{ {
// Assign varying locations. // Assign varying locations.
AssignVaryingLocations(programState, resources, &intermediateSources, &xfbBufferMap); AssignVaryingLocations(options, programState, resources, locationsUsedForXfbExtension,
variableInfoMapOut);
if (!programState.getLinkedTransformFeedbackVaryings().empty() &&
options.supportsTransformFeedbackExtension)
{
AssignTransformFeedbackExtensionQualifiers(
programState, resources, locationsUsedForXfbExtension, variableInfoMapOut);
}
} }
AssignUniformBindings(options, &intermediateSources, variableInfoMapOut); AssignUniformBindings(options, &intermediateSources, variableInfoMapOut);
AssignTextureBindings(options, programState, variableInfoMapOut); AssignTextureBindings(options, programState, variableInfoMapOut);
AssignNonTextureBindings(options, programState, variableInfoMapOut); AssignNonTextureBindings(options, programState, variableInfoMapOut);
CleanupUnusedEntities(options.useOldRewriteStructSamplers, programState, resources,
&intermediateSources);
for (const gl::ShaderType shaderType : gl::AllShaderTypes()) for (const gl::ShaderType shaderType : gl::AllShaderTypes())
{ {
(*shaderSourcesOut)[shaderType] = intermediateSources[shaderType].getShaderSource(); (*shaderSourcesOut)[shaderType] = intermediateSources[shaderType].getShaderSource();
......
...@@ -38,6 +38,7 @@ struct GlslangSourceOptions ...@@ -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 ...@@ -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) ...@@ -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 ...@@ -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) ...@@ -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) ...@@ -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)
{ {
......
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