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