Handle all logical sections in SPIRVEditor

This commit is contained in:
baldurk
2018-12-20 16:16:31 +00:00
parent 2af0c8c9f8
commit b006ea81a6
3 changed files with 402 additions and 239 deletions
+339 -210
View File
@@ -143,169 +143,135 @@ SPIRVEditor::SPIRVEditor(std::vector<uint32_t> &spirvWords) : spirv(spirvWords)
// 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.
enum class SectionState
{
Preamble, // OpCapability, OpExtension, anything before OpEntryPoint
EntryPoints, // REQUIRED: OpEntryPoint
ExecutionMode, // OpExecutionMode
Debug, // OPTIONAL: debug instructions (OpString, OpSource*, Op*Name, etc)
Decoration, // OPTIONAL (technically): Op*Decorate*
TypeVar, // REQUIRED: OpType*, OpVariable
FunctionBodies, // REQUIRED: OpFunction*
} section;
//
// 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)
section = SectionState::Preamble;
// set the book-ends: start of the first section and end of the last
sections[SPIRVSection::Count - 1].endOffset = spirvWords.size();
#define START_SECTION(section) \
if(sections[section].startOffset == 0) \
sections[section].startOffset = it.offset;
for(SPIRVIterator it(spirv, FirstRealWord); it; it++)
{
spv::Op opcode = it.opcode();
if(opcode == spv::OpEntryPoint)
if(opcode == spv::OpCapability)
{
if(section != SectionState::Preamble && section != SectionState::EntryPoints)
RDCERR("Unexpected current section when encountering OpEntryPoint: %d", section);
if(section != SectionState::EntryPoints)
entryPointSection.startOffset = it.offset;
section = SectionState::EntryPoints;
START_SECTION(SPIRVSection::Capabilities);
}
else if(opcode == spv::OpExecutionMode)
else if(opcode == spv::OpExtension)
{
if(section != SectionState::EntryPoints && section != SectionState::ExecutionMode)
RDCERR("Unexpected current section when encountering OpExecutionMode: %d", section);
if(section == SectionState::EntryPoints)
entryPointSection.endOffset = it.offset;
section = SectionState::ExecutionMode;
START_SECTION(SPIRVSection::Extensions);
}
else if(opcode == spv::OpString || opcode == spv::OpSource || opcode == spv::OpSourceContinued ||
opcode == spv::OpSourceExtension || opcode == spv::OpName || opcode == spv::OpMemberName)
else if(opcode == spv::OpExtInstImport)
{
if(section != SectionState::EntryPoints && section != SectionState::ExecutionMode &&
section != SectionState::Debug)
RDCERR("Unexpected current section when encountering debug instruction %s: %d",
ToStr(opcode).c_str(), section);
if(section == SectionState::EntryPoints)
entryPointSection.endOffset = it.offset;
if(section != SectionState::Debug)
debugSection.startOffset = it.offset;
section = SectionState::Debug;
START_SECTION(SPIRVSection::ExtInst);
}
else if(opcode == spv::OpMemoryModel)
{
START_SECTION(SPIRVSection::MemoryModel);
}
else if(opcode == spv::OpEntryPoint)
{
START_SECTION(SPIRVSection::EntryPoints);
}
else if(opcode == spv::OpExecutionMode || opcode == spv::OpExecutionModeId)
{
START_SECTION(SPIRVSection::ExecutionMode);
}
else if(opcode == spv::OpString || opcode == spv::OpSource ||
opcode == spv::OpSourceContinued || opcode == spv::OpSourceExtension ||
opcode == spv::OpName || opcode == spv::OpMemberName || opcode == spv::OpModuleProcessed)
{
START_SECTION(SPIRVSection::Debug);
}
else if(opcode == spv::OpDecorate || opcode == spv::OpMemberDecorate ||
opcode == spv::OpGroupDecorate || opcode == spv::OpGroupMemberDecorate ||
opcode == spv::OpDecorationGroup)
{
if(section != SectionState::EntryPoints && section != SectionState::ExecutionMode &&
section != SectionState::Debug && section != SectionState::Decoration)
RDCERR("Unexpected current section when encountering decoration instruction %s: %d",
ToStr(opcode).c_str(), section);
if(section == SectionState::EntryPoints)
entryPointSection.endOffset = it.offset;
if(section == SectionState::Debug)
{
debugSection.endOffset = it.offset;
}
else if(section != SectionState::Decoration)
{
// coming from some other section that isn't debug, insert a dummy debug section
RDCDEBUG("Debug section is empty, inserting OpNop which will later be stripped");
spirv.insert(spirv.begin() + it.offset, SPV_NOP);
debugSection.startOffset = it.offset;
it.offset++;
debugSection.endOffset = it.offset;
}
if(section != SectionState::Decoration)
decorationSection.startOffset = it.offset;
section = SectionState::Decoration;
START_SECTION(SPIRVSection::Annotations);
}
else if(opcode == spv::OpFunction)
{
if(section != SectionState::FunctionBodies)
{
if(section != SectionState::TypeVar)
RDCERR("Unexpected current section when encountering OpFunction: %d", section);
// we've now met the function bodies
section = SectionState::FunctionBodies;
typeVarSection.endOffset = it.offset;
if(typeVarSection.startOffset == typeVarSection.endOffset || typeVarSection.startOffset == 0)
RDCERR("No types found in this shader! There should be at least one for the entry point");
}
START_SECTION(SPIRVSection::Functions);
}
else
{
// if we've reached another instruction, ignore it if we've reached the function bodies. Also
// ignore during preamble
if(section != SectionState::FunctionBodies && section != SectionState::Preamble)
// 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[SPIRVSection::Functions].startOffset == 0)
{
// if it's an instruction not covered above, and we haven't hit the functions, it's a
// type/variable/constant instruction.
if(section != SectionState::EntryPoints && section != SectionState::ExecutionMode &&
section != SectionState::Debug && section != SectionState::Decoration &&
section != SectionState::TypeVar)
RDCERR("Unexpected current section when encountering type/variable instruction %s: %d",
ToStr(opcode).c_str(), section);
if(section == SectionState::EntryPoints)
entryPointSection.endOffset = it.offset;
if(section == SectionState::Decoration)
{
// if we're coming from decoration, all is well. We inserted a dummy debug section if
// needed above before starting it.
decorationSection.endOffset = it.offset;
}
else if(section == SectionState::Debug)
{
debugSection.endOffset = it.offset;
// if we're coming straight from debug, we need to insert a dummy decoration section
RDCDEBUG("Decoration section is empty, inserting OpNop which will later be stripped");
spirv.insert(spirv.begin() + it.offset, SPV_NOP);
decorationSection.startOffset = it.offset;
it.offset++;
decorationSection.endOffset = it.offset;
}
else if(section != SectionState::TypeVar)
{
// coming from some other section that isn't debug or decoration, insert a dummy debug
// AND a dummy decoration section.
RDCDEBUG("Debug/decoration sections empty, inserting OpNops to later be stripped");
spirv.insert(spirv.begin() + it.offset, SPV_NOP);
debugSection.startOffset = it.offset;
it.offset++;
debugSection.endOffset = it.offset;
spirv.insert(spirv.begin() + it.offset, SPV_NOP);
decorationSection.startOffset = it.offset;
it.offset++;
decorationSection.endOffset = it.offset;
}
if(section != SectionState::TypeVar)
typeVarSection.startOffset = it.offset;
section = SectionState::TypeVar;
START_SECTION(SPIRVSection::TypesVariablesConstants);
}
}
RegisterOp(it);
}
#undef START_SECTION
// ensure we got everything right. First section should start at the beginning
RDCASSERTEQUAL(sections[SPIRVSection::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 = SPIRVSection::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
// into the later section by offset since the offsets overlap). That's why we're adding these
// padding nops in the first place!
for(uint32_t s = 0; s < SPIRVSection::Count; s++)
{
if(sections[s].startOffset == sections[s].endOffset)
{
spirv.insert(spirv.begin() + sections[s].startOffset, SPV_NOP);
sections[s].endOffset++;
for(uint32_t t = s + 1; t < SPIRVSection::Count; t++)
{
sections[t].startOffset++;
sections[t].endOffset++;
}
}
}
// each section should now precisely match each other end-to-end and not be empty
for(uint32_t s = SPIRVSection::First; s < SPIRVSection::Count; s++)
{
RDCASSERTNOTEQUAL(sections[s].startOffset, 0);
RDCASSERTNOTEQUAL(sections[s].endOffset, 0);
RDCASSERT(sections[s].endOffset - sections[s].startOffset > 0, sections[s].startOffset,
sections[s].endOffset);
if(s != 0)
RDCASSERTEQUAL(sections[s - 1].endOffset, sections[s].startOffset);
if(s + 1 < SPIRVSection::Count)
RDCASSERTEQUAL(sections[s].endOffset, sections[s + 1].startOffset);
}
}
void SPIRVEditor::StripNops()
@@ -348,16 +314,20 @@ void SPIRVEditor::SetName(uint32_t id, const char *name)
SPIRVOperation op(spv::OpName, uintName);
spirv.insert(spirv.begin() + debugSection.endOffset, op.begin(), op.end());
RegisterOp(SPIRVIterator(spirv, debugSection.endOffset));
addWords(debugSection.endOffset, op.size());
size_t offset = sections[SPIRVSection::Debug].endOffset;
spirv.insert(spirv.begin() + offset, op.begin(), op.end());
RegisterOp(SPIRVIterator(spirv, offset));
addWords(offset, op.size());
}
void SPIRVEditor::AddDecoration(const SPIRVOperation &op)
{
spirv.insert(spirv.begin() + decorationSection.endOffset, op.begin(), op.end());
RegisterOp(SPIRVIterator(spirv, decorationSection.endOffset));
addWords(decorationSection.endOffset, op.size());
size_t offs = sections[SPIRVSection::Annotations].endOffset;
spirv.insert(spirv.begin() + offs, op.begin(), op.end());
RegisterOp(SPIRVIterator(spirv, offs));
addWords(offs, op.size());
}
void SPIRVEditor::AddCapability(spv::Capability cap)
@@ -397,6 +367,20 @@ void SPIRVEditor::AddExtension(const std::string &extension)
addWords(it.offset, op.size());
}
void SPIRVEditor::AddExecutionMode(SPIRVId entry, spv::ExecutionMode mode,
std::vector<uint32_t> params)
{
size_t offset = sections[SPIRVSection::ExecutionMode].endOffset;
params.insert(params.begin(), (uint32_t)mode);
params.insert(params.begin(), (uint32_t)entry);
SPIRVOperation op(spv::OpExecutionMode, params);
spirv.insert(spirv.begin() + offset, op.begin(), op.end());
RegisterOp(SPIRVIterator(spirv, offset));
addWords(offset, op.size());
}
SPIRVId SPIRVEditor::ImportExtInst(const char *setname)
{
SPIRVId ret = extSets[setname];
@@ -432,31 +416,37 @@ SPIRVId SPIRVEditor::ImportExtInst(const char *setname)
SPIRVId SPIRVEditor::AddType(const SPIRVOperation &op)
{
size_t offset = sections[SPIRVSection::Types].endOffset;
SPIRVId id = op[1];
idOffsets[id] = typeVarSection.endOffset;
spirv.insert(spirv.begin() + typeVarSection.endOffset, op.begin(), op.end());
RegisterOp(SPIRVIterator(spirv, typeVarSection.endOffset));
addWords(typeVarSection.endOffset, op.size());
idOffsets[id] = offset;
spirv.insert(spirv.begin() + offset, op.begin(), op.end());
RegisterOp(SPIRVIterator(spirv, offset));
addWords(offset, op.size());
return id;
}
SPIRVId SPIRVEditor::AddVariable(const SPIRVOperation &op)
{
size_t offset = sections[SPIRVSection::Variables].endOffset;
SPIRVId id = op[2];
idOffsets[id] = typeVarSection.endOffset;
spirv.insert(spirv.begin() + typeVarSection.endOffset, op.begin(), op.end());
RegisterOp(SPIRVIterator(spirv, typeVarSection.endOffset));
addWords(typeVarSection.endOffset, op.size());
idOffsets[id] = offset;
spirv.insert(spirv.begin() + offset, op.begin(), op.end());
RegisterOp(SPIRVIterator(spirv, offset));
addWords(offset, op.size());
return id;
}
SPIRVId SPIRVEditor::AddConstant(const SPIRVOperation &op)
{
size_t offset = sections[SPIRVSection::Constants].endOffset;
SPIRVId id = op[2];
idOffsets[id] = typeVarSection.endOffset;
spirv.insert(spirv.begin() + typeVarSection.endOffset, op.begin(), op.end());
RegisterOp(SPIRVIterator(spirv, typeVarSection.endOffset));
addWords(typeVarSection.endOffset, op.size());
idOffsets[id] = offset;
spirv.insert(spirv.begin() + offset, op.begin(), op.end());
RegisterOp(SPIRVIterator(spirv, offset));
addWords(offset, op.size());
return id;
}
@@ -482,8 +472,8 @@ SPIRVIterator SPIRVEditor::GetID(SPIRVId id)
SPIRVIterator SPIRVEditor::GetEntry(SPIRVId id)
{
SPIRVIterator it(spirv, entryPointSection.startOffset);
SPIRVIterator end(spirv, entryPointSection.endOffset);
SPIRVIterator it(spirv, sections[SPIRVSection::EntryPoints].startOffset);
SPIRVIterator end(spirv, sections[SPIRVSection::EntryPoints].endOffset);
while(it && it < end)
{
@@ -495,51 +485,6 @@ SPIRVIterator SPIRVEditor::GetEntry(SPIRVId id)
return SPIRVIterator();
}
SPIRVIterator SPIRVEditor::BeginEntries()
{
return SPIRVIterator(spirv, entryPointSection.startOffset);
}
SPIRVIterator SPIRVEditor::BeginDebug()
{
return SPIRVIterator(spirv, debugSection.startOffset);
}
SPIRVIterator SPIRVEditor::BeginDecorations()
{
return SPIRVIterator(spirv, decorationSection.startOffset);
}
SPIRVIterator SPIRVEditor::BeginTypes()
{
return SPIRVIterator(spirv, typeVarSection.startOffset);
}
SPIRVIterator SPIRVEditor::BeginFunctions()
{
return SPIRVIterator(spirv, typeVarSection.endOffset);
}
SPIRVIterator SPIRVEditor::EndEntries()
{
return SPIRVIterator(spirv, entryPointSection.endOffset);
}
SPIRVIterator SPIRVEditor::EndDebug()
{
return SPIRVIterator(spirv, debugSection.endOffset);
}
SPIRVIterator SPIRVEditor::EndDecorations()
{
return SPIRVIterator(spirv, decorationSection.endOffset);
}
SPIRVIterator SPIRVEditor::EndTypes()
{
return SPIRVIterator(spirv, typeVarSection.endOffset);
}
SPIRVId SPIRVEditor::DeclareStructType(std::vector<uint32_t> members)
{
SPIRVId typeId = MakeId();
@@ -598,6 +543,11 @@ void SPIRVEditor::RegisterOp(SPIRVIterator it)
entries.push_back(entry);
}
else if(opcode == spv::OpMemoryModel)
{
addressmodel = (spv::AddressingModel)it.word(2);
memorymodel = (spv::MemoryModel)it.word(3);
}
else if(opcode == spv::OpCapability)
{
capabilities.insert((spv::Capability)it.word(1));
@@ -848,8 +798,7 @@ void SPIRVEditor::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 *section :
{&entryPointSection, &debugSection, &decorationSection, &typeVarSection})
for(LogicalSection &section : sections)
{
// we have three cases to consider: either the offset matches start, is within (up to and
// including end) or is outside the section.
@@ -858,24 +807,24 @@ void SPIRVEditor::addWords(size_t offs, int32_t num)
// one section or inside the next. Note this means we don't support inserting at the start of a
// section.
if(offs == section->startOffset)
if(offs == section.startOffset)
{
// if the offset matches the start, we're appending at the end of the previous section so move
// both
section->startOffset += num;
section->endOffset += num;
section.startOffset += num;
section.endOffset += num;
}
else if(offs > section->startOffset && offs <= section->endOffset)
else if(offs > section.startOffset && offs <= section.endOffset)
{
// if the offset is in the section (up to and including the end) then we're inserting in this
// section, so move the end only
section->endOffset += num;
section.endOffset += num;
}
else if(section->startOffset >= offs)
else if(section.startOffset >= offs)
{
// otherwise move both or neither depending on which side the offset is.
section->startOffset += num;
section->endOffset += num;
section.startOffset += num;
section.endOffset += num;
}
}
@@ -926,3 +875,183 @@ std::map<SPIRVFunction, SPIRVId> &SPIRVEditor::GetTable<SPIRVFunction>()
{
return functionTypes;
}
#if ENABLED(ENABLE_UNIT_TESTS)
#include "3rdparty/catch/catch.hpp"
#include "core/core.h"
#include "spirv_common.h"
static void RemoveSection(std::vector<uint32_t> &spirv, size_t offsets[SPIRVSection::Count][2],
SPIRVSection::Type section)
{
SPIRVEditor ed(spirv);
for(SPIRVIterator it = ed.Begin(section), end = ed.End(section); it < end; it++)
ed.Remove(it);
size_t oldLength = offsets[section][1] - offsets[section][0];
// section will still contain a nop
offsets[section][1] = offsets[section][0] + 4;
// subsequent sections will be shorter by the length - 4, because a nop will still be inserted
// as padding to ensure no section is truly empty.
size_t delta = oldLength - 4;
for(uint32_t s = section + 1; s < SPIRVSection::Count; s++)
{
offsets[s][0] -= delta;
offsets[s][1] -= delta;
}
}
TEST_CASE("Test SPIR-V editor section handling", "[spirv]")
{
InitSPIRVCompiler();
RenderDoc::Inst().RegisterShutdownFunction(&ShutdownSPIRVCompiler);
SPIRVCompilationSettings settings;
settings.entryPoint = "main";
settings.lang = SPIRVSourceLanguage::VulkanGLSL;
settings.stage = SPIRVShaderStage::Fragment;
// simple shader that has at least something in every section
std::vector<std::string> sources = {
R"(#version 450 core
#extension GL_EXT_shader_16bit_storage : require
layout(binding = 0) uniform block {
float16_t val;
};
layout(location = 0) out vec4 col;
void main() {
col = vec4(sin(gl_FragCoord.x)*float(val), 0, 0, 1);
}
)",
};
std::vector<uint32_t> spirv;
std::string errors = CompileSPIRV(settings, sources, spirv);
INFO("SPIR-V compilation" << errors);
// ensure that compilation succeeded
REQUIRE(spirv.size() > 0);
// these offsets may change if the compiler changes above. Verify manually with spirv-dis that
// they should be updated.
// For convenience the offsets are in bytes (which spirv-dis uses) and are converted in the loops
// below.
size_t offsets[SPIRVSection::Count][2] = {
// Capabilities
{0x14, 0x2c},
// Extensions
{0x2c, 0x6c},
// ExtInst
{0x6c, 0x84},
// MemoryModel
{0x84, 0x90},
// EntryPoints
{0x90, 0xac},
// ExecutionMode
{0xac, 0xb8},
// Debug
{0xb8, 0x144},
// Annotations
{0x144, 0x1a4},
// TypesVariables
{0x1a4, 0x2cc},
// Functions
{0x2cc, 0x39c},
};
SECTION("Check that SPIR-V is correct with no changes")
{
SPIRVEditor ed(spirv);
for(uint32_t s = SPIRVSection::First; s < SPIRVSection::Count; s++)
{
INFO("Section " << s);
CHECK(ed.Begin((SPIRVSection::Type)s).offs() == offsets[s][0] / sizeof(uint32_t));
CHECK(ed.End((SPIRVSection::Type)s).offs() == offsets[s][1] / sizeof(uint32_t));
}
}
// we remove all sections we consider optional in arbitrary order. We don't care about keeping the
// SPIR-V valid all we're testing is the section offsets are correct.
RemoveSection(spirv, offsets, SPIRVSection::Extensions);
SECTION("Check with extensions removed")
{
SPIRVEditor ed(spirv);
for(uint32_t s = SPIRVSection::First; s < SPIRVSection::Count; s++)
{
INFO("Section " << s);
CHECK(ed.Begin((SPIRVSection::Type)s).offs() == offsets[s][0] / sizeof(uint32_t));
CHECK(ed.End((SPIRVSection::Type)s).offs() == offsets[s][1] / sizeof(uint32_t));
}
}
RemoveSection(spirv, offsets, SPIRVSection::Debug);
SECTION("Check with debug removed")
{
SPIRVEditor ed(spirv);
for(uint32_t s = SPIRVSection::First; s < SPIRVSection::Count; s++)
{
INFO("Section " << s);
CHECK(ed.Begin((SPIRVSection::Type)s).offs() == offsets[s][0] / sizeof(uint32_t));
CHECK(ed.End((SPIRVSection::Type)s).offs() == offsets[s][1] / sizeof(uint32_t));
}
}
RemoveSection(spirv, offsets, SPIRVSection::ExtInst);
SECTION("Check with extension imports removed")
{
SPIRVEditor ed(spirv);
for(uint32_t s = SPIRVSection::First; s < SPIRVSection::Count; s++)
{
INFO("Section " << s);
CHECK(ed.Begin((SPIRVSection::Type)s).offs() == offsets[s][0] / sizeof(uint32_t));
CHECK(ed.End((SPIRVSection::Type)s).offs() == offsets[s][1] / sizeof(uint32_t));
}
}
RemoveSection(spirv, offsets, SPIRVSection::ExecutionMode);
SECTION("Check with execution mode removed")
{
SPIRVEditor ed(spirv);
for(uint32_t s = SPIRVSection::First; s < SPIRVSection::Count; s++)
{
INFO("Section " << s);
CHECK(ed.Begin((SPIRVSection::Type)s).offs() == offsets[s][0] / sizeof(uint32_t));
CHECK(ed.End((SPIRVSection::Type)s).offs() == offsets[s][1] / sizeof(uint32_t));
}
}
RemoveSection(spirv, offsets, SPIRVSection::Annotations);
SECTION("Check with annotations removed")
{
SPIRVEditor ed(spirv);
for(uint32_t s = SPIRVSection::First; s < SPIRVSection::Count; s++)
{
INFO("Section " << s);
CHECK(ed.Begin((SPIRVSection::Type)s).offs() == offsets[s][0] / sizeof(uint32_t));
CHECK(ed.End((SPIRVSection::Type)s).offs() == offsets[s][1] / sizeof(uint32_t));
}
}
}
#endif
+38 -13
View File
@@ -87,6 +87,7 @@ public:
spv::Op opcode() { return spv::Op(cur() & spv::OpCodeMask); }
uint32_t &word(size_t idx) { return words->at(offset + idx); }
const uint32_t &word(size_t idx) const { return words->at(offset + idx); }
size_t offs() const { return offset; }
size_t size() const { return cur() >> spv::WordCountShift; }
private:
inline uint32_t &cur() { return words->at(offset); }
@@ -355,6 +356,30 @@ struct SPIRVFunction
SPIRVOperation decl(SPIRVEditor &editor) const;
};
// hack around enum class being useless for array indices :(
struct SPIRVSection
{
enum Type
{
Capabilities,
First = Capabilities,
Extensions,
ExtInst,
MemoryModel,
EntryPoints,
ExecutionMode,
Debug,
Annotations,
TypesVariablesConstants,
// handy aliases
Types = TypesVariablesConstants,
Variables = TypesVariablesConstants,
Constants = TypesVariablesConstants,
Functions,
Count,
};
};
class SPIRVEditor
{
public:
@@ -385,6 +410,7 @@ public:
void AddDecoration(const SPIRVOperation &op);
void AddCapability(spv::Capability cap);
void AddExtension(const std::string &extension);
void AddExecutionMode(SPIRVId entry, spv::ExecutionMode mode, std::vector<uint32_t> params = {});
SPIRVId ImportExtInst(const char *setname);
SPIRVId AddType(const SPIRVOperation &op);
SPIRVId AddVariable(const SPIRVOperation &op);
@@ -395,15 +421,14 @@ public:
// the entry point has 'two' opcodes, the entrypoint declaration and the function.
// This returns the first, GetID returns the second.
SPIRVIterator GetEntry(SPIRVId id);
SPIRVIterator BeginEntries();
SPIRVIterator BeginDebug();
SPIRVIterator BeginDecorations();
SPIRVIterator BeginTypes();
SPIRVIterator BeginFunctions();
SPIRVIterator EndEntries();
SPIRVIterator EndDebug();
SPIRVIterator EndDecorations();
SPIRVIterator EndTypes();
SPIRVIterator Begin(SPIRVSection::Type section)
{
return SPIRVIterator(spirv, sections[section].startOffset);
}
SPIRVIterator End(SPIRVSection::Type section)
{
return SPIRVIterator(spirv, 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
@@ -479,10 +504,10 @@ private:
size_t endOffset = 0;
};
LogicalSection entryPointSection;
LogicalSection debugSection;
LogicalSection decorationSection;
LogicalSection typeVarSection;
LogicalSection sections[SPIRVSection::Count];
spv::AddressingModel addressmodel;
spv::MemoryModel memorymodel;
std::vector<size_t> idOffsets;
+25 -16
View File
@@ -61,7 +61,9 @@ static void ConvertToMeshOutputCompute(const ShaderReflection &refl, const SPIRV
uint32_t numOutputs = (uint32_t)refl.outputSignature.size();
RDCASSERT(numOutputs > 0);
for(SPIRVIterator it = editor.BeginDecorations(), end = editor.EndDecorations(); it < end; ++it)
for(SPIRVIterator it = editor.Begin(SPIRVSection::Annotations),
end = editor.End(SPIRVSection::Annotations);
it < end; ++it)
{
// we will use descriptor set 0 bindings 0..N for our own purposes.
//
@@ -118,7 +120,9 @@ static void ConvertToMeshOutputCompute(const ShaderReflection &refl, const SPIRV
std::map<SPIRVId, SPIRVId> typeReplacements;
// rewrite any inputs and outputs to be private storage class
for(SPIRVIterator it = editor.BeginTypes(), end = editor.EndTypes(); it < end; ++it)
for(SPIRVIterator it = editor.Begin(SPIRVSection::TypesVariablesConstants),
end = editor.End(SPIRVSection::TypesVariablesConstants);
it < end; ++it)
{
// rewrite any input/output variables to private, and build up inputs/outputs list
if(it.opcode() == spv::OpTypePointer)
@@ -254,7 +258,7 @@ static void ConvertToMeshOutputCompute(const ShaderReflection &refl, const SPIRV
}
}
for(SPIRVIterator it = editor.BeginFunctions(); it; ++it)
for(SPIRVIterator it = editor.Begin(SPIRVSection::Functions); it; ++it)
{
// identify functions with result types we might want to replace
if(it.opcode() == spv::OpFunction || it.opcode() == spv::OpFunctionParameter ||
@@ -275,7 +279,9 @@ static void ConvertToMeshOutputCompute(const ShaderReflection &refl, const SPIRV
}
// detect builtin inputs or outputs, and remove builtin decorations
for(SPIRVIterator it = editor.BeginDecorations(), end = editor.EndDecorations(); it < end; ++it)
for(SPIRVIterator it = editor.Begin(SPIRVSection::Annotations),
end = editor.End(SPIRVSection::Annotations);
it < end; ++it)
{
// remove any builtin decorations
if(it.opcode() == spv::OpDecorate && it.word(2) == spv::DecorationBuiltIn)
@@ -325,7 +331,8 @@ static void ConvertToMeshOutputCompute(const ShaderReflection &refl, const SPIRV
RDCASSERT(entryID);
for(SPIRVIterator it = editor.BeginDebug(), end2 = editor.EndDebug(); it < end2; ++it)
for(SPIRVIterator it = editor.Begin(SPIRVSection::Debug), end2 = editor.End(SPIRVSection::Debug);
it < end2; ++it)
{
if(it.opcode() == spv::OpName &&
(inputs.find(it.word(1)) != inputs.end() || outputs.find(it.word(1)) != outputs.end()))
@@ -632,7 +639,7 @@ static void ConvertToMeshOutputCompute(const ShaderReflection &refl, const SPIRV
// editor.SetName(wrapperEntry, "RenderDoc_MeshFetch_Wrapper_Entrypoint");
// we remove all entry points and just create one of our own.
SPIRVIterator it = editor.BeginEntries();
SPIRVIterator it = editor.Begin(SPIRVSection::EntryPoints);
{
// there should already have been at least one entry point
@@ -657,11 +664,12 @@ static void ConvertToMeshOutputCompute(const ShaderReflection &refl, const SPIRV
++it;
}
for(SPIRVIterator end = editor.EndEntries(); it < end; ++it)
for(SPIRVIterator end = editor.End(SPIRVSection::EntryPoints); it < end; ++it)
editor.Remove(it);
// Strip away any execution modes from the original shaders
for(it = editor.BeginEntries(); it < editor.BeginDebug(); ++it)
for(it = editor.Begin(SPIRVSection::ExecutionMode); it < editor.End(SPIRVSection::ExecutionMode);
++it)
{
if(it.opcode() == spv::OpExecutionMode)
{
@@ -694,9 +702,7 @@ static void ConvertToMeshOutputCompute(const ShaderReflection &refl, const SPIRV
}
// Add our compute shader execution mode
editor.AddOperation(
it, SPIRVOperation(spv::OpExecutionMode, {wrapperEntry, spv::ExecutionModeLocalSize,
MeshOutputDispatchWidth, 1, 1}));
editor.AddExecutionMode(wrapperEntry, spv::ExecutionModeLocalSize, {MeshOutputDispatchWidth, 1, 1});
SPIRVId uint32ID = editor.DeclareType(scalar<uint32_t>());
@@ -971,7 +977,9 @@ static void ConvertToMeshOutputCompute(const ShaderReflection &refl, const SPIRV
ops.push_back(SPIRVOperation(
spv::OpVectorShuffle, {uvec2Type, packed, result, result2, c * 2 + 0, c * 2 + 1}));
editor.SetName(packed, StringFormat::Fmt("packed_%c", "xyzw"[c]).c_str());
char swizzle[] = "xyzw";
editor.SetName(packed, StringFormat::Fmt("packed_%c", swizzle[c]).c_str());
// double comp = PackDouble2x32(packed);
comps[c] = editor.MakeId();
@@ -1140,7 +1148,8 @@ static void AddXFBAnnotations(const ShaderReflection &refl, const SPIRVPatchData
bool hasXFB = false;
for(SPIRVIterator it = editor.EndEntries(); it < editor.BeginDebug(); ++it)
for(SPIRVIterator it = editor.Begin(SPIRVSection::ExecutionMode);
it < editor.End(SPIRVSection::ExecutionMode); ++it)
{
if(it.opcode() == spv::OpExecutionMode && it.word(1) == entryid &&
it.word(2) == spv::ExecutionModeXfb)
@@ -1152,7 +1161,8 @@ static void AddXFBAnnotations(const ShaderReflection &refl, const SPIRVPatchData
if(hasXFB)
{
for(SPIRVIterator it = editor.BeginDecorations(); it < editor.EndDecorations(); ++it)
for(SPIRVIterator it = editor.Begin(SPIRVSection::Annotations);
it < editor.End(SPIRVSection::Annotations); ++it)
{
// remove any existing xfb decorations
if(it.opcode() == spv::OpDecorate &&
@@ -1210,8 +1220,7 @@ static void AddXFBAnnotations(const ShaderReflection &refl, const SPIRVPatchData
}
else
{
editor.AddOperation(editor.EndEntries(),
SPIRVOperation(spv::OpExecutionMode, {entryid, spv::ExecutionModeXfb}));
editor.AddExecutionMode(entryid, spv::ExecutionModeXfb);
}
editor.AddCapability(spv::CapabilityTransformFeedback);