Vulkan: Separate out XFB data from ShaderInterfaceVariableInfo

Right now the transform feedback data is embedded in the
ShaderInterfaceVariableInfo. This caused ShaderInterfaceVariableInfo
becomes non-trivial copy-able. This CL moves transform feedback related
data out and into its own array, and entire vector of
ShaderInterfaceVariableInfo is now memcpied. Further, most programs
don't use transform feedback. Right now because transform feedback data
is embedded in the ShaderInterfaceVariableInfo, it bloated the size of
ShaderInterfaceVariableInfo even if you do not use XFB. This CL makes
transform feedback variable info data a std::unique_ptr so that if not
used, it is just a nullptr. When we load/save the structure, the ones
that has nullptr gets skipped.

Bug: b/296433003
Change-Id: I61940a683611717ab0445fcbf44b89b1b7166ee4
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/4799344
Commit-Queue: Charlie Lao <cclao@google.com>
Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org>
Reviewed-by: Roman Lavrov <romanl@google.com>
This commit is contained in:
Charlie Lao
2023-08-21 15:51:30 -07:00
committed by Angle LUCI CQ
parent 60b56591de
commit 014e584f75
7 changed files with 189 additions and 93 deletions

View File

@@ -99,6 +99,7 @@ class BinaryInputStream : angle::NonCopyable
void readBool(bool *outValue) { *outValue = readBool(); }
void readBytes(unsigned char outArray[], size_t count) { read<unsigned char>(outArray, count); }
const unsigned char *getBytes(size_t count) { return read<unsigned char>(nullptr, count); }
std::string readString()
{
@@ -171,7 +172,7 @@ class BinaryInputStream : angle::NonCopyable
size_t mLength;
template <typename T>
void read(T *v, size_t num)
const uint8_t *read(T *v, size_t num)
{
static_assert(std::is_fundamental<T>::value, "T must be a fundamental type.");
@@ -180,7 +181,7 @@ class BinaryInputStream : angle::NonCopyable
if (!checkedLength.IsValid())
{
mError = true;
return;
return nullptr;
}
angle::CheckedNumeric<size_t> checkedOffset(mOffset);
@@ -189,11 +190,17 @@ class BinaryInputStream : angle::NonCopyable
if (!checkedOffset.IsValid() || checkedOffset.ValueOrDie() > mLength)
{
mError = true;
return;
return nullptr;
}
memcpy(v, mData + mOffset, checkedLength.ValueOrDie());
const uint8_t *srcBytes = mData + mOffset;
if (v != nullptr)
{
memcpy(v, srcBytes, checkedLength.ValueOrDie());
}
mOffset = checkedOffset.ValueOrDie();
return srcBytes;
}
template <typename T>

View File

@@ -151,6 +151,7 @@ class FastVector final
const_reference back() const;
void swap(FastVector<T, N, Storage> &other);
void resetWithRawData(size_type count, const uint8_t *data);
void resize(size_type count);
void resize(size_type count, const value_type &value);
@@ -449,6 +450,16 @@ void FastVector<T, N, Storage>::swap(FastVector<T, N, Storage> &other)
std::swap(mFixedStorage, other.mFixedStorage);
}
template <class T, size_t N, class Storage>
void FastVector<T, N, Storage>::resetWithRawData(size_type count, const uint8_t *data)
{
static_assert(std::is_trivially_copyable<value_type>(),
"This is a special method for trivially copyable types.");
ASSERT(count > 0 && data != nullptr);
resize_impl(count);
std::memcpy(mData, data, count * sizeof(value_type));
}
template <class T, size_t N, class Storage>
ANGLE_INLINE void FastVector<T, N, Storage>::resize(size_type count)
{
@@ -614,6 +625,11 @@ class FastMap final
void clear() { mData.clear(); }
void resetWithRawData(size_type count, const uint8_t *data)
{
mData.resetWithRawData(count, data);
}
bool empty() const { return mData.empty(); }
size_t size() const { return mData.size(); }

View File

@@ -208,6 +208,27 @@ TEST(FastVector, Resize)
EXPECT_EQ(2u, vec.size());
}
// Test resetWithRawData on the vector
TEST(FastVector, resetWithRawData)
{
FastVector<int, 5> vec;
int data[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
vec.resetWithRawData(9, reinterpret_cast<uint8_t *>(&data[0]));
EXPECT_EQ(9u, vec.size());
for (size_t i = 0; i < vec.size(); i++)
{
EXPECT_EQ(vec[i], data[i]);
}
vec.resetWithRawData(4, reinterpret_cast<uint8_t *>(&data[0]));
EXPECT_EQ(4u, vec.size());
for (size_t i = 0; i < vec.size(); i++)
{
EXPECT_EQ(vec[i], data[i]);
}
}
// Test iterating over the vector
TEST(FastVector, Iteration)
{

View File

@@ -66,6 +66,7 @@ ShaderInterfaceVariableInfoMap::~ShaderInterfaceVariableInfoMap() = default;
void ShaderInterfaceVariableInfoMap::clear()
{
mData.clear();
mXFBData.clear();
for (gl::ShaderType shaderType : gl::AllShaderTypes())
{
mIdToIndexMap[shaderType].clear();
@@ -78,36 +79,43 @@ void ShaderInterfaceVariableInfoMap::clear()
void ShaderInterfaceVariableInfoMap::save(gl::BinaryOutputStream *stream)
{
for (gl::ShaderType shaderType : gl::AllShaderTypes())
for (const IdToIndexMap &idToIndexMap : mIdToIndexMap)
{
stream->writeInt(mIdToIndexMap[shaderType].size());
for (const VariableIndex &variableIndex : mIdToIndexMap[shaderType])
stream->writeInt(idToIndexMap.size());
if (idToIndexMap.size() > 0)
{
stream->writeInt(variableIndex.index);
stream->writeBytes(reinterpret_cast<const uint8_t *>(idToIndexMap.data()),
idToIndexMap.size() * sizeof(*idToIndexMap.data()));
}
}
stream->writeInt(mData.size());
for (const ShaderInterfaceVariableInfo &info : mData)
stream->writeVector(mData);
ASSERT(mXFBData.size() <= mData.size());
size_t xfbInfoCount = 0;
for (const XFBVariableInfoPtr &info : mXFBData)
{
stream->writeInt(info.descriptorSet);
stream->writeInt(info.binding);
stream->writeInt(info.location);
stream->writeInt(info.component);
stream->writeInt(info.index);
// PackedEnumBitSet uses uint8_t
stream->writeInt(info.activeStages.bits());
if (!info)
{
continue;
}
xfbInfoCount++;
}
stream->writeInt(xfbInfoCount);
for (size_t xfbIndex = 0; xfbIndex < mXFBData.size(); xfbIndex++)
{
if (!mXFBData[xfbIndex])
{
continue;
}
stream->writeInt(xfbIndex);
XFBInterfaceVariableInfo &info = *mXFBData[xfbIndex];
SaveShaderInterfaceVariableXfbInfo(info.xfb, stream);
stream->writeInt(info.fieldXfb.size());
for (const ShaderInterfaceVariableXfbInfo &xfb : info.fieldXfb)
{
SaveShaderInterfaceVariableXfbInfo(xfb, stream);
}
stream->writeBool(info.useRelaxedPrecision);
stream->writeBool(info.varyingIsInput);
stream->writeBool(info.varyingIsOutput);
stream->writeInt(info.attributeComponentCount);
stream->writeInt(info.attributeLocationCount);
}
// Store gl_PerVertex members only for stages that have it.
@@ -121,46 +129,41 @@ void ShaderInterfaceVariableInfoMap::save(gl::BinaryOutputStream *stream)
}
void ShaderInterfaceVariableInfoMap::load(gl::BinaryInputStream *stream)
{
ShaderInterfaceVariableInfoMap::VariableInfoArray data;
gl::ShaderMap<ShaderInterfaceVariableInfoMap::IdToIndexMap> idToIndexMap;
gl::ShaderMap<gl::PerVertexMemberBitSet> inputPerVertexActiveMembers;
gl::ShaderMap<gl::PerVertexMemberBitSet> outputPerVertexActiveMembers;
for (gl::ShaderType shaderType : gl::AllShaderTypes())
for (IdToIndexMap &idToIndexMap : mIdToIndexMap)
{
size_t idCount = stream->readInt<size_t>();
for (uint32_t id = 0; id < idCount; ++id)
// ASSERT(idToIndexMap.empty());
size_t count = stream->readInt<size_t>();
if (count > 0)
{
uint32_t index = stream->readInt<uint32_t>();
idToIndexMap[shaderType][id] = {index};
idToIndexMap.resetWithRawData(count,
stream->getBytes(count * sizeof(*idToIndexMap.data())));
}
}
size_t dataSize = stream->readInt<size_t>();
for (size_t infoIndex = 0; infoIndex < dataSize; ++infoIndex)
stream->readVector(&mData);
ASSERT(mXFBData.empty());
size_t xfbInfoCount = stream->readInt<size_t>();
ASSERT(xfbInfoCount <= mData.size());
if (xfbInfoCount > 0)
{
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.index = stream->readInt<uint32_t>();
// PackedEnumBitSet uses uint8_t
info.activeStages = gl::ShaderBitSet(stream->readInt<uint8_t>());
LoadShaderInterfaceVariableXfbInfo(stream, &info.xfb);
info.fieldXfb.resize(stream->readInt<size_t>());
for (ShaderInterfaceVariableXfbInfo &xfb : info.fieldXfb)
mXFBData.resize(mData.size());
for (size_t i = 0; i < xfbInfoCount; ++i)
{
LoadShaderInterfaceVariableXfbInfo(stream, &xfb);
}
info.useRelaxedPrecision = stream->readBool();
info.varyingIsInput = stream->readBool();
info.varyingIsOutput = stream->readBool();
info.attributeComponentCount = stream->readInt<uint8_t>();
info.attributeLocationCount = stream->readInt<uint8_t>();
size_t xfbIndex = stream->readInt<size_t>();
mXFBData[xfbIndex] = std::make_unique<XFBInterfaceVariableInfo>();
data.push_back(info);
XFBInterfaceVariableInfo &info = *mXFBData[xfbIndex];
LoadShaderInterfaceVariableXfbInfo(stream, &info.xfb);
info.fieldXfb.resize(stream->readInt<size_t>());
for (ShaderInterfaceVariableXfbInfo &xfb : info.fieldXfb)
{
LoadShaderInterfaceVariableXfbInfo(stream, &xfb);
}
}
}
outputPerVertexActiveMembers[gl::ShaderType::Vertex] =
@@ -178,8 +181,6 @@ void ShaderInterfaceVariableInfoMap::load(gl::BinaryInputStream *stream)
outputPerVertexActiveMembers[gl::ShaderType::Geometry] =
gl::PerVertexMemberBitSet(stream->readInt<uint8_t>());
mData.swap(data);
mIdToIndexMap.swap(idToIndexMap);
mInputPerVertexActiveMembers.swap(inputPerVertexActiveMembers);
mOutputPerVertexActiveMembers.swap(outputPerVertexActiveMembers);
}
@@ -227,6 +228,23 @@ ShaderInterfaceVariableInfo &ShaderInterfaceVariableInfoMap::getMutable(gl::Shad
return mData[index];
}
XFBInterfaceVariableInfo *ShaderInterfaceVariableInfoMap::getXFBMutable(gl::ShaderType shaderType,
uint32_t id)
{
ASSERT(hasVariable(shaderType, id));
uint32_t index = getVariableIndex(shaderType, id).index;
if (index >= mXFBData.size())
{
mXFBData.resize(index + 1);
}
if (!mXFBData[index])
{
mXFBData[index] = std::make_unique<XFBInterfaceVariableInfo>();
}
mData[index].hasTransformFeedback = true;
return mXFBData[index].get();
}
ShaderInterfaceVariableInfo &ShaderInterfaceVariableInfoMap::add(gl::ShaderType shaderType,
uint32_t id)
{

View File

@@ -32,7 +32,8 @@ class ShaderInterfaceVariableInfoMap final : angle::NonCopyable
public:
// For each interface variable, a ShaderInterfaceVariableInfo is created. These are stored in a
// flat array.
using VariableInfoArray = std::vector<ShaderInterfaceVariableInfo>;
using VariableInfoArray = std::vector<ShaderInterfaceVariableInfo>;
using XFBVariableInfoArray = std::vector<XFBVariableInfoPtr>;
// Each interface variable has an associted SPIR-V id (which is different per shader type).
// The following map is from a SPIR-V id to its associated info in VariableInfoArray.
@@ -62,6 +63,7 @@ class ShaderInterfaceVariableInfoMap final : angle::NonCopyable
void setOutputPerVertexActiveMembers(gl::ShaderType shaderType,
gl::PerVertexMemberBitSet activeMembers);
ShaderInterfaceVariableInfo &getMutable(gl::ShaderType shaderType, uint32_t id);
XFBInterfaceVariableInfo *getXFBMutable(gl::ShaderType shaderType, uint32_t id);
const ShaderInterfaceVariableInfo &getDefaultUniformInfo(gl::ShaderType shaderType) const;
const ShaderInterfaceVariableInfo &getAtomicCounterInfo(gl::ShaderType shaderType) const;
@@ -78,6 +80,14 @@ class ShaderInterfaceVariableInfoMap final : angle::NonCopyable
uint32_t id) const;
const VariableInfoArray &getData() const { return mData; }
const gl::ShaderMap<IdToIndexMap> &getIdToIndexMap() const { return mIdToIndexMap; }
const XFBInterfaceVariableInfo &getXFBDataForVariableInfo(
const ShaderInterfaceVariableInfo *info) const
{
size_t index = info - mData.data();
ASSERT(index < mXFBData.size());
ASSERT(mXFBData[index]);
return *mXFBData[index];
}
const gl::ShaderMap<gl::PerVertexMemberBitSet> &getInputPerVertexActiveMembers() const
{
return mInputPerVertexActiveMembers;
@@ -95,6 +105,8 @@ class ShaderInterfaceVariableInfoMap final : angle::NonCopyable
const VariableIndex &getVariableIndex(gl::ShaderType shaderType, uint32_t id) const;
VariableInfoArray mData;
// Transform feedback array will be empty if no XFB is used.
XFBVariableInfoArray mXFBData;
gl::ShaderMap<IdToIndexMap> mIdToIndexMap;
// Active members of `in gl_PerVertex` and `out gl_PerVertex`

View File

@@ -158,29 +158,31 @@ void AddVaryingLocationInfo(ShaderInterfaceVariableInfoMap *infoMap,
}
// Modify an existing out variable and add transform feedback information.
ShaderInterfaceVariableInfo *SetXfbInfo(ShaderInterfaceVariableInfoMap *infoMap,
gl::ShaderType shaderType,
uint32_t varId,
int fieldIndex,
uint32_t xfbBuffer,
uint32_t xfbOffset,
uint32_t xfbStride,
uint32_t arraySize,
uint32_t columnCount,
uint32_t rowCount,
uint32_t arrayIndex,
GLenum componentType)
void SetXfbInfo(ShaderInterfaceVariableInfoMap *infoMap,
gl::ShaderType shaderType,
uint32_t varId,
int fieldIndex,
uint32_t xfbBuffer,
uint32_t xfbOffset,
uint32_t xfbStride,
uint32_t arraySize,
uint32_t columnCount,
uint32_t rowCount,
uint32_t arrayIndex,
GLenum componentType)
{
ShaderInterfaceVariableInfo &info = infoMap->getMutable(shaderType, varId);
ShaderInterfaceVariableXfbInfo *xfb = &info.xfb;
XFBInterfaceVariableInfo *info = infoMap->getXFBMutable(shaderType, varId);
ASSERT(info != nullptr);
ShaderInterfaceVariableXfbInfo *xfb = &info->xfb;
if (fieldIndex >= 0)
{
if (info.fieldXfb.size() <= static_cast<size_t>(fieldIndex))
if (info->fieldXfb.size() <= static_cast<size_t>(fieldIndex))
{
info.fieldXfb.resize(fieldIndex + 1);
info->fieldXfb.resize(fieldIndex + 1);
}
xfb = &info.fieldXfb[fieldIndex];
xfb = &info->fieldXfb[fieldIndex];
}
ASSERT(xfb->buffer == ShaderInterfaceVariableXfbInfo::kInvalid);
@@ -201,8 +203,6 @@ ShaderInterfaceVariableInfo *SetXfbInfo(ShaderInterfaceVariableInfoMap *infoMap,
xfb->rowCount = rowCount;
xfb->arrayIndex = arrayIndex;
xfb->componentType = componentType;
return &info;
}
void AssignTransformFeedbackEmulationBindings(gl::ShaderType shaderType,
@@ -1604,6 +1604,7 @@ class SpirvTransformFeedbackCodeGenerator final : angle::NonCopyable
{}
void visitVariable(const ShaderInterfaceVariableInfo &info,
const XFBInterfaceVariableInfo &xfbInfo,
gl::ShaderType shaderType,
spirv::IdResultType typeId,
spirv::IdResult id,
@@ -1654,15 +1655,15 @@ class SpirvTransformFeedbackCodeGenerator final : angle::NonCopyable
const bool usePrecisionFixer,
spirv::Blob *blobOut);
void addExecutionMode(spirv::IdRef entryPointId, spirv::Blob *blobOut);
void addMemberDecorate(const ShaderInterfaceVariableInfo &info,
void addMemberDecorate(const XFBInterfaceVariableInfo &info,
spirv::IdRef id,
spirv::Blob *blobOut);
void addDecorate(const ShaderInterfaceVariableInfo &info,
void addDecorate(const XFBInterfaceVariableInfo &xfbInfo,
spirv::IdRef id,
spirv::Blob *blobOut);
private:
void gatherXfbVaryings(const ShaderInterfaceVariableInfo &info, spirv::IdRef id);
void gatherXfbVaryings(const XFBInterfaceVariableInfo &info, spirv::IdRef id);
void visitXfbVarying(const ShaderInterfaceVariableXfbInfo &xfb,
spirv::IdRef baseId,
uint32_t fieldIndex);
@@ -1741,6 +1742,7 @@ constexpr size_t SpirvTransformFeedbackCodeGenerator::kXfbDecorationCount;
constexpr spv::Decoration SpirvTransformFeedbackCodeGenerator::kXfbDecorations[kXfbDecorationCount];
void SpirvTransformFeedbackCodeGenerator::visitVariable(const ShaderInterfaceVariableInfo &info,
const XFBInterfaceVariableInfo &xfbInfo,
gl::ShaderType shaderType,
spirv::IdResultType typeId,
spirv::IdResult id,
@@ -1748,13 +1750,14 @@ void SpirvTransformFeedbackCodeGenerator::visitVariable(const ShaderInterfaceVar
{
if (mIsEmulated)
{
gatherXfbVaryings(info, id);
gatherXfbVaryings(xfbInfo, id);
return;
}
// Note if the variable is captured by transform feedback. In that case, the TransformFeedback
// capability needs to be added.
if ((info.xfb.buffer != ShaderInterfaceVariableInfo::kInvalid || !info.fieldXfb.empty()) &&
if ((xfbInfo.xfb.buffer != ShaderInterfaceVariableInfo::kInvalid ||
!xfbInfo.fieldXfb.empty()) &&
info.activeStages[shaderType])
{
mHasTransformFeedbackOutput = true;
@@ -1922,7 +1925,7 @@ TransformationState SpirvTransformFeedbackCodeGenerator::transformVariable(
return TransformationState::Unchanged;
}
void SpirvTransformFeedbackCodeGenerator::gatherXfbVaryings(const ShaderInterfaceVariableInfo &info,
void SpirvTransformFeedbackCodeGenerator::gatherXfbVaryings(const XFBInterfaceVariableInfo &info,
spirv::IdRef id)
{
visitXfbVarying(info.xfb, id, ShaderInterfaceVariableXfbInfo::kInvalid);
@@ -2411,7 +2414,7 @@ void SpirvTransformFeedbackCodeGenerator::addExecutionMode(spirv::IdRef entryPoi
}
}
void SpirvTransformFeedbackCodeGenerator::addMemberDecorate(const ShaderInterfaceVariableInfo &info,
void SpirvTransformFeedbackCodeGenerator::addMemberDecorate(const XFBInterfaceVariableInfo &info,
spirv::IdRef id,
spirv::Blob *blobOut)
{
@@ -2452,7 +2455,7 @@ void SpirvTransformFeedbackCodeGenerator::addMemberDecorate(const ShaderInterfac
}
}
void SpirvTransformFeedbackCodeGenerator::addDecorate(const ShaderInterfaceVariableInfo &info,
void SpirvTransformFeedbackCodeGenerator::addDecorate(const XFBInterfaceVariableInfo &info,
spirv::IdRef id,
spirv::Blob *blobOut)
{
@@ -3416,9 +3419,12 @@ void SpirvTransformer::visitVariable(const uint32_t *instruction)
mVaryingPrecisionFixer.visitVariable(*info, mOptions.shaderType, typeId, id, storageClass,
mSpirvBlobOut);
}
if (mOptions.isTransformFeedbackStage)
if (mOptions.isTransformFeedbackStage && mVariableInfoById[id]->hasTransformFeedback)
{
mXfbCodeGenerator.visitVariable(*info, mOptions.shaderType, typeId, id, storageClass);
const XFBInterfaceVariableInfo &xfbInfo =
mVariableInfoMap.getXFBDataForVariableInfo(mVariableInfoById[id]);
mXfbCodeGenerator.visitVariable(*info, xfbInfo, mOptions.shaderType, typeId, id,
storageClass);
}
mMultisampleTransformer.visitVariable(mOptions.shaderType, typeId, id, storageClass);
@@ -3517,9 +3523,11 @@ TransformationState SpirvTransformer::transformDecorate(const uint32_t *instruct
case spv::DecorationBlock:
// If this is the Block decoration of a shader I/O block, add the transform feedback
// decorations to its members right away.
if (mOptions.isTransformFeedbackStage)
if (mOptions.isTransformFeedbackStage && mVariableInfoById[id]->hasTransformFeedback)
{
mXfbCodeGenerator.addMemberDecorate(*info, id, mSpirvBlobOut);
const XFBInterfaceVariableInfo &xfbInfo =
mVariableInfoMap.getXFBDataForVariableInfo(mVariableInfoById[id]);
mXfbCodeGenerator.addMemberDecorate(xfbInfo, id, mSpirvBlobOut);
}
break;
case spv::DecorationInvariant:
@@ -3569,9 +3577,11 @@ TransformationState SpirvTransformer::transformDecorate(const uint32_t *instruct
}
// Add Xfb decorations, if any.
if (mOptions.isTransformFeedbackStage)
if (mOptions.isTransformFeedbackStage && mVariableInfoById[id]->hasTransformFeedback)
{
mXfbCodeGenerator.addDecorate(*info, id, mSpirvBlobOut);
const XFBInterfaceVariableInfo &xfbInfo =
mVariableInfoMap.getXFBDataForVariableInfo(mVariableInfoById[id]);
mXfbCodeGenerator.addDecorate(xfbInfo, id, mSpirvBlobOut);
}
return TransformationState::Transformed;

View File

@@ -74,6 +74,7 @@ struct ShaderInterfaceVariableXfbInfo
// 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.
ANGLE_ENABLE_STRUCT_PADDING_WARNINGS
struct ShaderInterfaceVariableInfo
{
ShaderInterfaceVariableInfo();
@@ -90,25 +91,36 @@ struct ShaderInterfaceVariableInfo
uint32_t location = kInvalid;
uint32_t component = kInvalid;
uint32_t index = kInvalid;
// The stages this shader interface variable is active.
gl::ShaderBitSet activeStages;
// Used for transform feedback extension to decorate vertex shader output.
ShaderInterfaceVariableXfbInfo xfb;
std::vector<ShaderInterfaceVariableXfbInfo> fieldXfb;
// Indicates that the precision needs to be modified in the generated SPIR-V
// to support only transferring medium precision data when there's a precision
// mismatch between the shaders. For example, either the VS casts highp->mediump
// or the FS casts mediump->highp.
bool useRelaxedPrecision = false;
uint8_t useRelaxedPrecision : 1 = false;
// Indicate if varying is input or output, or both (in case of for example gl_Position in a
// geometry shader)
bool varyingIsInput = false;
bool varyingIsOutput = false;
uint8_t varyingIsInput : 1 = false;
uint8_t varyingIsOutput : 1 = false;
uint8_t hasTransformFeedback : 1 = false;
uint8_t padding : 4 = 0;
// For vertex attributes, this is the number of components / locations. These are used by the
// vertex attribute aliasing transformation only.
uint8_t attributeComponentCount = 0;
uint8_t attributeLocationCount = 0;
};
ANGLE_DISABLE_STRUCT_PADDING_WARNINGS
struct XFBInterfaceVariableInfo
{
// Used for transform feedback extension to decorate vertex shader output.
ShaderInterfaceVariableXfbInfo xfb;
std::vector<ShaderInterfaceVariableXfbInfo> fieldXfb;
};
using XFBVariableInfoPtr = std::unique_ptr<XFBInterfaceVariableInfo>;
uint32_t SpvGetXfbBufferBlockId(const uint32_t bufferIndex);