private void AddResourceRow(ShaderReflection shaderDetails, VulkanPipelineState.ShaderStage stage, int bindset, int bind, VulkanPipelineState.Pipeline pipe, TreelistView.TreeListView resources, FetchTexture[] texs, FetchBuffer[] bufs, ref Dictionary<ResourceId, SamplerData> samplers) { ShaderResource shaderRes = null; BindpointMap bindMap = null; bool isrw = false; uint bindPoint = 0; if (shaderDetails != null) { for (int i = 0; i < shaderDetails.ReadOnlyResources.Length; i++) { var ro = shaderDetails.ReadOnlyResources[i]; if (stage.BindpointMapping.ReadOnlyResources[ro.bindPoint].bindset == bindset && stage.BindpointMapping.ReadOnlyResources[ro.bindPoint].bind == bind) { bindPoint = (uint)i; shaderRes = ro; bindMap = stage.BindpointMapping.ReadOnlyResources[ro.bindPoint]; } } for (int i = 0; i < shaderDetails.ReadWriteResources.Length; i++) { var rw = shaderDetails.ReadWriteResources[i]; if (stage.BindpointMapping.ReadWriteResources[rw.bindPoint].bindset == bindset && stage.BindpointMapping.ReadWriteResources[rw.bindPoint].bind == bind) { bindPoint = (uint)i; isrw = true; shaderRes = rw; bindMap = stage.BindpointMapping.ReadWriteResources[rw.bindPoint]; } } } VulkanPipelineState.Pipeline.DescriptorSet.DescriptorBinding.BindingElement[] slotBinds = null; ShaderBindType bindType = ShaderBindType.Unknown; ShaderStageBits stageBits = (ShaderStageBits)0; if (bindset < pipe.DescSets.Length && bind < pipe.DescSets[bindset].bindings.Length) { slotBinds = pipe.DescSets[bindset].bindings[bind].binds; bindType = pipe.DescSets[bindset].bindings[bind].type; stageBits = pipe.DescSets[bindset].bindings[bind].stageFlags; } else { if (shaderRes.IsSampler) bindType = ShaderBindType.Sampler; else if (shaderRes.IsSampler && shaderRes.IsTexture) bindType = ShaderBindType.ImageSampler; else if (shaderRes.resType == ShaderResourceType.Buffer) bindType = ShaderBindType.ReadOnlyTBuffer; else bindType = ShaderBindType.ReadOnlyImage; } bool usedSlot = bindMap != null && bindMap.used; bool stageBitsIncluded = stageBits.HasFlag((ShaderStageBits)(1 << (int)stage.stage)); // skip descriptors that aren't for this shader stage if (!usedSlot && !stageBitsIncluded) return; if (bindType == ShaderBindType.ConstantBuffer) return; // TODO - check compatibility between bindType and shaderRes.resType ? // consider it filled if any array element is filled bool filledSlot = false; for (int idx = 0; slotBinds != null && idx < slotBinds.Length; idx++) { filledSlot |= slotBinds[idx].res != ResourceId.Null; if (bindType == ShaderBindType.Sampler || bindType == ShaderBindType.ImageSampler) filledSlot |= slotBinds[idx].sampler != ResourceId.Null; } // if it's masked out by stage bits, act as if it's not filled, so it's marked in red if (!stageBitsIncluded) filledSlot = false; // 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" ) { TreelistView.NodeCollection parentNodes = resources.Nodes; string setname = bindset.ToString(); string slotname = bind.ToString(); if (shaderRes != null && shaderRes.name.Length > 0) slotname += ": " + shaderRes.name; int arrayLength = 0; if (slotBinds != null) arrayLength = slotBinds.Length; else arrayLength = (int)bindMap.arraySize; // for arrays, add a parent element that we add the real cbuffers below if (arrayLength > 1) { var node = parentNodes.Add(new object[] { "", setname, slotname, String.Format("Array[{0}]", arrayLength), "", "", "", "" }); node.TreeColumn = 0; if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); parentNodes = node.Nodes; } for (int idx = 0; idx < arrayLength; idx++) { VulkanPipelineState.Pipeline.DescriptorSet.DescriptorBinding.BindingElement descriptorBind = null; if (slotBinds != null) descriptorBind = slotBinds[idx]; if (arrayLength > 1) { slotname = String.Format("{0}[{1}]", bind, idx); if (shaderRes != null && shaderRes.name.Length > 0) slotname += ": " + shaderRes.name; } bool isbuf = false; UInt32 w = 1, h = 1, d = 1; UInt32 a = 1; UInt32 samples = 1; UInt64 len = 0; string format = "Unknown"; string name = "Empty"; ShaderResourceType restype = ShaderResourceType.None; object tag = null; bool viewDetails = false; if (filledSlot && descriptorBind != null) { name = "Object " + descriptorBind.res.ToString(); format = descriptorBind.viewfmt.ToString(); // check to see if it's a texture for (int t = 0; t < texs.Length; t++) { if (texs[t].ID == descriptorBind.res) { w = texs[t].width; h = texs[t].height; d = texs[t].depth; a = texs[t].arraysize; name = texs[t].name; restype = texs[t].resType; samples = texs[t].msSamp; if (HasImportantViewParams(descriptorBind, texs[t])) viewDetails = true; tag = new ViewTexTag( descriptorBind.viewfmt, descriptorBind.baseMip, descriptorBind.baseLayer, descriptorBind.numMip, descriptorBind.numLayer, texs[t] ); } } // if not a texture, it must be a buffer for (int t = 0; t < bufs.Length; t++) { if (bufs[t].ID == descriptorBind.res) { len = bufs[t].length; w = 0; h = 0; d = 0; a = 0; name = bufs[t].name; restype = ShaderResourceType.Buffer; ulong descriptorLen = descriptorBind.size; if(descriptorLen == ulong.MaxValue) descriptorLen = len - descriptorBind.offset; tag = new BufferResTag(isrw, bindPoint, bufs[t].ID, descriptorBind.offset, descriptorLen); if (HasImportantViewParams(descriptorBind, bufs[t])) viewDetails = true; isbuf = true; } } } else { name = "Empty"; format = "-"; w = h = d = a = 0; } TreelistView.Node node = null; if (bindType == ShaderBindType.ReadWriteBuffer || bindType == ShaderBindType.ReadOnlyTBuffer || bindType == ShaderBindType.ReadWriteTBuffer ) { if (!isbuf) { node = parentNodes.Add(new object[] { "", bindset, slotname, bindType, "-", "-", "", }); EmptyRow(node); } else { string range = "-"; if (descriptorBind != null) range = String.Format("{0} - {1}", descriptorBind.offset, descriptorBind.size); node = parentNodes.Add(new object[] { "", bindset, slotname, bindType, name, String.Format("{0} bytes", len), range, }); node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; node.Tag = tag; if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); } } else if (bindType == ShaderBindType.Sampler) { if (descriptorBind == null || descriptorBind.sampler == ResourceId.Null) { node = parentNodes.Add(new object[] { "", bindset, slotname, bindType, "-", "-", "", }); EmptyRow(node); } else { node = parentNodes.Add(MakeSampler(bindset.ToString(), slotname, descriptorBind)); if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); var data = new SamplerData(node); node.Tag = data; if (!samplers.ContainsKey(descriptorBind.sampler)) samplers.Add(descriptorBind.sampler, data); } } else { if (descriptorBind == null || descriptorBind.res == ResourceId.Null) { node = parentNodes.Add(new object[] { "", bindset, slotname, bindType, "-", "-", "", }); EmptyRow(node); } else { string typename = restype.Str() + " " + bindType.Str().Replace("&", "&&"); string dim; if (restype == ShaderResourceType.Texture3D) dim = String.Format("{0}x{1}x{2}", w, h, d); else if (restype == ShaderResourceType.Texture1D || restype == ShaderResourceType.Texture1DArray) dim = w.ToString(); else dim = String.Format("{0}x{1}", w, h); if (descriptorBind.swizzle[0] != TextureSwizzle.Red || descriptorBind.swizzle[1] != TextureSwizzle.Green || descriptorBind.swizzle[2] != TextureSwizzle.Blue || descriptorBind.swizzle[3] != TextureSwizzle.Alpha) { format += String.Format(" swizzle[{0}{1}{2}{3}]", descriptorBind.swizzle[0].Str(), descriptorBind.swizzle[1].Str(), descriptorBind.swizzle[2].Str(), descriptorBind.swizzle[3].Str()); } if (restype == ShaderResourceType.Texture1DArray || restype == ShaderResourceType.Texture2DArray || restype == ShaderResourceType.Texture2DMSArray || restype == ShaderResourceType.TextureCubeArray) { dim += String.Format(" {0}[{1}]", restype.Str(), a); } if (restype == ShaderResourceType.Texture2DMS || restype == ShaderResourceType.Texture2DMSArray) dim += String.Format(", {0}x MSAA", samples); node = parentNodes.Add(new object[] { "", bindset, slotname, typename, name, dim, format, }); node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; node.Tag = tag; if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); ViewDetailsRow(node, viewDetails); } if (bindType == ShaderBindType.ImageSampler) { if (descriptorBind == null || descriptorBind.sampler == ResourceId.Null) { node = parentNodes.Add(new object[] { "", bindset, slotname, bindType, "-", "-", "", }); EmptyRow(node); } else { var texnode = node; if (!samplers.ContainsKey(descriptorBind.sampler)) { node = parentNodes.Add(MakeSampler("", "", descriptorBind)); if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); var data = new SamplerData(node); node.Tag = data; samplers.Add(descriptorBind.sampler, data); } if (texnode != null) { m_CombinedImageSamplers[texnode] = samplers[descriptorBind.sampler].node; samplers[descriptorBind.sampler].images.Add(texnode); } } } } } } }
// Set a shader stage's resources and values private void SetShaderState(FetchTexture[] texs, FetchBuffer[] bufs, VulkanPipelineState.ShaderStage stage, VulkanPipelineState.Pipeline pipe, Label shader, TreelistView.TreeListView resources, TreelistView.TreeListView cbuffers) { ShaderReflection shaderDetails = stage.ShaderDetails; if (stage.Shader == ResourceId.Null) shader.Text = "Unbound"; else shader.Text = stage.ShaderName; if (shaderDetails != null && shaderDetails.DebugInfo.entryFunc.Length > 0) { if (shaderDetails.DebugInfo.files.Length > 0 || shaderDetails.DebugInfo.entryFunc != "main") shader.Text = shaderDetails.DebugInfo.entryFunc + "()"; if (shaderDetails.DebugInfo.files.Length > 0) { string shaderfn = ""; int entryFile = shaderDetails.DebugInfo.entryFile; if (entryFile < 0 || entryFile >= shaderDetails.DebugInfo.files.Length) entryFile = 0; shaderfn = shaderDetails.DebugInfo.files[entryFile].BaseFilename; shader.Text += " - " + shaderfn; } } int vs = 0; vs = resources.VScrollValue(); resources.BeginUpdate(); resources.Nodes.Clear(); var samplers = new Dictionary<ResourceId, SamplerData>(); for(int bindset = 0; bindset < pipe.DescSets.Length; bindset++) { for(int bind = 0; bind < pipe.DescSets[bindset].bindings.Length; bind++) { ShaderResource shaderRes = null; BindpointMap bindMap = null; bool isrw = false; uint bindPoint = 0; if (shaderDetails != null) { for(int i=0; i < shaderDetails.ReadOnlyResources.Length; i++) { var ro = shaderDetails.ReadOnlyResources[i]; if (stage.BindpointMapping.ReadOnlyResources[ro.bindPoint].bindset == bindset && stage.BindpointMapping.ReadOnlyResources[ro.bindPoint].bind == bind) { bindPoint = (uint)i; shaderRes = ro; bindMap = stage.BindpointMapping.ReadOnlyResources[ro.bindPoint]; } } for(int i=0; i < shaderDetails.ReadWriteResources.Length; i++) { var rw = shaderDetails.ReadWriteResources[i]; if (stage.BindpointMapping.ReadWriteResources[rw.bindPoint].bindset == bindset && stage.BindpointMapping.ReadWriteResources[rw.bindPoint].bind == bind) { bindPoint = (uint)i; isrw = true; shaderRes = rw; bindMap = stage.BindpointMapping.ReadWriteResources[rw.bindPoint]; } } } VulkanPipelineState.Pipeline.DescriptorSet.DescriptorBinding.BindingElement[] slotBinds = pipe.DescSets[bindset].bindings[bind].binds; ShaderBindType bindType = pipe.DescSets[bindset].bindings[bind].type; ShaderStageBits stageBits = pipe.DescSets[bindset].bindings[bind].stageFlags; // skip descriptors that aren't for this shader stage if (!stageBits.HasFlag((ShaderStageBits)(1 << (int)stage.stage))) continue; // these are treated as uniform buffers if (bindType == ShaderBindType.ReadOnlyBuffer) continue; // consider it filled if any array element is filled bool filledSlot = false; for (int idx = 0; idx < slotBinds.Length; idx++) { filledSlot |= slotBinds[idx].res != ResourceId.Null; if(bindType == ShaderBindType.Sampler || bindType == ShaderBindType.ImageSampler) filledSlot |= slotBinds[idx].sampler != ResourceId.Null; } bool usedSlot = bindMap != null && bindMap.used; // 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" ) { TreelistView.NodeCollection parentNodes = resources.Nodes; string setname = bindset.ToString(); string slotname = bind.ToString(); if(shaderRes != null) slotname += ": " + shaderRes.name; // for arrays, add a parent element that we add the real cbuffers below if (slotBinds.Length > 1) { var node = parentNodes.Add(new object[] { "", setname, slotname, String.Format("Array[{0}]", slotBinds.Length), "", "", "", "" }); node.TreeColumn = 0; if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); parentNodes = node.Nodes; } for (int idx = 0; idx < slotBinds.Length; idx++) { var descriptorBind = slotBinds[idx]; if (slotBinds.Length > 1) { slotname = String.Format("{0}[{1}]", bind, idx); if (shaderRes != null && shaderRes.name.Length > 0) slotname += ": " + shaderRes.name; } bool isbuf = false; UInt32 w = 1, h = 1, d = 1; UInt32 a = 1; UInt32 samples = 1; UInt64 len = 0; string format = "Unknown"; string name = "Object " + descriptorBind.res.ToString(); ShaderResourceType restype = ShaderResourceType.None; object tag = null; if (!filledSlot) { name = "Empty"; format = "-"; w = h = d = a = 0; } // check to see if it's a texture for (int t = 0; t < texs.Length; t++) { if (texs[t].ID == descriptorBind.res) { w = texs[t].width; h = texs[t].height; d = texs[t].depth; a = texs[t].arraysize; format = texs[t].format.ToString(); name = texs[t].name; restype = texs[t].resType; samples = texs[t].msSamp; tag = texs[t]; } } // if not a texture, it must be a buffer for (int t = 0; t < bufs.Length; t++) { if (bufs[t].ID == descriptorBind.res) { len = bufs[t].byteSize; w = bufs[t].length; h = 0; d = 0; a = 0; format = ""; name = bufs[t].name; restype = ShaderResourceType.Buffer; tag = new BufferResTag(isrw, bindPoint, bufs[t].ID); isbuf = true; } } TreelistView.Node node = null; if (bindType == ShaderBindType.ReadWriteBuffer || bindType == ShaderBindType.ReadOnlyTBuffer || bindType == ShaderBindType.ReadWriteTBuffer ) { if (!isbuf) { node = parentNodes.Add(new object[] { "", bindset, slotname, bindType, "-", "-", "", "", }); EmptyRow(node); } else { node = parentNodes.Add(new object[] { "", bindset, slotname, bindType, name, String.Format("{0} bytes", len), String.Format("{0} - {1}", descriptorBind.offset, descriptorBind.size), "", }); node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; node.Tag = tag; if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); } } else if (bindType == ShaderBindType.Sampler) { if (descriptorBind.sampler == ResourceId.Null) { node = parentNodes.Add(new object[] { "", bindset, slotname, bindType, "-", "-", "", "", }); EmptyRow(node); } else { node = parentNodes.Add(MakeSampler(bindset.ToString(), slotname, descriptorBind)); if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); var data = new SamplerData(node); node.Tag = data; if (!samplers.ContainsKey(descriptorBind.sampler)) samplers.Add(descriptorBind.sampler, data); } } else { if (descriptorBind.res == ResourceId.Null) { node = parentNodes.Add(new object[] { "", bindset, slotname, bindType, "-", "-", "", "", }); EmptyRow(node); } else { string typename = restype.Str() + " " + bindType.Str().Replace("&", "&&"); string dim; if (restype == ShaderResourceType.Texture3D) dim = String.Format("{0}x{1}x{2}", w, h, d); else if (restype == ShaderResourceType.Texture1D || restype == ShaderResourceType.Texture1DArray) dim = w.ToString(); else dim = String.Format("{0}x{1}", w, h); string arraydim = "-"; if(restype == ShaderResourceType.Texture1DArray || restype == ShaderResourceType.Texture2DArray || restype == ShaderResourceType.Texture2DMSArray || restype == ShaderResourceType.TextureCubeArray) arraydim = String.Format("{0}[{1}]", restype.Str(), a); if (restype == ShaderResourceType.Texture2DMS || restype == ShaderResourceType.Texture2DMSArray) dim += String.Format(", {0}x MSAA", samples); node = parentNodes.Add(new object[] { "", bindset, slotname, typename, name, dim, format, arraydim, }); node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; node.Tag = tag; if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); } if (bindType == ShaderBindType.ImageSampler) { if (descriptorBind.sampler == ResourceId.Null) { node = parentNodes.Add(new object[] { "", bindset, slotname, bindType, "-", "-", "", "", }); EmptyRow(node); } else { var texnode = node; if (!samplers.ContainsKey(descriptorBind.sampler)) { node = parentNodes.Add(MakeSampler("", "", descriptorBind)); if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); var data = new SamplerData(node); node.Tag = data; samplers.Add(descriptorBind.sampler, data); } if (texnode != null) { m_CombinedImageSamplers[texnode] = samplers[descriptorBind.sampler].node; samplers[descriptorBind.sampler].images.Add(texnode); } } } } } } } } resources.EndUpdate(); resources.NodesSelection.Clear(); resources.SetVScrollValue(vs); vs = cbuffers.VScrollValue(); cbuffers.BeginUpdate(); cbuffers.Nodes.Clear(); for(int bindset = 0; bindset < pipe.DescSets.Length; bindset++) { for(int bind = 0; bind < pipe.DescSets[bindset].bindings.Length; bind++) { ConstantBlock cblock = null; BindpointMap bindMap = null; uint slot = uint.MaxValue; if (shaderDetails != null) { for (slot = 0; slot < (uint)shaderDetails.ConstantBlocks.Length; slot++) { ConstantBlock cb = shaderDetails.ConstantBlocks[slot]; if (stage.BindpointMapping.ConstantBlocks[cb.bindPoint].bindset == bindset && stage.BindpointMapping.ConstantBlocks[cb.bindPoint].bind == bind) { cblock = cb; bindMap = stage.BindpointMapping.ConstantBlocks[cb.bindPoint]; break; } } if (slot >= (uint)shaderDetails.ConstantBlocks.Length) slot = uint.MaxValue; } var slotBinds = pipe.DescSets[bindset].bindings[bind].binds; ShaderBindType bindType = pipe.DescSets[bindset].bindings[bind].type; ShaderStageBits stageBits = pipe.DescSets[bindset].bindings[bind].stageFlags; // skip descriptors that aren't for this shader stage if (!stageBits.HasFlag((ShaderStageBits)(1 << (int)stage.stage))) continue; // these are treated as uniform buffers if (bindType != ShaderBindType.ReadOnlyBuffer) continue; bool usedSlot = bindMap != null && bindMap.used; // consider it filled if any array element is filled (or it's push constants) bool filledSlot = cblock != null && !cblock.bufferBacked; for (int idx = 0; idx < slotBinds.Length; idx++) filledSlot |= slotBinds[idx].res != ResourceId.Null; // 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" ) { TreelistView.NodeCollection parentNodes = cbuffers.Nodes; string setname = bindset.ToString(); string slotname = bind.ToString(); if (cblock != null && cblock.name.Length > 0) slotname += ": " + cblock.name; // for arrays, add a parent element that we add the real cbuffers below if (slotBinds.Length > 1) { var node = parentNodes.Add(new object[] { "", setname, slotname, String.Format("Array[{0}]", slotBinds.Length), "", "" }); node.TreeColumn = 0; if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); parentNodes = node.Nodes; } for (int idx = 0; idx < slotBinds.Length; idx++) { var descriptorBind = slotBinds[idx]; if (slotBinds.Length > 1) { slotname = String.Format("{0}[{1}]", bind, idx); if (cblock != null && cblock.name.Length > 0) slotname += ": " + cblock.name; } string name = "UBO " + descriptorBind.res.ToString(); UInt64 length = descriptorBind.size; int numvars = cblock != null ? cblock.variables.Length : 0; if (!filledSlot) { name = "Empty"; length = 0; } for (int t = 0; t < bufs.Length; t++) if (bufs[t].ID == descriptorBind.res) name = bufs[t].name; if (name == "") name = "UBO " + descriptorBind.res.ToString(); string sizestr = String.Format("{0} Variables, {1} bytes", numvars, length); string vecrange = String.Format("{0} - {1}", descriptorBind.offset, descriptorBind.offset + descriptorBind.size); // push constants if (cblock != null && !cblock.bufferBacked) { setname = ""; slotname = cblock.name; name = "Push constants"; vecrange = ""; sizestr = String.Format("{0} Variables", numvars); // could maybe get range from ShaderVariable.reg if it's filled out // from SPIR-V side. } var node = parentNodes.Add(new object[] { "", setname, slotname, name, vecrange, sizestr }); node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; node.Tag = new CBufferTag(slot, (uint)idx); if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); } } } } // search for push constants and add them last if (shaderDetails != null) { for (int cb = 0; cb < shaderDetails.ConstantBlocks.Length; cb++) { var cblock = shaderDetails.ConstantBlocks[cb]; if (cblock.bufferBacked == false) { // could maybe get range from ShaderVariable.reg if it's filled out // from SPIR-V side. var node = cbuffers.Nodes.Add(new object[] { "", "", cblock.name, "Push constants", "", String.Format("{0} Variables", cblock.variables.Length) }); node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; node.Tag = new CBufferTag((uint)cb, 0); } } } cbuffers.EndUpdate(); cbuffers.NodesSelection.Clear(); cbuffers.SetVScrollValue(vs); }