Created
October 2, 2018 16:49
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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