diff --git a/renderdoc/driver/shaders/spirv/spirv_common.h b/renderdoc/driver/shaders/spirv/spirv_common.h index a16b130e4..d9dd200da 100644 --- a/renderdoc/driver/shaders/spirv/spirv_common.h +++ b/renderdoc/driver/shaders/spirv/spirv_common.h @@ -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 &sources, vector &spirv); -void ParseSPIRV(uint32_t *spirv, size_t spirvLength, SPVModule &module, ShaderReflection *reflection = NULL); +void ParseSPIRV(uint32_t *spirv, size_t spirvLength, SPVModule &module); diff --git a/renderdoc/driver/shaders/spirv/spirv_disassemble.cpp b/renderdoc/driver/shaders/spirv/spirv_disassemble.cpp index 91d7add77..82f693a7c 100644 --- a/renderdoc/driver/shaders/spirv/spirv_disassemble.cpp +++ b/renderdoc/driver/shaders/spirv/spirv_disassemble.cpp @@ -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 inputs; + vector outputs; + vector cblocks; vector cblockmap; + vector resources; vector 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 *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<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 inputs; - vector outputs; - vector cblocks; - vector 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 *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<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<> diff --git a/renderdoc/driver/vulkan/vk_core.h b/renderdoc/driver/vulkan/vk_core.h index 9360f4215..f76db30f9 100644 --- a/renderdoc/driver/vulkan/vk_core.h +++ b/renderdoc/driver/vulkan/vk_core.h @@ -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 m_DescriptorSetInfo; + struct ShaderModuleInfo + { + SPVModule spirv; + ShaderReflection reflTemplate; + ShaderBindpointMapping mapping; + }; + map m_ShaderModuleInfo; + + struct ShaderInfo + { + ResourceId module; + ShaderReflection refl; + ShaderBindpointMapping mapping; + }; + map m_ShaderInfo; + VulkanCreationInfo m_CreationInfo; set m_SubmittedFences; diff --git a/renderdoc/driver/vulkan/vk_replay.cpp b/renderdoc/driver/vulkan/vk_replay.cpp index 5e388270b..c1e523c5c 100644 --- a/renderdoc/driver/vulkan/vk_replay.cpp +++ b/renderdoc/driver/vulkan/vk_replay.cpp @@ -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 diff --git a/renderdoc/driver/vulkan/wrappers/vk_shader_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_shader_funcs.cpp index 82d625830..dbfdd00ca 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_shader_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_shader_funcs.cpp @@ -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; } } diff --git a/renderdocui/Code/CommonPipelineState.cs b/renderdocui/Code/CommonPipelineState.cs index 803e37227..9361875fc 100644 --- a/renderdocui/Code/CommonPipelineState.cs +++ b/renderdocui/Code/CommonPipelineState.cs @@ -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++; } diff --git a/renderdocui/Interop/Shader.cs b/renderdocui/Interop/Shader.cs index 4f38c5e9a..0135ed6b2 100644 --- a/renderdocui/Interop/Shader.cs +++ b/renderdocui/Interop/Shader.cs @@ -449,6 +449,7 @@ namespace renderdoc [StructLayout(LayoutKind.Sequential)] public class BindpointMap { + public Int32 bindset; public Int32 bind; public bool used; }; diff --git a/renderdocui/Windows/PipelineState/VulkanPipelineStateViewer.cs b/renderdocui/Windows/PipelineState/VulkanPipelineStateViewer.cs index 37d5b7133..57e40fc48 100644 --- a/renderdocui/Windows/PipelineState/VulkanPipelineStateViewer.cs +++ b/renderdocui/Windows/PipelineState/VulkanPipelineStateViewer.cs @@ -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();