private void UpdateState()
        {
            if (!m_Core.LogLoaded)
                return;

            FetchTexture[] texs = m_Core.CurTextures;
            FetchBuffer[] bufs = m_Core.CurBuffers;
            D3D11PipelineState state = m_Core.CurD3D11PipelineState;
            FetchDrawcall draw = m_Core.CurDrawcall;

            HideViewDetailsTooltip();
            m_ViewDetailNodes.Clear();

            var tick = global::renderdocui.Properties.Resources.tick;
            var cross = global::renderdocui.Properties.Resources.cross;

            bool[] usedVBuffers = new bool[128];
            UInt32[] layoutOffs = new UInt32[128];

            for (int i = 0; i < 128; i++)
            {
                usedVBuffers[i] = false;
                layoutOffs[i] = 0;
            }

            ////////////////////////////////////////////////
            // Input Assembler

            if(state.m_IA.Bytecode == null)
                iaBytecode.Text = "None";
            else
                iaBytecode.Text = state.m_IA.LayoutName;

            if (state.m_IA.Bytecode != null && state.m_IA.Bytecode.DebugInfo != null && state.m_IA.Bytecode.DebugInfo.entryFunc.Length > 0)
                iaBytecode.Text += " (" + state.m_IA.Bytecode.DebugInfo.entryFunc + ")";

            iaBytecodeMismatch.Text = "";
            iaBytecodeMismatch.Visible = false;

            // check for IA-VS mismatches here.
            // This should be moved to a "Render Doctor" window reporting problems
            if (state.m_IA.Bytecode != null && state.m_VS.ShaderDetails != null)
            {
                string mismatchDetails = "";

                // VS wants more elements
                if (state.m_IA.Bytecode.InputSig.Length < state.m_VS.ShaderDetails.InputSig.Length)
                {
                    int excess = state.m_VS.ShaderDetails.InputSig.Length - state.m_IA.Bytecode.InputSig.Length;

                    bool allSystem = true;

                    // The VS signature can consume more elements as long as they are all system value types
                    // (ie. SV_VertexID or SV_InstanceID)
                    for (int e = 0; e < excess; e++)
                    {
                        if(state.m_VS.ShaderDetails.InputSig[state.m_VS.ShaderDetails.InputSig.Length - 1 - e].systemValue == SystemAttribute.None)
                        {
                            allSystem = false;
                            break;
                        }
                    }

                    if (!allSystem)
                        mismatchDetails += "IA bytecode provides fewer elements than VS wants.\n";
                }

                {
                    var IA = state.m_IA.Bytecode.InputSig;
                    var VS = state.m_VS.ShaderDetails.InputSig;

                    int count = Math.Min(IA.Length, VS.Length);

                    for (int i = 0; i < count; i++)
                    {
                        // misorder or misnamed semantics
                        if (IA[i].semanticIdxName.ToUpperInvariant() != VS[i].semanticIdxName.ToUpperInvariant())
                            mismatchDetails += String.Format("IA bytecode semantic {0}: {1} != VS bytecode semantic {0}: {2}\n", i,
                                                                IA[i].semanticIdxName, VS[i].semanticIdxName);

                        // VS wants more components
                        if (IA[i].compCount < VS[i].compCount)
                            mismatchDetails += String.Format("IA bytecode semantic {0} ({1}) is {3}-wide, VS bytecode semantic {0} ({1}) {2} is {4}-wide\n", i,
                                                                IA[i].semanticIdxName, VS[i].semanticIdxName,
                                                                IA[i].compCount, VS[i].compCount);

                        // VS wants different types
                        if (IA[i].compType != VS[i].compType)
                            mismatchDetails += String.Format("IA bytecode semantic {0} ({1}) is {3}, VS bytecode semantic {0} ({2}) is {4}\n", i,
                                                                IA[i].semanticIdxName, VS[i].semanticIdxName,
                                                                IA[i].compType, VS[i].compType);
                    }
                }

                if (mismatchDetails.Length != 0)
                {
                    iaBytecodeMismatch.Text = "Warning: Mismatch detected between bytecode used to create IA and currently bound VS inputs";
                    toolTip.SetToolTip(iaBytecodeMismatch, mismatchDetails.Trim());
                    iaBytecodeMismatch.Visible = true;
                }
            }

            int vs = 0;

            vs = inputLayouts.VScrollValue();
            inputLayouts.Nodes.Clear();
            inputLayouts.BeginUpdate();
            if (state.m_IA.layouts != null)
            {
                int i = 0;
                foreach (var l in state.m_IA.layouts)
                {
                    string byteOffs = l.ByteOffset.ToString();

                    // D3D11 specific value
                    if (l.ByteOffset == uint.MaxValue)
                    {
                        byteOffs = String.Format("APPEND_ALIGNED ({0})", layoutOffs[l.InputSlot]);
                    }
                    else
                    {
                        layoutOffs[l.InputSlot] = l.ByteOffset;
                    }

                    layoutOffs[l.InputSlot] += l.Format.compByteWidth * l.Format.compCount;

                    bool iaUsed = false;

                    if (state.m_IA.Bytecode != null)
                    {
                        for (int ia = 0; ia < state.m_IA.Bytecode.InputSig.Length; ia++)
                        {
                            if (state.m_IA.Bytecode.InputSig[ia].semanticName.ToUpperInvariant() == l.SemanticName.ToUpperInvariant() &&
                                state.m_IA.Bytecode.InputSig[ia].semanticIndex == l.SemanticIndex)
                            {
                                iaUsed = true;
                                break;
                            }
                        }
                    }

                    i++;

                    if(!iaUsed && !showDisabled.Checked)
                        continue;

                    var node = inputLayouts.Nodes.Add(new object[] {
                                              i, l.SemanticName, l.SemanticIndex.ToString(), l.Format, l.InputSlot.ToString(), byteOffs,
                                              l.PerInstance ? "PER_INSTANCE" : "PER_VERTEX", l.InstanceDataStepRate.ToString() });

                    if (iaUsed)
                        usedVBuffers[l.InputSlot] = true;

                    node.Image = global::renderdocui.Properties.Resources.action;
                    node.HoverImage = global::renderdocui.Properties.Resources.action_hover;

                    if (!iaUsed)
                        InactiveRow(node);
                }
            }
            inputLayouts.NodesSelection.Clear();
            inputLayouts.EndUpdate();
            inputLayouts.SetVScrollValue(vs);

            PrimitiveTopology topo = draw != null ? draw.topology : PrimitiveTopology.Unknown;

            topology.Text = topo.ToString();
            if (topo > PrimitiveTopology.PatchList)
            {
                int numCPs = (int)topo - (int)PrimitiveTopology.PatchList + 1;

                topology.Text = string.Format("PatchList ({0} Control Points)", numCPs);
            }

            switch (topo)
            {
                case PrimitiveTopology.PointList:
                    topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_pointlist;
                    break;
                case PrimitiveTopology.LineList:
                    topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_linelist;
                    break;
                case PrimitiveTopology.LineStrip:
                    topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_linestrip;
                    break;
                case PrimitiveTopology.TriangleList:
                    topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_trilist;
                    break;
                case PrimitiveTopology.TriangleStrip:
                    topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_tristrip;
                    break;
                case PrimitiveTopology.LineList_Adj:
                    topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_linelist_adj;
                    break;
                case PrimitiveTopology.LineStrip_Adj:
                    topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_linestrip_adj;
                    break;
                case PrimitiveTopology.TriangleList_Adj:
                    topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_trilist_adj;
                    break;
                case PrimitiveTopology.TriangleStrip_Adj:
                    topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_tristrip_adj;
                    break;
                default:
                    topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_patch;
                    break;
            }

            vs = iabuffers.VScrollValue();
            iabuffers.Nodes.Clear();
            iabuffers.BeginUpdate();

            bool ibufferUsed = draw != null && (draw.flags & DrawcallFlags.UseIBuffer) != 0;

            if (state.m_IA.ibuffer != null && state.m_IA.ibuffer.Buffer != ResourceId.Null)
            {
                if (ibufferUsed || showDisabled.Checked)
                {
                    string ptr = "Buffer " + state.m_IA.ibuffer.Buffer.ToString();
                    string name = ptr;
                    UInt64 length = 1;

                    if (!ibufferUsed)
                    {
                        length = 0;
                    }

                    for (int t = 0; t < bufs.Length; t++)
                    {
                        if (bufs[t].ID == state.m_IA.ibuffer.Buffer)
                        {
                            name = bufs[t].name;
                            length = bufs[t].length;
                        }
                    }

                    var node = iabuffers.Nodes.Add(new object[] { "Index", name, draw != null ? draw.indexByteWidth : 0, state.m_IA.ibuffer.Offset, length });

                    node.Image = global::renderdocui.Properties.Resources.action;
                    node.HoverImage = global::renderdocui.Properties.Resources.action_hover;
                    node.Tag = new IABufferTag(state.m_IA.ibuffer.Buffer, draw.indexOffset);

                    if (!ibufferUsed)
                        InactiveRow(node);

                    if (state.m_IA.ibuffer.Buffer == ResourceId.Null)
                        EmptyRow(node);
                }
            }
            else
            {
                if (ibufferUsed || showEmpty.Checked)
                {
                    var node = iabuffers.Nodes.Add(new object[] { "Index", "No Buffer Set", "-", "-", "-" });

                    node.Image = global::renderdocui.Properties.Resources.action;
                    node.HoverImage = global::renderdocui.Properties.Resources.action_hover;
                    node.Tag = new IABufferTag(state.m_IA.ibuffer.Buffer, draw.indexOffset);

                    EmptyRow(node);

                    if (!ibufferUsed)
                        InactiveRow(node);
                }
            }

            m_VBNodes.Clear();

            if (state.m_IA.vbuffers != null)
            {
                int i = 0;
                foreach (var v in state.m_IA.vbuffers)
                {
                    bool filledSlot = (v.Buffer != ResourceId.Null);
                    bool usedSlot = (usedVBuffers[i]);

                    // 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"
                        )
                    {
                        string name = "Buffer " + v.Buffer.ToString();
                        UInt64 length = 1;

                        for (int t = 0; t < bufs.Length; t++)
                        {
                            if (bufs[t].ID == v.Buffer)
                            {
                                name = bufs[t].name;
                                length = bufs[t].length;
                            }
                        }

                        TreelistView.Node node = null;

                        if(filledSlot)
                            node = iabuffers.Nodes.Add(new object[] { i, name, v.Stride, v.Offset, length });
                        else
                            node = iabuffers.Nodes.Add(new object[] { i, "No Buffer Set", "-", "-", "-" });

                        node.Image = global::renderdocui.Properties.Resources.action;
                        node.HoverImage = global::renderdocui.Properties.Resources.action_hover;
                        node.Tag = new IABufferTag(v.Buffer, v.Offset);

                        if (!filledSlot)
                            EmptyRow(node);

                        if (!usedSlot)
                            InactiveRow(node);

                        m_VBNodes.Add(node);
                    }

                    i++;
                }
            }
            iabuffers.NodesSelection.Clear();
            iabuffers.EndUpdate();
            iabuffers.SetVScrollValue(vs);

            SetShaderState(texs, bufs, state.m_VS, vsShader, vsResources, vsSamplers, vsCBuffers, vsClasses);
            SetShaderState(texs, bufs, state.m_GS, gsShader, gsResources, gsSamplers, gsCBuffers, gsClasses);
            SetShaderState(texs, bufs, state.m_HS, hsShader, hsResources, hsSamplers, hsCBuffers, hsClasses);
            SetShaderState(texs, bufs, state.m_DS, dsShader, dsResources, dsSamplers, dsCBuffers, dsClasses);
            SetShaderState(texs, bufs, state.m_PS, psShader, psResources, psSamplers, psCBuffers, psClasses);
            SetShaderState(texs, bufs, state.m_CS, csShader, csResources, csSamplers, csCBuffers, csClasses);

            vs = csUAVs.VScrollValue();
            csUAVs.Nodes.Clear();
            csUAVs.BeginUpdate();

            if (state.m_CS.UAVs != null)
            {
                int i = 0;
                foreach (var r in state.m_CS.UAVs)
                {
                    ShaderResource shaderInput = null;

                    if (state.m_CS.ShaderDetails != null)
                    {
                        foreach (var bind in state.m_CS.ShaderDetails.ReadWriteResources)
                        {
                            if (bind.bindPoint == i)
                            {
                                shaderInput = bind;
                                break;
                            }
                        }
                    }

                    bool filledSlot = (r.Resource != ResourceId.Null);
                    bool usedSlot = (shaderInput != 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"
                        )
                    {
                        string slotname = i.ToString();

                        if (shaderInput != null && shaderInput.name.Length > 0)
                            slotname += ": " + shaderInput.name;

                        UInt64 w = 1;
                        UInt32 h = 1, d = 1;
                        UInt32 a = 1;
                        string format = "Unknown";
                        string name = "UAV " + r.Resource.ToString();
                        string typename = "Unknown";
                        object tag = null;
                        bool viewDetails = false;

                        if (!filledSlot)
                        {
                            name = "Empty";
                            format = "-";
                            typename = "-";
                            w = h = d = a = 0;
                        }

                        for (int t = 0; t < texs.Length; t++)
                        {
                            if (texs[t].ID == r.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.Str();

                                if (texs[t].resType == ShaderResourceType.Texture2DMS ||
                                    texs[t].resType == ShaderResourceType.Texture2DMSArray)
                                {
                                    typename += String.Format(" {0}x", texs[t].msSamp);
                                }

                                // if it's a typeless format, show the format of the view
                                if (texs[t].format != r.Format)
                                {
                                    format = "Viewed as " + r.Format.ToString();
                                }

                                if (HasImportantViewParams(r, texs[t]))
                                    viewDetails = true;

                                tag = new ViewTexTag(r, texs[t]);
                            }
                        }

                        for (int t = 0; t < bufs.Length; t++)
                        {
                            if (bufs[t].ID == r.Resource)
                            {
                                w = bufs[t].length;
                                h = 0;
                                d = 0;
                                a = 0;
                                format = "";
                                name = bufs[t].name;
                                typename = "Buffer";

                                if (r.ElementSize > 0)
                                {
                                    typename = "RWStructuredBuffer[" + (bufs[t].length / r.ElementSize) + "]";
                                }
                                else if (r.Flags.HasFlag(D3D11BufferViewFlags.Raw))
                                {
                                    typename = "RWByteAddressBuffer";
                                }

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

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

                                if (HasImportantViewParams(r, bufs[t]))
                                    viewDetails = true;

                                tag = new ViewBufTag(r, bufs[t]);
                            }
                        }

                        var node = csUAVs.Nodes.Add(new object[] { slotname, name, typename, w, h, d, a, format });

                        node.Tag = tag;

                        node.Image = global::renderdocui.Properties.Resources.action;
                        node.HoverImage = global::renderdocui.Properties.Resources.action_hover;

                        if (!filledSlot)
                            EmptyRow(node);

                        if (!usedSlot)
                            InactiveRow(node);

                        if (viewDetails)
                            ViewDetailsRow(node);
                    }

                    i++;
                }
            }
            csUAVs.NodesSelection.Clear();
            csUAVs.EndUpdate();
            csUAVs.SetVScrollValue(vs);

            bool streamoutSet = false;
            vs = gsStreams.VScrollValue();
            gsStreams.BeginUpdate();
            gsStreams.Nodes.Clear();
            if (state.m_SO.Outputs != null)
            {
                int i = 0;
                foreach (var s in state.m_SO.Outputs)
                {
                    bool filledSlot = (s.Buffer != ResourceId.Null);
                    bool usedSlot = (filledSlot);

                    streamoutSet |= filledSlot;

                    // 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"
                        )
                    {
                        string name = "Buffer " + s.Buffer.ToString();
                        UInt64 length = 0;

                        if (!filledSlot)
                        {
                            name = "Empty";
                        }

                        FetchBuffer fetch = null;

                        for (int t = 0; t < bufs.Length; t++)
                        {
                            if (bufs[t].ID == s.Buffer)
                            {
                                name = bufs[t].name;
                                length = bufs[t].length;

                                fetch = bufs[t];
                            }
                        }

                        var node = gsStreams.Nodes.Add(new object[] { i, name, length, s.Offset });

                        node.Image = global::renderdocui.Properties.Resources.action;
                        node.HoverImage = global::renderdocui.Properties.Resources.action_hover;
                        node.Tag = fetch;

                        if (!filledSlot)
                            EmptyRow(node);

                        if (!usedSlot)
                            InactiveRow(node);
                    }
                    i++;
                }
            }
            gsStreams.EndUpdate();
            gsStreams.NodesSelection.Clear();
            gsStreams.SetVScrollValue(vs);

            gsStreams.Visible = gsStreams.Parent.Visible = streamoutSet;
            if (streamoutSet)
                geomTableLayout.ColumnStyles[1].Width = 50.0f;
            else
                geomTableLayout.ColumnStyles[1].Width = 0;

            ////////////////////////////////////////////////
            // Rasterizer

            vs = viewports.VScrollValue();
            viewports.BeginUpdate();
            viewports.Nodes.Clear();
            if (state.m_RS.Viewports != null)
            {
                int i = 0;
                foreach (var v in state.m_RS.Viewports)
                {
                    if (v.Enabled || showEmpty.Checked)
                    {
                        var node = viewports.Nodes.Add(new object[] { i, v.TopLeft[0], v.TopLeft[1], v.Width, v.Height, v.MinDepth, v.MaxDepth });

                        if (v.Width == 0 || v.Height == 0 || v.MinDepth == v.MaxDepth)
                            EmptyRow(node);

                        if (!v.Enabled)
                            InactiveRow(node);
                    }

                    i++;
                }
            }
            viewports.NodesSelection.Clear();
            viewports.EndUpdate();
            viewports.SetVScrollValue(vs);

            vs = scissors.VScrollValue();
            scissors.BeginUpdate();
            scissors.Nodes.Clear();
            if (state.m_RS.Scissors != null)
            {
                int i = 0;
                foreach (var s in state.m_RS.Scissors)
                {
                    if (s.Enabled || showEmpty.Checked)
                    {
                        var node = scissors.Nodes.Add(new object[] { i, s.left, s.top, s.right - s.left, s.bottom - s.top });

                        if (s.right == s.left || s.bottom == s.top)
                            EmptyRow(node);

                        if (!s.Enabled)
                            InactiveRow(node);
                    }

                    i++;
                }
            }
            scissors.NodesSelection.Clear();
            scissors.EndUpdate();
            scissors.SetVScrollValue(vs);

            fillMode.Text = state.m_RS.m_State.FillMode.ToString();
            cullMode.Text = state.m_RS.m_State.CullMode.ToString();
            frontCCW.Image = state.m_RS.m_State.FrontCCW ? tick : cross;

            scissorEnable.Image = state.m_RS.m_State.ScissorEnable ? tick : cross;
            lineAAEnable.Image = state.m_RS.m_State.AntialiasedLineEnable ? tick : cross;
            multisampleEnable.Image = state.m_RS.m_State.MultisampleEnable ? tick : cross;

            depthClip.Image = state.m_RS.m_State.DepthClip ? tick : cross;
            depthBias.Text = state.m_RS.m_State.DepthBias.ToString();
            depthBiasClamp.Text = Formatter.Format(state.m_RS.m_State.DepthBiasClamp);
            slopeScaledBias.Text = Formatter.Format(state.m_RS.m_State.SlopeScaledDepthBias);
            forcedSampleCount.Text = state.m_RS.m_State.ForcedSampleCount.ToString();
            conservativeRaster.Image = state.m_RS.m_State.ConservativeRasterization ? tick : cross;

            ////////////////////////////////////////////////
            // Output Merger

            bool[] targets = new bool[8];

            for (int i = 0; i < 8; i++)
                targets[i] = false;

            vs = targetOutputs.VScrollValue();
            targetOutputs.BeginUpdate();
            targetOutputs.Nodes.Clear();
            if (state.m_OM.RenderTargets != null)
            {
                int i = 0;
                foreach (var p in state.m_OM.RenderTargets)
                {
                    if (p.Resource != ResourceId.Null || showEmpty.Checked)
                    {
                        UInt32 w = 1, h = 1, d = 1;
                        UInt32 a = 1;
                        string format = "Unknown";
                        string name = "Texture " + p.ToString();
                        string typename = "Unknown";
                        object tag = null;
                        bool viewDetails = false;

                        if (p.Resource == ResourceId.Null)
                        {
                            name = "Empty";
                            format = "-";
                            typename = "-";
                            w = h = d = a = 0;
                        }

                        for (int t = 0; t < texs.Length; t++)
                        {
                            if (texs[t].ID == p.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.Str();

                                if (texs[t].resType == ShaderResourceType.Texture2DMS ||
                                    texs[t].resType == ShaderResourceType.Texture2DMSArray)
                                {
                                    typename += String.Format(" {0}x", texs[t].msSamp);
                                }

                                // if it's a typeless format, show the format of the view
                                if (texs[t].format != p.Format)
                                {
                                    format = "Viewed as " + p.Format.ToString();
                                }

                                if (HasImportantViewParams(p, texs[t]))
                                    viewDetails = true;

                                tag = new ViewTexTag(p, texs[t]);
                            }
                        }

                        var node = targetOutputs.Nodes.Add(new object[] { i, name, typename, w, h, d, a, format });

                        node.Image = global::renderdocui.Properties.Resources.action;
                        node.HoverImage = global::renderdocui.Properties.Resources.action_hover;
                        node.Tag = tag;

                        if (p.Resource == ResourceId.Null)
                        {
                            EmptyRow(node);
                        }
                        else
                        {
                            targets[i] = true;

                            if (viewDetails)
                                ViewDetailsRow(node);
                        }
                    }

                    i++;
                }
            }

            if (state.m_OM.UAVs != null)
            {
                int i = 0;
                foreach (var r in state.m_OM.UAVs)
                {
                    ShaderResource shaderInput = null;

                    if (state.m_PS.ShaderDetails != null)
                    {
                        foreach (var bind in state.m_PS.ShaderDetails.ReadWriteResources)
                        {
                            if (bind.bindPoint == i + state.m_OM.UAVStartSlot)
                            {
                                shaderInput = bind;
                                break;
                            }
                        }
                    }

                    bool filledSlot = (r.Resource != ResourceId.Null);
                    bool usedSlot = (shaderInput != null);

                    // note: we don't show empty UAVs as these "slots" are already showed as empty RTs.

                    // 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"
                        )
                    {
                        UInt64 w = 1;
                        UInt32 h = 1, d = 1;
                        UInt32 a = 1;
                        string format = "Unknown";
                        string name = "UAV " + r.Resource.ToString();
                        string typename = "Unknown";
                        object tag = null;
                        bool viewDetails = false;

                        if (!filledSlot)
                        {
                            name = "Empty";
                            format = "-";
                            typename = "-";
                            w = h = d = a = 0;
                        }

                        for (int t = 0; t < texs.Length; t++)
                        {
                            if (texs[t].ID == r.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.Str();

                                if (texs[t].resType == ShaderResourceType.Texture2DMS ||
                                    texs[t].resType == ShaderResourceType.Texture2DMSArray)
                                {
                                    typename += String.Format(" {0}x", texs[t].msSamp);
                                }

                                // if it's a typeless format, show the format of the view
                                if (texs[t].format != r.Format)
                                {
                                    format = "Viewed as " + r.Format.ToString();
                                }

                                if (HasImportantViewParams(r, texs[t]))
                                    viewDetails = true;

                                tag = new ViewTexTag(r, texs[t]);
                            }
                        }

                        for (int t = 0; t < bufs.Length; t++)
                        {
                            if (bufs[t].ID == r.Resource)
                            {
                                w = bufs[t].length;
                                h = 0;
                                d = 0;
                                a = 0;
                                format = "";
                                name = bufs[t].name;
                                typename = "Buffer";

                                if (r.ElementSize > 0)
                                {
                                    typename = "RWStructuredBuffer[" + (bufs[t].length / r.ElementSize) + "]";
                                }
                                else if (r.Flags.HasFlag(D3D11BufferViewFlags.Raw))
                                {
                                    typename = "RWByteAddressBuffer";
                                }

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

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

                                if (HasImportantViewParams(r, bufs[t]))
                                    viewDetails = true;

                                tag = new ViewBufTag(r, bufs[t]);
                            }
                        }

                        var node = targetOutputs.Nodes.Add(new object[] { i + state.m_OM.UAVStartSlot, name, typename, w, h, d, a, format });

                        node.Image = global::renderdocui.Properties.Resources.action;
                        node.HoverImage = global::renderdocui.Properties.Resources.action_hover;
                        node.Tag = tag;

                        if (r.Resource == ResourceId.Null)
                            EmptyRow(node);

                        if (viewDetails)
                            ViewDetailsRow(node);
                    }

                    i++;
                }
            }

            if (state.m_OM.DepthTarget.Resource != ResourceId.Null || showEmpty.Checked)
            {
                UInt32 w = 1, h = 1, d = 1;
                UInt32 a = 1;
                string format = "Unknown";
                string name = "Depth Target " + state.m_OM.DepthTarget.Resource.ToString();
                string typename = "Unknown";
                object tag = null;
                bool viewDetails = false;

                if (state.m_OM.DepthTarget.Resource == ResourceId.Null)
                {
                    name = "Empty";
                    format = "-";
                    typename = "-";
                    w = h = d = a = 0;
                }

                for (int t = 0; t < texs.Length; t++)
                {
                    if (texs[t].ID == state.m_OM.DepthTarget.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.Str();

                        if (texs[t].resType == ShaderResourceType.Texture2DMS ||
                            texs[t].resType == ShaderResourceType.Texture2DMSArray)
                        {
                            typename += String.Format(" {0}x", texs[t].msSamp);
                        }

                        // if it's a typeless format, show the format of the view
                        if (texs[t].format != state.m_OM.DepthTarget.Format)
                        {
                            format = "Viewed as " + state.m_OM.DepthTarget.Format.ToString();
                        }

                        if (HasImportantViewParams(state.m_OM.DepthTarget, texs[t]))
                            viewDetails = true;

                        tag = new ViewTexTag(state.m_OM.DepthTarget, texs[t]);
                    }
                }

                var node = targetOutputs.Nodes.Add(new object[] { "Depth", name, typename, w, h, d, a, format });

                node.Image = global::renderdocui.Properties.Resources.action;
                node.HoverImage = global::renderdocui.Properties.Resources.action_hover;
                node.Tag = tag;

                if (viewDetails)
                    ViewDetailsRow(node);

                if (state.m_OM.DepthTarget.Resource == ResourceId.Null)
                    EmptyRow(node);
            }
            targetOutputs.EndUpdate();
            targetOutputs.NodesSelection.Clear();
            targetOutputs.SetVScrollValue(vs);

            vs = blendOperations.VScrollValue();
            blendOperations.BeginUpdate();
            blendOperations.Nodes.Clear();
            {
                int i = 0;
                foreach(var blend in state.m_OM.m_BlendState.Blends)
                {
                    bool filledSlot = (blend.Enabled == true || targets[i]);
                    bool usedSlot = (targets[i]);

                    // 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 = blendOperations.Nodes.Add(new object[] { i,
                                                        blend.Enabled,
                                                        blend.LogicEnabled,

                                                        blend.m_Blend.Source,
                                                        blend.m_Blend.Destination,
                                                        blend.m_Blend.Operation,

                                                        blend.m_AlphaBlend.Source,
                                                        blend.m_AlphaBlend.Destination,
                                                        blend.m_AlphaBlend.Operation,

                                                        blend.LogicOp,

                                                        ((blend.WriteMask & 0x1) == 0 ? "_" : "R") +
                                                        ((blend.WriteMask & 0x2) == 0 ? "_" : "G") +
                                                        ((blend.WriteMask & 0x4) == 0 ? "_" : "B") +
                                                        ((blend.WriteMask & 0x8) == 0 ? "_" : "A")
                    });

                        if (!filledSlot)
                            EmptyRow(node);

                        if (!usedSlot)
                            InactiveRow(node);
                    }

                    i++;
                }
            }
            blendOperations.NodesSelection.Clear();
            blendOperations.EndUpdate();
            blendOperations.SetVScrollValue(vs);

            alphaToCoverage.Image = state.m_OM.m_BlendState.AlphaToCoverage ? tick : cross;
            independentBlend.Image = state.m_OM.m_BlendState.IndependentBlend ? tick : cross;

            blendFactor.Text = state.m_OM.m_BlendState.BlendFactor[0].ToString("F2") + ", " +
                                state.m_OM.m_BlendState.BlendFactor[1].ToString("F2") + ", " +
                                state.m_OM.m_BlendState.BlendFactor[2].ToString("F2") + ", " +
                                state.m_OM.m_BlendState.BlendFactor[3].ToString("F2");

            sampleMask.Text = state.m_OM.m_BlendState.SampleMask.ToString("X8");

            depthEnable.Image = state.m_OM.m_State.DepthEnable ? tick : cross;
            depthFunc.Text = state.m_OM.m_State.DepthFunc;
            depthWrite.Image = state.m_OM.m_State.DepthWrites ? tick : cross;

            stencilEnable.Image = state.m_OM.m_State.StencilEnable ? tick : cross;
            stencilReadMask.Text = state.m_OM.m_State.StencilReadMask.ToString("X2");
            stencilWriteMask.Text = state.m_OM.m_State.StencilWriteMask.ToString("X2");
            stencilRef.Text = state.m_OM.m_State.StencilRef.ToString("X2");

            stencilFuncs.BeginUpdate();
            stencilFuncs.Nodes.Clear();
            stencilFuncs.Nodes.Add(new object[] { "Front", state.m_OM.m_State.m_FrontFace.Func, state.m_OM.m_State.m_FrontFace.FailOp,
                                                 state.m_OM.m_State.m_FrontFace.DepthFailOp, state.m_OM.m_State.m_FrontFace.PassOp });
            stencilFuncs.Nodes.Add(new object[] { "Back", state.m_OM.m_State.m_BackFace.Func, state.m_OM.m_State.m_BackFace.FailOp,
                                                 state.m_OM.m_State.m_BackFace.DepthFailOp, state.m_OM.m_State.m_BackFace.PassOp });
            stencilFuncs.EndUpdate();
            stencilFuncs.NodesSelection.Clear();

            // highlight the appropriate stages in the flowchart
            if (draw == null)
            {
                pipeFlow.SetStagesEnabled(new bool[] { true, true, true, true, true, true, true, true, true });
            }
            else if ((draw.flags & DrawcallFlags.Dispatch) != 0)
            {
                pipeFlow.SetStagesEnabled(new bool[] { false, false, false, false, false, false, false, false, true });
            }
            else
            {
                pipeFlow.SetStagesEnabled(new bool[] {
                    true,
                    true,
                    state.m_HS.Shader != ResourceId.Null,
                    state.m_DS.Shader != ResourceId.Null,
                    state.m_GS.Shader != ResourceId.Null,
                    true,
                    state.m_PS.Shader != ResourceId.Null,
                    true,
                    false
                });

                // if(streamout only)
                //{
                //    pipeFlow.Rasterizer = false;
                //    pipeFlow.OutputMerger = false;
                //}
            }
        }
        // Set a shader stage's resources and values
        private void SetShaderState(FetchTexture[] texs, FetchBuffer[] bufs,
            D3D11PipelineState.ShaderStage stage,
            Label shader, TreelistView.TreeListView resources, TreelistView.TreeListView samplers,
            TreelistView.TreeListView cbuffers, TreelistView.TreeListView classes)
        {
            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 && 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 = shaderDetails.DebugInfo.entryFunc + "()" + " - " + shaderfn;
            }

            int vs = 0;

            vs = resources.VScrollValue();
            resources.BeginUpdate();
            resources.Nodes.Clear();
            if (stage.SRVs != null)
            {
                int i = 0;
                foreach (var r in stage.SRVs)
                {
                    ShaderResource shaderInput = null;

                    if (shaderDetails != null)
                    {
                        foreach (var bind in shaderDetails.ReadOnlyResources)
                        {
                            if (bind.IsSRV && bind.bindPoint == i)
                            {
                                shaderInput = bind;
                                break;
                            }
                        }
                    }

                    bool filledSlot = (r.Resource != ResourceId.Null);
                    bool usedSlot = (shaderInput != 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"
                        )
                    {
                        string slotname = i.ToString();

                        if (shaderInput != null && shaderInput.name.Length > 0)
                            slotname += ": " + shaderInput.name;

                        UInt64 w = 1;
                        UInt32 h = 1, d = 1;
                        UInt32 a = 1;
                        string format = "Unknown";
                        string name = "Shader Resource " + r.Resource.ToString();
                        string typename = "Unknown";
                        object tag = null;
                        bool viewDetails = false;

                        if (!filledSlot)
                        {
                            name = "Empty";
                            format = "-";
                            typename = "-";
                            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 == r.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.Str();

                                if (texs[t].resType == ShaderResourceType.Texture2DMS ||
                                    texs[t].resType == ShaderResourceType.Texture2DMSArray)
                                {
                                    typename += String.Format(" {0}x", texs[t].msSamp);
                                }

                                // if it's a typeless format, show the format of the view
                                if (texs[t].format != r.Format)
                                {
                                    format = "Viewed as " + r.Format.ToString();
                                }

                                tag = new ViewTexTag(r, texs[t]);

                                if (HasImportantViewParams(r, texs[t]))
                                    viewDetails = true;
                            }
                        }

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

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

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

                                // get the buffer type, whether it's just a basic type or a complex struct
                                if (shaderInput != null && !shaderInput.IsTexture)
                                {
                                    if (r.Format.compType == FormatComponentType.None)
                                    {
                                        if (shaderInput.variableType.members.Length > 0)
                                            format = "struct " + shaderInput.variableType.Name;
                                        else
                                            format = shaderInput.variableType.Name;
                                    }
                                    else
                                    {
                                        format = r.Format.ToString();
                                    }
                                }

                                tag = new ViewBufTag(r, bufs[t]);

                                if (HasImportantViewParams(r, bufs[t]))
                                    viewDetails = true;
                            }
                        }

                        var node = resources.Nodes.Add(new object[] { slotname, name, typename, w, h, d, a, 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);

                        if (viewDetails)
                            ViewDetailsRow(node);
                    }
                    i++;
                }
            }
            resources.EndUpdate();
            resources.NodesSelection.Clear();
            resources.SetVScrollValue(vs);

            vs = samplers.VScrollValue();
            samplers.BeginUpdate();
            samplers.Nodes.Clear();
            if (stage.Samplers != null)
            {
                int i = 0;
                foreach (var s in stage.Samplers)
                {
                    ShaderResource shaderInput = null;

                    if (shaderDetails != null)
                    {
                        foreach (var bind in shaderDetails.ReadOnlyResources)
                        {
                            if (bind.IsSampler && bind.bindPoint == i)
                            {
                                shaderInput = bind;
                                break;
                            }
                        }
                    }

                    bool filledSlot = (s.AddressU.Length > 0);
                    bool usedSlot = (shaderInput != 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"
                        )
                    {
                        string slotname = i.ToString();

                        if (shaderInput != null && shaderInput.name.Length > 0)
                            slotname += ": " + shaderInput.name;

                        if (s.customSamplerName)
                            slotname += "(" + s.SamplerName + ")";

                        string borderColor = s.BorderColor[0].ToString() + ", " +
                                                s.BorderColor[1].ToString() + ", " +
                                                s.BorderColor[2].ToString() + ", " +
                                                s.BorderColor[3].ToString();

                        string addressing = "";

                        string addPrefix = "";
                        string addVal = "";

                        string[] addr = { s.AddressU, s.AddressV, s.AddressW };

                        // arrange like either UVW: WRAP or UV: WRAP, W: CLAMP
                        for (int a = 0; a < 3; a++)
                        {
                            string prefix = "" + "UVW"[a];

                            if (a == 0 || addr[a] == addr[a - 1])
                            {
                                addPrefix += prefix;
                            }
                            else
                            {
                                addressing += addPrefix + ": " + addVal + ", ";

                                addPrefix = prefix;
                            }
                            addVal = addr[a];
                        }

                        addressing += addPrefix + ": " + addVal;

                        if(s.UseBorder)
                            addressing += String.Format("<{0}>", borderColor);

                        string filter = s.Filter;

                        if (s.MaxAniso > 0)
                            filter += String.Format(" {0}x", s.MaxAniso);

                        if (s.UseComparison)
                            filter += String.Format(" ({0})", s.Comparison);

                        var node = samplers.Nodes.Add(new object[] { slotname, addressing,
                                                            filter,
                                                            (s.MinLOD == -float.MaxValue ? "0" : s.MinLOD.ToString()) + " - " +
                                                            (s.MaxLOD == float.MaxValue ? "FLT_MAX" : s.MaxLOD.ToString()),
                                                            s.MipLODBias.ToString() });

                        if (!filledSlot)
                            EmptyRow(node);

                        if (!usedSlot)
                            InactiveRow(node);
                    }

                    i++;
                }
            }
            samplers.EndUpdate();
            samplers.NodesSelection.Clear();
            samplers.SetVScrollValue(vs);

            vs = cbuffers.VScrollValue();
            cbuffers.BeginUpdate();
            cbuffers.Nodes.Clear();
            if (stage.ConstantBuffers != null)
            {
                UInt32 i = 0;
                foreach (var b in stage.ConstantBuffers)
                {
                    ConstantBlock shaderCBuf = null;

                    if (shaderDetails != null && i < shaderDetails.ConstantBlocks.Length && shaderDetails.ConstantBlocks[i].name.Length > 0)
                        shaderCBuf = shaderDetails.ConstantBlocks[i];

                    bool filledSlot = (b.Buffer != ResourceId.Null);
                    bool usedSlot = (shaderCBuf != 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"
                        )
                    {
                        string name = "Constant Buffer " + b.Buffer.ToString();
                        UInt64 length = 1;
                        int numvars = shaderCBuf != null ? shaderCBuf.variables.Length : 0;
                        UInt32 byteSize = shaderCBuf != null ? shaderCBuf.byteSize : 0;

                        if (!filledSlot)
                        {
                            name = "Empty";
                            length = 0;
                        }

                        for (int t = 0; t < bufs.Length; t++)
                        {
                            if (bufs[t].ID == b.Buffer)
                            {
                                name = bufs[t].name;
                                length = bufs[t].length;
                            }
                        }

                        string slotname = i.ToString();

                        if (shaderCBuf != null && shaderCBuf.name.Length > 0)
                            slotname += ": " + shaderCBuf.name;

                        string sizestr;
                        if (byteSize == length)
                            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;

                        string vecrange = String.Format("{0} - {1}", b.VecOffset, b.VecOffset + b.VecCount);

                        var node = cbuffers.Nodes.Add(new object[] { slotname, name, vecrange, sizestr });

                        node.Image = global::renderdocui.Properties.Resources.action;
                        node.HoverImage = global::renderdocui.Properties.Resources.action_hover;
                        node.Tag = i;

                        if (!filledSlot)
                            EmptyRow(node);

                        if (!usedSlot)
                            InactiveRow(node);
                    }
                    i++;
                }
            }
            cbuffers.EndUpdate();
            cbuffers.NodesSelection.Clear();
            cbuffers.SetVScrollValue(vs);

            vs = classes.VScrollValue();
            classes.BeginUpdate();
            classes.Nodes.Clear();
            {
                UInt32 i = 0;
                foreach (var inst in stage.ClassInstances)
                {
                    string interfaceName = String.Format("Interface {0}", i);

                    if (shaderDetails != null && i < shaderDetails.Interfaces.Length)
                        interfaceName = shaderDetails.Interfaces[i].Name;

                    classes.Nodes.Add(new object[] { i.ToString(), interfaceName, inst });

                    i++;
                }
            }
            classes.EndUpdate();
            classes.NodesSelection.Clear();
            classes.SetVScrollValue(vs);

            classes.Visible = classes.Parent.Visible = (stage.ClassInstances.Length > 0);
        }
        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 UpdateState()
        {
            if (!m_Core.LogLoaded)
                return;

            m_CombinedImageSamplers.Clear();

            FetchTexture[] texs = m_Core.CurTextures;
            FetchBuffer[] bufs = m_Core.CurBuffers;
            VulkanPipelineState state = m_Core.CurVulkanPipelineState;
            FetchDrawcall draw = m_Core.CurDrawcall;

            var tick = global::renderdocui.Properties.Resources.tick;
            var cross = global::renderdocui.Properties.Resources.cross;

            bool[] usedBindings = new bool[128];

            for (int i = 0; i < 128; i++)
                usedBindings[i] = false;

            ////////////////////////////////////////////////
            // Vertex Input

            int vs = 0;

            vs = viAttrs.VScrollValue();
            viAttrs.Nodes.Clear();
            viAttrs.BeginUpdate();
            if (state.VI.attrs != null)
            {
                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 = -1;
                        if(a.location < state.VS.BindpointMapping.InputAttributes.Length)
                            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++;
                }
            }
            viAttrs.NodesSelection.Clear();
            viAttrs.EndUpdate();
            viAttrs.SetVScrollValue(vs);

            m_BindNodes.Clear();

            PrimitiveTopology topo = draw != null ? draw.topology : PrimitiveTopology.Unknown;

            topology.Text = topo.ToString();
            if (topo > PrimitiveTopology.PatchList)
            {
                int numCPs = (int)topo - (int)PrimitiveTopology.PatchList + 1;

                topology.Text = string.Format("PatchList ({0} Control Points)", numCPs);
            }

            primRestart.Visible = state.IA.primitiveRestartEnable;

            switch (topo)
            {
                case PrimitiveTopology.PointList:
                    topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_pointlist;
                    break;
                case PrimitiveTopology.LineList:
                    topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_linelist;
                    break;
                case PrimitiveTopology.LineStrip:
                    topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_linestrip;
                    break;
                case PrimitiveTopology.TriangleList:
                    topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_trilist;
                    break;
                case PrimitiveTopology.TriangleStrip:
                    topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_tristrip;
                    break;
                case PrimitiveTopology.LineList_Adj:
                    topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_linelist_adj;
                    break;
                case PrimitiveTopology.LineStrip_Adj:
                    topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_linestrip_adj;
                    break;
                case PrimitiveTopology.TriangleList_Adj:
                    topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_trilist_adj;
                    break;
                case PrimitiveTopology.TriangleStrip_Adj:
                    topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_tristrip_adj;
                    break;
                default:
                    topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_patch;
                    break;
            }

            vs = viBuffers.VScrollValue();
            viBuffers.Nodes.Clear();
            viBuffers.BeginUpdate();

            bool ibufferUsed = draw != null && (draw.flags & DrawcallFlags.UseIBuffer) != 0;

            if (state.IA.ibuffer != null && state.IA.ibuffer.buf != ResourceId.Null)
            {
                if (ibufferUsed || showDisabled.Checked)
                {
                    string ptr = "Buffer " + state.IA.ibuffer.buf.ToString();
                    string name = ptr;
                    UInt64 length = 1;

                    if (!ibufferUsed)
                    {
                        length = 0;
                    }

                    for (int t = 0; t < bufs.Length; t++)
                    {
                        if (bufs[t].ID == state.IA.ibuffer.buf)
                        {
                            name = bufs[t].name;
                            length = bufs[t].length;
                        }
                    }

                    var node = viBuffers.Nodes.Add(new object[] { "Index", name, "Index", state.IA.ibuffer.offs, draw.indexByteWidth, length });

                    node.Image = global::renderdocui.Properties.Resources.action;
                    node.HoverImage = global::renderdocui.Properties.Resources.action_hover;
                    node.Tag = new IABufferTag(state.IA.ibuffer.buf, draw != null ? draw.indexOffset : 0);

                    if (!ibufferUsed)
                        InactiveRow(node);

                    if (state.IA.ibuffer.buf == ResourceId.Null)
                        EmptyRow(node);
                }
            }
            else
            {
                if (ibufferUsed || showEmpty.Checked)
                {
                    var node = viBuffers.Nodes.Add(new object[] { "Index", "No Buffer Set", "-", "-", "-", "-" });

                    node.Image = global::renderdocui.Properties.Resources.action;
                    node.HoverImage = global::renderdocui.Properties.Resources.action_hover;
                    node.Tag = new IABufferTag(state.IA.ibuffer.buf, draw != null ? draw.indexOffset : 0);

                    EmptyRow(node);

                    if (!ibufferUsed)
                        InactiveRow(node);
                }
            }

            m_VBNodes.Clear();

            if (state.VI.vbuffers != null)
            {
                int i = 0;
                for(; i < Math.Max(state.VI.vbuffers.Length, state.VI.binds.Length); i++)
                {
                    var vbuff = (i < state.VI.vbuffers.Length ? state.VI.vbuffers[i] : null);
                    VulkanPipelineState.VertexInput.Binding bind = null;

                    for (int b = 0; b < state.VI.binds.Length; b++)
                    {
                        if (state.VI.binds[b].vbufferBinding == i)
                            bind = state.VI.binds[b];
                    }

                    bool filledSlot = ((vbuff != null && vbuff.buffer != ResourceId.Null) || bind != null);
                    bool usedSlot = (usedBindings[i]);

                    // 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"
                        )
                    {
                        string name = "No Buffer";
                        string rate = "-";
                        UInt64 length = 1;
                        UInt64 offset = 0;
                        UInt32 stride = 0;

                        if (vbuff != null)
                        {
                            name = "Buffer " + vbuff.buffer.ToString();
                            offset = vbuff.offset;

                            for (int t = 0; t < bufs.Length; t++)
                            {
                                if (bufs[t].ID == vbuff.buffer)
                                {
                                    name = bufs[t].name;
                                    length = bufs[t].length;
                                }
                            }
                        }

                        if (bind != null)
                        {
                            stride = bind.bytestride;
                            rate = bind.perInstance ? "Instance" : "Vertex";
                        }
                        else
                        {
                            name += ", No Binding";
                        }

                        TreelistView.Node node = null;

                        if(filledSlot)
                            node = viBuffers.Nodes.Add(new object[] { i, name, rate, offset, stride, length });
                        else
                            node = viBuffers.Nodes.Add(new object[] { i, "No Binding", "-", "-", "-", "-" });

                        node.Image = global::renderdocui.Properties.Resources.action;
                        node.HoverImage = global::renderdocui.Properties.Resources.action_hover;
                        node.Tag = new IABufferTag(vbuff != null ? vbuff.buffer : ResourceId.Null, vbuff != null ? vbuff.offset : 0);

                        if (!filledSlot || bind == null || vbuff == null)
                            EmptyRow(node);

                        if (!usedSlot)
                            InactiveRow(node);

                        m_VBNodes.Add(node);
                    }
                }

                for (; i < usedBindings.Length; i++)
                {
                    if (usedBindings[i])
                    {
                        TreelistView.Node node = viBuffers.Nodes.Add(new object[] { i, "No Binding", "-", "-", "-", "-" });

                        node.Image = global::renderdocui.Properties.Resources.action;
                        node.HoverImage = global::renderdocui.Properties.Resources.action_hover;
                        node.Tag = new IABufferTag(ResourceId.Null, 0);

                        EmptyRow(node);

                        InactiveRow(node);

                        m_VBNodes.Add(node);
                    }
                }
            }
            viBuffers.NodesSelection.Clear();
            viBuffers.EndUpdate();
            viBuffers.SetVScrollValue(vs);

            SetShaderState(texs, bufs, state.VS, state.graphics, vsShader, vsResources, vsCBuffers);
            SetShaderState(texs, bufs, state.GS, state.graphics, gsShader, gsResources, gsCBuffers);
            SetShaderState(texs, bufs, state.TCS, state.graphics, hsShader, hsResources, hsCBuffers);
            SetShaderState(texs, bufs, state.TES, state.graphics, dsShader, dsResources, dsCBuffers);
            SetShaderState(texs, bufs, state.FS, state.graphics, psShader, psResources, psCBuffers);
            SetShaderState(texs, bufs, state.CS, state.compute, csShader, csResources, csCBuffers);

            ////////////////////////////////////////////////
            // Rasterizer

            vs = viewports.VScrollValue();
            viewports.BeginUpdate();
            viewports.Nodes.Clear();

            int vs2 = scissors.VScrollValue();
            scissors.BeginUpdate();
            scissors.Nodes.Clear();

            if (state.Pass.renderpass.obj != ResourceId.Null)
            {
                scissors.Nodes.Add(new object[] { "Render Area",
                    state.Pass.renderArea.x, state.Pass.renderArea.y,
                    state.Pass.renderArea.width, state.Pass.renderArea.height });
            }

            {
                int i = 0;
                foreach (var v in state.VP.viewportScissors)
                {
                    var node = viewports.Nodes.Add(new object[] { i, v.vp.x, v.vp.y, v.vp.Width, v.vp.Height, v.vp.MinDepth, v.vp.MaxDepth });

                    if (v.vp.Width == 0 || v.vp.Height == 0 || v.vp.MinDepth == v.vp.MaxDepth)
                        EmptyRow(node);

                    node = scissors.Nodes.Add(new object[] { i, v.scissor.x, v.scissor.y, v.scissor.width, v.scissor.height });

                    if (v.scissor.width == 0 || v.scissor.height == 0)
                        EmptyRow(node);

                    i++;
                }
            }

            viewports.NodesSelection.Clear();
            viewports.EndUpdate();
            viewports.SetVScrollValue(vs);

            scissors.NodesSelection.Clear();
            scissors.EndUpdate();
            scissors.SetVScrollValue(vs2);

            fillMode.Text = state.RS.FillMode.ToString();
            cullMode.Text = state.RS.CullMode.ToString();
            frontCCW.Image = state.RS.FrontCCW ? tick : cross;

            depthBias.Text = Formatter.Format(state.RS.depthBias);
            depthBiasClamp.Text = Formatter.Format(state.RS.depthBiasClamp);
            slopeScaledBias.Text = Formatter.Format(state.RS.slopeScaledDepthBias);

            depthClamp.Image = state.RS.depthClampEnable ? tick : cross;
            rasterizerDiscard.Image = state.RS.rasterizerDiscardEnable ? tick : cross;
            lineWidth.Text = Formatter.Format(state.RS.lineWidth);

            sampleCount.Text = state.MSAA.rasterSamples.ToString();
            sampleShading.Image = state.MSAA.sampleShadingEnable ? tick : cross;
            minSampleShading.Text = Formatter.Format(state.MSAA.minSampleShading);
            sampleMask.Text = state.MSAA.sampleMask.ToString("X8");

            ////////////////////////////////////////////////
            // Output Merger

            bool[] targets = new bool[8];

            for (int i = 0; i < 8; i++)
                targets[i] = false;

            vs = targetOutputs.VScrollValue();
            targetOutputs.BeginUpdate();
            targetOutputs.Nodes.Clear();
            if (state.Pass.framebuffer.attachments != null)
            {
                int i = 0;
                foreach (var p in state.Pass.framebuffer.attachments)
                {
                    int colIdx = Array.IndexOf(state.Pass.renderpass.colorAttachments, (uint)i);

                    bool filledSlot = (p.img != ResourceId.Null);
                    bool usedSlot = (colIdx >= 0 || state.Pass.renderpass.depthstencilAttachment == i);

                    // 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"
                        )
                    {
                        UInt32 w = 1, h = 1, d = 1;
                        UInt32 a = 1;
                        string format = p.viewfmt.ToString();
                        string name = "Texture " + p.ToString();
                        string typename = "Unknown";
                        object tag = null;
                        bool viewDetails = false;

                        if (p.img == ResourceId.Null)
                        {
                            name = "Empty";
                            format = "-";
                            typename = "-";
                            w = h = d = a = 0;
                        }

                        for (int t = 0; t < texs.Length; t++)
                        {
                            if (texs[t].ID == p.img)
                            {
                                w = texs[t].width;
                                h = texs[t].height;
                                d = texs[t].depth;
                                a = texs[t].arraysize;
                                name = texs[t].name;
                                typename = texs[t].resType.Str();

                                if (!texs[t].customName && state.FS.ShaderDetails != null)
                                {
                                    for(int s=0; s < state.FS.ShaderDetails.OutputSig.Length; s++)
                                    {
                                        if(state.FS.ShaderDetails.OutputSig[s].regIndex == colIdx &&
                                            (state.FS.ShaderDetails.OutputSig[s].systemValue == SystemAttribute.None ||
                                            state.FS.ShaderDetails.OutputSig[s].systemValue == SystemAttribute.ColourOutput))
                                        {
                                            name = String.Format("<{0}>", state.FS.ShaderDetails.OutputSig[s].varName);
                                        }
                                    }
                                }

                                if (HasImportantViewParams(p, texs[t]))
                                    viewDetails = true;

                                tag = new ViewTexTag(p.viewfmt, p.baseMip, p.baseLayer, p.numMip, p.numLayer, texs[t]);
                            }
                        }

                        if (p.swizzle[0] != TextureSwizzle.Red ||
                            p.swizzle[1] != TextureSwizzle.Green ||
                            p.swizzle[2] != TextureSwizzle.Blue ||
                            p.swizzle[3] != TextureSwizzle.Alpha)
                        {
                            format += String.Format(" swizzle[{0}{1}{2}{3}]",
                                p.swizzle[0].Str(),
                                p.swizzle[1].Str(),
                                p.swizzle[2].Str(),
                                p.swizzle[3].Str());
                        }

                        var node = targetOutputs.Nodes.Add(new object[] { i, name, typename, w, h, d, a, format });

                        node.Image = global::renderdocui.Properties.Resources.action;
                        node.HoverImage = global::renderdocui.Properties.Resources.action_hover;
                        node.Tag = tag;

                        if (p.img == ResourceId.Null)
                        {
                            EmptyRow(node);
                        }
                        else if (!usedSlot)
                        {
                            InactiveRow(node);
                        }
                        else
                        {
                            targets[i] = true;

                            ViewDetailsRow(node, viewDetails);
                        }
                    }

                    i++;
                }
            }

            targetOutputs.EndUpdate();
            targetOutputs.NodesSelection.Clear();
            targetOutputs.SetVScrollValue(vs);

            vs = blendOperations.VScrollValue();
            blendOperations.BeginUpdate();
            blendOperations.Nodes.Clear();
            {
                int i = 0;
                foreach(var blend in state.CB.attachments)
                {
                    bool filledSlot = true;
                    bool usedSlot = (targets[i]);

                    // 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 = blendOperations.Nodes.Add(new object[] { i,
                                                        blend.blendEnable,

                                                        blend.m_Blend.Source,
                                                        blend.m_Blend.Destination,
                                                        blend.m_Blend.Operation,

                                                        blend.m_AlphaBlend.Source,
                                                        blend.m_AlphaBlend.Destination,
                                                        blend.m_AlphaBlend.Operation,

                                                        ((blend.WriteMask & 0x1) == 0 ? "_" : "R") +
                                                        ((blend.WriteMask & 0x2) == 0 ? "_" : "G") +
                                                        ((blend.WriteMask & 0x4) == 0 ? "_" : "B") +
                                                        ((blend.WriteMask & 0x8) == 0 ? "_" : "A")
                    });

                        if (!filledSlot)
                            EmptyRow(node);

                        if (!usedSlot)
                            InactiveRow(node);
                    }

                    i++;
                }
            }
            blendOperations.NodesSelection.Clear();
            blendOperations.EndUpdate();
            blendOperations.SetVScrollValue(vs);

            blendFactor.Text = state.CB.blendConst[0].ToString("F2") + ", " +
                                state.CB.blendConst[1].ToString("F2") + ", " +
                                state.CB.blendConst[2].ToString("F2") + ", " +
                                state.CB.blendConst[3].ToString("F2");
            logicOp.Text = state.CB.logicOpEnable ? state.CB.LogicOp : "-";
            alphaToOne.Image = state.CB.alphaToOneEnable ? tick : cross;

            depthEnable.Image = state.DS.depthTestEnable ? tick : cross;
            depthFunc.Text = state.DS.depthCompareOp;
            depthWrite.Image = state.DS.depthWriteEnable ? tick : cross;

            if (state.DS.depthBoundsEnable)
            {
                depthBounds.Text = Formatter.Format(state.DS.minDepthBounds) + "-" + Formatter.Format(state.DS.maxDepthBounds);
                depthBounds.Image = null;
            }
            else
            {
                depthBounds.Text = "";
                depthBounds.Image = cross;
            }

            stencilFuncs.BeginUpdate();
            stencilFuncs.Nodes.Clear();
            if (state.DS.stencilTestEnable)
            {
                stencilFuncs.Nodes.Add(new object[] { "Front", state.DS.front.func, state.DS.front.failOp,
                                                 state.DS.front.depthFailOp, state.DS.front.passOp,
                                                 state.DS.front.writeMask.ToString("X2"),
                                                 state.DS.front.compareMask.ToString("X2"),
                                                 state.DS.front.stencilref.ToString("X2")});
                stencilFuncs.Nodes.Add(new object[] { "Back", state.DS.back.func, state.DS.back.failOp,
                                                 state.DS.back.depthFailOp, state.DS.back.passOp,
                                                 state.DS.back.writeMask.ToString("X2"),
                                                 state.DS.back.compareMask.ToString("X2"),
                                                 state.DS.back.stencilref.ToString("X2")});
            }
            else
            {
                stencilFuncs.Nodes.Add(new object[] { "Front", "-", "-", "-", "-", "-", "-", "-" });
                stencilFuncs.Nodes.Add(new object[] { "Back", "-", "-", "-", "-", "-", "-", "-" });
            }
            stencilFuncs.EndUpdate();
            stencilFuncs.NodesSelection.Clear();

            // highlight the appropriate stages in the flowchart
            if (draw == null)
            {
                pipeFlow.SetStagesEnabled(new bool[] { true, true, true, true, true, true, true, true, true });
            }
            else if ((draw.flags & DrawcallFlags.Dispatch) != 0)
            {
                pipeFlow.SetStagesEnabled(new bool[] { false, false, false, false, false, false, false, false, true });
            }
            else
            {
                pipeFlow.SetStagesEnabled(new bool[] {
                    true,
                    true,
                    state.TCS.Shader != ResourceId.Null,
                    state.TES.Shader != ResourceId.Null,
                    state.GS.Shader != ResourceId.Null,
                    true,
                    state.FS.Shader != ResourceId.Null,
                    true,
                    false
                });

                // if(streamout only)
                //{
                //    pipeFlow.Rasterizer = false;
                //    pipeFlow.OutputMerger = false;
                //}
            }
        }
        private void UpdateState()
        {
            if (!m_Core.LogLoaded)
                return;

            FetchTexture[] texs = m_Core.CurTextures;
            FetchBuffer[] bufs = m_Core.CurBuffers;
            D3D12PipelineState state = m_Core.CurD3D12PipelineState;
            FetchDrawcall draw = m_Core.CurDrawcall;

            HideViewDetailsTooltip();
            m_ViewDetailNodes.Clear();

            var tick = global::renderdocui.Properties.Resources.tick;
            var cross = global::renderdocui.Properties.Resources.cross;

            bool[] usedVBuffers = new bool[128];
            UInt32[] layoutOffs = new UInt32[128];

            for (int i = 0; i < 128; i++)
            {
                usedVBuffers[i] = false;
                layoutOffs[i] = 0;
            }

            ////////////////////////////////////////////////
            // Input Assembler

            int vs = 0;

            vs = inputLayouts.VScrollValue();
            inputLayouts.Nodes.Clear();
            inputLayouts.BeginUpdate();
            if (state.m_IA.layouts != null)
            {
                int i = 0;
                foreach (var l in state.m_IA.layouts)
                {
                    string byteOffs = l.ByteOffset.ToString();

                    // D3D12 specific value
                    if (l.ByteOffset == uint.MaxValue)
                    {
                        byteOffs = String.Format("APPEND_ALIGNED ({0})", layoutOffs[l.InputSlot]);
                    }
                    else
                    {
                        layoutOffs[l.InputSlot] = l.ByteOffset;
                    }

                    layoutOffs[l.InputSlot] += l.Format.compByteWidth * l.Format.compCount;

                    bool iaUsed = false;

                    if (state.m_VS.Shader != ResourceId.Null)
                    {
                        for (int ia = 0; ia < state.m_VS.ShaderDetails.InputSig.Length; ia++)
                        {
                            if (state.m_VS.ShaderDetails.InputSig[ia].semanticName.ToUpperInvariant() == l.SemanticName.ToUpperInvariant() &&
                                state.m_VS.ShaderDetails.InputSig[ia].semanticIndex == l.SemanticIndex)
                            {
                                iaUsed = true;
                                break;
                            }
                        }
                    }

                    i++;

                    if(!iaUsed && !showDisabled.Checked)
                        continue;

                    var node = inputLayouts.Nodes.Add(new object[] {
                                              i, l.SemanticName, l.SemanticIndex.ToString(), l.Format, l.InputSlot.ToString(), byteOffs,
                                              l.PerInstance ? "PER_INSTANCE" : "PER_VERTEX", l.InstanceDataStepRate.ToString() });

                    if (iaUsed)
                        usedVBuffers[l.InputSlot] = true;

                    node.Image = global::renderdocui.Properties.Resources.action;
                    node.HoverImage = global::renderdocui.Properties.Resources.action_hover;

                    if (!iaUsed)
                        InactiveRow(node);
                }
            }
            inputLayouts.NodesSelection.Clear();
            inputLayouts.EndUpdate();
            inputLayouts.SetVScrollValue(vs);

            PrimitiveTopology topo = draw != null ? draw.topology : PrimitiveTopology.Unknown;

            topology.Text = topo.ToString();
            if (topo > PrimitiveTopology.PatchList)
            {
                int numCPs = (int)topo - (int)PrimitiveTopology.PatchList + 1;

                topology.Text = string.Format("PatchList ({0} Control Points)", numCPs);
            }

            switch (topo)
            {
                case PrimitiveTopology.PointList:
                    topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_pointlist;
                    break;
                case PrimitiveTopology.LineList:
                    topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_linelist;
                    break;
                case PrimitiveTopology.LineStrip:
                    topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_linestrip;
                    break;
                case PrimitiveTopology.TriangleList:
                    topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_trilist;
                    break;
                case PrimitiveTopology.TriangleStrip:
                    topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_tristrip;
                    break;
                case PrimitiveTopology.LineList_Adj:
                    topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_linelist_adj;
                    break;
                case PrimitiveTopology.LineStrip_Adj:
                    topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_linestrip_adj;
                    break;
                case PrimitiveTopology.TriangleList_Adj:
                    topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_trilist_adj;
                    break;
                case PrimitiveTopology.TriangleStrip_Adj:
                    topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_tristrip_adj;
                    break;
                default:
                    topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_patch;
                    break;
            }

            vs = iabuffers.VScrollValue();
            iabuffers.Nodes.Clear();
            iabuffers.BeginUpdate();

            bool ibufferUsed = draw != null && (draw.flags & DrawcallFlags.UseIBuffer) != 0;

            if (ibufferUsed)
            {
                if (state.m_IA.indexStripCutValue != 0)
                    restartIndex.Text = String.Format("Restart Idx: 0x{0:X}", state.m_IA.indexStripCutValue);
                else
                    restartIndex.Text = "Restart Idx: Disabled";
            }
            else
            {
                restartIndex.Text = "";
            }

            if (state.m_IA.ibuffer != null && state.m_IA.ibuffer.Buffer != ResourceId.Null)
            {
                if (ibufferUsed || showDisabled.Checked)
                {
                    string ptr = "Buffer " + state.m_IA.ibuffer.Buffer.ToString();
                    string name = ptr;
                    UInt64 length = 1;

                    if (!ibufferUsed)
                    {
                        length = 0;
                    }

                    for (int t = 0; t < bufs.Length; t++)
                    {
                        if (bufs[t].ID == state.m_IA.ibuffer.Buffer)
                        {
                            name = bufs[t].name;
                            length = bufs[t].length;
                        }
                    }

                    var node = iabuffers.Nodes.Add(new object[] { "Index", name, draw != null ? draw.indexByteWidth : 0, state.m_IA.ibuffer.Offset, length });

                    node.Image = global::renderdocui.Properties.Resources.action;
                    node.HoverImage = global::renderdocui.Properties.Resources.action_hover;
                    node.Tag = new IABufferTag(state.m_IA.ibuffer.Buffer, draw != null ? draw.indexOffset : 0);

                    if (!ibufferUsed)
                        InactiveRow(node);

                    if (state.m_IA.ibuffer.Buffer == ResourceId.Null)
                        EmptyRow(node);
                }
            }
            else
            {
                if (ibufferUsed || showEmpty.Checked)
                {
                    var node = iabuffers.Nodes.Add(new object[] { "Index", "No Buffer Set", "-", "-", "-" });

                    node.Image = global::renderdocui.Properties.Resources.action;
                    node.HoverImage = global::renderdocui.Properties.Resources.action_hover;
                    node.Tag = new IABufferTag(state.m_IA.ibuffer.Buffer, draw != null ? draw.indexOffset : 0);

                    EmptyRow(node);

                    if (!ibufferUsed)
                        InactiveRow(node);
                }
            }

            m_VBNodes.Clear();

            if (state.m_IA.vbuffers != null)
            {
                int i = 0;
                foreach (var v in state.m_IA.vbuffers)
                {
                    bool filledSlot = (v.Buffer != ResourceId.Null);
                    bool usedSlot = (usedVBuffers[i]);

                    // 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"
                        )
                    {
                        string name = "Buffer " + v.Buffer.ToString();
                        UInt64 length = 1;

                        for (int t = 0; t < bufs.Length; t++)
                        {
                            if (bufs[t].ID == v.Buffer)
                            {
                                name = bufs[t].name;
                                length = bufs[t].length;
                            }
                        }

                        TreelistView.Node node = null;

                        if(filledSlot)
                            node = iabuffers.Nodes.Add(new object[] { i, name, v.Stride, v.Offset, length });
                        else
                            node = iabuffers.Nodes.Add(new object[] { i, "No Buffer Set", "-", "-", "-" });

                        node.Image = global::renderdocui.Properties.Resources.action;
                        node.HoverImage = global::renderdocui.Properties.Resources.action_hover;
                        node.Tag = new IABufferTag(v.Buffer, v.Offset);

                        if (!filledSlot)
                            EmptyRow(node);

                        if (!usedSlot)
                            InactiveRow(node);

                        m_VBNodes.Add(node);
                    }

                    i++;
                }
            }
            iabuffers.NodesSelection.Clear();
            iabuffers.EndUpdate();
            iabuffers.SetVScrollValue(vs);

            SetShaderState(state.m_VS, vsShader, vsResources, vsSamplers, vsCBuffers, vsUAVs);
            SetShaderState(state.m_GS, gsShader, gsResources, gsSamplers, gsCBuffers, gsUAVs);
            SetShaderState(state.m_HS, hsShader, hsResources, hsSamplers, hsCBuffers, hsUAVs);
            SetShaderState(state.m_DS, dsShader, dsResources, dsSamplers, dsCBuffers, dsUAVs);
            SetShaderState(state.m_PS, psShader, psResources, psSamplers, psCBuffers, psUAVs);
            SetShaderState(state.m_CS, csShader, csResources, csSamplers, csCBuffers, csUAVs);

            bool streamoutSet = false;
            vs = gsStreams.VScrollValue();
            gsStreams.BeginUpdate();
            gsStreams.Nodes.Clear();
            if (state.m_SO.Outputs != null)
            {
                int i = 0;
                foreach (var s in state.m_SO.Outputs)
                {
                    bool filledSlot = (s.Buffer != ResourceId.Null);
                    bool usedSlot = (filledSlot);

                    streamoutSet |= filledSlot;

                    // 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"
                        )
                    {
                        string name = "Buffer " + s.Buffer.ToString();
                        UInt64 length = 0;

                        if (!filledSlot)
                        {
                            name = "Empty";
                        }

                        FetchBuffer fetch = null;

                        for (int t = 0; t < bufs.Length; t++)
                        {
                            if (bufs[t].ID == s.Buffer)
                            {
                                name = bufs[t].name;
                                length = bufs[t].length;

                                fetch = bufs[t];
                            }
                        }

                        var node = gsStreams.Nodes.Add(new object[] { i, name, length, s.Offset });

                        node.Image = global::renderdocui.Properties.Resources.action;
                        node.HoverImage = global::renderdocui.Properties.Resources.action_hover;
                        node.Tag = fetch;

                        if (!filledSlot)
                            EmptyRow(node);

                        if (!usedSlot)
                            InactiveRow(node);
                    }
                    i++;
                }
            }
            gsStreams.EndUpdate();
            gsStreams.NodesSelection.Clear();
            gsStreams.SetVScrollValue(vs);

            gsStreams.Visible = gsStreams.Parent.Visible = streamoutSet;
            if (streamoutSet)
                geomTableLayout.ColumnStyles[1].Width = 50.0f;
            else
                geomTableLayout.ColumnStyles[1].Width = 0;

            ////////////////////////////////////////////////
            // Rasterizer

            vs = viewports.VScrollValue();
            viewports.BeginUpdate();
            viewports.Nodes.Clear();
            if (state.m_RS.Viewports != null)
            {
                int i = 0;
                foreach (var v in state.m_RS.Viewports)
                {
                    var node = viewports.Nodes.Add(new object[] { i, v.TopLeft[0], v.TopLeft[1], v.Width, v.Height, v.MinDepth, v.MaxDepth });

                    if (v.Width == 0 || v.Height == 0 || v.MinDepth == v.MaxDepth)
                        EmptyRow(node);

                    i++;
                }
            }
            viewports.NodesSelection.Clear();
            viewports.EndUpdate();
            viewports.SetVScrollValue(vs);

            vs = scissors.VScrollValue();
            scissors.BeginUpdate();
            scissors.Nodes.Clear();
            if (state.m_RS.Scissors != null)
            {
                int i = 0;
                foreach (var s in state.m_RS.Scissors)
                {
                    var node = scissors.Nodes.Add(new object[] { i, s.left, s.top, s.right - s.left, s.bottom - s.top });

                    if (s.right == s.left || s.bottom == s.top)
                        EmptyRow(node);

                    i++;
                }
            }
            scissors.NodesSelection.Clear();
            scissors.EndUpdate();
            scissors.SetVScrollValue(vs);

            fillMode.Text = state.m_RS.m_State.FillMode.ToString();
            cullMode.Text = state.m_RS.m_State.CullMode.ToString();
            frontCCW.Image = state.m_RS.m_State.FrontCCW ? tick : cross;

            lineAAEnable.Image = state.m_RS.m_State.AntialiasedLineEnable ? tick : cross;
            multisampleEnable.Image = state.m_RS.m_State.MultisampleEnable ? tick : cross;

            depthClip.Image = state.m_RS.m_State.DepthClip ? tick : cross;
            depthBias.Text = state.m_RS.m_State.DepthBias.ToString();
            depthBiasClamp.Text = Formatter.Format(state.m_RS.m_State.DepthBiasClamp);
            slopeScaledBias.Text = Formatter.Format(state.m_RS.m_State.SlopeScaledDepthBias);
            forcedSampleCount.Text = state.m_RS.m_State.ForcedSampleCount.ToString();
            conservativeRaster.Image = state.m_RS.m_State.ConservativeRasterization ? tick : cross;

            ////////////////////////////////////////////////
            // Output Merger

            bool[] targets = new bool[8];

            for (int i = 0; i < 8; i++)
                targets[i] = false;

            vs = targetOutputs.VScrollValue();
            targetOutputs.BeginUpdate();
            targetOutputs.Nodes.Clear();
            if (state.m_OM.RenderTargets != null)
            {
                int i = 0;
                foreach (var p in state.m_OM.RenderTargets)
                {
                    if (p.Resource != ResourceId.Null || showEmpty.Checked)
                    {
                        UInt32 w = 1, h = 1, d = 1;
                        UInt32 a = 1;
                        string format = "Unknown";
                        string name = "Texture " + p.ToString();
                        string typename = "Unknown";
                        object tag = null;
                        bool viewDetails = false;

                        if (p.Resource == ResourceId.Null)
                        {
                            name = "Empty";
                            format = "-";
                            typename = "-";
                            w = h = d = a = 0;
                        }

                        for (int t = 0; t < texs.Length; t++)
                        {
                            if (texs[t].ID == p.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.Str();

                                if (texs[t].resType == ShaderResourceType.Texture2DMS ||
                                    texs[t].resType == ShaderResourceType.Texture2DMSArray)
                                {
                                    typename += String.Format(" {0}x", texs[t].msSamp);
                                }

                                // if it's a typeless format, show the format of the view
                                if (texs[t].format != p.Format)
                                {
                                    format = "Viewed as " + p.Format.ToString();
                                }

                                if (HasImportantViewParams(p, texs[t]))
                                    viewDetails = true;

                                tag = new ViewTexTag(p, texs[t], false, null);
                            }
                        }

                        var node = targetOutputs.Nodes.Add(new object[] { i, name, typename, w, h, d, a, format });

                        node.Image = global::renderdocui.Properties.Resources.action;
                        node.HoverImage = global::renderdocui.Properties.Resources.action_hover;
                        node.Tag = tag;

                        if (p.Resource == ResourceId.Null)
                        {
                            EmptyRow(node);
                        }
                        else
                        {
                            targets[i] = true;

                            ViewDetailsRow(node, viewDetails);
                        }
                    }

                    i++;
                }
            }

            if (state.m_OM.DepthTarget.Resource != ResourceId.Null || showEmpty.Checked)
            {
                UInt32 w = 1, h = 1, d = 1;
                UInt32 a = 1;
                string format = "Unknown";
                string name = "Depth Target " + state.m_OM.DepthTarget.Resource.ToString();
                string typename = "Unknown";
                object tag = null;
                bool viewDetails = false;

                if (state.m_OM.DepthTarget.Resource == ResourceId.Null)
                {
                    name = "Empty";
                    format = "-";
                    typename = "-";
                    w = h = d = a = 0;
                }

                for (int t = 0; t < texs.Length; t++)
                {
                    if (texs[t].ID == state.m_OM.DepthTarget.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.Str();

                        if (texs[t].resType == ShaderResourceType.Texture2DMS ||
                            texs[t].resType == ShaderResourceType.Texture2DMSArray)
                        {
                            typename += String.Format(" {0}x", texs[t].msSamp);
                        }

                        // if it's a typeless format, show the format of the view
                        if (texs[t].format != state.m_OM.DepthTarget.Format)
                        {
                            format = "Viewed as " + state.m_OM.DepthTarget.Format.ToString();
                        }

                        if (HasImportantViewParams(state.m_OM.DepthTarget, texs[t]))
                            viewDetails = true;

                        tag = new ViewTexTag(state.m_OM.DepthTarget, texs[t], false, null);
                    }
                }

                var node = targetOutputs.Nodes.Add(new object[] { "Depth", name, typename, w, h, d, a, format });

                node.Image = global::renderdocui.Properties.Resources.action;
                node.HoverImage = global::renderdocui.Properties.Resources.action_hover;
                node.Tag = tag;

                ViewDetailsRow(node, viewDetails);

                if (state.m_OM.DepthTarget.Resource == ResourceId.Null)
                    EmptyRow(node);
            }
            targetOutputs.EndUpdate();
            targetOutputs.NodesSelection.Clear();
            targetOutputs.SetVScrollValue(vs);

            vs = blendOperations.VScrollValue();
            blendOperations.BeginUpdate();
            blendOperations.Nodes.Clear();
            {
                int i = 0;
                foreach(var blend in state.m_OM.m_BlendState.Blends)
                {
                    bool filledSlot = (blend.Enabled == true || targets[i]);
                    bool usedSlot = (targets[i]);

                    // 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 = blendOperations.Nodes.Add(new object[] { i,
                                                        blend.Enabled,
                                                        blend.LogicEnabled,

                                                        blend.m_Blend.Source,
                                                        blend.m_Blend.Destination,
                                                        blend.m_Blend.Operation,

                                                        blend.m_AlphaBlend.Source,
                                                        blend.m_AlphaBlend.Destination,
                                                        blend.m_AlphaBlend.Operation,

                                                        blend.LogicOp,

                                                        ((blend.WriteMask & 0x1) == 0 ? "_" : "R") +
                                                        ((blend.WriteMask & 0x2) == 0 ? "_" : "G") +
                                                        ((blend.WriteMask & 0x4) == 0 ? "_" : "B") +
                                                        ((blend.WriteMask & 0x8) == 0 ? "_" : "A")
                    });

                        if (!filledSlot)
                            EmptyRow(node);

                        if (!usedSlot)
                            InactiveRow(node);
                    }

                    i++;
                }
            }
            blendOperations.NodesSelection.Clear();
            blendOperations.EndUpdate();
            blendOperations.SetVScrollValue(vs);

            alphaToCoverage.Image = state.m_OM.m_BlendState.AlphaToCoverage ? tick : cross;
            independentBlend.Image = state.m_OM.m_BlendState.IndependentBlend ? tick : cross;

            blendFactor.Text = state.m_OM.m_BlendState.BlendFactor[0].ToString("F2") + ", " +
                                state.m_OM.m_BlendState.BlendFactor[1].ToString("F2") + ", " +
                                state.m_OM.m_BlendState.BlendFactor[2].ToString("F2") + ", " +
                                state.m_OM.m_BlendState.BlendFactor[3].ToString("F2");

            sampleMask.Text = state.m_RS.SampleMask.ToString("X8");

            depthEnable.Image = state.m_OM.m_State.DepthEnable ? tick : cross;
            depthFunc.Text = state.m_OM.m_State.DepthFunc;
            depthWrite.Image = state.m_OM.m_State.DepthWrites ? tick : cross;

            stencilEnable.Image = state.m_OM.m_State.StencilEnable ? tick : cross;
            stencilReadMask.Text = state.m_OM.m_State.StencilReadMask.ToString("X2");
            stencilWriteMask.Text = state.m_OM.m_State.StencilWriteMask.ToString("X2");
            stencilRef.Text = state.m_OM.m_State.StencilRef.ToString("X2");

            stencilFuncs.BeginUpdate();
            stencilFuncs.Nodes.Clear();
            stencilFuncs.Nodes.Add(new object[] { "Front", state.m_OM.m_State.m_FrontFace.Func, state.m_OM.m_State.m_FrontFace.FailOp,
                                                 state.m_OM.m_State.m_FrontFace.DepthFailOp, state.m_OM.m_State.m_FrontFace.PassOp });
            stencilFuncs.Nodes.Add(new object[] { "Back", state.m_OM.m_State.m_BackFace.Func, state.m_OM.m_State.m_BackFace.FailOp,
                                                 state.m_OM.m_State.m_BackFace.DepthFailOp, state.m_OM.m_State.m_BackFace.PassOp });
            stencilFuncs.EndUpdate();
            stencilFuncs.NodesSelection.Clear();

            // highlight the appropriate stages in the flowchart
            if (draw == null)
            {
                pipeFlow.SetStagesEnabled(new bool[] { true, true, true, true, true, true, true, true, true });
            }
            else if ((draw.flags & DrawcallFlags.Dispatch) != 0)
            {
                pipeFlow.SetStagesEnabled(new bool[] { false, false, false, false, false, false, false, false, true });
            }
            else
            {
                pipeFlow.SetStagesEnabled(new bool[] {
                    true,
                    true,
                    state.m_HS.Shader != ResourceId.Null,
                    state.m_DS.Shader != ResourceId.Null,
                    state.m_GS.Shader != ResourceId.Null,
                    true,
                    state.m_PS.Shader != ResourceId.Null,
                    true,
                    false
                });

                // if(streamout only)
                //{
                //    pipeFlow.Rasterizer = false;
                //    pipeFlow.OutputMerger = false;
                //}
            }
        }
        private void AddResourceRow(D3D12PipelineState.ShaderStage stage,
            TreelistView.TreeListView list,
            int space, int reg, bool uav)
        {
            D3D12PipelineState state = m_Core.CurD3D12PipelineState;
            FetchTexture[] texs = m_Core.CurTextures;
            FetchBuffer[] bufs = m_Core.CurBuffers;

            BindpointMap bind = null;
            ShaderResource shaderInput = null;

            D3D12PipelineState.ResourceView r = uav ? stage.Spaces[space].UAVs[reg] : stage.Spaces[space].SRVs[reg];

            // consider this register to not exist - it's in a gap defined by sparse root signature elements
            if (r.RootElement == uint.MaxValue)
                return;

            if (stage.BindpointMapping != null && stage.ShaderDetails != null)
            {
                BindpointMap[] binds = uav ? stage.BindpointMapping.ReadWriteResources : stage.BindpointMapping.ReadOnlyResources;
                ShaderResource[] resources = uav ? stage.ShaderDetails.ReadWriteResources : stage.ShaderDetails.ReadOnlyResources;
                for (int i=0; i < binds.Length; i++)
                {
                    var b = binds[i];
                    var res = resources[i];

                    bool regMatch = b.bind == reg;

                    // handle unbounded arrays specially. It's illegal to have an unbounded array with
                    // anything after it
                    if (b.bind <= reg)
                        regMatch = (b.arraySize == UInt32.MaxValue) || (b.bind + b.arraySize > reg);

                    if (b.bindset == space && regMatch && !res.IsSampler)
                    {
                        bind = b;
                        shaderInput = res;
                        break;
                    }
                }
            }

            TreelistView.NodeCollection parent = list.Nodes;

            string rootel = r.Immediate ? String.Format("#{0} Direct", r.RootElement) : rootel = String.Format("#{0} Table[{1}]", r.RootElement, r.TableIndex);

            bool filledSlot = r.Resource != ResourceId.Null;
            bool usedSlot = (bind != null && bind.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"
                )
            {
                string regname = reg.ToString();

                if (shaderInput != null && shaderInput.name.Length > 0)
                    regname += ": " + shaderInput.name;

                UInt64 w = 1;
                UInt32 h = 1, d = 1;
                UInt32 a = 1;
                string format = "Unknown";
                string name = "Unbound";
                string typename = "Unknown";
                object tag = null;
                bool viewDetails = false;

                if (!filledSlot)
                {
                    name = "Empty";
                    format = "-";
                    typename = "-";
                    w = h = d = a = 0;
                }

                if (r != null)
                {
                    name = "Shader Resource " + r.Resource.ToString();

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

                            if (texs[t].resType == ShaderResourceType.Texture2DMS ||
                                texs[t].resType == ShaderResourceType.Texture2DMSArray)
                            {
                                typename += String.Format(" {0}x", texs[t].msSamp);
                            }

                            // if it's a typeless format, show the format of the view
                            if (texs[t].format != r.Format)
                            {
                                format = "Viewed as " + r.Format.ToString();
                            }

                            tag = new ViewTexTag(r, texs[t], true, shaderInput);

                            if (HasImportantViewParams(r, texs[t]))
                                viewDetails = true;
                        }
                    }

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

                            if (r.BufferFlags.HasFlag(D3DBufferViewFlags.Raw))
                            {
                                typename = uav ? "RWByteAddressBuffer" : "ByteAddressBuffer";
                            }
                            else if (r.ElementSize > 0)
                            {
                                // for structured buffers, display how many 'elements' there are in the buffer
                                typename = (uav ? "RWStructuredBuffer" : "StructuredBuffer");
                                a = (uint)(bufs[t].length / r.ElementSize);
                            }

                            if (r.CounterResource != ResourceId.Null)
                            {
                                typename += " (Count: " + r.BufferStructCount + ")";
                            }

                            // get the buffer type, whether it's just a basic type or a complex struct
                            if (shaderInput != null && !shaderInput.IsTexture)
                            {
                                if (shaderInput.variableType.members.Length > 0)
                                    format = "struct " + shaderInput.variableType.Name;
                                else if (r.Format.compType == FormatComponentType.None)
                                    format = shaderInput.variableType.Name;
                                else
                                    format = r.Format.ToString();
                            }

                            tag = new ViewBufTag(r, bufs[t], true, shaderInput);

                            if (HasImportantViewParams(r, bufs[t]))
                                viewDetails = true;
                        }
                    }
                }

                var node = parent.Add(new object[] { rootel, space, regname, name, typename, w, h, d, a, 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);
            }
        }