diff --git a/renderdoc/driver/shaders/spirv/CMakeLists.txt b/renderdoc/driver/shaders/spirv/CMakeLists.txt index 81e709179..8c63fc630 100644 --- a/renderdoc/driver/shaders/spirv/CMakeLists.txt +++ b/renderdoc/driver/shaders/spirv/CMakeLists.txt @@ -100,6 +100,8 @@ set(sources spirv_compile.h spirv_reflect.cpp spirv_reflect.h + spirv_processor.cpp + spirv_processor.h spirv_disassemble.cpp spirv_stringise.cpp ${glslang_sources}) diff --git a/renderdoc/driver/shaders/spirv/renderdoc_spirv.vcxproj b/renderdoc/driver/shaders/spirv/renderdoc_spirv.vcxproj index 47200d201..1cf72159d 100644 --- a/renderdoc/driver/shaders/spirv/renderdoc_spirv.vcxproj +++ b/renderdoc/driver/shaders/spirv/renderdoc_spirv.vcxproj @@ -162,6 +162,7 @@ precompiled.h + @@ -219,6 +220,7 @@ + diff --git a/renderdoc/driver/shaders/spirv/renderdoc_spirv.vcxproj.filters b/renderdoc/driver/shaders/spirv/renderdoc_spirv.vcxproj.filters index a035517db..c88771a72 100644 --- a/renderdoc/driver/shaders/spirv/renderdoc_spirv.vcxproj.filters +++ b/renderdoc/driver/shaders/spirv/renderdoc_spirv.vcxproj.filters @@ -142,6 +142,7 @@ + @@ -293,5 +294,6 @@ JSON-Generated helpers + \ No newline at end of file diff --git a/renderdoc/driver/shaders/spirv/spirv_editor.cpp b/renderdoc/driver/shaders/spirv/spirv_editor.cpp index 16b6d4e75..0489dfbb7 100644 --- a/renderdoc/driver/shaders/spirv/spirv_editor.cpp +++ b/renderdoc/driver/shaders/spirv/spirv_editor.cpp @@ -54,153 +54,16 @@ Scalar::Scalar(Iter it) } } -Operation Vector::decl(Editor &editor) const +Editor::Editor(std::vector &spirvWords) : m_ExternalSPIRV(spirvWords) { - return OpTypeVector(Id(), editor.DeclareType(scalar), count); } -Operation Matrix::decl(Editor &editor) const +void Editor::Prepare() { - return OpTypeMatrix(Id(), editor.DeclareType(vector), count); -} + Processor::Parse(m_ExternalSPIRV); -Operation Pointer::decl(Editor &editor) const -{ - return OpTypePointer(Id(), storage, baseId); -} - -Operation Image::decl(Editor &editor) const -{ - return OpTypeImage(Id(), editor.DeclareType(retType), dim, depth, arrayed, ms, sampled, format); -} - -Operation Sampler::decl(Editor &editor) const -{ - return OpTypeSampler(Id()); -} - -Operation SampledImage::decl(Editor &editor) const -{ - return OpTypeSampledImage(Id(), baseId); -} - -Operation Function::decl(Editor &editor) const -{ - return OpTypeFunction(Id(), returnId, argumentIds); -} - -Editor::Editor(std::vector &spirvWords) : spirv(spirvWords) -{ - if(spirv.size() < FirstRealWord || spirv[0] != MagicNumber) - { - RDCERR("Empty or invalid SPIR-V module"); + if(m_SPIRV.empty()) return; - } - - idOffsets.resize(spirv[3]); - idTypes.resize(spirv[3]); - - // [4] is reserved - RDCASSERT(spirv[4] == 0); - - // simple state machine to track which section we're in. - // Note that a couple of sections are optional and could be skipped over, at which point we insert - // a dummy OpNop so they're not empty (which will be stripped later) and record them as in - // between. - // - // We only handle single-shader modules at the moment, so some things are required by virtue of - // being required in a shader - e.g. at least the Shader capability, at least one entry point, etc - // - // Capabilities: REQUIRED (we assume - must declare Shader capability) - // Extensions: OPTIONAL - // ExtInst: OPTIONAL - // MemoryModel: REQUIRED (required by spec) - // EntryPoints: REQUIRED (we assume) - // ExecutionMode: OPTIONAL - // Debug: OPTIONAL - // Annotations: OPTIONAL (in theory - would require empty shader) - // TypesVariables: REQUIRED (must at least have the entry point function type) - // Functions: REQUIRED (must have the entry point) - - // set the book-ends: start of the first section and end of the last - sections[Section::Count - 1].endOffset = spirvWords.size(); - -#define START_SECTION(section) \ - if(sections[section].startOffset == 0) \ - sections[section].startOffset = it.offs(); - - for(Iter it(spirv, FirstRealWord); it; it++) - { - Op opcode = it.opcode(); - - if(opcode == Op::Capability) - { - START_SECTION(Section::Capabilities); - } - else if(opcode == Op::Extension) - { - START_SECTION(Section::Extensions); - } - else if(opcode == Op::ExtInstImport) - { - START_SECTION(Section::ExtInst); - } - else if(opcode == Op::MemoryModel) - { - START_SECTION(Section::MemoryModel); - } - else if(opcode == Op::EntryPoint) - { - START_SECTION(Section::EntryPoints); - } - else if(opcode == Op::ExecutionMode || opcode == Op::ExecutionModeId) - { - START_SECTION(Section::ExecutionMode); - } - else if(opcode == Op::String || opcode == Op::Source || opcode == Op::SourceContinued || - opcode == Op::SourceExtension || opcode == Op::Name || opcode == Op::MemberName || - opcode == Op::ModuleProcessed) - { - START_SECTION(Section::Debug); - } - else if(opcode == Op::Decorate || opcode == Op::MemberDecorate || opcode == Op::GroupDecorate || - opcode == Op::GroupMemberDecorate || opcode == Op::DecorationGroup || - opcode == Op::DecorateStringGOOGLE || opcode == Op::MemberDecorateStringGOOGLE) - { - START_SECTION(Section::Annotations); - } - else if(opcode == Op::Function) - { - START_SECTION(Section::Functions); - } - else - { - // if we've reached another instruction, check if we've reached the function section yet. If - // we have then assume it's an instruction inside a function and ignore. If we haven't, assume - // it's a type/variable/constant type instruction - if(sections[Section::Functions].startOffset == 0) - { - START_SECTION(Section::TypesVariablesConstants); - } - } - - RegisterOp(it); - } - -#undef START_SECTION - - // ensure we got everything right. First section should start at the beginning - RDCASSERTEQUAL(sections[Section::First].startOffset, FirstRealWord); - - // we now set the endOffset of each section to the start of the next. Any empty sections - // temporarily have startOffset set to endOffset, we'll pad them with a nop below. - for(int s = Section::Count - 1; s > 0; s--) - { - RDCASSERTEQUAL(sections[s - 1].endOffset, 0); - sections[s - 1].endOffset = sections[s].startOffset; - if(sections[s - 1].startOffset == 0) - sections[s - 1].startOffset = sections[s - 1].endOffset; - } // find any empty sections and insert a nop into the stream there. We need to fixup later section // offsets by hand as addWords doesn't handle empty sections properly (it thinks we're inserting @@ -208,20 +71,20 @@ Editor::Editor(std::vector &spirvWords) : spirv(spirvWords) // padding nops in the first place! for(uint32_t s = 0; s < Section::Count; s++) { - if(sections[s].startOffset == sections[s].endOffset) + if(m_Sections[s].startOffset == m_Sections[s].endOffset) { - spirv.insert(spirv.begin() + sections[s].startOffset, OpNopWord); - sections[s].endOffset++; + m_SPIRV.insert(m_SPIRV.begin() + m_Sections[s].startOffset, OpNopWord); + m_Sections[s].endOffset++; for(uint32_t t = s + 1; t < Section::Count; t++) { - sections[t].startOffset++; - sections[t].endOffset++; + m_Sections[t].startOffset++; + m_Sections[t].endOffset++; } // look through every id, and update its offset for(size_t &o : idOffsets) - if(o >= sections[s].startOffset) + if(o >= m_Sections[s].startOffset) o++; } } @@ -229,31 +92,31 @@ Editor::Editor(std::vector &spirvWords) : spirv(spirvWords) // each section should now precisely match each other end-to-end and not be empty for(uint32_t s = Section::First; s < Section::Count; s++) { - RDCASSERTNOTEQUAL(sections[s].startOffset, 0); - RDCASSERTNOTEQUAL(sections[s].endOffset, 0); + RDCASSERTNOTEQUAL(m_Sections[s].startOffset, 0); + RDCASSERTNOTEQUAL(m_Sections[s].endOffset, 0); - RDCASSERT(sections[s].endOffset - sections[s].startOffset > 0, sections[s].startOffset, - sections[s].endOffset); + RDCASSERT(m_Sections[s].endOffset - m_Sections[s].startOffset > 0, m_Sections[s].startOffset, + m_Sections[s].endOffset); if(s != 0) - RDCASSERTEQUAL(sections[s - 1].endOffset, sections[s].startOffset); + RDCASSERTEQUAL(m_Sections[s - 1].endOffset, m_Sections[s].startOffset); if(s + 1 < Section::Count) - RDCASSERTEQUAL(sections[s].endOffset, sections[s + 1].startOffset); + RDCASSERTEQUAL(m_Sections[s].endOffset, m_Sections[s + 1].startOffset); } } -void Editor::StripNops() +Editor::~Editor() { - for(size_t i = FirstRealWord; i < spirv.size();) + for(size_t i = FirstRealWord; i < m_SPIRV.size();) { - while(spirv[i] == OpNopWord) + while(m_SPIRV[i] == OpNopWord) { - spirv.erase(spirv.begin() + i); + m_SPIRV.erase(m_SPIRV.begin() + i); addWords(i, -1); } - uint32_t len = spirv[i] >> WordCountShift; + uint32_t len = m_SPIRV[i] >> WordCountShift; if(len == 0) { @@ -263,14 +126,15 @@ void Editor::StripNops() i += len; } + + m_ExternalSPIRV.swap(m_SPIRV); } Id Editor::MakeId() { - uint32_t ret = spirv[3]; - spirv[3]++; - idOffsets.resize(spirv[3]); - idTypes.resize(spirv[3]); + uint32_t ret = m_SPIRV[3]; + m_SPIRV[3]++; + Processor::PreParse(m_SPIRV[3]); return Id::fromWord(ret); } @@ -293,16 +157,16 @@ void Editor::SetName(Id id, const char *name) break; } - op.insertInto(spirv, it.offs()); - RegisterOp(Iter(spirv, it.offs())); + op.insertInto(m_SPIRV, it.offs()); + RegisterOp(Iter(m_SPIRV, it.offs())); addWords(it.offs(), op.size()); } void Editor::AddDecoration(const Operation &op) { - size_t offset = sections[Section::Annotations].endOffset; - op.insertInto(spirv, offset); - RegisterOp(Iter(spirv, offset)); + size_t offset = m_Sections[Section::Annotations].endOffset; + op.insertInto(m_SPIRV, offset); + RegisterOp(Iter(m_SPIRV, offset)); addWords(offset, op.size()); } @@ -314,8 +178,8 @@ void Editor::AddCapability(Capability cap) // insert the operation at the very start Operation op(Op::Capability, {(uint32_t)cap}); - op.insertInto(spirv, FirstRealWord); - RegisterOp(Iter(spirv, FirstRealWord)); + op.insertInto(m_SPIRV, FirstRealWord); + RegisterOp(Iter(m_SPIRV, FirstRealWord)); addWords(FirstRealWord, op.size()); } @@ -326,7 +190,7 @@ void Editor::AddExtension(const rdcstr &extension) return; // start at the beginning - Iter it(spirv, FirstRealWord); + Iter it(m_SPIRV, FirstRealWord); // skip past any capabilities while(it.opcode() == Op::Capability) @@ -338,17 +202,17 @@ void Editor::AddExtension(const rdcstr &extension) memcpy(&uintName[0], extension.c_str(), sz); Operation op(Op::Extension, uintName); - op.insertInto(spirv, it.offs()); + op.insertInto(m_SPIRV, it.offs()); RegisterOp(it); addWords(it.offs(), op.size()); } void Editor::AddExecutionMode(const Operation &mode) { - size_t offset = sections[Section::ExecutionMode].endOffset; + size_t offset = m_Sections[Section::ExecutionMode].endOffset; - mode.insertInto(spirv, offset); - RegisterOp(Iter(spirv, offset)); + mode.insertInto(m_SPIRV, offset); + RegisterOp(Iter(m_SPIRV, offset)); addWords(offset, mode.size()); } @@ -360,7 +224,7 @@ Id Editor::ImportExtInst(const char *setname) return ret; // start at the beginning - Iter it(spirv, FirstRealWord); + Iter it(m_SPIRV, FirstRealWord); // skip past any capabilities and extensions while(it.opcode() == Op::Capability || it.opcode() == Op::Extension) @@ -376,7 +240,7 @@ Id Editor::ImportExtInst(const char *setname) uintName.insert(uintName.begin(), ret.value()); Operation op(Op::ExtInstImport, uintName); - op.insertInto(spirv, it.offs()); + op.insertInto(m_SPIRV, it.offs()); RegisterOp(it); addWords(it.offs(), op.size()); @@ -387,48 +251,45 @@ Id Editor::ImportExtInst(const char *setname) Id Editor::AddType(const Operation &op) { - size_t offset = sections[Section::Types].endOffset; + size_t offset = m_Sections[Section::Types].endOffset; Id id = Id::fromWord(op[1]); - idOffsets[id.value()] = offset; - op.insertInto(spirv, offset); - RegisterOp(Iter(spirv, offset)); + op.insertInto(m_SPIRV, offset); + RegisterOp(Iter(m_SPIRV, offset)); addWords(offset, op.size()); return id; } Id Editor::AddVariable(const Operation &op) { - size_t offset = sections[Section::Variables].endOffset; + size_t offset = m_Sections[Section::Variables].endOffset; Id id = Id::fromWord(op[2]); - idOffsets[id.value()] = offset; - op.insertInto(spirv, offset); - RegisterOp(Iter(spirv, offset)); + op.insertInto(m_SPIRV, offset); + RegisterOp(Iter(m_SPIRV, offset)); addWords(offset, op.size()); return id; } Id Editor::AddConstant(const Operation &op) { - size_t offset = sections[Section::Constants].endOffset; + size_t offset = m_Sections[Section::Constants].endOffset; Id id = Id::fromWord(op[2]); - idOffsets[id.value()] = offset; - op.insertInto(spirv, offset); - RegisterOp(Iter(spirv, offset)); + op.insertInto(m_SPIRV, offset); + RegisterOp(Iter(m_SPIRV, offset)); addWords(offset, op.size()); return id; } void Editor::AddFunction(const Operation *ops, size_t count) { - idOffsets[ops[0][2]] = spirv.size(); + size_t offset = m_SPIRV.size(); for(size_t i = 0; i < count; i++) - ops[i].appendTo(spirv); + ops[i].appendTo(m_SPIRV); - RegisterOp(Iter(spirv, idOffsets[ops[0][2]])); + RegisterOp(Iter(m_SPIRV, offset)); } Iter Editor::GetID(Id id) @@ -436,15 +297,15 @@ Iter Editor::GetID(Id id) size_t offs = idOffsets[id.value()]; if(offs) - return Iter(spirv, offs); + return Iter(m_SPIRV, offs); return Iter(); } Iter Editor::GetEntry(Id id) { - Iter it(spirv, sections[Section::EntryPoints].startOffset); - Iter end(spirv, sections[Section::EntryPoints].endOffset); + Iter it(m_SPIRV, m_Sections[Section::EntryPoints].startOffset); + Iter end(m_SPIRV, m_Sections[Section::EntryPoints].endOffset); while(it && it < end) { @@ -471,7 +332,7 @@ void Editor::AddOperation(Iter iter, const Operation &op) return; // add op - op.insertInto(spirv, iter.offs()); + op.insertInto(m_SPIRV, iter.offs()); // update offsets addWords(iter.offs(), op.size()); @@ -479,301 +340,119 @@ void Editor::AddOperation(Iter iter, const Operation &op) void Editor::RegisterOp(Iter it) { - Op opcode = it.opcode(); + Processor::RegisterOp(it); OpDecoder opdata(it); - if(opdata.result != Id() && opdata.resultType != Id()) - { - RDCASSERT(opdata.result.value() < idTypes.size()); - idTypes[opdata.result.value()] = opdata.resultType; - } - if(opdata.result != Id()) - idOffsets[opdata.result.value()] = it.offs(); - - if(opcode == Op::EntryPoint) + if(opdata.op == Op::TypeVoid || opdata.op == Op::TypeBool || opdata.op == Op::TypeInt || + opdata.op == Op::TypeFloat) { - entries.push_back(OpEntryPoint(it)); + Scalar scalar(it); + scalarTypeToId[scalar] = opdata.result; } - else if(opcode == Op::MemoryModel) + else if(opdata.op == Op::TypeVector) { - OpMemoryModel decoded(it); - addressmodel = decoded.addressingModel; - memorymodel = decoded.memoryModel; + OpTypeVector decoded(it); + vectorTypeToId[Vector(scalarTypes[decoded.componentType], decoded.componentCount)] = + decoded.result; } - else if(opcode == Op::Capability) + else if(opdata.op == Op::TypeMatrix) { - OpCapability decoded(it); - capabilities.insert(decoded.capability); + OpTypeMatrix decoded(it); + matrixTypeToId[Matrix(vectorTypes[decoded.columnType], decoded.columnCount)] = decoded.result; } - else if(opcode == Op::Extension) + else if(opdata.op == Op::TypeImage) { - OpExtension decoded(it); - extensions.insert(decoded.name); + OpTypeImage decoded(it); + imageTypeToId[Image(scalarTypes[decoded.sampledType], decoded.dim, decoded.depth, decoded.arrayed, + decoded.mS, decoded.sampled, decoded.imageFormat)] = decoded.result; } - else if(opcode == Op::ExtInstImport) + else if(opdata.op == Op::TypeSampler) { - OpExtInstImport decoded(it); - extSets[decoded.name] = decoded.result; + samplerTypeToId[Sampler()] = opdata.result; } - else if(opcode == Op::Function) + else if(opdata.op == Op::TypeSampledImage) { - functions.push_back(opdata.result); + OpTypeSampledImage decoded(it); + sampledImageTypeToId[SampledImage(decoded.imageType)] = decoded.result; } - else if(opcode == Op::Variable) + else if(opdata.op == Op::TypePointer) { - variables.push_back(OpVariable(it)); + OpTypePointer decoded(it); + pointerTypeToId[Pointer(decoded.type, decoded.storageClass)] = decoded.result; } - else if(opcode == Op::Decorate) + else if(opdata.op == Op::TypeFunction) + { + OpTypeFunction decoded(it); + functionTypeToId[FunctionType(decoded.returnType, decoded.parameters)] = decoded.result; + } + else if(opdata.op == Op::Decorate) { OpDecorate decorate(it); - auto it = std::lower_bound(decorations.begin(), decorations.end(), decorate, - [](const OpDecorate &a, const OpDecorate &b) { return a < b; }); - decorations.insert(it, decorate); - if(decorate.decoration == Decoration::DescriptorSet) bindings[decorate.target].set = decorate.decoration.descriptorSet; if(decorate.decoration == Decoration::Binding) bindings[decorate.target].binding = decorate.decoration.binding; } - else if(opcode == Op::TypeVoid || opcode == Op::TypeBool || opcode == Op::TypeInt || - opcode == Op::TypeFloat) - { - Scalar scalar(it); - scalarTypes[scalar] = opdata.result; - } - else if(opcode == Op::TypeVector) - { - OpTypeVector decoded(it); - - Iter scalarIt = GetID(decoded.componentType); - - if(!scalarIt) - { - RDCERR("Vector type declared with unknown scalar component type %u", decoded.componentType); - return; - } - - vectorTypes[Vector(scalarIt, decoded.componentCount)] = decoded.result; - } - else if(opcode == Op::TypeMatrix) - { - OpTypeMatrix decodedMatrix(it); - - Iter vectorIt = GetID(decodedMatrix.columnType); - - if(!vectorIt) - { - RDCERR("Matrix type declared with unknown vector component type %u", decodedMatrix.columnType); - return; - } - - OpTypeVector decodedVector(vectorIt); - - Iter scalarIt = GetID(decodedVector.componentType); - - matrixTypes[Matrix(Vector(scalarIt, decodedVector.componentCount), decodedMatrix.columnCount)] = - decodedMatrix.result; - } - else if(opcode == Op::TypeImage) - { - OpTypeImage decoded(it); - - Iter scalarIt = GetID(decoded.sampledType); - - if(!scalarIt) - { - RDCERR("Image type declared with unknown scalar component type %u", decoded.sampledType); - return; - } - - imageTypes[Image(scalarIt, decoded.dim, decoded.depth, decoded.arrayed, decoded.mS, - decoded.sampled, decoded.imageFormat)] = decoded.result; - } - else if(opcode == Op::TypeSampler) - { - samplerTypes[Sampler()] = opdata.result; - } - else if(opcode == Op::TypeSampledImage) - { - OpTypeSampledImage decoded(it); - - sampledImageTypes[SampledImage(decoded.imageType)] = decoded.result; - } - else if(opcode == Op::TypePointer) - { - OpTypePointer decoded(it); - - pointerTypes[Pointer(decoded.type, decoded.storageClass)] = decoded.result; - } - else if(opcode == Op::TypeStruct) - { - structTypes.insert(opdata.result); - } - else if(opcode == Op::TypeFunction) - { - OpTypeFunction decoded(it); - - functionTypes[Function(decoded.returnType, decoded.parameters)] = decoded.result; - } } void Editor::UnregisterOp(Iter it) { - Op opcode = it.opcode(); + Processor::UnregisterOp(it); OpDecoder opdata(it); - if(opdata.result != Id() && opdata.resultType != Id()) - idTypes[opdata.result.value()] = Id(); - if(opdata.result != Id()) - idOffsets[opdata.result.value()] = 0; - - if(opcode == Op::EntryPoint) + if(opdata.op == Op::TypeVoid || opdata.op == Op::TypeBool || opdata.op == Op::TypeInt || + opdata.op == Op::TypeFloat) { - OpEntryPoint decoded(it); - - for(auto entryIt = entries.begin(); entryIt != entries.end(); ++entryIt) - { - if(entryIt->entryPoint == decoded.entryPoint) - { - entries.erase(entryIt); - break; - } - } + Scalar scalar(it); + scalarTypeToId.erase(scalar); } - else if(opcode == Op::Function) + else if(opdata.op == Op::TypeVector) { - for(auto funcIt = functions.begin(); funcIt != functions.end(); ++funcIt) - { - if(*funcIt == opdata.result) - { - functions.erase(funcIt); - break; - } - } + OpTypeVector decoded(it); + vectorTypeToId.erase(Vector(scalarTypes[decoded.componentType], decoded.componentCount)); } - else if(opcode == Op::Variable) + else if(opdata.op == Op::TypeMatrix) { - for(auto varIt = variables.begin(); varIt != variables.end(); ++varIt) - { - if(varIt->result == opdata.result) - { - variables.erase(varIt); - break; - } - } + OpTypeMatrix decoded(it); + matrixTypeToId.erase(Matrix(vectorTypes[decoded.columnType], decoded.columnCount)); } - else if(opcode == Op::Decorate) + else if(opdata.op == Op::TypeImage) + { + OpTypeImage decoded(it); + imageTypeToId.erase(Image(scalarTypes[decoded.sampledType], decoded.dim, decoded.depth, + decoded.arrayed, decoded.mS, decoded.sampled, decoded.imageFormat)); + } + else if(opdata.op == Op::TypeSampler) + { + samplerTypeToId.erase(Sampler()); + } + else if(opdata.op == Op::TypeSampledImage) + { + OpTypeSampledImage decoded(it); + sampledImageTypeToId.erase(SampledImage(decoded.imageType)); + } + else if(opdata.op == Op::TypePointer) + { + OpTypePointer decoded(it); + pointerTypeToId.erase(Pointer(decoded.type, decoded.storageClass)); + } + else if(opdata.op == Op::TypeFunction) + { + OpTypeFunction decoded(it); + functionTypeToId.erase(FunctionType(decoded.returnType, decoded.parameters)); + } + else if(opdata.op == Op::Decorate) { OpDecorate decorate(it); - auto it = std::lower_bound(decorations.begin(), decorations.end(), decorate, - [](const OpDecorate &a, const OpDecorate &b) { return a < b; }); - if(it != decorations.end() && *it == decorate) - decorations.erase(it); - if(decorate.decoration == Decoration::DescriptorSet) bindings[decorate.target].set = Binding().set; if(decorate.decoration == Decoration::Binding) bindings[decorate.target].binding = Binding().binding; } - else if(opcode == Op::Capability) - { - OpCapability decoded(it); - capabilities.erase(decoded.capability); - } - else if(opcode == Op::Extension) - { - OpExtension decoded(it); - extensions.erase(decoded.name); - } - else if(opcode == Op::ExtInstImport) - { - OpExtInstImport decoded(it); - extSets.erase(decoded.name); - } - else if(opcode == Op::TypeVoid || opcode == Op::TypeBool || opcode == Op::TypeInt || - opcode == Op::TypeFloat) - { - Scalar scalar(it); - scalarTypes.erase(scalar); - } - else if(opcode == Op::TypeVector) - { - OpTypeVector decoded(it); - - Iter scalarIt = GetID(decoded.componentType); - - if(!scalarIt) - { - RDCERR("Vector type declared with unknown scalar component type %u", decoded.componentType); - return; - } - - vectorTypes.erase(Vector(scalarIt, decoded.componentCount)); - } - else if(opcode == Op::TypeMatrix) - { - OpTypeMatrix decodedMatrix(it); - - Iter vectorIt = GetID(decodedMatrix.columnType); - - if(!vectorIt) - { - RDCERR("Matrix type declared with unknown vector component type %u", decodedMatrix.columnType); - return; - } - - OpTypeVector decodedVector(vectorIt); - - Iter scalarIt = GetID(decodedVector.componentType); - - matrixTypes.erase( - Matrix(Vector(scalarIt, decodedVector.componentCount), decodedMatrix.columnCount)); - } - else if(opcode == Op::TypeImage) - { - OpTypeImage decoded(it); - - Iter scalarIt = GetID(decoded.sampledType); - - if(!scalarIt) - { - RDCERR("Image type declared with unknown scalar component type %u", decoded.sampledType); - return; - } - - imageTypes.erase(Image(scalarIt, decoded.dim, decoded.depth, decoded.arrayed, decoded.mS, - decoded.sampled, decoded.imageFormat)); - } - else if(opcode == Op::TypeSampler) - { - samplerTypes.erase(Sampler()); - } - else if(opcode == Op::TypeSampledImage) - { - OpTypeSampledImage decoded(it); - - sampledImageTypes.erase(SampledImage(decoded.imageType)); - } - else if(opcode == Op::TypePointer) - { - OpTypePointer decoded(it); - - pointerTypes.erase(Pointer(decoded.type, decoded.storageClass)); - } - else if(opcode == Op::TypeStruct) - { - structTypes.erase(opdata.result); - } - else if(opcode == Op::TypeFunction) - { - OpTypeFunction decoded(it); - - functionTypes.erase(Function(decoded.returnType, decoded.parameters)); - } } void Editor::addWords(size_t offs, int32_t num) @@ -781,7 +460,7 @@ void Editor::addWords(size_t offs, int32_t num) // look through every section, any that are >= this point, adjust the offsets // note that if we're removing words then any offsets pointing directly to the removed words // will go backwards - but they no longer have anywhere valid to point. - for(LogicalSection §ion : sections) + for(LogicalSection §ion : m_Sections) { // we have three cases to consider: either the offset matches start, is within (up to and // including end) or is outside the section. @@ -817,6 +496,56 @@ void Editor::addWords(size_t offs, int32_t num) o += num; } +Operation Editor::MakeDeclaration(const Scalar &s) +{ + if(s.type == Op::TypeVoid) + return OpTypeVoid(Id()); + else if(s.type == Op::TypeBool) + return OpTypeBool(Id()); + else if(s.type == Op::TypeFloat) + return OpTypeFloat(Id(), s.width); + else if(s.type == Op::TypeInt) + return OpTypeInt(Id(), s.width, s.signedness ? 1U : 0U); + else + return OpNop(); +} + +Operation Editor::MakeDeclaration(const Vector &v) +{ + return OpTypeVector(Id(), DeclareType(v.scalar), v.count); +} + +Operation Editor::MakeDeclaration(const Matrix &m) +{ + return OpTypeMatrix(Id(), DeclareType(m.vector), m.count); +} + +Operation Editor::MakeDeclaration(const Pointer &p) +{ + return OpTypePointer(Id(), p.storage, p.baseId); +} + +Operation Editor::MakeDeclaration(const Image &i) +{ + return OpTypeImage(Id(), DeclareType(i.retType), i.dim, i.depth, i.arrayed, i.ms, i.sampled, + i.format); +} + +Operation Editor::MakeDeclaration(const Sampler &s) +{ + return OpTypeSampler(Id()); +} + +Operation Editor::MakeDeclaration(const SampledImage &s) +{ + return OpTypeSampledImage(Id(), s.baseId); +} + +Operation Editor::MakeDeclaration(const FunctionType &f) +{ + return OpTypeFunction(Id(), f.returnId, f.argumentIds); +} + #define TYPETABLE(StructType, variable) \ template <> \ std::map &Editor::GetTable() \ @@ -829,14 +558,14 @@ void Editor::addWords(size_t offs, int32_t num) return variable; \ } -TYPETABLE(Scalar, scalarTypes); -TYPETABLE(Vector, vectorTypes); -TYPETABLE(Matrix, matrixTypes); -TYPETABLE(Pointer, pointerTypes); -TYPETABLE(Image, imageTypes); -TYPETABLE(Sampler, samplerTypes); -TYPETABLE(SampledImage, sampledImageTypes); -TYPETABLE(Function, functionTypes); +TYPETABLE(Scalar, scalarTypeToId); +TYPETABLE(Vector, vectorTypeToId); +TYPETABLE(Matrix, matrixTypeToId); +TYPETABLE(Pointer, pointerTypeToId); +TYPETABLE(Image, imageTypeToId); +TYPETABLE(Sampler, samplerTypeToId); +TYPETABLE(SampledImage, sampledImageTypeToId); +TYPETABLE(FunctionType, functionTypeToId); }; // namespace rdcspv @@ -852,6 +581,8 @@ static void RemoveSection(std::vector &spirv, size_t offsets[rdcspv::S { rdcspv::Editor ed(spirv); + ed.Prepare(); + for(rdcspv::Iter it = ed.Begin(section), end = ed.End(section); it < end; it++) ed.Remove(it); @@ -883,7 +614,7 @@ static void CheckSPIRV(rdcspv::Editor &ed, size_t offsets[rdcspv::Section::Count // should only be one entry point REQUIRE(ed.GetEntries().size() == 1); - rdcspv::Id entryId = ed.GetEntries()[0].entryPoint; + rdcspv::Id entryId = ed.GetEntries()[0].id; // check that the iterator places us precisely at the start of the functions section CHECK(ed.GetID(entryId).offs() == ed.Begin(rdcspv::Section::Functions).offs()); @@ -956,6 +687,8 @@ void main() { { rdcspv::Editor ed(spirv); + ed.Prepare(); + CheckSPIRV(ed, offsets); } @@ -967,6 +700,8 @@ void main() { { rdcspv::Editor ed(spirv); + ed.Prepare(); + CheckSPIRV(ed, offsets); } @@ -976,6 +711,8 @@ void main() { { rdcspv::Editor ed(spirv); + ed.Prepare(); + CheckSPIRV(ed, offsets); } @@ -985,6 +722,8 @@ void main() { { rdcspv::Editor ed(spirv); + ed.Prepare(); + CheckSPIRV(ed, offsets); } @@ -994,6 +733,8 @@ void main() { { rdcspv::Editor ed(spirv); + ed.Prepare(); + CheckSPIRV(ed, offsets); } @@ -1003,6 +744,8 @@ void main() { { rdcspv::Editor ed(spirv); + ed.Prepare(); + CheckSPIRV(ed, offsets); } } diff --git a/renderdoc/driver/shaders/spirv/spirv_editor.h b/renderdoc/driver/shaders/spirv/spirv_editor.h index 5e3b2fe2a..ac4a678ee 100644 --- a/renderdoc/driver/shaders/spirv/spirv_editor.h +++ b/renderdoc/driver/shaders/spirv/spirv_editor.h @@ -33,6 +33,7 @@ #include "common/common.h" #include "spirv_common.h" #include "spirv_op_helpers.h" +#include "spirv_processor.h" namespace rdcspv { @@ -56,241 +57,19 @@ struct Binding bool operator==(const Binding &o) const { return set == o.set && binding == o.binding; } }; -struct Scalar -{ - Scalar() : type(Op::Max), width(0), signedness(false) {} - constexpr Scalar(Op t, uint32_t w, bool s) : type(t), width(w), signedness(s) {} - Scalar(Iter op); +template +using TypeToId = std::pair; - Op type; - uint32_t width; - bool signedness; +template +using TypeToIds = std::vector>; - bool operator<(const Scalar &o) const - { - if(type != o.type) - return type < o.type; - if(signedness != o.signedness) - return signedness < o.signedness; - return width < o.width; - } - - bool operator!=(const Scalar &o) const { return !operator==(o); } - bool operator==(const Scalar &o) const - { - return type == o.type && width == o.width && signedness == o.signedness; - } - - Operation decl(Editor &editor) const - { - if(type == Op::TypeVoid) - return OpTypeVoid(Id()); - else if(type == Op::TypeBool) - return OpTypeBool(Id()); - else if(type == Op::TypeFloat) - return OpTypeFloat(Id(), width); - else if(type == Op::TypeInt) - return OpTypeInt(Id(), width, signedness ? 1U : 0U); - else - return OpNop(); - } -}; - -// helper to create Scalar objects for known types -template -inline constexpr Scalar scalar(); - -#define SCALAR_TYPE(ctype, op, width, sign) \ - template <> \ - inline constexpr Scalar scalar() \ - { \ - return Scalar(op, width, sign); \ - } - -SCALAR_TYPE(void, Op::TypeVoid, 0, false); -SCALAR_TYPE(bool, Op::TypeBool, 0, false); -SCALAR_TYPE(uint8_t, Op::TypeInt, 8, false); -SCALAR_TYPE(uint16_t, Op::TypeInt, 16, false); -SCALAR_TYPE(uint32_t, Op::TypeInt, 32, false); -SCALAR_TYPE(uint64_t, Op::TypeInt, 64, false); -SCALAR_TYPE(int8_t, Op::TypeInt, 8, true); -SCALAR_TYPE(int16_t, Op::TypeInt, 16, true); -SCALAR_TYPE(int32_t, Op::TypeInt, 32, true); -SCALAR_TYPE(int64_t, Op::TypeInt, 64, true); -SCALAR_TYPE(float, Op::TypeFloat, 32, false); -SCALAR_TYPE(double, Op::TypeFloat, 64, false); - -struct Vector -{ - Vector(const Scalar &s, uint32_t c) : scalar(s), count(c) {} - Scalar scalar; - uint32_t count; - - bool operator<(const Vector &o) const - { - if(scalar != o.scalar) - return scalar < o.scalar; - return count < o.count; - } - - bool operator!=(const Vector &o) const { return !operator==(o); } - bool operator==(const Vector &o) const { return scalar == o.scalar && count == o.count; } - Operation decl(Editor &editor) const; -}; - -struct Matrix -{ - Matrix(const Vector &v, uint32_t c) : vector(v), count(c) {} - Vector vector; - uint32_t count; - - bool operator<(const Matrix &o) const - { - if(vector != o.vector) - return vector < o.vector; - return count < o.count; - } - - bool operator!=(const Matrix &o) const { return !operator==(o); } - bool operator==(const Matrix &o) const { return vector == o.vector && count == o.count; } - Operation decl(Editor &editor) const; -}; - -struct Pointer -{ - Pointer(Id b, StorageClass s) : baseId(b), storage(s) {} - Id baseId; - StorageClass storage; - - bool operator<(const Pointer &o) const - { - if(baseId != o.baseId) - return baseId < o.baseId; - return storage < o.storage; - } - - bool operator!=(const Pointer &o) const { return !operator==(o); } - bool operator==(const Pointer &o) const { return baseId == o.baseId && storage == o.storage; } - Operation decl(Editor &editor) const; -}; - -struct Image -{ - Image(Scalar ret, Dim d, uint32_t dp, uint32_t ar, uint32_t m, uint32_t samp, ImageFormat f) - : retType(ret), dim(d), depth(dp), arrayed(ar), ms(m), sampled(samp), format(f) - { - } - - Scalar retType; - Dim dim; - uint32_t depth; - uint32_t arrayed; - uint32_t ms; - uint32_t sampled; - ImageFormat format; - - bool operator<(const Image &o) const - { - if(retType != o.retType) - return retType < o.retType; - if(dim != o.dim) - return dim < o.dim; - if(depth != o.depth) - return depth < o.depth; - if(arrayed != o.arrayed) - return arrayed < o.arrayed; - if(ms != o.ms) - return ms < o.ms; - if(sampled != o.sampled) - return sampled < o.sampled; - return format < o.format; - } - bool operator!=(const Image &o) const { return !operator==(o); } - bool operator==(const Image &o) const - { - return retType == o.retType && dim == o.dim && depth == o.depth && arrayed == o.arrayed && - ms == o.ms && sampled == o.sampled && format == o.format; - } - Operation decl(Editor &editor) const; -}; - -struct Sampler -{ - // no properties, all sampler types are equal - bool operator<(const Sampler &o) const { return false; } - bool operator!=(const Sampler &o) const { return false; } - bool operator==(const Sampler &o) const { return true; } - Operation decl(Editor &editor) const; -}; - -struct SampledImage -{ - SampledImage(Id b) : baseId(b) {} - Id baseId; - - bool operator<(const SampledImage &o) const { return baseId < o.baseId; } - bool operator!=(const SampledImage &o) const { return !operator==(o); } - bool operator==(const SampledImage &o) const { return baseId == o.baseId; } - Operation decl(Editor &editor) const; -}; - -struct Function -{ - Function(Id ret, const rdcarray &args) : returnId(ret), argumentIds(args) {} - Id returnId; - rdcarray argumentIds; - - bool operator<(const Function &o) const - { - if(returnId != o.returnId) - return returnId < o.returnId; - return argumentIds < o.argumentIds; - } - - bool operator!=(const Function &o) const { return !operator==(o); } - bool operator==(const Function &o) const - { - return returnId == o.returnId && argumentIds == o.argumentIds; - } - Operation decl(Editor &editor) const; -}; - -template -using TypeId = std::pair; - -template -using TypeIds = std::vector>; - -// hack around enum class being useless for array indices :( -struct Section -{ - enum Type - { - Capabilities, - First = Capabilities, - Extensions, - ExtInst, - MemoryModel, - EntryPoints, - ExecutionMode, - Debug, - Annotations, - TypesVariablesConstants, - // handy aliases - Types = TypesVariablesConstants, - Variables = TypesVariablesConstants, - Constants = TypesVariablesConstants, - Functions, - Count, - }; -}; - -class Editor +class Editor : public Processor { public: Editor(std::vector &spirvWords); - ~Editor() { StripNops(); } - void StripNops(); + ~Editor(); + + void Prepare(); Id MakeId(); @@ -325,33 +104,33 @@ public: // the entry point has 'two' opcodes, the entrypoint declaration and the function. // This returns the first, GetID returns the second. Iter GetEntry(Id id); - Iter Begin(Section::Type section) { return Iter(spirv, sections[section].startOffset); } - Iter End(Section::Type section) { return Iter(spirv, sections[section].endOffset); } + Iter Begin(Section::Type section) { return Iter(m_SPIRV, m_Sections[section].startOffset); } + Iter End(Section::Type section) { return Iter(m_SPIRV, m_Sections[section].endOffset); } // fetches the id of this type. If it exists already the old ID will be returned, otherwise it // will be declared and the new ID returned - template - Id DeclareType(const Type &t) + template + Id DeclareType(const SPIRVType &t) { - std::map &table = GetTable(); + std::map &table = GetTable(); auto it = table.lower_bound(t); if(it != table.end() && it->first == t) return it->second; - Operation decl = t.decl(*this); + Operation decl = MakeDeclaration(t); Id id = MakeId(); decl[1] = id.value(); AddType(decl); - table.insert(it, std::pair(t, id)); + table.insert(it, std::pair(t, id)); return id; } - template - Id GetType(const Type &t) + template + Id GetType(const SPIRVType &t) { - std::map &table = GetTable(); + std::map &table = GetTable(); auto it = table.find(t); if(it != table.end()) @@ -360,12 +139,12 @@ public: return Id(); } - template - TypeIds GetTypes() + template + TypeToIds GetTypes() { - std::map &table = GetTable(); + std::map &table = GetTable(); - TypeIds ret; + TypeToIds ret; for(auto it = table.begin(); it != table.end(); ++it) ret.push_back(*it); @@ -373,10 +152,10 @@ public: return ret; } - template - const std::map &GetTypeInfo() const + template + const std::map &GetTypeInfo() const { - return GetTable(); + return GetTable(); } Binding GetBinding(Id id) const @@ -386,7 +165,7 @@ public: return Binding(); return it->second; } - const std::set &GetStructTypes() const { return structTypes; } + Id DeclareStructType(const std::vector &members); // helper for AddConstant @@ -403,62 +182,41 @@ public: return AddConstant(Operation(Op::Constant, words)); } - // accessors to structs/vectors of data - const std::vector &GetEntries() { return entries; } - const std::vector &GetVariables() { return variables; } - const std::vector &GetFunctions() { return functions; } - Id GetIDType(Id id) { return idTypes[id.value()]; } private: + using Processor::Parse; inline void addWords(size_t offs, size_t num) { addWords(offs, (int32_t)num); } void addWords(size_t offs, int32_t num); - void RegisterOp(Iter iter); - void UnregisterOp(Iter iter); + Operation MakeDeclaration(const Scalar &s); + Operation MakeDeclaration(const Vector &v); + Operation MakeDeclaration(const Matrix &m); + Operation MakeDeclaration(const Pointer &p); + Operation MakeDeclaration(const Image &i); + Operation MakeDeclaration(const Sampler &s); + Operation MakeDeclaration(const SampledImage &s); + Operation MakeDeclaration(const FunctionType &f); - struct LogicalSection - { - size_t startOffset = 0; - size_t endOffset = 0; - }; - - LogicalSection sections[Section::Count]; - - AddressingModel addressmodel; - MemoryModel memorymodel; - - std::vector decorations; + virtual void RegisterOp(Iter iter); + virtual void UnregisterOp(Iter iter); std::map bindings; - std::vector idOffsets; - std::vector idTypes; + std::map scalarTypeToId; + std::map vectorTypeToId; + std::map matrixTypeToId; + std::map pointerTypeToId; + std::map imageTypeToId; + std::map samplerTypeToId; + std::map sampledImageTypeToId; + std::map functionTypeToId; - std::vector entries; - std::vector variables; - std::vector functions; - std::set extensions; - std::set capabilities; + template + std::map &GetTable(); - std::map extSets; + template + const std::map &GetTable() const; - std::map scalarTypes; - std::map vectorTypes; - std::map matrixTypes; - std::map pointerTypes; - std::map imageTypes; - std::map samplerTypes; - std::map sampledImageTypes; - std::map functionTypes; - - std::set structTypes; - - template - std::map &GetTable(); - - template - const std::map &GetTable() const; - - std::vector &spirv; + std::vector &m_ExternalSPIRV; }; inline bool operator<(const OpDecorate &a, const OpDecorate &b) diff --git a/renderdoc/driver/shaders/spirv/spirv_processor.cpp b/renderdoc/driver/shaders/spirv/spirv_processor.cpp new file mode 100644 index 000000000..f84cd3f2d --- /dev/null +++ b/renderdoc/driver/shaders/spirv/spirv_processor.cpp @@ -0,0 +1,348 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2019 Baldur Karlsson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + +#include "spirv_processor.h" +#include "spirv_op_helpers.h" + +namespace rdcspv +{ +Processor::Processor() +{ +} + +void Processor::Parse(const std::vector &spirvWords) +{ + m_SPIRV = spirvWords; + + if(m_SPIRV.size() < FirstRealWord || m_SPIRV[0] != MagicNumber) + { + RDCERR("Empty or invalid SPIR-V module"); + m_SPIRV.clear(); + return; + } + + // [4] is reserved + RDCASSERT(m_SPIRV[4] == 0); + + PreParse(m_SPIRV[3]); + + // simple state machine to track which section we're in. + // Note that a couple of sections are optional and could be skipped over, at which point we insert + // a dummy OpNop so they're not empty (which will be stripped later) and record them as in + // between. + // + // We only handle single-shader modules at the moment, so some things are required by virtue of + // being required in a shader - e.g. at least the Shader capability, at least one entry point, etc + // + // Capabilities: REQUIRED (we assume - must declare Shader capability) + // Extensions: OPTIONAL + // ExtInst: OPTIONAL + // MemoryModel: REQUIRED (required by spec) + // EntryPoints: REQUIRED (we assume) + // ExecutionMode: OPTIONAL + // Debug: OPTIONAL + // Annotations: OPTIONAL (in theory - would require empty shader) + // TypesVariables: REQUIRED (must at least have the entry point function type) + // Functions: REQUIRED (must have the entry point) + + // set the book-ends: start of the first section and end of the last + m_Sections[Section::Count - 1].endOffset = m_SPIRV.size(); + +#define START_SECTION(section) \ + if(m_Sections[section].startOffset == 0) \ + m_Sections[section].startOffset = it.offs(); + + for(Iter it(m_SPIRV, FirstRealWord); it; it++) + { + Op opcode = it.opcode(); + + if(opcode == Op::Capability) + { + START_SECTION(Section::Capabilities); + } + else if(opcode == Op::Extension) + { + START_SECTION(Section::Extensions); + } + else if(opcode == Op::ExtInstImport) + { + START_SECTION(Section::ExtInst); + } + else if(opcode == Op::MemoryModel) + { + START_SECTION(Section::MemoryModel); + } + else if(opcode == Op::EntryPoint) + { + START_SECTION(Section::EntryPoints); + } + else if(opcode == Op::ExecutionMode || opcode == Op::ExecutionModeId) + { + START_SECTION(Section::ExecutionMode); + } + else if(opcode == Op::String || opcode == Op::Source || opcode == Op::SourceContinued || + opcode == Op::SourceExtension || opcode == Op::Name || opcode == Op::MemberName || + opcode == Op::ModuleProcessed) + { + START_SECTION(Section::Debug); + } + else if(opcode == Op::Decorate || opcode == Op::MemberDecorate || opcode == Op::GroupDecorate || + opcode == Op::GroupMemberDecorate || opcode == Op::DecorationGroup || + opcode == Op::DecorateStringGOOGLE || opcode == Op::MemberDecorateStringGOOGLE) + { + START_SECTION(Section::Annotations); + } + else if(opcode == Op::Function) + { + START_SECTION(Section::Functions); + } + else + { + // if we've reached another instruction, check if we've reached the function section yet. If + // we have then assume it's an instruction inside a function and ignore. If we haven't, assume + // it's a type/variable/constant type instruction + if(m_Sections[Section::Functions].startOffset == 0) + { + START_SECTION(Section::TypesVariablesConstants); + } + } + + RegisterOp(it); + } + +#undef START_SECTION + + PostParse(); + + // ensure we got everything right. First section should start at the beginning + RDCASSERTEQUAL(m_Sections[Section::First].startOffset, FirstRealWord); + + // we now set the endOffset of each section to the start of the next. Any empty sections + // temporarily have startOffset set to endOffset, we'll pad them with a nop below. + for(int s = Section::Count - 1; s > 0; s--) + { + RDCASSERTEQUAL(m_Sections[s - 1].endOffset, 0); + m_Sections[s - 1].endOffset = m_Sections[s].startOffset; + if(m_Sections[s - 1].startOffset == 0) + m_Sections[s - 1].startOffset = m_Sections[s - 1].endOffset; + } +} + +void Processor::PreParse(uint32_t maxId) +{ + idOffsets.resize(maxId); + idTypes.resize(maxId); +} + +void Processor::RegisterOp(Iter it) +{ + OpDecoder opdata(it); + if(opdata.result != Id() && opdata.resultType != Id()) + { + RDCASSERT(opdata.result.value() < idTypes.size()); + idTypes[opdata.result.value()] = opdata.resultType; + } + + if(opdata.result != Id()) + idOffsets[opdata.result.value()] = it.offs(); + if(opdata.op == Op::Capability) + { + OpCapability decoded(it); + capabilities.insert(decoded.capability); + } + else if(opdata.op == Op::Extension) + { + OpExtension decoded(it); + extensions.insert(decoded.name); + } + else if(opdata.op == Op::ExtInstImport) + { + OpExtInstImport decoded(it); + extSets[decoded.name] = decoded.result; + } + else if(opdata.op == Op::Function) + { + functions.push_back(opdata.result); + } + else if(opdata.op == Op::EntryPoint) + { + OpEntryPoint decoded(it); + entries.push_back(EntryPoint(decoded.executionModel, decoded.entryPoint, decoded.name)); + } + else if(opdata.op == Op::Variable) + { + OpVariable decoded(it); + + // only register global variables here + if(decoded.storageClass != rdcspv::StorageClass::Function) + globals.push_back(Variable(decoded.resultType, decoded.result, decoded.storageClass)); + } + else if(opdata.op == Op::TypeVoid || opdata.op == Op::TypeBool || opdata.op == Op::TypeInt || + opdata.op == Op::TypeFloat) + { + scalarTypes[opdata.result] = Scalar(it); + } + else if(opdata.op == Op::TypeVector) + { + OpTypeVector decoded(it); + + vectorTypes[opdata.result] = Vector(scalarTypes[decoded.componentType], decoded.componentCount); + } + else if(opdata.op == Op::TypeMatrix) + { + OpTypeMatrix decoded(it); + + matrixTypes[opdata.result] = Matrix(vectorTypes[decoded.columnType], decoded.columnCount); + } + else if(opdata.op == Op::TypeImage) + { + OpTypeImage decoded(it); + + imageTypes[opdata.result] = + Image(scalarTypes[decoded.sampledType], decoded.dim, decoded.depth, decoded.arrayed, + decoded.mS, decoded.sampled, decoded.imageFormat); + } + else if(opdata.op == Op::TypeSampler) + { + samplerTypes[opdata.result] = Sampler(); + } + else if(opdata.op == Op::TypeSampledImage) + { + OpTypeSampledImage decoded(it); + + sampledImageTypes[decoded.result] = SampledImage(decoded.imageType); + } + else if(opdata.op == Op::TypePointer) + { + OpTypePointer decoded(it); + + pointerTypes[decoded.result] = Pointer(decoded.type, decoded.storageClass); + } + else if(opdata.op == Op::TypeFunction) + { + OpTypeFunction decoded(it); + + functionTypes[decoded.result] = FunctionType(decoded.returnType, decoded.parameters); + } +} + +void Processor::UnregisterOp(Iter it) +{ + OpDecoder opdata(it); + if(opdata.result != Id() && opdata.resultType != Id()) + idTypes[opdata.result.value()] = Id(); + + if(opdata.result != Id()) + idOffsets[opdata.result.value()] = 0; + + if(opdata.op == Op::Capability) + { + OpCapability decoded(it); + capabilities.erase(decoded.capability); + } + else if(opdata.op == Op::Extension) + { + OpExtension decoded(it); + extensions.erase(decoded.name); + } + else if(opdata.op == Op::ExtInstImport) + { + OpExtInstImport decoded(it); + extSets.erase(decoded.name); + } + else if(opdata.op == Op::Function) + { + for(auto funcIt = functions.begin(); funcIt != functions.end(); ++funcIt) + { + if(*funcIt == opdata.result) + { + functions.erase(funcIt); + break; + } + } + } + else if(opdata.op == Op::EntryPoint) + { + OpEntryPoint decoded(it); + + for(auto entryIt = entries.begin(); entryIt != entries.end(); ++entryIt) + { + if(entryIt->id == decoded.entryPoint) + { + entries.erase(entryIt); + break; + } + } + } + else if(opdata.op == Op::Variable) + { + for(auto varIt = globals.begin(); varIt != globals.end(); ++varIt) + { + if(varIt->id == opdata.result) + { + globals.erase(varIt); + break; + } + } + } + else if(opdata.op == Op::TypeVoid || opdata.op == Op::TypeBool || opdata.op == Op::TypeInt || + opdata.op == Op::TypeFloat) + { + scalarTypes.erase(opdata.result); + } + else if(opdata.op == Op::TypeVector) + { + vectorTypes.erase(opdata.result); + } + else if(opdata.op == Op::TypeMatrix) + { + matrixTypes.erase(opdata.result); + } + else if(opdata.op == Op::TypeImage) + { + imageTypes.erase(opdata.result); + } + else if(opdata.op == Op::TypeSampler) + { + samplerTypes.erase(opdata.result); + } + else if(opdata.op == Op::TypeSampledImage) + { + sampledImageTypes.erase(opdata.result); + } + else if(opdata.op == Op::TypePointer) + { + pointerTypes.erase(opdata.result); + } + else if(opdata.op == Op::TypeFunction) + { + functionTypes.erase(opdata.result); + } +} + +void Processor::PostParse() +{ +} + +}; // namespace rdcspv \ No newline at end of file diff --git a/renderdoc/driver/shaders/spirv/spirv_processor.h b/renderdoc/driver/shaders/spirv/spirv_processor.h new file mode 100644 index 000000000..280953e8c --- /dev/null +++ b/renderdoc/driver/shaders/spirv/spirv_processor.h @@ -0,0 +1,347 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2019 Baldur Karlsson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include +#include "common/common.h" +#include "spirv_common.h" + +namespace rdcspv +{ +struct Scalar +{ + Scalar() : type(Op::Max), width(0), signedness(false) {} + constexpr Scalar(Op t, uint32_t w, bool s) : type(t), width(w), signedness(s) {} + Scalar(Iter op); + + Op type; + uint32_t width; + bool signedness; + + bool operator<(const Scalar &o) const + { + if(type != o.type) + return type < o.type; + if(signedness != o.signedness) + return signedness < o.signedness; + return width < o.width; + } + + bool operator!=(const Scalar &o) const { return !operator==(o); } + bool operator==(const Scalar &o) const + { + return type == o.type && width == o.width && signedness == o.signedness; + } +}; + +// helper to create Scalar objects for known types +template +inline constexpr Scalar scalar(); + +#define SCALAR_TYPE(ctype, op, width, sign) \ + template <> \ + inline constexpr Scalar scalar() \ + { \ + return Scalar(op, width, sign); \ + } + +SCALAR_TYPE(void, Op::TypeVoid, 0, false); +SCALAR_TYPE(bool, Op::TypeBool, 0, false); +SCALAR_TYPE(uint8_t, Op::TypeInt, 8, false); +SCALAR_TYPE(uint16_t, Op::TypeInt, 16, false); +SCALAR_TYPE(uint32_t, Op::TypeInt, 32, false); +SCALAR_TYPE(uint64_t, Op::TypeInt, 64, false); +SCALAR_TYPE(int8_t, Op::TypeInt, 8, true); +SCALAR_TYPE(int16_t, Op::TypeInt, 16, true); +SCALAR_TYPE(int32_t, Op::TypeInt, 32, true); +SCALAR_TYPE(int64_t, Op::TypeInt, 64, true); +SCALAR_TYPE(float, Op::TypeFloat, 32, false); +SCALAR_TYPE(double, Op::TypeFloat, 64, false); + +struct Vector +{ + Vector() : scalar(), count(0) {} + Vector(const Scalar &s, uint32_t c) : scalar(s), count(c) {} + Scalar scalar; + uint32_t count; + + bool operator<(const Vector &o) const + { + if(scalar != o.scalar) + return scalar < o.scalar; + return count < o.count; + } + + bool operator!=(const Vector &o) const { return !operator==(o); } + bool operator==(const Vector &o) const { return scalar == o.scalar && count == o.count; } +}; + +struct Matrix +{ + Matrix() : vector(), count(0) {} + Matrix(const Vector &v, uint32_t c) : vector(v), count(c) {} + Vector vector; + uint32_t count; + + bool operator<(const Matrix &o) const + { + if(vector != o.vector) + return vector < o.vector; + return count < o.count; + } + + bool operator!=(const Matrix &o) const { return !operator==(o); } + bool operator==(const Matrix &o) const { return vector == o.vector && count == o.count; } +}; + +struct Pointer +{ + Pointer() : baseId(), storage(StorageClass::Max) {} + Pointer(Id b, StorageClass s) : baseId(b), storage(s) {} + Id baseId; + StorageClass storage; + + bool operator<(const Pointer &o) const + { + if(baseId != o.baseId) + return baseId < o.baseId; + return storage < o.storage; + } + + bool operator!=(const Pointer &o) const { return !operator==(o); } + bool operator==(const Pointer &o) const { return baseId == o.baseId && storage == o.storage; } +}; + +struct Image +{ + Image() + : retType(), dim(Dim::Max), depth(0), arrayed(0), ms(0), sampled(0), format(ImageFormat::Max) + { + } + Image(Scalar ret, Dim d, uint32_t dp, uint32_t ar, uint32_t m, uint32_t samp, ImageFormat f) + : retType(ret), dim(d), depth(dp), arrayed(ar), ms(m), sampled(samp), format(f) + { + } + + Scalar retType; + Dim dim; + uint32_t depth; + uint32_t arrayed; + uint32_t ms; + uint32_t sampled; + ImageFormat format; + + bool operator<(const Image &o) const + { + if(retType != o.retType) + return retType < o.retType; + if(dim != o.dim) + return dim < o.dim; + if(depth != o.depth) + return depth < o.depth; + if(arrayed != o.arrayed) + return arrayed < o.arrayed; + if(ms != o.ms) + return ms < o.ms; + if(sampled != o.sampled) + return sampled < o.sampled; + return format < o.format; + } + bool operator!=(const Image &o) const { return !operator==(o); } + bool operator==(const Image &o) const + { + return retType == o.retType && dim == o.dim && depth == o.depth && arrayed == o.arrayed && + ms == o.ms && sampled == o.sampled && format == o.format; + } +}; + +struct Sampler +{ + // no properties, all sampler types are equal + bool operator<(const Sampler &o) const { return false; } + bool operator!=(const Sampler &o) const { return false; } + bool operator==(const Sampler &o) const { return true; } +}; + +struct SampledImage +{ + SampledImage() = default; + SampledImage(Id b) : baseId(b) {} + Id baseId; + + bool operator<(const SampledImage &o) const { return baseId < o.baseId; } + bool operator!=(const SampledImage &o) const { return !operator==(o); } + bool operator==(const SampledImage &o) const { return baseId == o.baseId; } +}; + +struct FunctionType +{ + FunctionType() = default; + FunctionType(Id ret, const rdcarray &args) : returnId(ret), argumentIds(args) {} + Id returnId; + rdcarray argumentIds; + + bool operator<(const FunctionType &o) const + { + if(returnId != o.returnId) + return returnId < o.returnId; + return argumentIds < o.argumentIds; + } + + bool operator!=(const FunctionType &o) const { return !operator==(o); } + bool operator==(const FunctionType &o) const + { + return returnId == o.returnId && argumentIds == o.argumentIds; + } +}; + +struct EntryPoint +{ + EntryPoint() = default; + EntryPoint(ExecutionModel e, Id i, rdcstr n) : executionModel(e), id(i), name(n) {} + ExecutionModel executionModel; + Id id; + rdcstr name; + + bool operator<(const EntryPoint &o) const + { + if(id != o.id) + return id < o.id; + return name < o.name; + } + + bool operator!=(const EntryPoint &o) const { return !operator==(o); } + bool operator==(const EntryPoint &o) const { return id == o.id && name == o.name; } +}; + +struct Variable +{ + Variable() = default; + Variable(Id t, Id i, StorageClass s) : type(t), id(i), storage(s) {} + Id type; + Id id; + StorageClass storage; + + bool operator<(const Variable &o) const + { + if(id != o.id) + return id < o.id; + if(type != o.type) + return type < o.type; + return storage < o.storage; + } + + bool operator!=(const Variable &o) const { return !operator==(o); } + bool operator==(const Variable &o) const + { + return id == o.id && type == o.type && storage == o.storage; + } +}; + +// hack around enum class being useless for array indices :( +struct Section +{ + enum Type + { + Capabilities, + First = Capabilities, + Extensions, + ExtInst, + MemoryModel, + EntryPoints, + ExecutionMode, + Debug, + Annotations, + TypesVariablesConstants, + // handy aliases + Types = TypesVariablesConstants, + Variables = TypesVariablesConstants, + Constants = TypesVariablesConstants, + Functions, + Count, + }; +}; + +class Processor +{ +public: + Processor(); + + // accessors to structs/vectors of data + const std::vector &GetFunctions() { return functions; } + const std::vector &GetEntries() { return entries; } + const std::vector &GetGlobals() { return globals; } + Id GetIDType(Id id) { return idTypes[id.value()]; } +protected: + virtual void Parse(const std::vector &spirvWords); + + std::vector m_SPIRV; + + // before parsing - e.g. to prepare any arrays that are max-id sized + virtual void PreParse(uint32_t maxId); + // even though we only need UnregisterOp when editing, we declare it here for ease of organisation + // so we can define pairs of logic for the same things. Rather than having mismatched code with + // the register of some map in one place and the unregister somewhere else that would be easy to + // break. + virtual void RegisterOp(Iter iter); + virtual void UnregisterOp(Iter iter); + // after parsing - e.g. to do any deferred post-processing + virtual void PostParse(); + + std::vector idOffsets; + std::vector idTypes; + + std::vector functions; + std::vector entries; + std::vector globals; + + std::set extensions; + std::set capabilities; + + std::map scalarTypes; + std::map vectorTypes; + std::map matrixTypes; + std::map pointerTypes; + std::map imageTypes; + std::map samplerTypes; + std::map sampledImageTypes; + std::map functionTypes; + + std::map extSets; + + struct LogicalSection + { + size_t startOffset = 0; + size_t endOffset = 0; + }; + + LogicalSection m_Sections[Section::Count]; +}; + +}; // namespace rdcspv \ No newline at end of file diff --git a/renderdoc/driver/shaders/spirv/spirv_reflect.cpp b/renderdoc/driver/shaders/spirv/spirv_reflect.cpp index e09b503e5..4765cf91b 100644 --- a/renderdoc/driver/shaders/spirv/spirv_reflect.cpp +++ b/renderdoc/driver/shaders/spirv/spirv_reflect.cpp @@ -59,15 +59,17 @@ void AddXFBAnnotations(const ShaderReflection &refl, const SPIRVPatchData &patch { rdcspv::Editor editor(modSpirv); + editor.Prepare(); + rdcarray outsig = refl.outputSignature; std::vector outpatch = patchData.outputs; rdcspv::Id entryid; - for(const rdcspv::OpEntryPoint &entry : editor.GetEntries()) + for(const rdcspv::EntryPoint &entry : editor.GetEntries()) { if(entry.name == entryName) { - entryid = entry.entryPoint; + entryid = entry.id; break; } } diff --git a/renderdoc/driver/vulkan/vk_bindless_feedback.cpp b/renderdoc/driver/vulkan/vk_bindless_feedback.cpp index 094aba3a3..79c8d9de4 100644 --- a/renderdoc/driver/vulkan/vk_bindless_feedback.cpp +++ b/renderdoc/driver/vulkan/vk_bindless_feedback.cpp @@ -41,6 +41,8 @@ void AnnotateShader(const SPIRVPatchData &patchData, const char *entryName, { rdcspv::Editor editor(modSpirv); + editor.Prepare(); + const bool useBufferAddress = (addr != 0); rdcspv::Id uint32ID = editor.DeclareType(rdcspv::scalar()); @@ -84,16 +86,16 @@ void AnnotateShader(const SPIRVPatchData &patchData, const char *entryName, // iterate over all variables. We do this here because in the absence of the buffer address // extension we might declare our own below and patch bindings - so we need to look these up now - for(const rdcspv::OpVariable &var : editor.GetVariables()) + for(const rdcspv::Variable &var : editor.GetGlobals()) { // skip variables without one of these storage classes, as they are not descriptors - if(var.storageClass != rdcspv::StorageClass::UniformConstant && - var.storageClass != rdcspv::StorageClass::Uniform && - var.storageClass != rdcspv::StorageClass::StorageBuffer) + if(var.storage != rdcspv::StorageClass::UniformConstant && + var.storage != rdcspv::StorageClass::Uniform && + var.storage != rdcspv::StorageClass::StorageBuffer) continue; // get this variable's binding info - rdcspv::Binding bind = editor.GetBinding(var.result); + rdcspv::Binding bind = editor.GetBinding(var.id); // if this is one of the bindings we care about auto it = offsetMap.find(bind); @@ -102,8 +104,7 @@ void AnnotateShader(const SPIRVPatchData &patchData, const char *entryName, // store the offset for this variable so we watch for access chains and know where to store to if(useBufferAddress) { - rdcspv::Id id = varLookup[var.result] = - editor.AddConstantImmediate(it->second.offset); + rdcspv::Id id = varLookup[var.id] = editor.AddConstantImmediate(it->second.offset); editor.SetName( id, StringFormat::Fmt("__feedbackOffset_set%u_bind%u", it->first.set, it->first.binding) @@ -114,8 +115,7 @@ void AnnotateShader(const SPIRVPatchData &patchData, const char *entryName, // check that the offset fits in 32-bit word, convert byte offset to uint32 index uint64_t index = it->second.offset / 4; RDCASSERT(index < 0xFFFFFFFFULL, bind.set, bind.binding, it->second.offset); - rdcspv::Id id = varLookup[var.result] = - editor.AddConstantImmediate(uint32_t(index)); + rdcspv::Id id = varLookup[var.id] = editor.AddConstantImmediate(uint32_t(index)); editor.SetName( id, StringFormat::Fmt("__feedbackIndex_set%u_bind%u", it->first.set, it->first.binding) @@ -209,16 +209,16 @@ void AnnotateShader(const SPIRVPatchData &patchData, const char *entryName, intTypeLookup[scalarType.second] = scalarType.first; rdcspv::Id entryID; - for(const rdcspv::OpEntryPoint &entry : editor.GetEntries()) + for(const rdcspv::EntryPoint &entry : editor.GetEntries()) { if(entry.name == entryName) { - entryID = entry.entryPoint; + entryID = entry.id; break; } } - rdcspv::TypeIds funcTypes = editor.GetTypes(); + rdcspv::TypeToIds funcTypes = editor.GetTypes(); // functions that have been patched with annotation & extra function parameters if needed std::set patchedFunctions; @@ -254,11 +254,11 @@ void AnnotateShader(const SPIRVPatchData &patchData, const char *entryName, rdcspv::OpFunction func(it); // find the function's type declaration, add the necessary arguments, redeclare and patch it - for(const rdcspv::TypeId &funcType : funcTypes) + for(const rdcspv::TypeToId &funcType : funcTypes) { if(funcType.second == func.functionType) { - rdcspv::Function patchedFuncType = funcType.first; + rdcspv::FunctionType patchedFuncType = funcType.first; for(size_t i = 0; i < patchArgIndices.size(); i++) patchedFuncType.argumentIds.push_back(funcParamType); diff --git a/renderdoc/driver/vulkan/vk_postvs.cpp b/renderdoc/driver/vulkan/vk_postvs.cpp index 93e801828..5e679bff4 100644 --- a/renderdoc/driver/vulkan/vk_postvs.cpp +++ b/renderdoc/driver/vulkan/vk_postvs.cpp @@ -56,6 +56,8 @@ static void ConvertToMeshOutputCompute(const ShaderReflection &refl, const SPIRV { rdcspv::Editor editor(modSpirv); + editor.Prepare(); + uint32_t numInputs = (uint32_t)refl.inputSignature.size(); uint32_t numOutputs = (uint32_t)refl.outputSignature.size(); @@ -348,12 +350,12 @@ static void ConvertToMeshOutputCompute(const ShaderReflection &refl, const SPIRV std::set entries; - for(const rdcspv::OpEntryPoint &entry : editor.GetEntries()) + for(const rdcspv::EntryPoint &entry : editor.GetEntries()) { if(entry.name == entryName) - entryID = entry.entryPoint; + entryID = entry.id; - entries.insert(entry.entryPoint); + entries.insert(entry.id); } RDCASSERT(entryID); @@ -740,7 +742,7 @@ static void ConvertToMeshOutputCompute(const ShaderReflection &refl, const SPIRV std::vector ops; rdcspv::Id voidType = editor.DeclareType(rdcspv::scalar()); - rdcspv::Id funcType = editor.DeclareType(rdcspv::Function(voidType, {})); + rdcspv::Id funcType = editor.DeclareType(rdcspv::FunctionType(voidType, {})); ops.push_back(rdcspv::OpFunction(voidType, wrapperEntry, rdcspv::FunctionControl::None, funcType));