mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-05-29 21:30:53 +00:00
Handle all logical sections in SPIRVEditor
This commit is contained in:
@@ -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 §ion : 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
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user