Commit 1bf0b917 by Shahbaz Youssefi Committed by Commit Bot

Vulkan: Pull generic SPIR-V transform functionality into base class

This is in preparation for adding another transformation for vertex attribute aliasing. This transformation is not merged with the existing one, because in practice vertex attribute aliasing is rare so this transformation would be rarely done. This makes sure the efficiency of the main transformer is not affected, and avoids complicating it further. Bug: angleproject:4249 Change-Id: I9c3f6cefbec99a355596579f4471d7ada810927e Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2482285Reviewed-by: 's avatarCourtney Goeltzenleuchter <courtneygo@google.com> Reviewed-by: 's avatarCharlie Lao <cclao@google.com> Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
parent c55cd6b4
...@@ -967,7 +967,7 @@ bool ValidateSpirv(const std::vector<uint32_t> &spirvBlob) ...@@ -967,7 +967,7 @@ bool ValidateSpirv(const std::vector<uint32_t> &spirvBlob)
if (!result) if (!result)
{ {
std::string readableSpirv; std::string readableSpirv;
spirvTools.Disassemble(spirvBlob, &readableSpirv, SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); spirvTools.Disassemble(spirvBlob, &readableSpirv, 0);
WARN() << "Invalid SPIR-V:\n" << readableSpirv; WARN() << "Invalid SPIR-V:\n" << readableSpirv;
} }
...@@ -982,33 +982,53 @@ bool ValidateSpirv(const std::vector<uint32_t> &spirvBlob) ...@@ -982,33 +982,53 @@ bool ValidateSpirv(const std::vector<uint32_t> &spirvBlob)
} }
#endif // ANGLE_ENABLE_ASSERTS #endif // ANGLE_ENABLE_ASSERTS
// A SPIR-V transformer. It walks the instructions and modifies them as necessary, for example to // SPIR-V 1.0 Table 2: Instruction Physical Layout
// assign bindings or locations. uint32_t GetSpirvInstructionLength(const uint32_t *instruction)
class SpirvTransformer final : angle::NonCopyable {
return instruction[0] >> 16;
}
uint32_t GetSpirvInstructionOp(const uint32_t *instruction)
{
constexpr uint32_t kOpMask = 0xFFFFu;
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;
}
// Base class for SPIR-V transformations.
class SpirvTransformerBase : angle::NonCopyable
{ {
public: public:
SpirvTransformer(const std::vector<uint32_t> &spirvBlobIn, SpirvTransformerBase(const std::vector<uint32_t> &spirvBlobIn,
bool removeEarlyFragmentTestsOptimization,
bool removeDebugInfo,
const ShaderInterfaceVariableInfoMap &variableInfoMap, const ShaderInterfaceVariableInfoMap &variableInfoMap,
gl::ShaderType shaderType, gl::ShaderType shaderType,
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; gl::ShaderBitSet allStages;
allStages.set(); allStages.set();
mRemoveEarlyFragmentTestsOptimization = removeEarlyFragmentTestsOptimization;
mRemoveDebugInfo = removeDebugInfo;
mBuiltinVariableInfo.activeStages = allStages; mBuiltinVariableInfo.activeStages = allStages;
} }
bool transform(); protected:
private:
// 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
{ {
...@@ -1020,6 +1040,102 @@ class SpirvTransformer final : angle::NonCopyable ...@@ -1020,6 +1040,102 @@ class SpirvTransformer final : angle::NonCopyable
kHeaderIndexInstructions = 5, kHeaderIndexInstructions = 5,
}; };
// Common utilities
void onTransformBegin();
const uint32_t *getCurrentInstruction(uint32_t *opCodeOut, uint32_t *wordCountOut) const;
size_t copyInstruction(const uint32_t *instruction, size_t wordCount);
uint32_t getNewId();
// SPIR-V to transform:
const std::vector<uint32_t> &mSpirvBlobIn;
const gl::ShaderType mShaderType;
// Input shader variable info map:
const ShaderInterfaceVariableInfoMap &mVariableInfoMap;
ShaderInterfaceVariableInfo mBuiltinVariableInfo;
// Transformed SPIR-V:
SpirvBlob *mSpirvBlobOut;
// Traversal state:
size_t mCurrentWord = 0;
bool mIsInFunctionSection = false;
// Transformation state:
// Shader variable info per id, if id is a shader variable.
std::vector<const ShaderInterfaceVariableInfo *> mVariableInfoById;
};
void SpirvTransformerBase::onTransformBegin()
{
// Glslang succeeded in outputting SPIR-V, so we assume it's valid.
ASSERT(mSpirvBlobIn.size() >= kHeaderIndexInstructions);
// Since SPIR-V comes from a local call to glslang, it necessarily has the same endianness as
// the running architecture, so no byte-swapping is necessary.
ASSERT(mSpirvBlobIn[kHeaderIndexMagic] == spv::MagicNumber);
// Make sure the transformer is not reused to avoid having to reinitialize it here.
ASSERT(mCurrentWord == 0);
ASSERT(mIsInFunctionSection == false);
// Make sure the SpirvBlob is not reused.
ASSERT(mSpirvBlobOut->empty());
// Copy the header to SpirvBlob, we need that to be defined for SpirvTransformerBase::getNewId
// to work.
mSpirvBlobOut->assign(mSpirvBlobIn.begin(), mSpirvBlobIn.begin() + kHeaderIndexInstructions);
mCurrentWord = kHeaderIndexInstructions;
}
const uint32_t *SpirvTransformerBase::getCurrentInstruction(uint32_t *opCodeOut,
uint32_t *wordCountOut) const
{
ASSERT(mCurrentWord < mSpirvBlobIn.size());
const uint32_t *instruction = &mSpirvBlobIn[mCurrentWord];
*wordCountOut = GetSpirvInstructionLength(instruction);
*opCodeOut = GetSpirvInstructionOp(instruction);
// Since glslang succeeded in producing SPIR-V, we assume it to be valid.
ASSERT(mCurrentWord + *wordCountOut <= mSpirvBlobIn.size());
return instruction;
}
size_t SpirvTransformerBase::copyInstruction(const uint32_t *instruction, size_t wordCount)
{
size_t instructionOffset = mSpirvBlobOut->size();
mSpirvBlobOut->insert(mSpirvBlobOut->end(), instruction, instruction + wordCount);
return instructionOffset;
}
uint32_t SpirvTransformerBase::getNewId()
{
return (*mSpirvBlobOut)[kHeaderIndexIndexBound]++;
}
// A SPIR-V transformer. It walks the instructions and modifies them as necessary, for example to
// assign bindings or locations.
class SpirvTransformer final : public SpirvTransformerBase
{
public:
SpirvTransformer(const std::vector<uint32_t> &spirvBlobIn,
bool removeEarlyFragmentTestsOptimization,
bool removeDebugInfo,
const ShaderInterfaceVariableInfoMap &variableInfoMap,
gl::ShaderType shaderType,
SpirvBlob *spirvBlobOut)
: SpirvTransformerBase(spirvBlobIn, variableInfoMap, shaderType, spirvBlobOut),
mHasTransformFeedbackOutput(false),
mRemoveEarlyFragmentTestsOptimization(removeEarlyFragmentTestsOptimization),
mRemoveDebugInfo(removeDebugInfo)
{}
bool transform();
private:
// A prepass to resolve interesting ids: // A prepass to resolve interesting ids:
void resolveVariableIds(); void resolveVariableIds();
...@@ -1046,30 +1162,16 @@ class SpirvTransformer final : angle::NonCopyable ...@@ -1046,30 +1162,16 @@ class SpirvTransformer final : angle::NonCopyable
// Any other instructions: // Any other instructions:
void writeInputPreamble(); void writeInputPreamble();
size_t copyInstruction(const uint32_t *instruction, size_t wordCount);
uint32_t getNewId();
void writeOpLoad(uint32_t id, uint32_t typeId, uint32_t tempVarId); void writeOpLoad(uint32_t id, uint32_t typeId, uint32_t tempVarId);
void writeOpStore(uint32_t tempVarId, uint32_t destId); void writeOpStore(uint32_t tempVarId, uint32_t destId);
void writeOpVariable(uint32_t id, uint32_t typeId, uint32_t storageClassId); void writeOpVariable(uint32_t id, uint32_t typeId, uint32_t storageClassId);
// SPIR-V to transform: // Special flags:
const std::vector<uint32_t> &mSpirvBlobIn;
const gl::ShaderType mShaderType;
bool mHasTransformFeedbackOutput; bool mHasTransformFeedbackOutput;
bool mRemoveEarlyFragmentTestsOptimization; bool mRemoveEarlyFragmentTestsOptimization;
bool mRemoveDebugInfo; bool mRemoveDebugInfo;
// Input shader variable info map:
const ShaderInterfaceVariableInfoMap &mVariableInfoMap;
ShaderInterfaceVariableInfo mBuiltinVariableInfo;
// Transformed SPIR-V:
SpirvBlob *mSpirvBlobOut;
// Traversal state: // Traversal state:
size_t mCurrentWord = 0;
bool mIsInFunctionSection = false;
bool mInsertFunctionVariables = false; bool mInsertFunctionVariables = false;
uint32_t mEntryPointId = 0; uint32_t mEntryPointId = 0;
uint32_t mOpFunctionId = 0; uint32_t mOpFunctionId = 0;
...@@ -1082,9 +1184,6 @@ class SpirvTransformer final : angle::NonCopyable ...@@ -1082,9 +1184,6 @@ class SpirvTransformer final : angle::NonCopyable
// name based on the Storage Class. // name based on the Storage Class.
std::vector<const char *> mNamesById; std::vector<const char *> mNamesById;
// Shader variable info per id, if id is a shader variable.
std::vector<const ShaderInterfaceVariableInfo *> mVariableInfoById;
// Each OpTypePointer instruction that defines a type with the Output storage class is // 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 // 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 // class. If inactive varyings are encountered, its type is changed to the Private one. The
...@@ -1101,28 +1200,12 @@ class SpirvTransformer final : angle::NonCopyable ...@@ -1101,28 +1200,12 @@ class SpirvTransformer final : angle::NonCopyable
bool SpirvTransformer::transform() bool SpirvTransformer::transform()
{ {
// Glslang succeeded in outputting SPIR-V, so we assume it's valid. onTransformBegin();
ASSERT(mSpirvBlobIn.size() >= kHeaderIndexInstructions);
// Since SPIR-V comes from a local call to glslang, it necessarily has the same endianness as
// the running architecture, so no byte-swapping is necessary.
ASSERT(mSpirvBlobIn[kHeaderIndexMagic] == spv::MagicNumber);
// Make sure the transformer is not reused to avoid having to reinitialize it here.
ASSERT(mCurrentWord == 0);
ASSERT(mIsInFunctionSection == false);
// Make sure the SpirvBlob is not reused.
ASSERT(mSpirvBlobOut->empty());
// Copy the header to SpirvBlob, we need that to be defined for SpirvTransformer::getNewId to
// work.
mSpirvBlobOut->assign(mSpirvBlobIn.begin(), mSpirvBlobIn.begin() + kHeaderIndexInstructions);
// First, find all necessary ids and associate them with the information required to transform // First, find all necessary ids and associate them with the information required to transform
// their decorations. // their decorations.
resolveVariableIds(); resolveVariableIds();
mCurrentWord = kHeaderIndexInstructions;
while (mCurrentWord < mSpirvBlobIn.size()) while (mCurrentWord < mSpirvBlobIn.size())
{ {
transformInstruction(); transformInstruction();
...@@ -1131,34 +1214,6 @@ bool SpirvTransformer::transform() ...@@ -1131,34 +1214,6 @@ bool SpirvTransformer::transform()
return true; return true;
} }
// SPIR-V 1.0 Table 2: Instruction Physical Layout
uint32_t GetSpirvInstructionLength(const uint32_t *instruction)
{
return instruction[0] >> 16;
}
uint32_t GetSpirvInstructionOp(const uint32_t *instruction)
{
constexpr uint32_t kOpMask = 0xFFFFu;
return instruction[0] & kOpMask;
}
void 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]; size_t indexBound = mSpirvBlobIn[kHeaderIndexIndexBound];
...@@ -1220,14 +1275,9 @@ void SpirvTransformer::resolveVariableIds() ...@@ -1220,14 +1275,9 @@ void SpirvTransformer::resolveVariableIds()
void SpirvTransformer::transformInstruction() void SpirvTransformer::transformInstruction()
{ {
ASSERT(mCurrentWord < mSpirvBlobIn.size()); uint32_t wordCount;
const uint32_t *instruction = &mSpirvBlobIn[mCurrentWord]; uint32_t opCode;
const uint32_t *instruction = getCurrentInstruction(&opCode, &wordCount);
const uint32_t wordCount = GetSpirvInstructionLength(instruction);
const uint32_t opCode = GetSpirvInstructionOp(instruction);
// Since glslang succeeded in producing SPIR-V, we assume it to be valid.
ASSERT(mCurrentWord + wordCount <= mSpirvBlobIn.size());
if (opCode == spv::OpFunction) if (opCode == spv::OpFunction)
{ {
...@@ -1927,18 +1977,6 @@ bool SpirvTransformer::transformExecutionMode(const uint32_t *instruction, size_ ...@@ -1927,18 +1977,6 @@ bool SpirvTransformer::transformExecutionMode(const uint32_t *instruction, size_
return false; return false;
} }
size_t SpirvTransformer::copyInstruction(const uint32_t *instruction, size_t wordCount)
{
size_t instructionOffset = mSpirvBlobOut->size();
mSpirvBlobOut->insert(mSpirvBlobOut->end(), instruction, instruction + wordCount);
return instructionOffset;
}
uint32_t SpirvTransformer::getNewId()
{
return (*mSpirvBlobOut)[kHeaderIndexIndexBound]++;
}
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 kInstIndex = 0;
......
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