mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-09-23 22:25:55 +00:00
Split some parts of SPIRVEditor into SPIRVProcessor
* This will then be shared by a read-only Reflector, with common processing to both happening in SPIRVProcessor with extra work happening in each child depending on whether it's reflecting/disassembling or editing.
This commit is contained in:
@@ -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})
|
||||
|
||||
@@ -162,6 +162,7 @@
|
||||
<ForcedIncludeFiles>precompiled.h</ForcedIncludeFiles>
|
||||
</ClCompile>
|
||||
<ClCompile Include="spirv_editor.cpp" />
|
||||
<ClCompile Include="spirv_processor.cpp" />
|
||||
<ClCompile Include="spirv_reflect.cpp" />
|
||||
<ClCompile Include="spirv_gen.cpp" />
|
||||
<ClCompile Include="spirv_stringise.cpp" />
|
||||
@@ -219,6 +220,7 @@
|
||||
<ClInclude Include="spirv_editor.h" />
|
||||
<ClInclude Include="spirv_gen.h" />
|
||||
<ClInclude Include="spirv_op_helpers.h" />
|
||||
<ClInclude Include="spirv_processor.h" />
|
||||
<ClInclude Include="spirv_reflect.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
|
||||
@@ -142,6 +142,7 @@
|
||||
</ClCompile>
|
||||
<ClCompile Include="spirv_reflect.cpp" />
|
||||
<ClCompile Include="glslang_compile.cpp" />
|
||||
<ClCompile Include="spirv_processor.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\..\3rdparty\glslang\OGLCompilersDLL\InitializeDll.h">
|
||||
@@ -293,5 +294,6 @@
|
||||
<ClInclude Include="spirv_op_helpers.h">
|
||||
<Filter>JSON-Generated helpers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="spirv_processor.h" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -54,153 +54,16 @@ Scalar::Scalar(Iter it)
|
||||
}
|
||||
}
|
||||
|
||||
Operation Vector::decl(Editor &editor) const
|
||||
Editor::Editor(std::vector<uint32_t> &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<uint32_t> &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<uint32_t> &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<uint32_t> &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<StructType, Id> &Editor::GetTable<StructType>() \
|
||||
@@ -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<uint32_t> &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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 <typename SPIRVType>
|
||||
using TypeToId = std::pair<SPIRVType, Id>;
|
||||
|
||||
Op type;
|
||||
uint32_t width;
|
||||
bool signedness;
|
||||
template <typename SPIRVType>
|
||||
using TypeToIds = std::vector<TypeToId<SPIRVType>>;
|
||||
|
||||
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 <typename T>
|
||||
inline constexpr Scalar scalar();
|
||||
|
||||
#define SCALAR_TYPE(ctype, op, width, sign) \
|
||||
template <> \
|
||||
inline constexpr Scalar scalar<ctype>() \
|
||||
{ \
|
||||
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<Id> &args) : returnId(ret), argumentIds(args) {}
|
||||
Id returnId;
|
||||
rdcarray<Id> 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 <typename Type>
|
||||
using TypeId = std::pair<Type, Id>;
|
||||
|
||||
template <typename Type>
|
||||
using TypeIds = std::vector<TypeId<Type>>;
|
||||
|
||||
// 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<uint32_t> &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 <typename Type>
|
||||
Id DeclareType(const Type &t)
|
||||
template <typename SPIRVType>
|
||||
Id DeclareType(const SPIRVType &t)
|
||||
{
|
||||
std::map<Type, Id> &table = GetTable<Type>();
|
||||
std::map<SPIRVType, Id> &table = GetTable<SPIRVType>();
|
||||
|
||||
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<Type, Id>(t, id));
|
||||
table.insert(it, std::pair<SPIRVType, Id>(t, id));
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
Id GetType(const Type &t)
|
||||
template <typename SPIRVType>
|
||||
Id GetType(const SPIRVType &t)
|
||||
{
|
||||
std::map<Type, Id> &table = GetTable<Type>();
|
||||
std::map<SPIRVType, Id> &table = GetTable<SPIRVType>();
|
||||
|
||||
auto it = table.find(t);
|
||||
if(it != table.end())
|
||||
@@ -360,12 +139,12 @@ public:
|
||||
return Id();
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
TypeIds<Type> GetTypes()
|
||||
template <typename SPIRVType>
|
||||
TypeToIds<SPIRVType> GetTypes()
|
||||
{
|
||||
std::map<Type, Id> &table = GetTable<Type>();
|
||||
std::map<SPIRVType, Id> &table = GetTable<SPIRVType>();
|
||||
|
||||
TypeIds<Type> ret;
|
||||
TypeToIds<SPIRVType> ret;
|
||||
|
||||
for(auto it = table.begin(); it != table.end(); ++it)
|
||||
ret.push_back(*it);
|
||||
@@ -373,10 +152,10 @@ public:
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
const std::map<Type, Id> &GetTypeInfo() const
|
||||
template <typename SPIRVType>
|
||||
const std::map<SPIRVType, Id> &GetTypeInfo() const
|
||||
{
|
||||
return GetTable<Type>();
|
||||
return GetTable<SPIRVType>();
|
||||
}
|
||||
|
||||
Binding GetBinding(Id id) const
|
||||
@@ -386,7 +165,7 @@ public:
|
||||
return Binding();
|
||||
return it->second;
|
||||
}
|
||||
const std::set<Id> &GetStructTypes() const { return structTypes; }
|
||||
|
||||
Id DeclareStructType(const std::vector<Id> &members);
|
||||
|
||||
// helper for AddConstant
|
||||
@@ -403,62 +182,41 @@ public:
|
||||
return AddConstant(Operation(Op::Constant, words));
|
||||
}
|
||||
|
||||
// accessors to structs/vectors of data
|
||||
const std::vector<OpEntryPoint> &GetEntries() { return entries; }
|
||||
const std::vector<OpVariable> &GetVariables() { return variables; }
|
||||
const std::vector<Id> &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<OpDecorate> decorations;
|
||||
virtual void RegisterOp(Iter iter);
|
||||
virtual void UnregisterOp(Iter iter);
|
||||
|
||||
std::map<Id, Binding> bindings;
|
||||
|
||||
std::vector<size_t> idOffsets;
|
||||
std::vector<Id> idTypes;
|
||||
std::map<Scalar, Id> scalarTypeToId;
|
||||
std::map<Vector, Id> vectorTypeToId;
|
||||
std::map<Matrix, Id> matrixTypeToId;
|
||||
std::map<Pointer, Id> pointerTypeToId;
|
||||
std::map<Image, Id> imageTypeToId;
|
||||
std::map<Sampler, Id> samplerTypeToId;
|
||||
std::map<SampledImage, Id> sampledImageTypeToId;
|
||||
std::map<FunctionType, Id> functionTypeToId;
|
||||
|
||||
std::vector<OpEntryPoint> entries;
|
||||
std::vector<OpVariable> variables;
|
||||
std::vector<Id> functions;
|
||||
std::set<rdcstr> extensions;
|
||||
std::set<Capability> capabilities;
|
||||
template <typename SPIRVType>
|
||||
std::map<SPIRVType, Id> &GetTable();
|
||||
|
||||
std::map<rdcstr, Id> extSets;
|
||||
template <typename SPIRVType>
|
||||
const std::map<SPIRVType, Id> &GetTable() const;
|
||||
|
||||
std::map<Scalar, Id> scalarTypes;
|
||||
std::map<Vector, Id> vectorTypes;
|
||||
std::map<Matrix, Id> matrixTypes;
|
||||
std::map<Pointer, Id> pointerTypes;
|
||||
std::map<Image, Id> imageTypes;
|
||||
std::map<Sampler, Id> samplerTypes;
|
||||
std::map<SampledImage, Id> sampledImageTypes;
|
||||
std::map<Function, Id> functionTypes;
|
||||
|
||||
std::set<Id> structTypes;
|
||||
|
||||
template <typename Type>
|
||||
std::map<Type, Id> &GetTable();
|
||||
|
||||
template <typename Type>
|
||||
const std::map<Type, Id> &GetTable() const;
|
||||
|
||||
std::vector<uint32_t> &spirv;
|
||||
std::vector<uint32_t> &m_ExternalSPIRV;
|
||||
};
|
||||
|
||||
inline bool operator<(const OpDecorate &a, const OpDecorate &b)
|
||||
|
||||
@@ -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<uint32_t> &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
|
||||
@@ -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 <stdint.h>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "common/common.h"
|
||||
#include "spirv_common.h"
|
||||
|
||||
namespace rdcspv
|
||||
{
|
||||
struct Scalar
|
||||
{
|
||||
Scalar() : type(Op::Max), width(0), signedness(false) {}
|
||||
constexpr Scalar(Op t, uint32_t w, bool s) : type(t), width(w), signedness(s) {}
|
||||
Scalar(Iter op);
|
||||
|
||||
Op type;
|
||||
uint32_t width;
|
||||
bool signedness;
|
||||
|
||||
bool operator<(const Scalar &o) const
|
||||
{
|
||||
if(type != o.type)
|
||||
return type < o.type;
|
||||
if(signedness != o.signedness)
|
||||
return signedness < o.signedness;
|
||||
return width < o.width;
|
||||
}
|
||||
|
||||
bool operator!=(const Scalar &o) const { return !operator==(o); }
|
||||
bool operator==(const Scalar &o) const
|
||||
{
|
||||
return type == o.type && width == o.width && signedness == o.signedness;
|
||||
}
|
||||
};
|
||||
|
||||
// helper to create Scalar objects for known types
|
||||
template <typename T>
|
||||
inline constexpr Scalar scalar();
|
||||
|
||||
#define SCALAR_TYPE(ctype, op, width, sign) \
|
||||
template <> \
|
||||
inline constexpr Scalar scalar<ctype>() \
|
||||
{ \
|
||||
return Scalar(op, width, sign); \
|
||||
}
|
||||
|
||||
SCALAR_TYPE(void, Op::TypeVoid, 0, false);
|
||||
SCALAR_TYPE(bool, Op::TypeBool, 0, false);
|
||||
SCALAR_TYPE(uint8_t, Op::TypeInt, 8, false);
|
||||
SCALAR_TYPE(uint16_t, Op::TypeInt, 16, false);
|
||||
SCALAR_TYPE(uint32_t, Op::TypeInt, 32, false);
|
||||
SCALAR_TYPE(uint64_t, Op::TypeInt, 64, false);
|
||||
SCALAR_TYPE(int8_t, Op::TypeInt, 8, true);
|
||||
SCALAR_TYPE(int16_t, Op::TypeInt, 16, true);
|
||||
SCALAR_TYPE(int32_t, Op::TypeInt, 32, true);
|
||||
SCALAR_TYPE(int64_t, Op::TypeInt, 64, true);
|
||||
SCALAR_TYPE(float, Op::TypeFloat, 32, false);
|
||||
SCALAR_TYPE(double, Op::TypeFloat, 64, false);
|
||||
|
||||
struct Vector
|
||||
{
|
||||
Vector() : scalar(), count(0) {}
|
||||
Vector(const Scalar &s, uint32_t c) : scalar(s), count(c) {}
|
||||
Scalar scalar;
|
||||
uint32_t count;
|
||||
|
||||
bool operator<(const Vector &o) const
|
||||
{
|
||||
if(scalar != o.scalar)
|
||||
return scalar < o.scalar;
|
||||
return count < o.count;
|
||||
}
|
||||
|
||||
bool operator!=(const Vector &o) const { return !operator==(o); }
|
||||
bool operator==(const Vector &o) const { return scalar == o.scalar && count == o.count; }
|
||||
};
|
||||
|
||||
struct Matrix
|
||||
{
|
||||
Matrix() : vector(), count(0) {}
|
||||
Matrix(const Vector &v, uint32_t c) : vector(v), count(c) {}
|
||||
Vector vector;
|
||||
uint32_t count;
|
||||
|
||||
bool operator<(const Matrix &o) const
|
||||
{
|
||||
if(vector != o.vector)
|
||||
return vector < o.vector;
|
||||
return count < o.count;
|
||||
}
|
||||
|
||||
bool operator!=(const Matrix &o) const { return !operator==(o); }
|
||||
bool operator==(const Matrix &o) const { return vector == o.vector && count == o.count; }
|
||||
};
|
||||
|
||||
struct Pointer
|
||||
{
|
||||
Pointer() : baseId(), storage(StorageClass::Max) {}
|
||||
Pointer(Id b, StorageClass s) : baseId(b), storage(s) {}
|
||||
Id baseId;
|
||||
StorageClass storage;
|
||||
|
||||
bool operator<(const Pointer &o) const
|
||||
{
|
||||
if(baseId != o.baseId)
|
||||
return baseId < o.baseId;
|
||||
return storage < o.storage;
|
||||
}
|
||||
|
||||
bool operator!=(const Pointer &o) const { return !operator==(o); }
|
||||
bool operator==(const Pointer &o) const { return baseId == o.baseId && storage == o.storage; }
|
||||
};
|
||||
|
||||
struct Image
|
||||
{
|
||||
Image()
|
||||
: retType(), dim(Dim::Max), depth(0), arrayed(0), ms(0), sampled(0), format(ImageFormat::Max)
|
||||
{
|
||||
}
|
||||
Image(Scalar ret, Dim d, uint32_t dp, uint32_t ar, uint32_t m, uint32_t samp, ImageFormat f)
|
||||
: retType(ret), dim(d), depth(dp), arrayed(ar), ms(m), sampled(samp), format(f)
|
||||
{
|
||||
}
|
||||
|
||||
Scalar retType;
|
||||
Dim dim;
|
||||
uint32_t depth;
|
||||
uint32_t arrayed;
|
||||
uint32_t ms;
|
||||
uint32_t sampled;
|
||||
ImageFormat format;
|
||||
|
||||
bool operator<(const Image &o) const
|
||||
{
|
||||
if(retType != o.retType)
|
||||
return retType < o.retType;
|
||||
if(dim != o.dim)
|
||||
return dim < o.dim;
|
||||
if(depth != o.depth)
|
||||
return depth < o.depth;
|
||||
if(arrayed != o.arrayed)
|
||||
return arrayed < o.arrayed;
|
||||
if(ms != o.ms)
|
||||
return ms < o.ms;
|
||||
if(sampled != o.sampled)
|
||||
return sampled < o.sampled;
|
||||
return format < o.format;
|
||||
}
|
||||
bool operator!=(const Image &o) const { return !operator==(o); }
|
||||
bool operator==(const Image &o) const
|
||||
{
|
||||
return retType == o.retType && dim == o.dim && depth == o.depth && arrayed == o.arrayed &&
|
||||
ms == o.ms && sampled == o.sampled && format == o.format;
|
||||
}
|
||||
};
|
||||
|
||||
struct Sampler
|
||||
{
|
||||
// no properties, all sampler types are equal
|
||||
bool operator<(const Sampler &o) const { return false; }
|
||||
bool operator!=(const Sampler &o) const { return false; }
|
||||
bool operator==(const Sampler &o) const { return true; }
|
||||
};
|
||||
|
||||
struct SampledImage
|
||||
{
|
||||
SampledImage() = default;
|
||||
SampledImage(Id b) : baseId(b) {}
|
||||
Id baseId;
|
||||
|
||||
bool operator<(const SampledImage &o) const { return baseId < o.baseId; }
|
||||
bool operator!=(const SampledImage &o) const { return !operator==(o); }
|
||||
bool operator==(const SampledImage &o) const { return baseId == o.baseId; }
|
||||
};
|
||||
|
||||
struct FunctionType
|
||||
{
|
||||
FunctionType() = default;
|
||||
FunctionType(Id ret, const rdcarray<Id> &args) : returnId(ret), argumentIds(args) {}
|
||||
Id returnId;
|
||||
rdcarray<Id> argumentIds;
|
||||
|
||||
bool operator<(const FunctionType &o) const
|
||||
{
|
||||
if(returnId != o.returnId)
|
||||
return returnId < o.returnId;
|
||||
return argumentIds < o.argumentIds;
|
||||
}
|
||||
|
||||
bool operator!=(const FunctionType &o) const { return !operator==(o); }
|
||||
bool operator==(const FunctionType &o) const
|
||||
{
|
||||
return returnId == o.returnId && argumentIds == o.argumentIds;
|
||||
}
|
||||
};
|
||||
|
||||
struct EntryPoint
|
||||
{
|
||||
EntryPoint() = default;
|
||||
EntryPoint(ExecutionModel e, Id i, rdcstr n) : executionModel(e), id(i), name(n) {}
|
||||
ExecutionModel executionModel;
|
||||
Id id;
|
||||
rdcstr name;
|
||||
|
||||
bool operator<(const EntryPoint &o) const
|
||||
{
|
||||
if(id != o.id)
|
||||
return id < o.id;
|
||||
return name < o.name;
|
||||
}
|
||||
|
||||
bool operator!=(const EntryPoint &o) const { return !operator==(o); }
|
||||
bool operator==(const EntryPoint &o) const { return id == o.id && name == o.name; }
|
||||
};
|
||||
|
||||
struct Variable
|
||||
{
|
||||
Variable() = default;
|
||||
Variable(Id t, Id i, StorageClass s) : type(t), id(i), storage(s) {}
|
||||
Id type;
|
||||
Id id;
|
||||
StorageClass storage;
|
||||
|
||||
bool operator<(const Variable &o) const
|
||||
{
|
||||
if(id != o.id)
|
||||
return id < o.id;
|
||||
if(type != o.type)
|
||||
return type < o.type;
|
||||
return storage < o.storage;
|
||||
}
|
||||
|
||||
bool operator!=(const Variable &o) const { return !operator==(o); }
|
||||
bool operator==(const Variable &o) const
|
||||
{
|
||||
return id == o.id && type == o.type && storage == o.storage;
|
||||
}
|
||||
};
|
||||
|
||||
// hack around enum class being useless for array indices :(
|
||||
struct Section
|
||||
{
|
||||
enum Type
|
||||
{
|
||||
Capabilities,
|
||||
First = Capabilities,
|
||||
Extensions,
|
||||
ExtInst,
|
||||
MemoryModel,
|
||||
EntryPoints,
|
||||
ExecutionMode,
|
||||
Debug,
|
||||
Annotations,
|
||||
TypesVariablesConstants,
|
||||
// handy aliases
|
||||
Types = TypesVariablesConstants,
|
||||
Variables = TypesVariablesConstants,
|
||||
Constants = TypesVariablesConstants,
|
||||
Functions,
|
||||
Count,
|
||||
};
|
||||
};
|
||||
|
||||
class Processor
|
||||
{
|
||||
public:
|
||||
Processor();
|
||||
|
||||
// accessors to structs/vectors of data
|
||||
const std::vector<Id> &GetFunctions() { return functions; }
|
||||
const std::vector<EntryPoint> &GetEntries() { return entries; }
|
||||
const std::vector<Variable> &GetGlobals() { return globals; }
|
||||
Id GetIDType(Id id) { return idTypes[id.value()]; }
|
||||
protected:
|
||||
virtual void Parse(const std::vector<uint32_t> &spirvWords);
|
||||
|
||||
std::vector<uint32_t> m_SPIRV;
|
||||
|
||||
// before parsing - e.g. to prepare any arrays that are max-id sized
|
||||
virtual void PreParse(uint32_t maxId);
|
||||
// even though we only need UnregisterOp when editing, we declare it here for ease of organisation
|
||||
// so we can define pairs of logic for the same things. Rather than having mismatched code with
|
||||
// the register of some map in one place and the unregister somewhere else that would be easy to
|
||||
// break.
|
||||
virtual void RegisterOp(Iter iter);
|
||||
virtual void UnregisterOp(Iter iter);
|
||||
// after parsing - e.g. to do any deferred post-processing
|
||||
virtual void PostParse();
|
||||
|
||||
std::vector<size_t> idOffsets;
|
||||
std::vector<Id> idTypes;
|
||||
|
||||
std::vector<Id> functions;
|
||||
std::vector<EntryPoint> entries;
|
||||
std::vector<Variable> globals;
|
||||
|
||||
std::set<rdcstr> extensions;
|
||||
std::set<Capability> capabilities;
|
||||
|
||||
std::map<Id, Scalar> scalarTypes;
|
||||
std::map<Id, Vector> vectorTypes;
|
||||
std::map<Id, Matrix> matrixTypes;
|
||||
std::map<Id, Pointer> pointerTypes;
|
||||
std::map<Id, Image> imageTypes;
|
||||
std::map<Id, Sampler> samplerTypes;
|
||||
std::map<Id, SampledImage> sampledImageTypes;
|
||||
std::map<Id, FunctionType> functionTypes;
|
||||
|
||||
std::map<rdcstr, Id> extSets;
|
||||
|
||||
struct LogicalSection
|
||||
{
|
||||
size_t startOffset = 0;
|
||||
size_t endOffset = 0;
|
||||
};
|
||||
|
||||
LogicalSection m_Sections[Section::Count];
|
||||
};
|
||||
|
||||
}; // namespace rdcspv
|
||||
@@ -59,15 +59,17 @@ void AddXFBAnnotations(const ShaderReflection &refl, const SPIRVPatchData &patch
|
||||
{
|
||||
rdcspv::Editor editor(modSpirv);
|
||||
|
||||
editor.Prepare();
|
||||
|
||||
rdcarray<SigParameter> outsig = refl.outputSignature;
|
||||
std::vector<SPIRVPatchData::InterfaceAccess> outpatch = patchData.outputs;
|
||||
|
||||
rdcspv::Id entryid;
|
||||
for(const rdcspv::OpEntryPoint &entry : editor.GetEntries())
|
||||
for(const rdcspv::EntryPoint &entry : editor.GetEntries())
|
||||
{
|
||||
if(entry.name == entryName)
|
||||
{
|
||||
entryid = entry.entryPoint;
|
||||
entryid = entry.id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +41,8 @@ void AnnotateShader(const SPIRVPatchData &patchData, const char *entryName,
|
||||
{
|
||||
rdcspv::Editor editor(modSpirv);
|
||||
|
||||
editor.Prepare();
|
||||
|
||||
const bool useBufferAddress = (addr != 0);
|
||||
|
||||
rdcspv::Id uint32ID = editor.DeclareType(rdcspv::scalar<uint32_t>());
|
||||
@@ -84,16 +86,16 @@ void AnnotateShader(const SPIRVPatchData &patchData, const char *entryName,
|
||||
|
||||
// iterate over all variables. We do this here because in the absence of the buffer address
|
||||
// extension we might declare our own below and patch bindings - so we need to look these up now
|
||||
for(const rdcspv::OpVariable &var : editor.GetVariables())
|
||||
for(const rdcspv::Variable &var : editor.GetGlobals())
|
||||
{
|
||||
// skip variables without one of these storage classes, as they are not descriptors
|
||||
if(var.storageClass != rdcspv::StorageClass::UniformConstant &&
|
||||
var.storageClass != rdcspv::StorageClass::Uniform &&
|
||||
var.storageClass != rdcspv::StorageClass::StorageBuffer)
|
||||
if(var.storage != rdcspv::StorageClass::UniformConstant &&
|
||||
var.storage != rdcspv::StorageClass::Uniform &&
|
||||
var.storage != rdcspv::StorageClass::StorageBuffer)
|
||||
continue;
|
||||
|
||||
// get this variable's binding info
|
||||
rdcspv::Binding bind = editor.GetBinding(var.result);
|
||||
rdcspv::Binding bind = editor.GetBinding(var.id);
|
||||
|
||||
// if this is one of the bindings we care about
|
||||
auto it = offsetMap.find(bind);
|
||||
@@ -102,8 +104,7 @@ void AnnotateShader(const SPIRVPatchData &patchData, const char *entryName,
|
||||
// store the offset for this variable so we watch for access chains and know where to store to
|
||||
if(useBufferAddress)
|
||||
{
|
||||
rdcspv::Id id = varLookup[var.result] =
|
||||
editor.AddConstantImmediate<uint64_t>(it->second.offset);
|
||||
rdcspv::Id id = varLookup[var.id] = editor.AddConstantImmediate<uint64_t>(it->second.offset);
|
||||
|
||||
editor.SetName(
|
||||
id, StringFormat::Fmt("__feedbackOffset_set%u_bind%u", it->first.set, it->first.binding)
|
||||
@@ -114,8 +115,7 @@ void AnnotateShader(const SPIRVPatchData &patchData, const char *entryName,
|
||||
// check that the offset fits in 32-bit word, convert byte offset to uint32 index
|
||||
uint64_t index = it->second.offset / 4;
|
||||
RDCASSERT(index < 0xFFFFFFFFULL, bind.set, bind.binding, it->second.offset);
|
||||
rdcspv::Id id = varLookup[var.result] =
|
||||
editor.AddConstantImmediate<uint32_t>(uint32_t(index));
|
||||
rdcspv::Id id = varLookup[var.id] = editor.AddConstantImmediate<uint32_t>(uint32_t(index));
|
||||
|
||||
editor.SetName(
|
||||
id, StringFormat::Fmt("__feedbackIndex_set%u_bind%u", it->first.set, it->first.binding)
|
||||
@@ -209,16 +209,16 @@ void AnnotateShader(const SPIRVPatchData &patchData, const char *entryName,
|
||||
intTypeLookup[scalarType.second] = scalarType.first;
|
||||
|
||||
rdcspv::Id entryID;
|
||||
for(const rdcspv::OpEntryPoint &entry : editor.GetEntries())
|
||||
for(const rdcspv::EntryPoint &entry : editor.GetEntries())
|
||||
{
|
||||
if(entry.name == entryName)
|
||||
{
|
||||
entryID = entry.entryPoint;
|
||||
entryID = entry.id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
rdcspv::TypeIds<rdcspv::Function> funcTypes = editor.GetTypes<rdcspv::Function>();
|
||||
rdcspv::TypeToIds<rdcspv::FunctionType> funcTypes = editor.GetTypes<rdcspv::FunctionType>();
|
||||
|
||||
// functions that have been patched with annotation & extra function parameters if needed
|
||||
std::set<rdcspv::Id> patchedFunctions;
|
||||
@@ -254,11 +254,11 @@ void AnnotateShader(const SPIRVPatchData &patchData, const char *entryName,
|
||||
rdcspv::OpFunction func(it);
|
||||
|
||||
// find the function's type declaration, add the necessary arguments, redeclare and patch it
|
||||
for(const rdcspv::TypeId<rdcspv::Function> &funcType : funcTypes)
|
||||
for(const rdcspv::TypeToId<rdcspv::FunctionType> &funcType : funcTypes)
|
||||
{
|
||||
if(funcType.second == func.functionType)
|
||||
{
|
||||
rdcspv::Function patchedFuncType = funcType.first;
|
||||
rdcspv::FunctionType patchedFuncType = funcType.first;
|
||||
for(size_t i = 0; i < patchArgIndices.size(); i++)
|
||||
patchedFuncType.argumentIds.push_back(funcParamType);
|
||||
|
||||
|
||||
@@ -56,6 +56,8 @@ static void ConvertToMeshOutputCompute(const ShaderReflection &refl, const SPIRV
|
||||
{
|
||||
rdcspv::Editor editor(modSpirv);
|
||||
|
||||
editor.Prepare();
|
||||
|
||||
uint32_t numInputs = (uint32_t)refl.inputSignature.size();
|
||||
|
||||
uint32_t numOutputs = (uint32_t)refl.outputSignature.size();
|
||||
@@ -348,12 +350,12 @@ static void ConvertToMeshOutputCompute(const ShaderReflection &refl, const SPIRV
|
||||
|
||||
std::set<rdcspv::Id> entries;
|
||||
|
||||
for(const rdcspv::OpEntryPoint &entry : editor.GetEntries())
|
||||
for(const rdcspv::EntryPoint &entry : editor.GetEntries())
|
||||
{
|
||||
if(entry.name == entryName)
|
||||
entryID = entry.entryPoint;
|
||||
entryID = entry.id;
|
||||
|
||||
entries.insert(entry.entryPoint);
|
||||
entries.insert(entry.id);
|
||||
}
|
||||
|
||||
RDCASSERT(entryID);
|
||||
@@ -740,7 +742,7 @@ static void ConvertToMeshOutputCompute(const ShaderReflection &refl, const SPIRV
|
||||
std::vector<rdcspv::Operation> ops;
|
||||
|
||||
rdcspv::Id voidType = editor.DeclareType(rdcspv::scalar<void>());
|
||||
rdcspv::Id funcType = editor.DeclareType(rdcspv::Function(voidType, {}));
|
||||
rdcspv::Id funcType = editor.DeclareType(rdcspv::FunctionType(voidType, {}));
|
||||
|
||||
ops.push_back(rdcspv::OpFunction(voidType, wrapperEntry, rdcspv::FunctionControl::None, funcType));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user