private void PostMarshal()
            {
                if (_ptr_Bytecode != IntPtr.Zero)
                    Bytecode = (ShaderReflection)CustomMarshal.PtrToStructure(_ptr_Bytecode, typeof(ShaderReflection), false);
                else
                    Bytecode = null;

                _ptr_Bytecode = IntPtr.Zero;
            }
Exemple #2
0
            private void PostMarshal()
            {
                if (_ptr_ShaderDetails != IntPtr.Zero)
                    ShaderDetails = (ShaderReflection)CustomMarshal.PtrToStructure(_ptr_ShaderDetails, typeof(ShaderReflection), false);
                else
                    ShaderDetails = null;

                _ptr_ShaderDetails = IntPtr.Zero;
            }
Exemple #3
0
        public ShaderReflection GetShaderDetails(ResourceId shader)
        {
            IntPtr mem = ReplayRenderer_GetShaderDetails(m_Real, shader);

            ShaderReflection ret = null;

            if (mem != IntPtr.Zero)
            {
                ret = (ShaderReflection)CustomMarshal.PtrToStructure(mem, typeof(ShaderReflection), false);
            }

            return(ret);
        }
        private object[] ExportViewHTML(D3D11PipelineState.ShaderStage.ResourceView view, int i, ShaderReflection refl, string extraParams)
        {
            FetchTexture[] texs = m_Core.CurTextures;
            FetchBuffer[] bufs = m_Core.CurBuffers;

            ShaderResource shaderInput = null;

            bool rw = false;

            if (refl != null)
            {
                foreach (var bind in refl.ReadOnlyResources)
                {
                    if (bind.bindPoint == i)
                    {
                        shaderInput = bind;
                        break;
                    }
                }
                foreach (var bind in refl.ReadWriteResources)
                {
                    if (bind.bindPoint == i)
                    {
                        shaderInput = bind;
                        rw = true;
                        break;
                    }
                }
            }

            string name = "Empty";
            string typename = "Unknown";
            string format = "Unknown";
            UInt64 w = 1;
            UInt32 h = 1, d = 1;
            UInt32 a = 0;

            string viewFormat = view.Format.ToString();

            FetchTexture tex = null;
            FetchBuffer buf = null;

            // check to see if it's a texture
            for (int t = 0; t < texs.Length; t++)
            {
                if (texs[t].ID == view.Resource)
                {
                    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;
                    typename = texs[t].resType.ToString();

                    tex = texs[t];
                }
            }

            // if not a texture, it must be a buffer
            for (int t = 0; t < bufs.Length; t++)
            {
                if (bufs[t].ID == view.Resource)
                {
                    w = bufs[t].length;
                    h = 0;
                    d = 0;
                    a = 0;
                    format = view.Format.ToString();
                    name = bufs[t].name;
                    typename = "Buffer";

                    // for structured buffers, display how many 'elements' there are in the buffer
                    if (view.ElementSize > 0)
                    {
                        typename = (rw ? "RWStructuredBuffer" : "StructuredBuffer") + "[" + (bufs[t].length / view.ElementSize) + "]";
                    }
                    else if (view.Flags.HasFlag(D3D11BufferViewFlags.Raw))
                    {
                        typename = rw ? "RWByteAddressBuffer" : "ByteAddressBuffer";
                    }

                    if (view.Flags.HasFlag(D3D11BufferViewFlags.Append) || view.Flags.HasFlag(D3D11BufferViewFlags.Counter))
                    {
                        typename += " (Count: " + view.BufferStructCount + ")";
                    }

                    if (shaderInput != null && !shaderInput.IsTexture)
                    {
                        if (view.Format.compType == FormatComponentType.None)
                        {
                            if (shaderInput.variableType.members.Length > 0)
                                viewFormat = format = "struct " + shaderInput.variableType.Name;
                            else
                                viewFormat = format = shaderInput.variableType.Name;
                        }
                        else
                        {
                            format = view.Format.ToString();
                        }
                    }

                    buf = bufs[t];
                }
            }

            string viewParams = "";

            if(buf != null)
            {
                viewParams = String.Format("First Element: {0}, Num Elements {1}, Flags {2}", view.FirstElement, view.NumElements, view.Flags);
            }

            if (tex != null)
            {
                if(tex.mips > 1)
                    viewParams = String.Format("Highest Mip: {0}, Num Mips: {1}", view.HighestMip, view.NumMipLevels);

                if (tex.arraysize > 1)
                {
                    if (viewParams.Length > 0)
                        viewParams += ", ";
                    viewParams += String.Format("First Slice: {0}, Array Size: {1}", view.FirstArraySlice, view.ArraySize);
                }
            }

            if (viewParams == "")
                viewParams = extraParams;
            else
                viewParams += ", " + extraParams;

            return new object[] {
                        i, name,
                        view.Type, typename,
                        w, h, d, a,
                        viewFormat, format,
                        viewParams };
        }
Exemple #5
0
        public ShaderViewer(Core core, ShaderReflection shader, ShaderStageType stage, ShaderDebugTrace trace, string debugContext)
        {
            InitializeComponent();

            constantRegs.Font =
                variableRegs.Font =
                watchRegs.Font =
                inSig.Font =
                outSig.Font =
                core.Config.PreferredFont;

            Icon = global::renderdocui.Properties.Resources.icon;

            this.SuspendLayout();

            mainLayout.Dock = DockStyle.Fill;

            constantRegs.Font =
                variableRegs.Font =
                watchRegs.Font =
                inSig.Font =
                outSig.Font =
                core.Config.PreferredFont;

            m_Core = core;
            m_ShaderDetails = shader;
            m_Trace = trace;
            m_Stage = null;
            switch (stage)
            {
                case ShaderStageType.Vertex: m_Stage = m_Core.CurD3D11PipelineState.m_VS; break;
                case ShaderStageType.Domain: m_Stage = m_Core.CurD3D11PipelineState.m_DS; break;
                case ShaderStageType.Hull: m_Stage = m_Core.CurD3D11PipelineState.m_HS; break;
                case ShaderStageType.Geometry: m_Stage = m_Core.CurD3D11PipelineState.m_GS; break;
                case ShaderStageType.Pixel: m_Stage = m_Core.CurD3D11PipelineState.m_PS; break;
                case ShaderStageType.Compute: m_Stage = m_Core.CurD3D11PipelineState.m_CS; break;
            }

            if (trace != null)
                Text = String.Format("Debugging {0} - {1}", m_Core.CurPipelineState.GetShaderName(stage), debugContext);
            else
                Text = m_Core.CurPipelineState.GetShaderName(stage);

            var disasm = shader.Disassembly;

            if (m_Core.Config.ShaderViewer_FriendlyNaming)
            {
                for (int i = 0; i < m_ShaderDetails.ConstantBlocks.Length; i++)
                {
                    var stem = string.Format("cb{0}", i);

                    var cbuf = m_ShaderDetails.ConstantBlocks[i];

                    if (cbuf.variables.Length == 0)
                        continue;

                    disasm = FriendlyName(disasm, stem, "", cbuf.variables);
                }

                foreach (var r in m_ShaderDetails.Resources)
                {
                    if (r.IsSRV)
                    {
                        var needle = string.Format(", t{0}([^0-9])", r.bindPoint);
                        var replacement = string.Format(", {0}$1", r.name);

                        Regex rgx = new Regex(needle);
                        disasm = rgx.Replace(disasm, replacement);
                    }
                    if (r.IsSampler)
                    {
                        var needle = string.Format(", s{0}([^0-9])", r.bindPoint);
                        var replacement = string.Format(", {0}$1", r.name);

                        Regex rgx = new Regex(needle);
                        disasm = rgx.Replace(disasm, replacement);
                    }
                    if (r.IsReadWrite)
                    {
                        var needle = string.Format(", u{0}([^0-9])", r.bindPoint);
                        var replacement = string.Format(", {0}$1", r.name);

                        Regex rgx = new Regex(needle);
                        disasm = rgx.Replace(disasm, replacement);
                    }
                }
            }

            {
                m_DisassemblyView = MakeEditor("scintillaDisassem", disasm, false);
                m_DisassemblyView.IsReadOnly = true;
                m_DisassemblyView.TabIndex = 0;

                m_DisassemblyView.KeyDown += new KeyEventHandler(m_DisassemblyView_KeyDown);
                m_DisassemblyView.KeyDown += new KeyEventHandler(readonlyScintilla_KeyDown);

                m_DisassemblyView.Markers[CURRENT_MARKER].BackColor = System.Drawing.Color.LightCoral;
                m_DisassemblyView.Markers[CURRENT_MARKER].Symbol = ScintillaNET.MarkerSymbol.Background;
                m_DisassemblyView.Markers[CURRENT_MARKER+1].BackColor = System.Drawing.Color.LightCoral;
                m_DisassemblyView.Markers[CURRENT_MARKER+1].Symbol = ScintillaNET.MarkerSymbol.ShortArrow;

                CurrentLineMarkers.Add(CURRENT_MARKER);
                CurrentLineMarkers.Add(CURRENT_MARKER+1);

                m_DisassemblyView.Markers[FINISHED_MARKER].BackColor = System.Drawing.Color.LightSlateGray;
                m_DisassemblyView.Markers[FINISHED_MARKER].Symbol = ScintillaNET.MarkerSymbol.Background;
                m_DisassemblyView.Markers[FINISHED_MARKER + 1].BackColor = System.Drawing.Color.LightSlateGray;
                m_DisassemblyView.Markers[FINISHED_MARKER + 1].Symbol = ScintillaNET.MarkerSymbol.RoundRectangle;

                FinishedMarkers.Add(FINISHED_MARKER);
                FinishedMarkers.Add(FINISHED_MARKER + 1);

                m_DisassemblyView.Markers[BREAKPOINT_MARKER].BackColor = System.Drawing.Color.Red;
                m_DisassemblyView.Markers[BREAKPOINT_MARKER].Symbol = ScintillaNET.MarkerSymbol.Background;
                m_DisassemblyView.Markers[BREAKPOINT_MARKER+1].BackColor = System.Drawing.Color.Red;
                m_DisassemblyView.Markers[BREAKPOINT_MARKER+1].Symbol = ScintillaNET.MarkerSymbol.Circle;

                BreakpointMarkers.Add(BREAKPOINT_MARKER);
                BreakpointMarkers.Add(BREAKPOINT_MARKER + 1);

                if (trace != null)
                {
                    m_DisassemblyView.ContextMenu = AssemblyContextMenu();
                    m_DisassemblyView.MouseDown += new MouseEventHandler(contextMouseDown);
                }

                m_Scintillas.Add(m_DisassemblyView);

                var w = Helpers.WrapDockContent(dockPanel, m_DisassemblyView, "Disassembly");
                w.DockState = DockState.Document;
                w.Show();

                w.CloseButton = false;
                w.CloseButtonVisible = false;
            }

            if (shader.DebugInfo.entryFunc.Length > 0 && shader.DebugInfo.files.Length > 0)
            {
                if(trace != null)
                    Text = String.Format("Debug {0}() - {1}", shader.DebugInfo.entryFunc, debugContext);
                else
                    Text = String.Format("{0}()", shader.DebugInfo.entryFunc);

                int fileIdx = 0;

                DockContent sel = null;
                foreach (var f in shader.DebugInfo.files)
                {
                    var name = f.BaseFilename;

                    ScintillaNET.Scintilla scintilla1 = MakeEditor("scintilla" + name, f.filetext, true);
                    scintilla1.IsReadOnly = true;

                    scintilla1.Tag = name;

                    scintilla1.KeyDown += new KeyEventHandler(readonlyScintilla_KeyDown);

                    var w = Helpers.WrapDockContent(dockPanel, scintilla1, name);
                    w.CloseButton = false;
                    w.CloseButtonVisible = false;
                    w.Show(dockPanel);

                    m_Scintillas.Add(scintilla1);

                    if (shader.DebugInfo.entryFile >= 0 && shader.DebugInfo.entryFile < shader.DebugInfo.files.Length)
                    {
                        if (fileIdx == shader.DebugInfo.entryFile)
                            sel = w;
                    }
                    else if (f.filetext.Contains(shader.DebugInfo.entryFunc))
                    {
                        sel = w;
                    }

                    fileIdx++;
                }

                if (shader.DebugInfo.files.Length > 2)
                    AddFileList();

                if (trace != null || sel == null)
                    sel = (DockContent)m_DisassemblyView.Parent;

                sel.Show();
            }

            m_FindAll = new FindAllDialog(FindAllFiles);
            m_FindAll.Hide();

            ShowConstants();
            ShowVariables();
            ShowWatch();
            ShowErrors();

            editStrip.Visible = false;

            m_ErrorsDock.Hide();

            if (trace == null)
            {
                debuggingStrip.Visible = false;
                m_ConstantsDock.Hide();
                m_VariablesDock.Hide();
                m_WatchDock.Hide();

                var insig = Helpers.WrapDockContent(dockPanel, inSigBox);
                insig.CloseButton = insig.CloseButtonVisible = false;

                var outsig = Helpers.WrapDockContent(dockPanel, outSigBox);
                outsig.CloseButton = outsig.CloseButtonVisible = false;

                insig.Show(dockPanel, DockState.DockBottom);
                outsig.Show(insig.Pane, DockAlignment.Right, 0.5);

                foreach (var s in m_ShaderDetails.InputSig)
                {
                    string name = s.varName.Length == 0 ? s.semanticName : String.Format("{0} ({1})", s.varName, s.semanticName);
                    if (s.semanticName.Length == 0) name = s.varName;

                    inSig.Nodes.Add(new object[] { name, s.semanticIndex, s.regIndex, s.TypeString, s.systemValue.ToString(),
                                                                SigParameter.GetComponentString(s.regChannelMask), SigParameter.GetComponentString(s.channelUsedMask) });
                }

                bool multipleStreams = false;
                for (int i = 0; i < m_ShaderDetails.OutputSig.Length; i++)
                {
                    if (m_ShaderDetails.OutputSig[i].stream > 0)
                    {
                        multipleStreams = true;
                        break;
                    }
                }

                foreach (var s in m_ShaderDetails.OutputSig)
                {
                    string name = s.varName.Length == 0 ? s.semanticName : String.Format("{0} ({1})", s.varName, s.semanticName);
                    if (s.semanticName.Length == 0) name = s.varName;

                    if(multipleStreams)
                        name = String.Format("Stream {0} : {1}", s.stream, name);

                    outSig.Nodes.Add(new object[] { name, s.semanticIndex, s.regIndex, s.TypeString, s.systemValue.ToString(),
                                                                SigParameter.GetComponentString(s.regChannelMask), SigParameter.GetComponentString(s.channelUsedMask) });
                }
            }
            else
            {
                inSigBox.Visible = false;
                outSigBox.Visible = false;

                m_DisassemblyView.Margins.Margin1.Width = 20;

                m_DisassemblyView.Margins.Margin2.Width = 0;

                m_DisassemblyView.Margins.Margin3.Width = 20;
                m_DisassemblyView.Margins.Margin3.IsMarkerMargin = true;
                m_DisassemblyView.Margins.Margin3.IsFoldMargin = false;
                m_DisassemblyView.Margins.Margin3.Type = ScintillaNET.MarginType.Symbol;

                m_DisassemblyView.Margins.Margin1.Mask = (int)m_DisassemblyView.Markers[BREAKPOINT_MARKER + 1].Mask;
                m_DisassemblyView.Margins.Margin3.Mask &= ~((int)m_DisassemblyView.Markers[BREAKPOINT_MARKER + 1].Mask);

                m_DisassemblyView.MouseMove += new MouseEventHandler(scintilla1_MouseMove);
                m_DisassemblyView.Leave += new EventHandler(scintilla1_Leave);
                m_DisassemblyView.KeyDown += new KeyEventHandler(scintilla1_DebuggingKeyDown);

                watchRegs.Items.Add(new ListViewItem(new string[] { "", "", "" }));
            }

            CurrentStep = 0;

            this.ResumeLayout(false);
        }
        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);
                                }
                            }
                        }
                    }
                }
            }
        }
        private void AddConstantBlockRow(ShaderReflection shaderDetails, VulkanPipelineState.ShaderStage stage,
            int bindset, int bind,
            VulkanPipelineState.Pipeline pipe, TreelistView.TreeListView cbuffers,
            FetchBuffer[] bufs)
        {
            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;
            }

            VulkanPipelineState.Pipeline.DescriptorSet.DescriptorBinding.BindingElement[] slotBinds = null;
            ShaderBindType bindType = ShaderBindType.ConstantBuffer;
            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;
            }

            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;

            // consider it filled if any array element is filled (or it's push constants)
            bool filledSlot = cblock != null && !cblock.bufferBacked;
            for (int idx = 0; slotBinds != null && idx < slotBinds.Length; idx++)
                filledSlot |= slotBinds[idx].res != 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 = cbuffers.Nodes;

                string setname = bindset.ToString();

                string slotname = bind.ToString();
                if (cblock != null && cblock.name.Length > 0)
                    slotname += ": " + cblock.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 (cblock != null && cblock.name.Length > 0)
                            slotname += ": " + cblock.name;
                    }

                    string name = "Empty";
                    UInt64 length = 0;
                    int numvars = cblock != null ? cblock.variables.Length : 0;
                    UInt64 byteSize = cblock != null ? cblock.byteSize : 0;

                    string vecrange = "-";

                    if (filledSlot && descriptorBind != null)
                    {
                        name = "";
                        length = descriptorBind.size;

                        for (int t = 0; t < bufs.Length; t++)
                        {
                            if (bufs[t].ID == descriptorBind.res)
                            {
                                name = bufs[t].name;
                                if(length == ulong.MaxValue)
                                    length = bufs[t].length - descriptorBind.offset;
                            }
                        }

                        if (name == "")
                            name = "UBO " + descriptorBind.res.ToString();

                        vecrange = String.Format("{0} - {1}", descriptorBind.offset, descriptorBind.offset + length);
                    }

                    string sizestr;

                    // push constants or specialization 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.
                    }
                    else
                    {
                        if (length == byteSize)
                            sizestr = String.Format("{0} Variables, {1} bytes", numvars, length);
                        else
                            sizestr = String.Format("{0} Variables, {1} bytes needed, {2} provided", numvars, byteSize, length);

                        if (length < byteSize)
                            filledSlot = false;
                    }

                    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);
                }
            }
        }