Skip to content

Instantly share code, notes, and snippets.

@zeux
Created October 2, 2018 16:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zeux/00f758afb8029768929536199fbdb588 to your computer and use it in GitHub Desktop.
Save zeux/00f758afb8029768929536199fbdb588 to your computer and use it in GitHub Desktop.
Workaround for Adreno drivers that match varyings across stages using SPIRV names or IDs, but not locations as they should.
class RemapInterfaceIdsPass : public spvtools::opt::Pass
{
public:
const char* name() const override { return "remap-interface-ids"; }
RemapInterfaceIdsPass(uint32_t start_id): start_id(start_id)
{
}
Status Process() override
{
using namespace spvtools::opt;
const uint32_t kInterfaceOperandIdx = 3;
const uint32_t kExecutionModelInIdx = 0;
const uint32_t kStorageClassInIdx = 0;
const uint32_t kLocationIdx = 2;
IRContext* c = context();
assert(c->module()->ComputeIdBound() <= start_id);
bool modified = false;
std::unordered_map<uint32_t, uint32_t> result_id_mapping;
for (Instruction& entrypoint : c->module()->entry_points())
{
assert(entrypoint.opcode() == SpvOpEntryPoint);
uint32_t execution_model = entrypoint.GetSingleWordInOperand(kExecutionModelInIdx);
for (uint32_t op = kInterfaceOperandIdx; op < entrypoint.NumOperands(); ++op)
{
uint32_t var = entrypoint.GetSingleWordOperand(op);
const Instruction* def = get_def_use_mgr()->GetDef(var);
assert(def && def->opcode() == SpvOpVariable);
uint32_t storage_class = def->GetSingleWordInOperand(kStorageClassInIdx);
if ((execution_model == SpvExecutionModelVertex && storage_class == SpvStorageClassOutput) ||
(execution_model == SpvExecutionModelFragment && storage_class == SpvStorageClassInput))
{
int32_t location = -1;
get_decoration_mgr()->ForEachDecoration(var, SpvDecorationLocation, [&](const Instruction& d) { location = d.GetSingleWordInOperand(kLocationIdx); });
if (location >= 0)
{
result_id_mapping[var] = start_id + location;
}
}
}
}
c->module()->ForEachInst(
[&result_id_mapping, &modified](Instruction* inst)
{
auto operand = inst->begin();
while (operand != inst->end())
{
auto type = operand->type;
if (spvIsIdType(type))
{
assert(operand->words.size() == 1);
uint32_t& id = operand->words[0];
auto it = result_id_mapping.find(id);
if (it != result_id_mapping.end() && id != it->second)
{
modified = true;
id = it->second;
// Update data cached in the instruction object.
if (type == SPV_OPERAND_TYPE_RESULT_ID)
inst->SetResultId(id);
else if (type == SPV_OPERAND_TYPE_TYPE_ID)
inst->SetResultType(id);
}
}
++operand;
}
},
true);
if (modified)
c->module()->SetIdBound(c->module()->ComputeIdBound());
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
}
private:
uint32_t start_id;
};
// Usage:
// Strip debug info to remove names from the varyings - this makes sure Adreno drivers don't use mismatched names
tools.RegisterPass(spvtools::CreateStripDebugInfoPass());
// Compact SPIRV ids - this makes sure that we use a small sequential list of ids, making the hacky 10000 constant below work
tools.RegisterPass(spvtools::CreateCompactIdsPass());
// Remaps SPIRV ids for VS outputs & FS inputs to make sure they're identical
tools.RegisterPass(std::unique_ptr<spvtools::opt::Pass>(new RemapInterfaceIdsPass(10000)));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment