Expose SPIR-V reflection data to UI

This commit is contained in:
baldurk
2015-10-04 00:06:58 +02:00
parent c8bba412d2
commit 8832573dbb
8 changed files with 301 additions and 194 deletions
@@ -48,6 +48,9 @@ void ShutdownSPIRVCompiler();
struct SPVInstruction;
struct ShaderReflection;
struct ShaderBindpointMapping;
struct SPVModule
{
SPVModule();
@@ -72,9 +75,9 @@ struct SPVModule
SPVInstruction *GetByID(uint32_t id);
void Disassemble();
void MakeReflection(ShaderReflection *reflection, ShaderBindpointMapping *mapping);
};
struct ShaderReflection;
string CompileSPIRV(SPIRVShaderStage shadType, const vector<string> &sources, vector<uint32_t> &spirv);
void ParseSPIRV(uint32_t *spirv, size_t spirvLength, SPVModule &module, ShaderReflection *reflection = NULL);
void ParseSPIRV(uint32_t *spirv, size_t spirvLength, SPVModule &module);
@@ -1751,7 +1751,211 @@ SystemAttribute BuiltInToSystemAttribute(const spv::BuiltIn el)
return eAttr_None;
}
void ParseSPIRV(uint32_t *spirv, size_t spirvLength, SPVModule &module, ShaderReflection *reflection)
void SPVModule::MakeReflection(ShaderReflection *reflection, ShaderBindpointMapping *mapping)
{
vector<SigParameter> inputs;
vector<SigParameter> outputs;
vector<ConstantBlock> cblocks; vector<BindpointMap> cblockmap;
vector<ShaderResource> resources; vector<BindpointMap> resmap;
create_array_uninit(mapping->InputAttributes, 16);
for(size_t i=0; i < 16; i++) mapping->InputAttributes[i] = -1;
// TODO need to fetch these
reflection->DispatchThreadsDimension[0] = 0;
reflection->DispatchThreadsDimension[1] = 0;
reflection->DispatchThreadsDimension[2] = 0;
for(size_t i=0; i < globals.size(); i++)
{
SPVInstruction *inst = globals[i];
if(inst->var->storage == spv::StorageClassInput || inst->var->storage == spv::StorageClassOutput)
{
vector<SigParameter> *sigarray = (inst->var->storage == spv::StorageClassInput ? &inputs : &outputs);
SigParameter sig;
string nm = inst->str.empty() ? StringFormat::Fmt("sig%u", inst->id) : inst->str;
sig.varName = nm;
sig.semanticIndex = 0;
sig.needSemanticIndex = false;
bool rowmajor = true;
sig.regIndex = 0;
for(size_t d=0; d < inst->decorations.size(); d++)
{
if(inst->decorations[d].decoration == spv::DecorationLocation)
sig.regIndex = inst->decorations[d].val;
else if(inst->decorations[d].decoration == spv::DecorationBuiltIn)
sig.systemValue = BuiltInToSystemAttribute((spv::BuiltIn)inst->decorations[d].val);
else if(inst->decorations[d].decoration == spv::DecorationRowMajor)
rowmajor = true;
else if(inst->decorations[d].decoration == spv::DecorationColMajor)
rowmajor = false;
}
RDCASSERT(sig.regIndex < 16);
SPVTypeData *type = inst->var->type;
if(type->type == SPVTypeData::ePointer)
type = type->baseType;
switch(type->baseType ? type->baseType->type : type->type)
{
case SPVTypeData::eBool:
case SPVTypeData::eUInt:
sig.compType = eCompType_UInt;
break;
case SPVTypeData::eSInt:
sig.compType = eCompType_SInt;
break;
case SPVTypeData::eFloat:
sig.compType = eCompType_Float;
break;
default:
RDCERR("Unexpected base type of input signature %u", type->baseType->type);
break;
}
sig.compCount = type->vectorSize;
sig.stream = 0;
sig.regChannelMask = sig.channelUsedMask = (1<<type->vectorSize)-1;
if(type->matrixSize == 1)
{
if(inst->var->storage == spv::StorageClassInput && sig.systemValue == eAttr_None)
mapping->InputAttributes[sig.regIndex] = (int32_t)sigarray->size();
sigarray->push_back(sig);
}
else
{
for(uint32_t m=0; m < type->matrixSize; m++)
{
SigParameter s = sig;
s.varName = StringFormat::Fmt("%s:%s%u", nm, rowmajor ? "row" : "col", m);
s.regIndex += m;
RDCASSERT(s.regIndex < 16);
if(inst->var->storage == spv::StorageClassInput && sig.systemValue == eAttr_None)
mapping->InputAttributes[s.regIndex] = (int32_t)sigarray->size();
sigarray->push_back(s);
}
}
}
else if(inst->var->storage == spv::StorageClassUniform || inst->var->storage == spv::StorageClassUniformConstant)
{
SPVTypeData *type = inst->var->type;
if(type->type == SPVTypeData::ePointer)
type = type->baseType;
if(type->type == SPVTypeData::eStruct)
{
ConstantBlock cblock;
cblock.name = inst->str.empty() ? StringFormat::Fmt("uniforms%u", inst->id) : inst->str;
cblock.bufferBacked = true;
cblock.bindPoint = (int32_t)cblocks.size();
BindpointMap bindmap = {0};
// TODO this needs to go through the bindpoint mapping
for(size_t d=0; d < inst->decorations.size(); d++)
{
if(inst->decorations[d].decoration == spv::DecorationDescriptorSet)
bindmap.bindset = (int32_t)inst->decorations[d].val;
if(inst->decorations[d].decoration == spv::DecorationBinding)
bindmap.bind = (int32_t)inst->decorations[d].val;
}
MakeConstantBlockVariables(type, cblock.variables);
cblocks.push_back(cblock);
bindmap.used = true; // VKTODOLOW see if this declared struct is used anywhere in the code
cblockmap.push_back(bindmap);
}
else
{
ShaderResource res;
res.name = inst->str.empty() ? StringFormat::Fmt("res%u", inst->id) : inst->str;
if(type->multisampled)
res.resType = type->arrayed ? eResType_Texture2DMSArray : eResType_Texture2DMS;
else if(type->texdim == spv::Dim1D)
res.resType = type->arrayed ? eResType_Texture1DArray : eResType_Texture1D;
else if(type->texdim == spv::Dim2D)
res.resType = type->arrayed ? eResType_Texture2DArray : eResType_Texture2D;
else if(type->texdim == spv::DimCube)
res.resType = type->arrayed ? eResType_TextureCubeArray : eResType_TextureCube;
else if(type->texdim == spv::Dim3D)
res.resType = eResType_Texture3D;
else if(type->texdim == spv::DimRect)
res.resType = eResType_TextureRect;
else if(type->texdim == spv::DimBuffer)
res.resType = eResType_Buffer;
// TODO once we're on SPIR-V 1.0, update this handling
res.IsSampler = true;
res.IsTexture = true;
res.IsSRV = true;
res.IsReadWrite = false;
if(type->baseType->type == SPVTypeData::eFloat)
res.variableType.descriptor.type = eVar_Float;
else if(type->baseType->type == SPVTypeData::eUInt)
res.variableType.descriptor.type = eVar_UInt;
else if(type->baseType->type == SPVTypeData::eSInt)
res.variableType.descriptor.type = eVar_Int;
else
RDCERR("Unexpected base type of resource %u", type->baseType->type);
res.variableType.descriptor.rows = 1;
res.variableType.descriptor.cols = 1;
res.variableType.descriptor.elements = 1;
res.variableType.descriptor.rowMajorStorage = false;
res.variableType.descriptor.rowMajorStorage = false;
res.bindPoint = (int32_t)resources.size();
BindpointMap bindmap = {0};
// TODO this needs to go through the bindpoint mapping
for(size_t d=0; d < inst->decorations.size(); d++)
{
if(inst->decorations[d].decoration == spv::DecorationDescriptorSet)
bindmap.bindset = (int32_t)inst->decorations[d].val;
if(inst->decorations[d].decoration == spv::DecorationBinding)
bindmap.bind = (int32_t)inst->decorations[d].val;
}
bindmap.used = true; // VKTODOLOW see if this declared resource is used anywhere in the code
resources.push_back(res);
resmap.push_back(bindmap);
}
}
else
{
RDCWARN("Unexpected storage class for global: %s", ToStr::Get(inst->var->storage));
}
}
reflection->InputSig = inputs;
reflection->OutputSig = outputs;
reflection->Resources = resources;
reflection->ConstantBlocks = cblocks;
mapping->ConstantBlocks = cblockmap;
mapping->Resources = resmap;
}
void ParseSPIRV(uint32_t *spirv, size_t spirvLength, SPVModule &module)
{
if(spirv[0] != (uint32_t)spv::MagicNumber)
{
@@ -2613,177 +2817,6 @@ void ParseSPIRV(uint32_t *spirv, size_t spirvLength, SPVModule &module, ShaderRe
};
std::sort(module.globals.begin(), module.globals.end(), SortByVarClass());
if(reflection)
{
vector<SigParameter> inputs;
vector<SigParameter> outputs;
vector<ConstantBlock> cblocks;
vector<ShaderResource> resources;
// TODO need to fetch these
reflection->DispatchThreadsDimension[0] = 0;
reflection->DispatchThreadsDimension[1] = 0;
reflection->DispatchThreadsDimension[2] = 0;
for(size_t i=0; i < module.globals.size(); i++)
{
SPVInstruction *inst = module.globals[i];
if(inst->var->storage == spv::StorageClassInput || inst->var->storage == spv::StorageClassOutput)
{
vector<SigParameter> *sigarray = (inst->var->storage == spv::StorageClassInput ? &inputs : &outputs);
SigParameter sig;
string nm = inst->str.empty() ? StringFormat::Fmt("sig%u", inst->id) : inst->str;
sig.varName = nm;
sig.semanticIndex = 0;
sig.needSemanticIndex = false;
bool rowmajor = true;
sig.regIndex = 0;
for(size_t d=0; d < inst->decorations.size(); d++)
{
if(inst->decorations[d].decoration == spv::DecorationLocation)
sig.regIndex = inst->decorations[d].val;
else if(inst->decorations[d].decoration == spv::DecorationBuiltIn)
sig.systemValue = BuiltInToSystemAttribute((spv::BuiltIn)inst->decorations[d].val);
else if(inst->decorations[d].decoration == spv::DecorationRowMajor)
rowmajor = true;
else if(inst->decorations[d].decoration == spv::DecorationColMajor)
rowmajor = false;
}
SPVTypeData *type = inst->var->type;
if(type->type == SPVTypeData::ePointer)
type = type->baseType;
switch(type->baseType ? type->baseType->type : type->type)
{
case SPVTypeData::eBool:
case SPVTypeData::eUInt:
sig.compType = eCompType_UInt;
break;
case SPVTypeData::eSInt:
sig.compType = eCompType_SInt;
break;
case SPVTypeData::eFloat:
sig.compType = eCompType_Float;
break;
default:
RDCERR("Unexpected base type of input signature %u", type->baseType->type);
break;
}
sig.compCount = type->vectorSize;
sig.stream = 0;
sig.regChannelMask = sig.channelUsedMask = (1<<type->vectorSize)-1;
if(type->matrixSize == 1)
{
sigarray->push_back(sig);
}
else
{
for(uint32_t m=0; m < type->matrixSize; m++)
{
SigParameter s = sig;
s.varName = StringFormat::Fmt("%s:%s%u", nm, rowmajor ? "row" : "col", m);
s.regIndex += m;
sigarray->push_back(s);
}
}
}
else if(inst->var->storage == spv::StorageClassUniform || inst->var->storage == spv::StorageClassUniformConstant)
{
SPVTypeData *type = inst->var->type;
if(type->type == SPVTypeData::ePointer)
type = type->baseType;
if(type->type == SPVTypeData::eStruct)
{
ConstantBlock cblock;
cblock.name = inst->str.empty() ? StringFormat::Fmt("uniforms%u", inst->id) : inst->str;
cblock.bufferBacked = true;
// TODO this needs to go through the bindpoint mapping
for(size_t d=0; d < inst->decorations.size(); d++)
{
if(inst->decorations[d].decoration == spv::DecorationBinding)
cblock.bindPoint = (int32_t)inst->decorations[d].val;
}
MakeConstantBlockVariables(type, cblock.variables);
cblocks.push_back(cblock);
}
else
{
ShaderResource res;
res.name = inst->str.empty() ? StringFormat::Fmt("res%u", inst->id) : inst->str;
if(type->multisampled)
res.resType = type->arrayed ? eResType_Texture2DMSArray : eResType_Texture2DMS;
else if(type->texdim == spv::Dim1D)
res.resType = type->arrayed ? eResType_Texture1DArray : eResType_Texture1D;
else if(type->texdim == spv::Dim2D)
res.resType = type->arrayed ? eResType_Texture2DArray : eResType_Texture2D;
else if(type->texdim == spv::DimCube)
res.resType = type->arrayed ? eResType_TextureCubeArray : eResType_TextureCube;
else if(type->texdim == spv::Dim3D)
res.resType = eResType_Texture3D;
else if(type->texdim == spv::DimRect)
res.resType = eResType_TextureRect;
else if(type->texdim == spv::DimBuffer)
res.resType = eResType_Buffer;
// TODO once we're on SPIR-V 1.0, update this handling
res.IsSampler = true;
res.IsTexture = true;
res.IsSRV = true;
res.IsReadWrite = false;
if(type->baseType->type == SPVTypeData::eFloat)
res.variableType.descriptor.type = eVar_Float;
else if(type->baseType->type == SPVTypeData::eUInt)
res.variableType.descriptor.type = eVar_UInt;
else if(type->baseType->type == SPVTypeData::eSInt)
res.variableType.descriptor.type = eVar_Int;
else
RDCERR("Unexpected base type of resource %u", type->baseType->type);
res.variableType.descriptor.rows = 1;
res.variableType.descriptor.cols = 1;
res.variableType.descriptor.elements = 1;
res.variableType.descriptor.rowMajorStorage = false;
res.variableType.descriptor.rowMajorStorage = false;
// TODO this needs to go through the bindpoint mapping
for(size_t d=0; d < inst->decorations.size(); d++)
{
if(inst->decorations[d].decoration == spv::DecorationBinding)
res.bindPoint = (int32_t)inst->decorations[d].val;
}
resources.push_back(res);
}
}
else
{
RDCWARN("Unexpected storage class for global: %s", ToStr::Get(inst->var->storage));
}
}
reflection->InputSig = inputs;
reflection->OutputSig = outputs;
reflection->Resources = resources;
reflection->ConstantBlocks = cblocks;
}
}
template<>
+18
View File
@@ -36,6 +36,8 @@
#include "vk_manager.h"
#include "vk_replay.h"
#include "driver/shaders/spirv/spirv_common.h"
using std::vector;
using std::list;
@@ -338,6 +340,22 @@ private:
};
map<ResourceId, DescriptorSetInfo> m_DescriptorSetInfo;
struct ShaderModuleInfo
{
SPVModule spirv;
ShaderReflection reflTemplate;
ShaderBindpointMapping mapping;
};
map<ResourceId, ShaderModuleInfo> m_ShaderModuleInfo;
struct ShaderInfo
{
ResourceId module;
ShaderReflection refl;
ShaderBindpointMapping mapping;
};
map<ResourceId, ShaderInfo> m_ShaderInfo;
VulkanCreationInfo m_CreationInfo;
set<ResourceId> m_SubmittedFences;
+10 -2
View File
@@ -1284,8 +1284,15 @@ FetchBuffer VulkanReplay::GetBuffer(ResourceId id)
ShaderReflection *VulkanReplay::GetShader(ResourceId id)
{
VULKANNOTIMP("GetShader");
return NULL;
auto it = m_pDriver->m_ShaderInfo.find(id);
if(it == m_pDriver->m_ShaderInfo.end())
{
RDCERR("Can't get shader details");
return NULL;
}
return &it->second.refl;
}
void VulkanReplay::SavePipelineState()
@@ -1360,6 +1367,7 @@ void VulkanReplay::SavePipelineState()
stages[i]->customName = false;
stages[i]->ShaderName = StringFormat::Fmt("Shader %llu", stages[i]->Shader);
stages[i]->stage = ShaderStageType(eShaderStage_Vertex + i);
stages[i]->BindpointMapping = m_pDriver->m_ShaderInfo[p.shaders[i]].mapping;
}
// Descriptor sets
@@ -134,11 +134,17 @@ bool WrappedVulkan::Serialise_vkCreateShaderModule(
ResourceId live = GetResourceManager()->WrapResource(Unwrap(device), sh);
GetResourceManager()->AddLiveResource(id, sh);
SPVModule spvmod;
ShaderReflection refl;
if(id == ResourceId(60606, true) || id == ResourceId(60633, true))
{
FILE *f = FileIO::fopen(StringFormat::Fmt("T:/tmp/shad%llu", id).c_str(), "wb");
FileIO::fwrite(info.pCode, 1, info.codeSize, f);
FileIO::fclose(f);
}
RDCASSERT(info.codeSize % sizeof(uint32_t) == 0);
ParseSPIRV((uint32_t *)info.pCode, info.codeSize/sizeof(uint32_t), spvmod, &refl);
ParseSPIRV((uint32_t *)info.pCode, info.codeSize/sizeof(uint32_t), m_ShaderModuleInfo[live].spirv);
m_ShaderModuleInfo[live].spirv.MakeReflection(&m_ShaderModuleInfo[live].reflTemplate, &m_ShaderModuleInfo[live].mapping);
}
}
@@ -203,6 +209,13 @@ bool WrappedVulkan::Serialise_vkCreateShader(
{
ResourceId live = GetResourceManager()->WrapResource(Unwrap(device), sh);
GetResourceManager()->AddLiveResource(id, sh);
m_ShaderInfo[live].module = GetResourceManager()->GetNonDispWrapper(info.module)->id;
m_ShaderInfo[live].mapping = m_ShaderModuleInfo[m_ShaderInfo[live].module].mapping;
m_ShaderInfo[live].refl = m_ShaderModuleInfo[m_ShaderInfo[live].module].reflTemplate;
m_ShaderInfo[live].refl.DebugInfo.entryFunc = info.pName;
// VKTODOLOW set this properly
m_ShaderInfo[live].refl.DebugInfo.entryFile = 0;
}
}
+11 -2
View File
@@ -565,7 +565,7 @@ namespace renderdocui.Code
{
int attrib = -1;
if (m_Vulkan.VS.BindpointMapping != null && m_Vulkan.VS.ShaderDetails != null)
attrib = m_Vulkan.VS.BindpointMapping.InputAttributes[i];
attrib = m_Vulkan.VS.BindpointMapping.InputAttributes[attrs[i].location];
else
attrib = i;
@@ -586,7 +586,16 @@ namespace renderdocui.Code
ret[a].Format = attrs[i].format;
ret[a].Used = true;
// VKTODOMED use shader reflection & attrs[i].location to get better name
if (m_Vulkan.VS.BindpointMapping != null && m_Vulkan.VS.ShaderDetails != null)
{
int attrib = m_Vulkan.VS.BindpointMapping.InputAttributes[attrs[i].location];
if (attrib >= 0 && attrib < m_Vulkan.VS.ShaderDetails.InputSig.Length)
ret[a].Name = m_Vulkan.VS.ShaderDetails.InputSig[attrib].varName;
if (attrib == -1) continue;
}
a++;
}
+1
View File
@@ -449,6 +449,7 @@ namespace renderdoc
[StructLayout(LayoutKind.Sequential)]
public class BindpointMap
{
public Int32 bindset;
public Int32 bind;
public bool used;
};
@@ -335,19 +335,41 @@ namespace renderdocui.Windows.PipelineState
int i = 0;
foreach (var a in state.VI.attrs)
{
bool filledSlot = true;
bool usedSlot = false;
string name = String.Format("Attribute {0}", i);
if (state.VS.Shader != ResourceId.Null)
{
int attrib = state.VS.BindpointMapping.InputAttributes[a.location];
if (attrib >= 0 && attrib < state.VS.ShaderDetails.InputSig.Length)
{
name = state.VS.ShaderDetails.InputSig[attrib].varName;
usedSlot = true;
}
}
// show if
if (usedSlot || // it's referenced by the shader - regardless of empty or not
(showDisabled.Checked && !usedSlot && filledSlot) || // it's bound, but not referenced, and we have "show disabled"
(showEmpty.Checked && !filledSlot) // it's empty, and we have "show empty"
)
{
var node = viAttrs.Nodes.Add(new object[] {
i, name, a.location, a.binding, a.format, a.byteoffset });
usedBindings[a.binding] = true;
node.Image = global::renderdocui.Properties.Resources.action;
node.HoverImage = global::renderdocui.Properties.Resources.action_hover;
if (!usedSlot)
InactiveRow(node);
}
i++;
var node = viAttrs.Nodes.Add(new object[] {
i, String.Format("attr{0}", i),
a.location, a.binding, a.format, a.byteoffset });
usedBindings[a.binding] = true;
node.Image = global::renderdocui.Properties.Resources.action;
node.HoverImage = global::renderdocui.Properties.Resources.action_hover;
// VKTODOMED use shader reflection to see if this attribute is used
//InactiveRow(node);
}
}
viAttrs.NodesSelection.Clear();