Commit 9091de43 by Shahbaz Youssefi Committed by Commit Bot

Vulkan: Support vertex attribute aliasing for non-matrix types

Initial implementation that supports float and vecN types. In this case, every attribute occupies a single location. When two attributes alias, they are either the same size, or one is bigger than the other. Take the following example: attribute vec3 a; // location 0 attribute vec3 b; // location 0 attribute vec4 c; // location 1 attribute vec2 d; // location 1 The shader may access either a or b (but not both). Similarly, it can access either c or d (but not both). The shader can be modified such that: - f(b) is replaced with f(a). - g(d) is replaced with g(c.xy). - b and d are removed As a result, there are no longer any aliasing attributes. In other words, when attributes alias, the larger attribute can be retained and the other attributes can be replaced by selecting the appropriate number of components from the retained attribute. Bug: angleproject:4249 Change-Id: I7c5461f777d659c92977e2572091a8ce5e422704 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2482286 Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: 's avatarCourtney Goeltzenleuchter <courtneygo@google.com> Reviewed-by: 's avatarCharlie Lao <cclao@google.com>
parent 26cd1cc6
...@@ -235,7 +235,8 @@ ShaderInterfaceVariableInfo *AddLocationInfo(ShaderInterfaceVariableInfoMap *inf ...@@ -235,7 +235,8 @@ ShaderInterfaceVariableInfo *AddLocationInfo(ShaderInterfaceVariableInfoMap *inf
const std::string &varName, const std::string &varName,
uint32_t location, uint32_t location,
uint32_t component, uint32_t component,
gl::ShaderType stage) gl::ShaderType stage,
uint8_t attributeComponentCount)
{ {
// 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.
...@@ -249,6 +250,7 @@ ShaderInterfaceVariableInfo *AddLocationInfo(ShaderInterfaceVariableInfoMap *inf ...@@ -249,6 +250,7 @@ ShaderInterfaceVariableInfo *AddLocationInfo(ShaderInterfaceVariableInfoMap *inf
info->location = location; info->location = location;
info->component = component; info->component = component;
info->activeStages.set(stage); info->activeStages.set(stage);
info->attributeComponentCount = attributeComponentCount;
return info; return info;
} }
...@@ -493,7 +495,13 @@ void AssignAttributeLocations(const gl::ProgramExecutable &programExecutable, ...@@ -493,7 +495,13 @@ void AssignAttributeLocations(const gl::ProgramExecutable &programExecutable,
ASSERT(attribute.active); ASSERT(attribute.active);
AddLocationInfo(variableInfoMapOut, attribute.mappedName, attribute.location, AddLocationInfo(variableInfoMapOut, attribute.mappedName, attribute.location,
ShaderInterfaceVariableInfo::kInvalid, stage); ShaderInterfaceVariableInfo::kInvalid, stage,
static_cast<uint8_t>(gl::VariableColumnCount(attribute.type)));
// Temporarily mark matrix attributes as such. TODO: remove when support for aliasing
// matrix attributes is added. http://anglebug.com/4249
(*variableInfoMapOut)[attribute.mappedName].isMatrixAttribute =
gl::VariableRowCount(attribute.type) > 1;
} }
} }
...@@ -530,7 +538,7 @@ void AssignOutputLocations(const gl::ProgramExecutable &programExecutable, ...@@ -530,7 +538,7 @@ void AssignOutputLocations(const gl::ProgramExecutable &programExecutable,
} }
AddLocationInfo(variableInfoMapOut, outputVar.mappedName, location, AddLocationInfo(variableInfoMapOut, outputVar.mappedName, location,
ShaderInterfaceVariableInfo::kInvalid, shaderType); ShaderInterfaceVariableInfo::kInvalid, shaderType, 0);
} }
} }
...@@ -539,8 +547,8 @@ void AssignOutputLocations(const gl::ProgramExecutable &programExecutable, ...@@ -539,8 +547,8 @@ void AssignOutputLocations(const gl::ProgramExecutable &programExecutable,
// location 0 to these entries, adding an entry for them here allows us to ASSERT that every // 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 // shader interface variable is processed during the SPIR-V transformation. This is done when
// iterating the ids provided by OpEntryPoint. // iterating the ids provided by OpEntryPoint.
AddLocationInfo(variableInfoMapOut, "webgl_FragColor", 0, 0, shaderType); AddLocationInfo(variableInfoMapOut, "webgl_FragColor", 0, 0, shaderType, 0);
AddLocationInfo(variableInfoMapOut, "webgl_FragData", 0, 0, shaderType); AddLocationInfo(variableInfoMapOut, "webgl_FragData", 0, 0, shaderType, 0);
} }
void AssignVaryingLocations(const GlslangSourceOptions &options, void AssignVaryingLocations(const GlslangSourceOptions &options,
...@@ -559,7 +567,7 @@ void AssignVaryingLocations(const GlslangSourceOptions &options, ...@@ -559,7 +567,7 @@ void AssignVaryingLocations(const GlslangSourceOptions &options,
AddLocationInfo(&(*variableInfoMapOut)[shaderType], sh::vk::kLineRasterEmulationPosition, AddLocationInfo(&(*variableInfoMapOut)[shaderType], sh::vk::kLineRasterEmulationPosition,
lineRasterEmulationPositionLocation, ShaderInterfaceVariableInfo::kInvalid, lineRasterEmulationPositionLocation, ShaderInterfaceVariableInfo::kInvalid,
shaderType); shaderType, 0);
} }
// Assign varying locations. // Assign varying locations.
...@@ -595,7 +603,7 @@ void AssignVaryingLocations(const GlslangSourceOptions &options, ...@@ -595,7 +603,7 @@ void AssignVaryingLocations(const GlslangSourceOptions &options,
? varying.frontVarying.parentStructMappedName ? varying.frontVarying.parentStructMappedName
: varying.frontVarying.varying->mappedName; : varying.frontVarying.varying->mappedName;
AddLocationInfo(&(*variableInfoMapOut)[varying.frontVarying.stage], name, location, AddLocationInfo(&(*variableInfoMapOut)[varying.frontVarying.stage], name, location,
component, varying.frontVarying.stage); component, varying.frontVarying.stage, 0);
} }
if (varying.backVarying.varying && (varying.backVarying.stage == shaderType)) if (varying.backVarying.varying && (varying.backVarying.stage == shaderType))
{ {
...@@ -603,7 +611,7 @@ void AssignVaryingLocations(const GlslangSourceOptions &options, ...@@ -603,7 +611,7 @@ void AssignVaryingLocations(const GlslangSourceOptions &options,
? varying.backVarying.parentStructMappedName ? varying.backVarying.parentStructMappedName
: varying.backVarying.varying->mappedName; : varying.backVarying.varying->mappedName;
AddLocationInfo(&(*variableInfoMapOut)[varying.backVarying.stage], name, location, AddLocationInfo(&(*variableInfoMapOut)[varying.backVarying.stage], name, location,
component, varying.backVarying.stage); component, varying.backVarying.stage, 0);
} }
} }
...@@ -682,7 +690,7 @@ void AssignTransformFeedbackExtensionQualifiers(const gl::ProgramExecutable &pro ...@@ -682,7 +690,7 @@ void AssignTransformFeedbackExtensionQualifiers(const gl::ProgramExecutable &pro
ASSERT(xfbVaryingLocation < locationsUsedForXfbExtension); ASSERT(xfbVaryingLocation < locationsUsedForXfbExtension);
AddLocationInfo(variableInfoMapOut, xfbVaryingName, xfbVaryingLocation, AddLocationInfo(variableInfoMapOut, xfbVaryingName, xfbVaryingLocation,
ShaderInterfaceVariableInfo::kInvalid, shaderType); ShaderInterfaceVariableInfo::kInvalid, shaderType, 0);
SetXfbInfo(variableInfoMapOut, xfbVaryingName, bufferIndex, currentOffset, SetXfbInfo(variableInfoMapOut, xfbVaryingName, bufferIndex, currentOffset,
currentStride); currentStride);
} }
...@@ -1028,6 +1036,11 @@ class SpirvTransformerBase : angle::NonCopyable ...@@ -1028,6 +1036,11 @@ class SpirvTransformerBase : angle::NonCopyable
mBuiltinVariableInfo.activeStages = allStages; mBuiltinVariableInfo.activeStages = allStages;
} }
std::vector<const ShaderInterfaceVariableInfo *> &getVariableInfoByIdMap()
{
return mVariableInfoById;
}
protected: protected:
// SPIR-V 1.0 Table 1: First Words of Physical Layout // SPIR-V 1.0 Table 1: First Words of Physical Layout
enum HeaderIndex enum HeaderIndex
...@@ -1216,7 +1229,7 @@ bool SpirvTransformer::transform() ...@@ -1216,7 +1229,7 @@ bool SpirvTransformer::transform()
void SpirvTransformer::resolveVariableIds() void SpirvTransformer::resolveVariableIds()
{ {
size_t indexBound = mSpirvBlobIn[kHeaderIndexIndexBound]; const size_t indexBound = mSpirvBlobIn[kHeaderIndexIndexBound];
// Allocate storage for id-to-name map. Used to associate ShaderInterfaceVariableInfo with ids // Allocate storage for id-to-name map. Used to associate ShaderInterfaceVariableInfo with ids
// based on name, but only when it's determined that the name corresponds to a shader interface // based on name, but only when it's determined that the name corresponds to a shader interface
...@@ -1963,8 +1976,7 @@ bool SpirvTransformer::transformAccessChain(const uint32_t *instruction, size_t ...@@ -1963,8 +1976,7 @@ bool SpirvTransformer::transformAccessChain(const uint32_t *instruction, size_t
bool SpirvTransformer::transformExecutionMode(const uint32_t *instruction, size_t wordCount) bool SpirvTransformer::transformExecutionMode(const uint32_t *instruction, size_t wordCount)
{ {
// SPIR-V 1.0 Section 3.32 Instructions, OpAccessChain, OpInBoundsAccessChain, OpPtrAccessChain, // SPIR-V 1.0 Section 3.32 Instructions, OpExecutionMode
// OpInBoundsPtrAccessChain
constexpr size_t kModeIndex = 2; constexpr size_t kModeIndex = 2;
const uint32_t executionMode = instruction[kModeIndex]; const uint32_t executionMode = instruction[kModeIndex];
...@@ -1979,14 +1991,13 @@ bool SpirvTransformer::transformExecutionMode(const uint32_t *instruction, size_ ...@@ -1979,14 +1991,13 @@ bool SpirvTransformer::transformExecutionMode(const uint32_t *instruction, size_
void SpirvTransformer::writeOpStore(uint32_t tempVarId, uint32_t destId) void SpirvTransformer::writeOpStore(uint32_t tempVarId, uint32_t destId)
{ {
constexpr size_t kInstIndex = 0;
constexpr size_t kIdIndex = 1; constexpr size_t kIdIndex = 1;
constexpr size_t kObjectIndex = 2; constexpr size_t kObjectIndex = 2;
constexpr size_t kOpStoreInstructionLength = 3; constexpr size_t kOpStoreInstructionLength = 3;
uint32_t opStore[kOpStoreInstructionLength]; uint32_t opStore[kOpStoreInstructionLength];
SetSpirvInstructionLength(&opStore[kInstIndex], kOpStoreInstructionLength); SetSpirvInstructionOp(opStore, spv::OpStore);
SetSpirvInstructionOp(&opStore[kInstIndex], spv::OpStore); SetSpirvInstructionLength(opStore, kOpStoreInstructionLength);
opStore[kIdIndex] = destId; opStore[kIdIndex] = destId;
opStore[kObjectIndex] = tempVarId; opStore[kObjectIndex] = tempVarId;
copyInstruction(opStore, kOpStoreInstructionLength); copyInstruction(opStore, kOpStoreInstructionLength);
...@@ -2027,6 +2038,615 @@ void SpirvTransformer::writeOpVariable(uint32_t id, uint32_t typeId, uint32_t st ...@@ -2027,6 +2038,615 @@ void SpirvTransformer::writeOpVariable(uint32_t id, uint32_t typeId, uint32_t st
opVariable[kStorageClassIdIndex] = storageClassId; opVariable[kStorageClassIdIndex] = storageClassId;
copyInstruction(opVariable, kOpVariableInstructionLength); copyInstruction(opVariable, kOpVariableInstructionLength);
} }
struct AliasingAttributeMap
{
// The SPIR-V id of the aliasing attribute with the most components. This attribute will be
// used to read from this location instead of every aliasing one.
uint32_t attribute = 0;
// SPIR-V ids of aliasing attributes.
std::vector<uint32_t> aliasingAttributes;
};
void ValidateShaderInterfaceVariableIsAttribute(const ShaderInterfaceVariableInfo *info)
{
ASSERT(info);
ASSERT(info->activeStages[gl::ShaderType::Vertex]);
ASSERT(info->attributeComponentCount > 0);
ASSERT(info->location != ShaderInterfaceVariableInfo::kInvalid);
}
void ValidateIsAliasingAttribute(const AliasingAttributeMap *aliasingMap, uint32_t id)
{
ASSERT(id != aliasingMap->attribute);
ASSERT(std::find(aliasingMap->aliasingAttributes.begin(), aliasingMap->aliasingAttributes.end(),
id) != aliasingMap->aliasingAttributes.end());
}
// A transformation that resolves vertex attribute aliases. Note that vertex attribute aliasing is
// only allowed in GLSL ES 100, where the attribute types can only be one of float, vec2, vec3,
// vec4, mat2, mat3, and mat4. Currently, matrix attributes are not handled.
// TODO: aliasing attributes of matrix type. http://anglebug.com/4249
class SpirvVertexAttributeAliasingTransformer final : public SpirvTransformerBase
{
public:
SpirvVertexAttributeAliasingTransformer(
const std::vector<uint32_t> &spirvBlobIn,
const ShaderInterfaceVariableInfoMap &variableInfoMap,
std::vector<const ShaderInterfaceVariableInfo *> &&variableInfoById,
gl::AttribArray<AliasingAttributeMap> &&aliasingAttributeMap,
SpirvBlob *spirvBlobOut)
: SpirvTransformerBase(spirvBlobIn, variableInfoMap, gl::ShaderType::Vertex, spirvBlobOut)
{
mVariableInfoById = std::move(variableInfoById);
mAliasingAttributeMap = std::move(aliasingAttributeMap);
}
bool transform();
private:
// Preprocess aliasing attributes in preparation for their removal.
void preprocessAliasingAttributes();
// Transform instructions:
void transformInstruction();
// Helpers:
uint32_t getAliasingAttributeReplacementId(uint32_t aliasingId) const;
// Instructions that are purely informational:
void visitTypeFloat(const uint32_t *instruction);
void visitTypeVector(const uint32_t *instruction);
// Instructions that potentially need transformation. They return true if the instruction is
// transformed. If false is returned, the instruction should be copied as-is.
bool transformEntryPoint(const uint32_t *instruction, size_t wordCount);
bool transformName(const uint32_t *instruction, size_t wordCount);
bool transformDecorate(const uint32_t *instruction, size_t wordCount);
bool transformVariable(const uint32_t *instruction, size_t wordCount);
bool transformAccessChain(const uint32_t *instruction, size_t wordCount);
bool transformLoad(const uint32_t *instruction, size_t wordCount);
// Generated instructions:
void writeCopyObject(uint32_t id, uint32_t typeId, uint32_t operandId);
void writeCompositeExtract(uint32_t id, uint32_t typeId, uint32_t compositeId, uint32_t field);
void writeVectorShuffle(uint32_t id,
uint32_t typeId,
uint32_t vec1Id,
uint32_t vec2Id,
const angle::FixedVector<uint32_t, 4> &fields);
// Transformation state:
// Map of aliasing attributes per location.
gl::AttribArray<AliasingAttributeMap> mAliasingAttributeMap;
// For each id, this map indicates whether it refers to an aliasing attribute that needs to be
// removed.
std::vector<bool> mIsAliasingAttributeById;
// Id of attribute types; float and veci. This array is one-based, and [0] is unused.
//
// [1]: id of OpTypeFloat 32
// [N]: id of OpTypeVector %[1] N, N = {2, 3, 4}
//
// In other words, index of the array corresponds to the number of components in the type.
uint32_t mFloatTypes[5] = {};
};
bool SpirvVertexAttributeAliasingTransformer::transform()
{
preprocessAliasingAttributes();
onTransformBegin();
while (mCurrentWord < mSpirvBlobIn.size())
{
transformInstruction();
}
return true;
}
void SpirvVertexAttributeAliasingTransformer::preprocessAliasingAttributes()
{
const size_t indexBound = mSpirvBlobIn[kHeaderIndexIndexBound];
mIsAliasingAttributeById.resize(indexBound + 1, false);
for (const AliasingAttributeMap &aliasingMap : mAliasingAttributeMap)
{
for (uint32_t aliasingId : aliasingMap.aliasingAttributes)
{
ASSERT(mIsAliasingAttributeById[aliasingId] == false);
mIsAliasingAttributeById[aliasingId] = true;
}
}
}
void SpirvVertexAttributeAliasingTransformer::transformInstruction()
{
uint32_t wordCount;
uint32_t opCode;
const uint32_t *instruction = getCurrentInstruction(&opCode, &wordCount);
// Only look at interesting instructions.
bool transformed = false;
switch (opCode)
{
// Informational instructions:
case spv::OpTypeFloat:
visitTypeFloat(instruction);
break;
case spv::OpTypeVector:
visitTypeVector(instruction);
break;
// Instructions that may need transformation:
case spv::OpEntryPoint:
transformed = transformEntryPoint(instruction, wordCount);
break;
case spv::OpName:
transformed = transformName(instruction, wordCount);
break;
case spv::OpDecorate:
transformed = transformDecorate(instruction, wordCount);
break;
case spv::OpVariable:
transformed = transformVariable(instruction, wordCount);
break;
case spv::OpAccessChain:
case spv::OpInBoundsAccessChain:
transformed = transformAccessChain(instruction, wordCount);
break;
case spv::OpLoad:
transformed = transformLoad(instruction, wordCount);
break;
default:
break;
}
// If the instruction was not transformed, copy it to output as is.
if (!transformed)
{
copyInstruction(instruction, wordCount);
}
// Advance to next instruction.
mCurrentWord += wordCount;
}
uint32_t SpirvVertexAttributeAliasingTransformer::getAliasingAttributeReplacementId(
uint32_t aliasingId) const
{
// Get variable info corresponding to the aliasing attribute.
const ShaderInterfaceVariableInfo *aliasingInfo = mVariableInfoById[aliasingId];
ValidateShaderInterfaceVariableIsAttribute(aliasingInfo);
// Find the replacement attribute.
const AliasingAttributeMap *aliasingMap = &mAliasingAttributeMap[aliasingInfo->location];
ValidateIsAliasingAttribute(aliasingMap, aliasingId);
const uint32_t replacementId = aliasingMap->attribute;
ASSERT(replacementId != 0 && replacementId < mIsAliasingAttributeById.size());
ASSERT(!mIsAliasingAttributeById[replacementId]);
return replacementId;
}
void SpirvVertexAttributeAliasingTransformer::visitTypeFloat(const uint32_t *instruction)
{
// SPIR-V 1.0 Section 3.32 Instructions, OpTypeFloat
constexpr size_t kIdIndex = 1;
constexpr size_t kWidthIndex = 2;
const uint32_t id = instruction[kIdIndex];
const uint32_t width = instruction[kWidthIndex];
// Only interested in OpTypeFloat 32.
if (width == 32)
{
ASSERT(mFloatTypes[1] == 0);
mFloatTypes[1] = id;
}
}
void SpirvVertexAttributeAliasingTransformer::visitTypeVector(const uint32_t *instruction)
{
// SPIR-V 1.0 Section 3.32 Instructions, OpTypeVector
constexpr size_t kIdIndex = 1;
constexpr size_t kComponentIdIndex = 2;
constexpr size_t kComponentCountIndex = 3;
const uint32_t id = instruction[kIdIndex];
const uint32_t componentId = instruction[kComponentIdIndex];
const uint32_t componentCount = instruction[kComponentCountIndex];
// Only interested in OpTypeVector %f32 N, where %f32 is the id of OpTypeFloat 32.
if (componentId == mFloatTypes[1])
{
ASSERT(componentCount >= 2 && componentCount <= 4);
ASSERT(mFloatTypes[componentCount] == 0);
mFloatTypes[componentCount] = id;
}
}
bool SpirvVertexAttributeAliasingTransformer::transformEntryPoint(const uint32_t *instruction,
size_t wordCount)
{
// Remove aliasing attributes from the shader interface declaration.
// SPIR-V 1.0 Section 3.32 Instructions, OpEntryPoint
constexpr size_t kNameIndex = 3;
// See comment in SpirvTransformer::transformEntryPoint.
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 aliasing attributes from entry point interface declaration.
// 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];
// If this is an attribute that's aliasing another one in the same location, remove it.
if (mIsAliasingAttributeById[id])
{
const ShaderInterfaceVariableInfo *info = mVariableInfoById[id];
ValidateShaderInterfaceVariableIsAttribute(info);
const AliasingAttributeMap *aliasingMap = &mAliasingAttributeMap[info->location];
ValidateIsAliasingAttribute(aliasingMap, id);
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);
return true;
}
bool SpirvVertexAttributeAliasingTransformer::transformName(const uint32_t *instruction,
size_t wordCount)
{
// SPIR-V 1.0 Section 3.32 Instructions, OpName
constexpr size_t kIdIndex = 1;
uint32_t id = instruction[kIdIndex];
// If id is not that of an aliasing attribute, there's nothing to do.
ASSERT(id < mIsAliasingAttributeById.size());
if (!mIsAliasingAttributeById[id])
{
return false;
}
// Drop debug annotations for this id.
return true;
}
bool SpirvVertexAttributeAliasingTransformer::transformDecorate(const uint32_t *instruction,
size_t wordCount)
{
// SPIR-V 1.0 Section 3.32 Instructions, OpDecorate
constexpr size_t kIdIndex = 1;
uint32_t id = instruction[kIdIndex];
// If id is not that of an aliasing attribute, there's nothing to do.
ASSERT(id < mIsAliasingAttributeById.size());
if (!mIsAliasingAttributeById[id])
{
return false;
}
// Drop every decoration for this id.
return true;
}
bool SpirvVertexAttributeAliasingTransformer::transformVariable(const uint32_t *instruction,
size_t wordCount)
{
// SPIR-V 1.0 Section 3.32 Instructions, OpVariable
constexpr size_t kIdIndex = 2;
constexpr size_t kStorageClassIndex = 3;
const uint32_t id = instruction[kIdIndex];
const uint32_t storageClass = instruction[kStorageClassIndex];
// If id is not that of an aliasing attribute, there's nothing to do.
ASSERT(id < mIsAliasingAttributeById.size());
if (!mIsAliasingAttributeById[id])
{
return false;
}
ASSERT(storageClass == spv::StorageClassInput);
// Drop the declaration.
return true;
}
bool SpirvVertexAttributeAliasingTransformer::transformAccessChain(const uint32_t *instruction,
size_t wordCount)
{
// SPIR-V 1.0 Section 3.32 Instructions, OpAccessChain, OpInBoundsAccessChain
constexpr size_t kBaseIdIndex = 3;
const uint32_t baseId = instruction[kBaseIdIndex];
// If base id is not that of an aliasing attribute, there's nothing to do.
ASSERT(baseId < mIsAliasingAttributeById.size());
if (!mIsAliasingAttributeById[baseId])
{
return false;
}
// Find the replacement attribute for the aliasing one.
const uint32_t replacementId = getAliasingAttributeReplacementId(baseId);
// Get variable info corresponding to the replacement attribute.
const ShaderInterfaceVariableInfo *replacementInfo = mVariableInfoById[replacementId];
ValidateShaderInterfaceVariableIsAttribute(replacementInfo);
// Copy the OpAccessChain instruction for modification. Currently, the instruction is:
//
// %id = OpAccessChain %type %base %index
//
// This is modified to:
//
// %id = OpAccessChain %type %replacement %index
//
// Note that the replacement has at least as many components as the aliasing attribute,
// and both attributes start at component 0 (GLSL ES restriction). So, indexing the replacement
// attribute with the same index yields the same result and type.
const size_t instOffset = copyInstruction(instruction, wordCount);
(*mSpirvBlobOut)[instOffset + kBaseIdIndex] = replacementId;
return true;
}
bool SpirvVertexAttributeAliasingTransformer::transformLoad(const uint32_t *instruction,
size_t wordCount)
{
// SPIR-V 1.0 Section 3.32 Instructions, OpLoad
constexpr size_t kTypeIdIndex = 1;
constexpr size_t kIdIndex = 2;
constexpr size_t kPointerIdIndex = 3;
const uint32_t id = instruction[kIdIndex];
const uint32_t typeId = instruction[kTypeIdIndex];
const uint32_t pointerId = instruction[kPointerIdIndex];
// If pointer id is not that of an aliasing attribute, there's nothing to do.
ASSERT(pointerId < mIsAliasingAttributeById.size());
if (!mIsAliasingAttributeById[pointerId])
{
return false;
}
// Find the replacement attribute for the aliasing one.
const uint32_t replacementId = getAliasingAttributeReplacementId(pointerId);
// Get variable info corresponding to the replacement attribute.
const ShaderInterfaceVariableInfo *replacementInfo = mVariableInfoById[replacementId];
ValidateShaderInterfaceVariableIsAttribute(replacementInfo);
// Replace the load instruction by a load from the replacement id, followed by a swizzle if
// necessary.
// Copy the load instruction for modification. Currently, the instruction is:
//
// %id = OpLoad %type %pointer
//
// This is modified to:
//
// %newId = OpLoad %replacementType %replacement
//
const uint32_t loadResultId = getNewId();
const uint32_t replacementTypeId = mFloatTypes[replacementInfo->attributeComponentCount];
ASSERT(replacementTypeId != 0);
const size_t instOffset = copyInstruction(instruction, wordCount);
(*mSpirvBlobOut)[instOffset + kIdIndex] = loadResultId;
(*mSpirvBlobOut)[instOffset + kTypeIdIndex] = replacementTypeId;
(*mSpirvBlobOut)[instOffset + kPointerIdIndex] = replacementId;
// If swizzle is not necessary, assign %newId to %id.
const ShaderInterfaceVariableInfo *aliasingInfo = mVariableInfoById[pointerId];
if (aliasingInfo->attributeComponentCount == replacementInfo->attributeComponentCount)
{
writeCopyObject(id, typeId, loadResultId);
return true;
}
// Take as many components from the replacement as the aliasing attribute wanted. This is done
// by either of the following instructions:
//
// - If aliasing attribute has only one component:
//
// %id = OpCompositeExtract %floatType %newId 0
//
// - If aliasing attribute has more than one component:
//
// %id = OpVectorShuffle %vecType %newId %newId 0 1 ...
//
ASSERT(aliasingInfo->attributeComponentCount < replacementInfo->attributeComponentCount);
ASSERT(mFloatTypes[aliasingInfo->attributeComponentCount] == typeId);
if (aliasingInfo->attributeComponentCount == 1)
{
writeCompositeExtract(id, typeId, loadResultId, 0);
}
else
{
angle::FixedVector<uint32_t, 4> swizzle = {0, 1, 2, 3};
swizzle.resize(aliasingInfo->attributeComponentCount);
writeVectorShuffle(id, typeId, loadResultId, loadResultId, swizzle);
}
return true;
}
void SpirvVertexAttributeAliasingTransformer::writeCopyObject(uint32_t id,
uint32_t typeId,
uint32_t operandId)
{
// SPIR-V 1.0 Section 3.32 Instructions, OpCopyObject
constexpr size_t kTypeIdIndex = 1;
constexpr size_t kIdIndex = 2;
constexpr size_t kOperandIdIndex = 3;
constexpr size_t kCopyObjectInstructionLength = 4;
std::array<uint32_t, kCopyObjectInstructionLength> copyObject = {};
// Fill the fields.
SetSpirvInstructionOp(copyObject.data(), spv::OpCopyObject);
SetSpirvInstructionLength(copyObject.data(), kCopyObjectInstructionLength);
copyObject[kTypeIdIndex] = typeId;
copyObject[kIdIndex] = id;
copyObject[kOperandIdIndex] = operandId;
copyInstruction(copyObject.data(), kCopyObjectInstructionLength);
}
void SpirvVertexAttributeAliasingTransformer::writeCompositeExtract(uint32_t id,
uint32_t typeId,
uint32_t compositeId,
uint32_t field)
{
// SPIR-V 1.0 Section 3.32 Instructions, OpCompositeExtract
constexpr size_t kTypeIdIndex = 1;
constexpr size_t kIdIndex = 2;
constexpr size_t kCompositeIdIndex = 3;
constexpr size_t kFieldIndex = 4;
constexpr size_t kCompositeExtractInstructionLength = 5;
std::array<uint32_t, kCompositeExtractInstructionLength> compositeExtract = {};
// Fill the fields.
SetSpirvInstructionOp(compositeExtract.data(), spv::OpCompositeExtract);
SetSpirvInstructionLength(compositeExtract.data(), kCompositeExtractInstructionLength);
compositeExtract[kTypeIdIndex] = typeId;
compositeExtract[kIdIndex] = id;
compositeExtract[kCompositeIdIndex] = compositeId;
compositeExtract[kFieldIndex] = field;
copyInstruction(compositeExtract.data(), kCompositeExtractInstructionLength);
}
void SpirvVertexAttributeAliasingTransformer::writeVectorShuffle(
uint32_t id,
uint32_t typeId,
uint32_t vec1Id,
uint32_t vec2Id,
const angle::FixedVector<uint32_t, 4> &fields)
{
// SPIR-V 1.0 Section 3.32 Instructions, OpVectorShuffle
constexpr size_t kTypeIdIndex = 1;
constexpr size_t kIdIndex = 2;
constexpr size_t kVec1IdIndex = 3;
constexpr size_t kVec2IdIndex = 4;
constexpr size_t kFieldsIndexStart = 5;
constexpr size_t kFieldsMaxCount = 4;
constexpr size_t kVectorShuffleInstructionBaseLength = 5;
ASSERT(kFieldsMaxCount == fields.max_size());
std::array<uint32_t, kVectorShuffleInstructionBaseLength + kFieldsMaxCount> vectorShuffle = {};
// Fill the fields.
SetSpirvInstructionOp(vectorShuffle.data(), spv::OpVectorShuffle);
SetSpirvInstructionLength(vectorShuffle.data(),
kVectorShuffleInstructionBaseLength + fields.size());
vectorShuffle[kTypeIdIndex] = typeId;
vectorShuffle[kIdIndex] = id;
vectorShuffle[kVec1IdIndex] = vec1Id;
vectorShuffle[kVec2IdIndex] = vec2Id;
for (size_t fieldIndex = 0; fieldIndex < fields.size(); ++fieldIndex)
{
vectorShuffle[kFieldsIndexStart + fieldIndex] = fields[fieldIndex];
}
copyInstruction(vectorShuffle.data(), kVectorShuffleInstructionBaseLength + fields.size());
}
bool CalculateAliasingAttributes(
const std::vector<const ShaderInterfaceVariableInfo *> &variableInfoById,
gl::AttribArray<AliasingAttributeMap> *aliasingAttributeMapOut)
{
bool anyAliasing = false;
for (size_t id = 0; id < variableInfoById.size(); ++id)
{
const ShaderInterfaceVariableInfo *info = variableInfoById[id];
// Ignore non attribute or inactive ids.
if (info == nullptr || info->attributeComponentCount == 0 ||
!info->activeStages[gl::ShaderType::Vertex])
{
continue;
}
// Ignore matrix attributes for now.
// TODO: aliasing matrix attributes. http://anglebug.com/4249
if (info->isMatrixAttribute)
{
continue;
}
ASSERT(info->location != ShaderInterfaceVariableInfo::kInvalid);
ASSERT(info->location < aliasingAttributeMapOut->size());
AliasingAttributeMap *aliasingMap = &(*aliasingAttributeMapOut)[info->location];
// If this is the first attribute in this location, remember it.
if (aliasingMap->attribute == 0)
{
aliasingMap->attribute = id;
continue;
}
anyAliasing = true;
// Otherwise, either add it to the list of aliasing attributes, or replace the main
// attribute (and add that to the list of aliasing attributes). The one with the largest
// number of component is used as the main attribute.
const ShaderInterfaceVariableInfo *curMainAttribute =
variableInfoById[aliasingMap->attribute];
ASSERT(curMainAttribute != nullptr && curMainAttribute->attributeComponentCount > 0 &&
!curMainAttribute->isMatrixAttribute);
if (info->attributeComponentCount > curMainAttribute->attributeComponentCount)
{
aliasingMap->aliasingAttributes.push_back(aliasingMap->attribute);
aliasingMap->attribute = id;
}
else
{
aliasingMap->aliasingAttributes.push_back(id);
}
}
return anyAliasing;
}
} // anonymous namespace } // anonymous namespace
const uint32_t ShaderInterfaceVariableInfo::kInvalid; const uint32_t ShaderInterfaceVariableInfo::kInvalid;
...@@ -2247,6 +2867,24 @@ angle::Result GlslangTransformSpirvCode(const GlslangErrorCallback &callback, ...@@ -2247,6 +2867,24 @@ angle::Result GlslangTransformSpirvCode(const GlslangErrorCallback &callback,
removeDebugInfo, variableInfoMap, shaderType, spirvBlobOut); removeDebugInfo, variableInfoMap, shaderType, spirvBlobOut);
ANGLE_GLSLANG_CHECK(callback, transformer.transform(), GlslangError::InvalidSpirv); ANGLE_GLSLANG_CHECK(callback, transformer.transform(), GlslangError::InvalidSpirv);
if (shaderType == gl::ShaderType::Vertex)
{
gl::AttribArray<AliasingAttributeMap> aliasingAttributeMap;
std::vector<const ShaderInterfaceVariableInfo *> &variableInfoById =
transformer.getVariableInfoByIdMap();
// If there are aliasing vertex attributes, transform the SPIR-V again to remove them.
if (CalculateAliasingAttributes(variableInfoById, &aliasingAttributeMap))
{
SpirvBlob preTransformBlob = std::move(*spirvBlobOut);
SpirvVertexAttributeAliasingTransformer aliasingTransformer(
preTransformBlob, variableInfoMap, std::move(variableInfoById),
std::move(aliasingAttributeMap), spirvBlobOut);
ANGLE_GLSLANG_CHECK(callback, aliasingTransformer.transform(),
GlslangError::InvalidSpirv);
}
}
ASSERT(ValidateSpirv(*spirvBlobOut)); ASSERT(ValidateSpirv(*spirvBlobOut));
return angle::Result::Continue; return angle::Result::Continue;
......
...@@ -88,6 +88,13 @@ struct ShaderInterfaceVariableInfo ...@@ -88,6 +88,13 @@ struct ShaderInterfaceVariableInfo
bool useRelaxedPrecision = false; bool useRelaxedPrecision = false;
// Indicate if varying is input or output // Indicate if varying is input or output
bool varyingIsOutput = false; bool varyingIsOutput = false;
// For vertex attributes, this is the number of components. This is used by the vertex
// attribute aliasing transformation only.
uint8_t attributeComponentCount = 0;
// Indicate whether this is a vertex attribute of matrix type. This is temporarily used to
// avoid handling aliasing matrix vertex attributes as they are currently not supported.
// TODO: remove when support for aliasing matrix attributes is added. http://anglebug.com/4249
bool isMatrixAttribute = false;
}; };
// TODO: http://anglebug.com/4524: Need a different hash key than a string, since // TODO: http://anglebug.com/4524: Need a different hash key than a string, since
......
...@@ -219,12 +219,14 @@ std::unique_ptr<rx::LinkEvent> ProgramExecutableVk::load(gl::BinaryInputStream * ...@@ -219,12 +219,14 @@ std::unique_ptr<rx::LinkEvent> ProgramExecutableVk::load(gl::BinaryInputStream *
info->location = stream->readInt<uint32_t>(); info->location = stream->readInt<uint32_t>();
info->component = stream->readInt<uint32_t>(); info->component = stream->readInt<uint32_t>();
// PackedEnumBitSet uses uint8_t // PackedEnumBitSet uses uint8_t
info->activeStages = gl::ShaderBitSet(stream->readInt<uint8_t>()); info->activeStages = gl::ShaderBitSet(stream->readInt<uint8_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>();
info->useRelaxedPrecision = stream->readBool(); info->useRelaxedPrecision = stream->readBool();
info->varyingIsOutput = stream->readBool(); info->varyingIsOutput = stream->readBool();
info->attributeComponentCount = stream->readInt<uint8_t>();
info->isMatrixAttribute = stream->readBool();
} }
} }
...@@ -250,6 +252,8 @@ void ProgramExecutableVk::save(gl::BinaryOutputStream *stream) ...@@ -250,6 +252,8 @@ void ProgramExecutableVk::save(gl::BinaryOutputStream *stream)
stream->writeInt<uint32_t>(it.second.xfbStride); stream->writeInt<uint32_t>(it.second.xfbStride);
stream->writeInt<uint8_t>(it.second.useRelaxedPrecision); stream->writeInt<uint8_t>(it.second.useRelaxedPrecision);
stream->writeInt<uint8_t>(it.second.varyingIsOutput); stream->writeInt<uint8_t>(it.second.varyingIsOutput);
stream->writeInt<uint8_t>(it.second.attributeComponentCount);
stream->writeInt<uint8_t>(it.second.isMatrixAttribute);
} }
} }
} }
......
...@@ -428,7 +428,7 @@ ...@@ -428,7 +428,7 @@
3748 VULKAN WIN NVIDIA : dEQP-GLES2.functional.fbo.render.repeated_clear.* = FAIL 3748 VULKAN WIN NVIDIA : dEQP-GLES2.functional.fbo.render.repeated_clear.* = FAIL
// Vertex attribute aliasing generates invalid SPIRV // Vertex attribute aliasing generates invalid SPIRV
4249 VULKAN : dEQP-GLES2.functional.attribute_location.bind* = SKIP 4249 VULKAN : dEQP-GLES2.functional.attribute_location.bind_aliasing.*mat* = SKIP
// Fails on Metal, some of filtering tests fail when MSAA is off and pass when MSAA is on. Some // Fails on Metal, some of filtering tests fail when MSAA is off and pass when MSAA is on. Some
// tests are opposite. The filtering tests mostly fail on a few pixels. // tests are opposite. The filtering tests mostly fail on a few pixels.
...@@ -502,7 +502,7 @@ ...@@ -502,7 +502,7 @@
4853 METAL AMD : dEQP-GLES2.functional.draw.draw_arrays.line_loop.multiple_attributes = FAIL 4853 METAL AMD : dEQP-GLES2.functional.draw.draw_arrays.line_loop.multiple_attributes = FAIL
// Vertex attribute aliasing generates invalid SPIRV // Vertex attribute aliasing generates invalid SPIRV
4249 METAL : dEQP-GLES2.functional.attribute_location.bind_aliasing.* = SKIP 4249 METAL : dEQP-GLES2.functional.attribute_location.bind_aliasing.*mat* = SKIP
// Globally disable Metal testing on Intel & NVIDIA for now // Globally disable Metal testing on Intel & NVIDIA for now
4235 METAL INTEL : dEQP-GLES2.* = SKIP 4235 METAL INTEL : dEQP-GLES2.* = SKIP
......
...@@ -3045,18 +3045,13 @@ void main() ...@@ -3045,18 +3045,13 @@ void main()
// don't allow vertex attribute aliasing. This test excludes matrix types. // don't allow vertex attribute aliasing. This test excludes matrix types.
TEST_P(VertexAttributeTest, AliasingVectorAttribLocations) TEST_P(VertexAttributeTest, AliasingVectorAttribLocations)
{ {
// TODO(syoussefi): Support vertex attribute aliasing on Vulkan. The metal backend will
// automatically be fixed in the process. http://anglebug.com/4249
ANGLE_SKIP_TEST_IF(IsVulkan());
ANGLE_SKIP_TEST_IF(IsMetal());
// http://anglebug.com/5180 // http://anglebug.com/5180
ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGL()); ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGL());
// http://anglebug.com/5181 // http://anglebug.com/3466
ANGLE_SKIP_TEST_IF(IsOSX() && IsOpenGL()); ANGLE_SKIP_TEST_IF(IsOSX() && IsOpenGL());
// http://anglebug.com/5182 // http://anglebug.com/3467
ANGLE_SKIP_TEST_IF(IsD3D()); ANGLE_SKIP_TEST_IF(IsD3D());
constexpr char kVS[] = R"(attribute vec4 position; constexpr char kVS[] = R"(attribute vec4 position;
...@@ -3215,10 +3210,10 @@ TEST_P(VertexAttributeTest, AliasingMatrixAttribLocations) ...@@ -3215,10 +3210,10 @@ TEST_P(VertexAttributeTest, AliasingMatrixAttribLocations)
// http://anglebug.com/5180 // http://anglebug.com/5180
ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGL()); ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGL());
// http://anglebug.com/5181 // http://anglebug.com/3466
ANGLE_SKIP_TEST_IF(IsOSX() && IsOpenGL()); ANGLE_SKIP_TEST_IF(IsOSX() && IsOpenGL());
// http://anglebug.com/5182 // http://anglebug.com/3467
ANGLE_SKIP_TEST_IF(IsD3D()); ANGLE_SKIP_TEST_IF(IsD3D());
constexpr char kVS[] = R"(attribute vec4 position; constexpr char kVS[] = R"(attribute vec4 position;
......
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