Beispiel #1
0
        public VulkanPipelineState GetVulkanPipelineState()
        {
            IntPtr mem = CustomMarshal.Alloc(typeof(VulkanPipelineState));

            bool success = ReplayRenderer_GetVulkanPipelineState(m_Real, mem);

            VulkanPipelineState ret = null;

            if (success)
            {
                ret = (VulkanPipelineState)CustomMarshal.PtrToStructure(mem, typeof(VulkanPipelineState), true);
            }

            CustomMarshal.Free(mem);

            return(ret);
        }
        private void ShowShaderViewer(VulkanPipelineState.ShaderStage stage, Dictionary<String, String> files)
        {
            VulkanPipelineStateViewer pipeviewer = this;

            ShaderViewer sv = new ShaderViewer(m_Core, false, "main", files,

            // Save Callback
            (ShaderViewer viewer, Dictionary<string, string> updatedfiles) =>
            {
                string compileSource = "";
                foreach (var kv in updatedfiles)
                    compileSource += kv.Value;

                // invoke off to the ReplayRenderer to replace the log's shader
                // with our edited one
                m_Core.Renderer.BeginInvoke((ReplayRenderer r) =>
                {
                    string errs = "";

                    ResourceId from = stage.Shader;
                    ResourceId to = r.BuildTargetShader("main", compileSource, stage.ShaderDetails.DebugInfo.compileFlags, stage.stage, out errs);

                    viewer.BeginInvoke((MethodInvoker)delegate { viewer.ShowErrors(errs); });
                    if (to == ResourceId.Null)
                    {
                        r.RemoveReplacement(from);
                        pipeviewer.BeginInvoke((MethodInvoker)delegate { m_Core.RefreshStatus(); });
                    }
                    else
                    {
                        r.ReplaceResource(from, to);
                        pipeviewer.BeginInvoke((MethodInvoker)delegate { m_Core.RefreshStatus(); });
                    }
                });
            },

            // Close Callback
            () =>
            {
                // remove the replacement on close (we could make this more sophisticated if there
                // was a place to control replaced resources/shaders).
                m_Core.Renderer.BeginInvoke((ReplayRenderer r) =>
                {
                    r.RemoveReplacement(stage.Shader);
                    pipeviewer.BeginInvoke((MethodInvoker)delegate { m_Core.RefreshStatus(); });
                });
            });

            sv.Show(m_DockContent.DockPanel);
        }
        private void ShowCBuffer(VulkanPipelineState.ShaderStage stage, CBufferTag tag)
        {
            if (tag == null || tag.slotIdx == uint.MaxValue) return;

            VulkanPipelineState.Pipeline pipe = m_Core.CurVulkanPipelineState.graphics;
            if(stage.stage == ShaderStageType.Compute)
                pipe = m_Core.CurVulkanPipelineState.compute;

            var existing = ConstantBufferPreviewer.Has(stage.stage, tag.slotIdx, tag.arrayIdx);
            if (existing != null)
            {
                existing.Show();
                return;
            }

            var prev = new ConstantBufferPreviewer(m_Core, stage.stage, tag.slotIdx, tag.arrayIdx);

            prev.ShowDock(m_DockContent.Pane, DockAlignment.Right, 0.3);
        }
        // Set a shader stage's resources and values
        private void SetShaderState(FetchTexture[] texs, FetchBuffer[] bufs,
            VulkanPipelineState.ShaderStage stage, VulkanPipelineState.Pipeline pipe,
            Label shader, TreelistView.TreeListView resources,
            TreelistView.TreeListView cbuffers)
        {
            ShaderReflection shaderDetails = stage.ShaderDetails;

            if (stage.Shader == ResourceId.Null)
                shader.Text = "Unbound";
            else
                shader.Text = stage.ShaderName;

            if (shaderDetails != null && shaderDetails.DebugInfo.entryFunc.Length > 0)
            {
                if (shaderDetails.DebugInfo.files.Length > 0 || shaderDetails.DebugInfo.entryFunc != "main")
                    shader.Text = shaderDetails.DebugInfo.entryFunc + "()";

                if (shaderDetails.DebugInfo.files.Length > 0)
                {
                    string shaderfn = "";

                    int entryFile = shaderDetails.DebugInfo.entryFile;
                    if (entryFile < 0 || entryFile >= shaderDetails.DebugInfo.files.Length)
                        entryFile = 0;

                    shaderfn = shaderDetails.DebugInfo.files[entryFile].BaseFilename;

                    shader.Text += " - " + shaderfn;
                }
            }

            int vs = 0;

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

            var samplers = new Dictionary<ResourceId, SamplerData>();

            for(int bindset = 0; bindset < pipe.DescSets.Length; bindset++)
            {
                for(int bind = 0; bind < pipe.DescSets[bindset].bindings.Length; bind++)
                {
                    AddResourceRow(shaderDetails, stage, bindset, bind, pipe, resources, texs, bufs, ref samplers);
                }

                // if we have a shader bound, go through and add rows for any resources it wants for binds that aren't
                // in this descriptor set (e.g. if layout mismatches)
                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 >= pipe.DescSets[bindset].bindings.Length)
                        {
                            AddResourceRow(shaderDetails, stage, bindset,
                                stage.BindpointMapping.ReadOnlyResources[ro.bindPoint].bind,
                                pipe, resources, texs, bufs, ref samplers);
                        }
                    }

                    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 >= pipe.DescSets[bindset].bindings.Length)
                        {
                            AddResourceRow(shaderDetails, stage, bindset,
                                stage.BindpointMapping.ReadWriteResources[rw.bindPoint].bind,
                                pipe, resources, texs, bufs, ref samplers);
                        }
                    }
                }
            }

            // if we have a shader bound, go through and add rows for any resources it wants for descriptor sets that aren't
            // bound at all
            if (shaderDetails != null)
            {
                for (int i = 0; i < shaderDetails.ReadOnlyResources.Length; i++)
                {
                    var ro = shaderDetails.ReadOnlyResources[i];

                    if (stage.BindpointMapping.ReadOnlyResources[ro.bindPoint].bindset >= pipe.DescSets.Length)
                    {
                        AddResourceRow(shaderDetails, stage,
                            stage.BindpointMapping.ReadOnlyResources[ro.bindPoint].bindset,
                            stage.BindpointMapping.ReadOnlyResources[ro.bindPoint].bind,
                            pipe, resources, texs, bufs, ref samplers);
                    }
                }

                for (int i = 0; i < shaderDetails.ReadWriteResources.Length; i++)
                {
                    var rw = shaderDetails.ReadWriteResources[i];

                    if (stage.BindpointMapping.ReadWriteResources[rw.bindPoint].bindset >= pipe.DescSets.Length)
                    {
                        AddResourceRow(shaderDetails, stage,
                            stage.BindpointMapping.ReadWriteResources[rw.bindPoint].bindset,
                            stage.BindpointMapping.ReadWriteResources[rw.bindPoint].bind,
                            pipe, resources, texs, bufs, ref samplers);
                    }
                }
            }

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

            vs = cbuffers.VScrollValue();
            cbuffers.BeginUpdate();
            cbuffers.Nodes.Clear();
            for(int bindset = 0; bindset < pipe.DescSets.Length; bindset++)
            {
                for(int bind = 0; bind < pipe.DescSets[bindset].bindings.Length; bind++)
                {
                    AddConstantBlockRow(shaderDetails, stage, bindset, bind, pipe, cbuffers, bufs);
                }

                // if we have a shader bound, go through and add rows for any cblocks it wants for binds that aren't
                // in this descriptor set (e.g. if layout mismatches)
                if (shaderDetails != null)
                {
                    for (int i = 0; i < shaderDetails.ConstantBlocks.Length; i++)
                    {
                        var cb = shaderDetails.ConstantBlocks[i];

                        if (stage.BindpointMapping.ConstantBlocks[cb.bindPoint].bindset == bindset &&
                            stage.BindpointMapping.ConstantBlocks[cb.bindPoint].bind >= pipe.DescSets[bindset].bindings.Length)
                        {
                            AddConstantBlockRow(shaderDetails, stage, bindset,
                                stage.BindpointMapping.ConstantBlocks[cb.bindPoint].bind,
                                pipe, cbuffers, bufs);
                        }
                    }
                }
            }

            // if we have a shader bound, go through and add rows for any resources it wants for descriptor sets that aren't
            // bound at all
            if (shaderDetails != null)
            {
                for (int i = 0; i < shaderDetails.ConstantBlocks.Length; i++)
                {
                    var cb = shaderDetails.ConstantBlocks[i];

                    if (stage.BindpointMapping.ConstantBlocks[cb.bindPoint].bindset >= pipe.DescSets.Length && cb.bufferBacked)
                    {
                        AddConstantBlockRow(shaderDetails, stage,
                            stage.BindpointMapping.ConstantBlocks[cb.bindPoint].bindset,
                            stage.BindpointMapping.ConstantBlocks[cb.bindPoint].bind,
                            pipe, cbuffers, bufs);
                    }
                }
            }

            // search for push constants and add them last
            if (shaderDetails != null)
            {
                for (int cb = 0; cb < shaderDetails.ConstantBlocks.Length; cb++)
                {
                    var cblock = shaderDetails.ConstantBlocks[cb];
                    if (cblock.bufferBacked == false)
                    {
                        // could maybe get range from ShaderVariable.reg if it's filled out
                        // from SPIR-V side.

                        var node = cbuffers.Nodes.Add(new object[] { "", "",
                        cblock.name, "Push constants",
                        "", String.Format("{0} Variables", cblock.variables.Length) });

                        node.Image = global::renderdocui.Properties.Resources.action;
                        node.HoverImage = global::renderdocui.Properties.Resources.action_hover;
                        node.Tag = new CBufferTag((uint)cb, 0);
                    }
                }
            }
            cbuffers.EndUpdate();
            cbuffers.NodesSelection.Clear();
            cbuffers.SetVScrollValue(vs);
        }
 public void SetStates(APIProperties props, D3D11PipelineState d3d11, GLPipelineState gl, VulkanPipelineState vk)
 {
     m_APIProps = props;
     m_D3D11 = d3d11;
     m_GL = gl;
     m_Vulkan = vk;
 }
Beispiel #6
0
        // setting a context filter allows replaying of deferred events. You can set the deferred
        // events to replay in a context, after replaying up to a given event on the main thread
        public void SetContextFilter(ILogViewerForm exclude, UInt32 frameID, UInt32 eventID,
            ResourceId ctx, UInt32 firstDeferred, UInt32 lastDeferred)
        {
            m_FrameID = frameID;
            m_EventID = eventID;

            m_DeferredEvent = lastDeferred;

            m_Renderer.Invoke((ReplayRenderer r) => { r.SetContextFilter(ctx, firstDeferred, lastDeferred); });
            m_Renderer.Invoke((ReplayRenderer r) => {
                r.SetFrameEvent(m_FrameID, m_EventID, true);
                m_D3D11PipelineState = r.GetD3D11PipelineState();
                m_GLPipelineState = r.GetGLPipelineState();
                m_VulkanPipelineState = r.GetVulkanPipelineState();
                m_PipelineState.SetStates(m_APIProperties, m_D3D11PipelineState, m_GLPipelineState, m_VulkanPipelineState);
            });

            foreach (var logviewer in m_LogViewers)
            {
                if (logviewer == exclude)
                    continue;

                Control c = (Control)logviewer;
                if (c.InvokeRequired)
                    c.BeginInvoke(new Action(() => logviewer.OnEventSelected(frameID, eventID)));
                else
                    logviewer.OnEventSelected(frameID, eventID);
            }
        }
Beispiel #7
0
        public void CloseLogfile()
        {
            if (!m_LogLoaded) return;

            m_LogFile = "";

            m_Renderer.CloseThreadSync();
            m_Renderer = new RenderManager();

            m_APIProperties = null;
            m_FrameInfo = null;
            m_DrawCalls = null;
            m_Buffers = null;
            m_Textures = null;

            m_D3D11PipelineState = null;
            m_GLPipelineState = null;
            m_VulkanPipelineState = null;
            m_PipelineState.SetStates(null, null, null, null);

            DebugMessages.Clear();
            UnreadMessageCount = 0;

            m_LogLoaded = false;

            if (m_LogWatcher != null)
                m_LogWatcher.EnableRaisingEvents = false;
            m_LogWatcher = null;

            foreach (var logviewer in m_LogViewers)
            {
                Control c = (Control)logviewer;
                if (c.InvokeRequired)
                    c.Invoke(new Action(() => logviewer.OnLogfileClosed()));
                else
                    logviewer.OnLogfileClosed();
            }
        }
        private void ExportHTML(XmlTextWriter writer, VulkanPipelineState.VertexInput vi)
        {
            {
                writer.WriteStartElement("h3");
                writer.WriteString("Attributes");
                writer.WriteEndElement();

                List<object[]> rows = new List<object[]>();

                foreach (var attr in vi.attrs)
                    rows.Add(new object[] { attr.location, attr.binding, attr.format.ToString(), attr.byteoffset });

                ExportHTMLTable(writer, new string[] { "Location", "Binding", "Format", "Offset" }, rows.ToArray());
            }

            {
                writer.WriteStartElement("h3");
                writer.WriteString("Bindings");
                writer.WriteEndElement();

                List<object[]> rows = new List<object[]>();

                foreach (var attr in vi.binds)
                    rows.Add(new object[] { attr.vbufferBinding, attr.bytestride, attr.perInstance ? "PER_INSTANCE" : "PER_VERTEX" });

                ExportHTMLTable(writer, new string[] { "Binding", "Byte Stride", "Step Rate" }, rows.ToArray());
            }

            {
                writer.WriteStartElement("h3");
                writer.WriteString("Vertex Buffers");
                writer.WriteEndElement();

                List<object[]> rows = new List<object[]>();

                int i = 0;
                foreach (var vb in vi.vbuffers)
                {
                    string name = "Buffer " + vb.buffer.ToString();
                    ulong length = 0;

                    if (vb.buffer == ResourceId.Null)
                    {
                        continue;
                    }
                    else
                    {
                        FetchBuffer buf = m_Core.GetBuffer(vb.buffer);
                        if(buf != null)
                        {
                            name = buf.name;
                            length = buf.length;
                        }
                    }

                    rows.Add(new object[] { i, name, vb.offset, length });

                    i++;
                }

                ExportHTMLTable(writer, new string[] { "Binding", "Buffer", "Offset", "Byte Length" }, rows.ToArray());
            }
        }
        private bool HasImportantViewParams(VulkanPipelineState.Pipeline.DescriptorSet.DescriptorBinding.BindingElement view, FetchTexture tex)
        {
            // Since mutable formats are more unclear in vulkan (it allows casting between any
            // similar format, and the underlying texture still has a valid format), we consider
            // a format difference to be important even though we display the view's format in
            // the row data itself
            if (view.viewfmt != tex.format ||
                view.baseMip > 0 || view.baseLayer > 0 ||
                (view.numMip < tex.mips && tex.mips > 1) ||
                (view.numLayer < tex.arraysize && tex.arraysize > 1))
                return true;

            return false;
        }
        private void ExportHTML(XmlTextWriter writer, VulkanPipelineState.CurrentPass pass)
        {
            {
                writer.WriteStartElement("h3");
                writer.WriteString("Framebuffer");
                writer.WriteEndElement();

                ExportHTMLTable(writer,
                    new string[] { "Width", "Height", "Layers" },
                    new object[] { pass.framebuffer.width, pass.framebuffer.height, pass.framebuffer.layers });

                List<object[]> rows = new List<object[]>();

                int i = 0;
                foreach (var a in pass.framebuffer.attachments)
                {
                    FetchTexture tex = m_Core.GetTexture(a.img);

                    string name = "Image " + a.img;

                    if (tex != null) name = tex.name;

                    rows.Add(new object[] {
                        i,
                        name, a.baseMip, a.numMip, a.baseLayer, a.numLayer });

                    i++;
                }

                ExportHTMLTable(writer,
                    new string[] {
                        "Slot",
                        "Image", "First mip", "Number of mips", "First array layer", "Number of layers",
                    },
                    rows.ToArray());
            }

            {
                writer.WriteStartElement("h3");
                writer.WriteString("Render Pass");
                writer.WriteEndElement();

                if (pass.renderpass.inputAttachments.Length > 0)
                {
                    object[][] inputs = new object[pass.renderpass.inputAttachments.Length][];

                    for (int i = 0; i < pass.renderpass.inputAttachments.Length; i++)
                        inputs[i] = new object[] { pass.renderpass.inputAttachments[i] };

                    ExportHTMLTable(writer, new string[] { "Input Attachment", }, inputs);

                    writer.WriteStartElement("p");
                    writer.WriteEndElement();

                }

                if (pass.renderpass.colorAttachments.Length > 0)
                {
                    object[][] colors = new object[pass.renderpass.colorAttachments.Length][];

                    for (int i = 0; i < pass.renderpass.colorAttachments.Length; i++)
                        colors[i] = new object[] { pass.renderpass.colorAttachments[i] };

                    ExportHTMLTable(writer, new string[] { "Color Attachment", }, colors);

                    writer.WriteStartElement("p");
                    writer.WriteEndElement();
                }

                if (pass.renderpass.depthstencilAttachment >= 0)
                {
                    writer.WriteStartElement("p");
                    writer.WriteString("Depth-stencil Attachment: " + pass.renderpass.depthstencilAttachment);
                    writer.WriteEndElement();
                }
            }

            {
                writer.WriteStartElement("h3");
                writer.WriteString("Render Area");
                writer.WriteEndElement();

                ExportHTMLTable(writer,
                    new string[] { "X", "Y", "Width", "Height" },
                    new object[] { pass.renderArea.x, pass.renderArea.y, pass.renderArea.width, pass.renderArea.height });
            }
        }
        private void ExportHTML(XmlTextWriter writer, VulkanPipelineState.DepthStencil ds)
        {
            {
                writer.WriteStartElement("h3");
                writer.WriteString("Depth State");
                writer.WriteEndElement();

                ExportHTMLTable(writer,
                    new string[] { "Depth Test Enable", "Depth Writes Enable", "Depth Function", "Depth Bounds" },
                    new object[] {
                        ds.depthTestEnable ? "Yes" : "No", ds.depthWriteEnable ? "Yes" : "No",
                        ds.depthCompareOp,
                        ds.depthBoundsEnable ? String.Format("{0} - {1}", Formatter.Format(ds.minDepthBounds), Formatter.Format(ds.maxDepthBounds)) : "Disabled",
                    });
            }

            {
                writer.WriteStartElement("h3");
                writer.WriteString("Stencil State");
                writer.WriteEndElement();

                if (ds.stencilTestEnable)
                {
                    List<object[]> rows = new List<object[]>();

                    rows.Add(new object[] {
                        "Front",
                        ds.front.stencilref.ToString("X2"), ds.front.compareMask.ToString("X2"), ds.front.writeMask.ToString("X2"),
                        ds.front.func, ds.front.passOp, ds.front.failOp, ds.front.depthFailOp
                    });

                    rows.Add(new object[] {
                        "Back",
                        ds.back.stencilref.ToString("X2"), ds.back.compareMask.ToString("X2"), ds.back.writeMask.ToString("X2"),
                        ds.back.func, ds.back.passOp, ds.back.failOp, ds.back.depthFailOp
                    });

                    ExportHTMLTable(writer,
                        new string[] { "Face", "Ref", "Compare Mask", "Write Mask", "Function", "Pass Op", "Fail Op", "Depth Fail Op" },
                        rows.ToArray());
                }
                else
                {
                    writer.WriteStartElement("p");
                    writer.WriteString("Disabled");
                    writer.WriteEndElement();
                }
            }
        }
        private void ExportHTML(XmlTextWriter writer, VulkanPipelineState.ColorBlend cb)
        {
            writer.WriteStartElement("h3");
            writer.WriteString("Color Blend State");
            writer.WriteEndElement();

            var blendConst = Formatter.Format(cb.blendConst[0]) + ", " +
                                Formatter.Format(cb.blendConst[1]) + ", " +
                                Formatter.Format(cb.blendConst[2]) + ", " +
                                Formatter.Format(cb.blendConst[3]);

            ExportHTMLTable(writer,
                new string[] { "Alpha to Coverage", "Alpha to One", "Logic Op", "Blend Constant" },
                new object[] { cb.alphaToCoverageEnable ? "Yes" : "No", cb.alphaToOneEnable ? "Yes" : "No",
                                cb.logicOpEnable ? cb.LogicOp : "Disabled", blendConst, });

            writer.WriteStartElement("h3");
            writer.WriteString("Attachment Blends");
            writer.WriteEndElement();

            List<object[]> rows = new List<object[]>();

            int i = 0;
            foreach (var b in cb.attachments)
            {
                rows.Add(new object[] {
                        i,
                        b.blendEnable ? "Yes" : "No",
                        b.m_Blend.Source, b.m_Blend.Destination, b.m_Blend.Operation,
                        b.m_AlphaBlend.Source, b.m_AlphaBlend.Destination, b.m_AlphaBlend.Operation,
                        ((b.WriteMask & 0x1) == 0 ? "_" : "R") +
                        ((b.WriteMask & 0x2) == 0 ? "_" : "G") +
                        ((b.WriteMask & 0x4) == 0 ? "_" : "B") +
                        ((b.WriteMask & 0x8) == 0 ? "_" : "A") });

                i++;
            }

            ExportHTMLTable(writer,
                new string[] {
                        "Slot",
                        "Blend Enable",
                        "Blend Source", "Blend Destination", "Blend Operation",
                        "Alpha Blend Source", "Alpha Blend Destination", "Alpha Blend Operation",
                        "Write Mask",
                    },
                rows.ToArray());
        }
        private void ExportHTML(XmlTextWriter writer, VulkanPipelineState.Raster rs)
        {
            {
                writer.WriteStartElement("h3");
                writer.WriteString("Raster State");
                writer.WriteEndElement();

                ExportHTMLTable(writer,
                    new string[] { "Fill Mode", "Cull Mode", "Front CCW" },
                    new object[] { rs.FillMode, rs.CullMode, rs.FrontCCW ? "Yes" : "No" });

                writer.WriteStartElement("p");
                writer.WriteEndElement();

                ExportHTMLTable(writer,
                    new string[] { "Depth Clip Enable", "Rasterizer Discard Enable" },
                    new object[] { rs.depthClampEnable ? "Yes" : "No", rs.rasterizerDiscardEnable ? "Yes" : "No" });

                writer.WriteStartElement("p");
                writer.WriteEndElement();

                ExportHTMLTable(writer,
                    new string[] { "Depth Bias", "Depth Bias Clamp", "Slope Scaled Bias", "Line Width" },
                    new object[] { Formatter.Format(rs.depthBias), Formatter.Format(rs.depthBiasClamp),
                                    Formatter.Format(rs.slopeScaledDepthBias), Formatter.Format(rs.lineWidth) });
            }

            VulkanPipelineState.MultiSample msaa = m_Core.CurVulkanPipelineState.MSAA;

            {
                writer.WriteStartElement("h3");
                writer.WriteString("Multisampling State");
                writer.WriteEndElement();

                ExportHTMLTable(writer,
                    new string[] { "Raster Samples", "Sample-rate shading", "Min Sample Shading Rate", "Sample Mask" },
                    new object[] { msaa.rasterSamples, msaa.sampleShadingEnable ? "Yes" : "No",
                                    Formatter.Format(msaa.minSampleShading), msaa.sampleMask.ToString("X8") });
            }

            VulkanPipelineState.ViewState vp = m_Core.CurVulkanPipelineState.VP;

            {
                writer.WriteStartElement("h3");
                writer.WriteString("Viewports");
                writer.WriteEndElement();

                List<object[]> rows = new List<object[]>();

                int i = 0;
                foreach (var vs in vp.viewportScissors)
                {
                    var v = vs.vp;

                    rows.Add(new object[] { i, v.x, v.y, v.Width, v.Height, v.MinDepth, v.MaxDepth });

                    i++;
                }

                ExportHTMLTable(writer, new string[] { "Slot", "X", "Y", "Width", "Height", "Min Depth", "Max Depth" }, rows.ToArray());
            }

            {
                writer.WriteStartElement("h3");
                writer.WriteString("Scissors");
                writer.WriteEndElement();

                List<object[]> rows = new List<object[]>();

                int i = 0;
                foreach (var vs in vp.viewportScissors)
                {
                    var s = vs.scissor;

                    rows.Add(new object[] { i, s.x, s.y, s.width, s.height });

                    i++;
                }

                ExportHTMLTable(writer, new string[] { "Slot", "X", "Y", "Width", "Height" }, rows.ToArray());
            }
        }
        private void ExportHTML(XmlTextWriter writer, VulkanPipelineState.ShaderStage sh)
        {
            ShaderReflection shaderDetails = sh.ShaderDetails;

            {
                writer.WriteStartElement("h3");
                writer.WriteString("Shader");
                writer.WriteEndElement();

                string shadername = "Unknown";

                if (sh.Shader == ResourceId.Null)
                    shadername = "Unbound";
                else
                    shadername = sh.ShaderName;

                if (shaderDetails != null && shaderDetails.DebugInfo.entryFunc.Length > 0 && shaderDetails.DebugInfo.files.Length > 0)
                    shadername = shaderDetails.DebugInfo.entryFunc + "()" + " - " +
                                    shaderDetails.DebugInfo.files[0].BaseFilename;

                writer.WriteStartElement("p");
                writer.WriteString(shadername);
                writer.WriteEndElement();

                if (sh.Shader == ResourceId.Null)
                    return;
            }

            var pipeline = (sh.stage == ShaderStageType.Compute ? m_Core.CurVulkanPipelineState.compute : m_Core.CurVulkanPipelineState.graphics);

            if(shaderDetails.ConstantBlocks.Length > 0)
            {
                writer.WriteStartElement("h3");
                writer.WriteString("UBOs");
                writer.WriteEndElement();

                List<object[]> rows = new List<object[]>();

                for(int i=0; i < shaderDetails.ConstantBlocks.Length; i++)
                {
                    var b = shaderDetails.ConstantBlocks[i];
                    var bindMap = sh.BindpointMapping.ConstantBlocks[i];

                    if (!bindMap.used) continue;

                    var set = pipeline.DescSets[sh.BindpointMapping.ConstantBlocks[i].bindset];
                    var bind = set.bindings[sh.BindpointMapping.ConstantBlocks[i].bind];

                    string setname = bindMap.bindset.ToString();

                    string slotname = bindMap.bind.ToString();
                    slotname += ": " + b.name;

                    for (int a = 0; a < bind.descriptorCount; a++)
                    {
                        var descriptorBind = bind.binds[a];

                        ResourceId id = bind.binds[a].res;

                        if (bindMap.arraySize > 1)
                            slotname = String.Format("{0}: {1}[{2}]", bindMap.bind, b.name, a);

                        string name = "";
                        ulong byteOffset = descriptorBind.offset;
                        ulong length = descriptorBind.size;
                        int numvars = b.variables.Length;

                        if (descriptorBind.res == ResourceId.Null)
                        {
                            name = "Empty";
                            length = 0;
                        }

                        FetchBuffer buf = m_Core.GetBuffer(id);
                        if (buf != null)
                        {
                            name = buf.name;

                            if (length == ulong.MaxValue)
                                length = buf.length - byteOffset;
                        }

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

                        // push constants
                        if (!b.bufferBacked)
                        {
                            setname = "";
                            slotname = b.name;
                            name = "Push constants";
                            byteOffset = 0;
                            length = 0;

                            // could maybe get range/size from ShaderVariable.reg if it's filled out
                            // from SPIR-V side.
                        }

                        rows.Add(new object[] { setname, slotname, name, byteOffset, length, numvars, b.byteSize });
                    }
                }

                ExportHTMLTable(writer, new string[] { "Set", "Bind", "Buffer", "Byte Offset", "Byte Size", "Number of Variables", "Bytes Needed" }, rows.ToArray());
            }

            if(shaderDetails.ReadOnlyResources.Length > 0)
            {
                writer.WriteStartElement("h3");
                writer.WriteString("Read-only Resources");
                writer.WriteEndElement();

                List<object[]> rows = new List<object[]>();

                for (int i = 0; i < shaderDetails.ReadOnlyResources.Length; i++)
                {
                    var b = shaderDetails.ReadOnlyResources[i];
                    var bindMap = sh.BindpointMapping.ReadOnlyResources[i];

                    if (!bindMap.used) continue;

                    var set = pipeline.DescSets[sh.BindpointMapping.ReadOnlyResources[i].bindset];
                    var bind = set.bindings[sh.BindpointMapping.ReadOnlyResources[i].bind];

                    string setname = bindMap.bindset.ToString();

                    string slotname = bindMap.bind.ToString();
                    slotname += ": " + b.name;

                    for (int a = 0; a < bind.descriptorCount; a++)
                    {
                        var descriptorBind = bind.binds[a];

                        ResourceId id = bind.binds[a].res;

                        if (bindMap.arraySize > 1)
                            slotname = String.Format("{0}: {1}[{2}]", bindMap.bind, b.name, a);

                        string name = "";

                        if (descriptorBind.res == ResourceId.Null)
                            name = "Empty";

                        FetchBuffer buf = m_Core.GetBuffer(id);
                        if (buf != null)
                            name = buf.name;

                        FetchTexture tex = m_Core.GetTexture(id);
                        if (tex != null)
                            name = tex.name;

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

                        UInt64 w = 1;
                        UInt32 h = 1, d = 1;
                        UInt32 arr = 0;
                        string format = "Unknown";
                        string viewParams = "";

                        if(tex != null)
                        {
                            w = tex.width;
                            h = tex.height;
                            d = tex.depth;
                            arr = tex.arraysize;
                            format = tex.format.ToString();
                            name = tex.name;

                            if (tex.mips > 1)
                                viewParams = String.Format("Mips: {0}-{1}", descriptorBind.baseMip, descriptorBind.baseMip+descriptorBind.numMip - 1);

                            if (tex.arraysize > 1)
                            {
                                if (viewParams.Length > 0)
                                    viewParams += ", ";
                                viewParams += String.Format("Layers: {0}-{1}", descriptorBind.baseLayer, descriptorBind.baseLayer + descriptorBind.numLayer - 1);
                            }
                        }

                        if(buf != null)
                        {
                            w = buf.length;
                            h = 0;
                            d = 0;
                            a = 0;
                            format = "-";
                            name = buf.name;

                            ulong length = descriptorBind.size;

                            if (length == ulong.MaxValue)
                                length = buf.length - descriptorBind.offset;

                            viewParams = String.Format("Byte Range: {0} - {1}", descriptorBind.offset, descriptorBind.offset + length);
                        }

                        if (bind.type != ShaderBindType.Sampler)
                            rows.Add(new object[] { setname, slotname, name, bind.type, w, h, d, arr, format, viewParams });

                        if (bind.type == ShaderBindType.ImageSampler || bind.type == ShaderBindType.Sampler)
                        {
                            name = "Sampler " + descriptorBind.sampler.ToString();

                            if (bind.type == ShaderBindType.ImageSampler)
                                setname = slotname = "";

                            object[] sampDetails = MakeSampler("", "", descriptorBind);
                            rows.Add(new object[] {
                                setname, slotname, name, bind.type,
                                "", "", "", "",
                                sampDetails[5],
                                sampDetails[6]
                            });
                        }
                    }
                }

                ExportHTMLTable(writer, new string[] {
                    "Set", "Bind", "Buffer",
                    "Resource Type",
                    "Width", "Height", "Depth", "Array Size",
                    "Resource Format",
                    "View Parameters"
                    },
                    rows.ToArray());
            }

            if(shaderDetails.ReadWriteResources.Length > 0)
            {
                writer.WriteStartElement("h3");
                writer.WriteString("Read-write Resources");
                writer.WriteEndElement();

                List<object[]> rows = new List<object[]>();

                for (int i = 0; i < shaderDetails.ReadWriteResources.Length; i++)
                {
                    var b = shaderDetails.ReadWriteResources[i];
                    var bindMap = sh.BindpointMapping.ReadWriteResources[i];

                    if (!bindMap.used) continue;

                    var set = pipeline.DescSets[sh.BindpointMapping.ReadWriteResources[i].bindset];
                    var bind = set.bindings[sh.BindpointMapping.ReadWriteResources[i].bind];

                    string setname = bindMap.bindset.ToString();

                    string slotname = bindMap.bind.ToString();
                    slotname += ": " + b.name;

                    for (int a = 0; a < bind.descriptorCount; a++)
                    {
                        var descriptorBind = bind.binds[a];

                        ResourceId id = bind.binds[a].res;

                        if (bindMap.arraySize > 1)
                            slotname = String.Format("{0}: {1}[{2}]", bindMap.bind, b.name, a);

                        string name = "";

                        if (descriptorBind.res == ResourceId.Null)
                            name = "Empty";

                        FetchBuffer buf = m_Core.GetBuffer(id);
                        if (buf != null)
                            name = buf.name;

                        FetchTexture tex = m_Core.GetTexture(id);
                        if (tex != null)
                            name = tex.name;

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

                        UInt64 w = 1;
                        UInt32 h = 1, d = 1;
                        UInt32 arr = 0;
                        string format = "Unknown";
                        string viewParams = "";

                        if (tex != null)
                        {
                            w = tex.width;
                            h = tex.height;
                            d = tex.depth;
                            arr = tex.arraysize;
                            format = tex.format.ToString();
                            name = tex.name;

                            if (tex.mips > 1)
                                viewParams = String.Format("Highest Mip: {0}", descriptorBind.baseMip);

                            if (tex.arraysize > 1)
                            {
                                if (viewParams.Length > 0)
                                    viewParams += ", ";
                                viewParams += String.Format("First array slice: {0}", descriptorBind.baseLayer);
                            }
                        }

                        if (buf != null)
                        {
                            w = buf.length;
                            h = 0;
                            d = 0;
                            a = 0;
                            format = "-";
                            name = buf.name;

                            ulong length = descriptorBind.size;

                            if (length == ulong.MaxValue)
                                length = buf.length - descriptorBind.offset;

                            viewParams = String.Format("Byte Range: {0} - {1}", descriptorBind.offset, descriptorBind.offset + length);
                        }

                        rows.Add(new object[] { setname, slotname, name, bind.type, w, h, d, arr, format, viewParams });
                    }
                }

                ExportHTMLTable(writer, new string[] {
                    "Set", "Bind", "Buffer",
                    "Resource Type",
                    "Width", "Height", "Depth", "Array Size",
                    "Resource Format",
                    "View Parameters"
                    },
                    rows.ToArray());
            }
        }
        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 ExportHTML(XmlTextWriter writer, VulkanPipelineState.InputAssembly ia)
        {
            {
                writer.WriteStartElement("h3");
                writer.WriteString("Index Buffer");
                writer.WriteEndElement();

                FetchBuffer ib = m_Core.GetBuffer(ia.ibuffer.buf);

                string name = "Empty";
                ulong length = 0;

                if (ib != null)
                {
                    name = ib.name;
                    length = ib.length;
                }

                string ifmt = "UNKNOWN";
                if (m_Core.CurDrawcall.indexByteWidth == 2)
                    ifmt = "UINT16";
                if (m_Core.CurDrawcall.indexByteWidth == 4)
                    ifmt = "UINT32";

                ExportHTMLTable(writer, new string[] { "Buffer", "Format", "Offset", "Byte Length", "Primitive Restart" },
                    new object[] { name, ifmt, ia.ibuffer.offs.ToString(), length.ToString(), ia.primitiveRestartEnable ? "Yes" : "No" });
            }

            writer.WriteStartElement("p");
            writer.WriteEndElement();

            ExportHTMLTable(writer,
                new string[] { "Primitive Topology", "Tessellation Control Points" },
                new object[] { m_Core.CurDrawcall.topology.Str(), m_Core.CurVulkanPipelineState.Tess.numControlPoints });
        }
        private bool HasImportantViewParams(VulkanPipelineState.Pipeline.DescriptorSet.DescriptorBinding.BindingElement view, FetchBuffer buf)
        {
            if (view.offset > 0 || view.size < buf.length)
                return true;

            return false;
        }
        private bool HasImportantViewParams(VulkanPipelineState.CurrentPass.Framebuffer.Attachment att, FetchTexture tex)
        {
            // see above in BindingElement overload for justification for comparing formats
            if (att.viewfmt != tex.format ||
                att.baseMip > 0 || att.baseLayer > 0 ||
                (att.numMip < tex.mips && tex.mips > 1) ||
                (att.numLayer < tex.arraysize && tex.arraysize > 1))
                return true;

            return false;
        }
        private void AddConstantBlockRow(ShaderReflection shaderDetails, VulkanPipelineState.ShaderStage stage,
            int bindset, int bind,
            VulkanPipelineState.Pipeline pipe, TreelistView.TreeListView cbuffers,
            FetchBuffer[] bufs)
        {
            ConstantBlock cblock = null;
            BindpointMap bindMap = null;

            uint slot = uint.MaxValue;
            if (shaderDetails != null)
            {
                for (slot = 0; slot < (uint)shaderDetails.ConstantBlocks.Length; slot++)
                {
                    ConstantBlock cb = shaderDetails.ConstantBlocks[slot];
                    if (stage.BindpointMapping.ConstantBlocks[cb.bindPoint].bindset == bindset &&
                        stage.BindpointMapping.ConstantBlocks[cb.bindPoint].bind == bind)
                    {
                        cblock = cb;
                        bindMap = stage.BindpointMapping.ConstantBlocks[cb.bindPoint];
                        break;
                    }
                }

                if (slot >= (uint)shaderDetails.ConstantBlocks.Length)
                    slot = uint.MaxValue;
            }

            VulkanPipelineState.Pipeline.DescriptorSet.DescriptorBinding.BindingElement[] slotBinds = null;
            ShaderBindType bindType = ShaderBindType.ConstantBuffer;
            ShaderStageBits stageBits = (ShaderStageBits)0;

            if (bindset < pipe.DescSets.Length && bind < pipe.DescSets[bindset].bindings.Length)
            {
                slotBinds = pipe.DescSets[bindset].bindings[bind].binds;
                bindType = pipe.DescSets[bindset].bindings[bind].type;
                stageBits = pipe.DescSets[bindset].bindings[bind].stageFlags;
            }

            bool usedSlot = bindMap != null && bindMap.used;
            bool stageBitsIncluded = stageBits.HasFlag((ShaderStageBits)(1 << (int)stage.stage));

            // skip descriptors that aren't for this shader stage
            if (!usedSlot && !stageBitsIncluded)
                return;

            if (bindType != ShaderBindType.ConstantBuffer)
                return;

            // consider it filled if any array element is filled (or it's push constants)
            bool filledSlot = cblock != null && !cblock.bufferBacked;
            for (int idx = 0; slotBinds != null && idx < slotBinds.Length; idx++)
                filledSlot |= slotBinds[idx].res != ResourceId.Null;

            // if it's masked out by stage bits, act as if it's not filled, so it's marked in red
            if (!stageBitsIncluded)
                filledSlot = false;

            // show if
            if (usedSlot || // it's referenced by the shader - regardless of empty or not
                (showDisabled.Checked && !usedSlot && filledSlot) || // it's bound, but not referenced, and we have "show disabled"
                (showEmpty.Checked && !filledSlot) // it's empty, and we have "show empty"
                )
            {
                TreelistView.NodeCollection parentNodes = cbuffers.Nodes;

                string setname = bindset.ToString();

                string slotname = bind.ToString();
                if (cblock != null && cblock.name.Length > 0)
                    slotname += ": " + cblock.name;

                int arrayLength = 0;
                if (slotBinds != null) arrayLength = slotBinds.Length;
                else arrayLength = (int)bindMap.arraySize;

                // for arrays, add a parent element that we add the real cbuffers below
                if (arrayLength > 1)
                {
                    var node = parentNodes.Add(new object[] { "", setname, slotname, String.Format("Array[{0}]", arrayLength), "", "" });

                    node.TreeColumn = 0;

                    if (!filledSlot)
                        EmptyRow(node);

                    if (!usedSlot)
                        InactiveRow(node);

                    parentNodes = node.Nodes;
                }

                for (int idx = 0; idx < arrayLength; idx++)
                {
                    VulkanPipelineState.Pipeline.DescriptorSet.DescriptorBinding.BindingElement descriptorBind = null;
                    if(slotBinds != null) descriptorBind = slotBinds[idx];

                    if (arrayLength > 1)
                    {
                        slotname = String.Format("{0}[{1}]", bind, idx);

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

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

                    string vecrange = "-";

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

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

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

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

                    string sizestr;

                    // push constants or specialization constants
                    if (cblock != null && !cblock.bufferBacked)
                    {
                        setname = "";
                        slotname = cblock.name;
                        name = "Push constants";
                        vecrange = "";
                        sizestr = String.Format("{0} Variables", numvars);

                        // could maybe get range from ShaderVariable.reg if it's filled out
                        // from SPIR-V side.
                    }
                    else
                    {
                        if (length == byteSize)
                            sizestr = String.Format("{0} Variables, {1} bytes", numvars, length);
                        else
                            sizestr = String.Format("{0} Variables, {1} bytes needed, {2} provided", numvars, byteSize, length);

                        if (length < byteSize)
                            filledSlot = false;
                    }

                    var node = parentNodes.Add(new object[] { "", setname, slotname, name, vecrange, sizestr });

                    node.Image = global::renderdocui.Properties.Resources.action;
                    node.HoverImage = global::renderdocui.Properties.Resources.action_hover;
                    node.Tag = new CBufferTag(slot, (uint)idx);

                    if (!filledSlot)
                        EmptyRow(node);

                    if (!usedSlot)
                        InactiveRow(node);
                }
            }
        }
Beispiel #20
0
        // when loading a log while replaying remotely, provide the proxy renderer that will be used
        // as well as the hostname to replay on.
        public void LoadLogfile(int proxyRenderer, string replayHost, string logFile, bool temporary)
        {
            m_LogFile = logFile;

            m_LogLoadingInProgress = true;

            if (File.Exists(Core.ConfigFilename))
                m_Config.Serialize(Core.ConfigFilename);

            float postloadProgress = 0.0f;

            bool progressThread = true;

            // start a modal dialog to prevent the user interacting with the form while the log is loading.
            // We'll close it down when log loading finishes (whether it succeeds or fails)
            ModalPopup modal = new ModalPopup(LogLoadCallback, true);

            Thread modalThread = Helpers.NewThread(new ThreadStart(() =>
            {
                modal.SetModalText(string.Format("Loading Log {0}.", m_LogFile));

                AppWindow.BeginInvoke(new Action(() =>
                {
                    modal.ShowDialog(AppWindow);
                }));
            }));
            modalThread.Start();

            // this thread continually ticks and notifies any threads of the progress, through a float
            // that is updated by the main loading code
            Thread thread = Helpers.NewThread(new ThreadStart(() =>
            {
                modal.LogfileProgressBegin();

                foreach (var p in m_ProgressListeners)
                    p.LogfileProgressBegin();

                while (progressThread)
                {
                    Thread.Sleep(2);

                    float progress = 0.8f * m_Renderer.LoadProgress + 0.19f * postloadProgress + 0.01f;

                    modal.LogfileProgress(progress);

                    foreach (var p in m_ProgressListeners)
                        p.LogfileProgress(progress);
                }
            }));
            thread.Start();

            // this function call will block until the log is either loaded, or there's some failure
            m_Renderer.Init(proxyRenderer, replayHost, logFile);

            // if the renderer isn't running, we hit a failure case so display an error message
            if (!m_Renderer.Running)
            {
                string errmsg = "Unknown error message";
                if (m_Renderer.InitException.Data.Contains("status"))
                    errmsg = ((ReplayCreateStatus)m_Renderer.InitException.Data["status"]).Str();

                if(proxyRenderer >= 0)
                    MessageBox.Show(String.Format("{0}\nFailed to transfer and replay on remote host {1}: {2}.\n\n" +
                                                    "Check diagnostic log in Help menu for more details.", logFile, replayHost, errmsg),
                                    "Error opening log", MessageBoxButtons.OK, MessageBoxIcon.Error);
                else
                    MessageBox.Show(String.Format("{0}\nFailed to open logfile for replay: {1}.\n\n" +
                                                    "Check diagnostic log in Help menu for more details.", logFile, errmsg),
                                    "Error opening log", MessageBoxButtons.OK, MessageBoxIcon.Error);

                progressThread = false;
                thread.Join();

                m_LogLoadingInProgress = false;

                modal.LogfileProgress(-1.0f);

                foreach (var p in m_ProgressListeners)
                    p.LogfileProgress(-1.0f);

                return;
            }

            if (!temporary)
            {
                m_Config.AddRecentFile(m_Config.RecentLogFiles, logFile, 10);

                if (File.Exists(Core.ConfigFilename))
                    m_Config.Serialize(Core.ConfigFilename);
            }

            m_FrameID = 0;
            m_EventID = 0;

            m_FrameInfo = null;
            m_APIProperties = null;

            // fetch initial data like drawcalls, textures and buffers
            m_Renderer.Invoke((ReplayRenderer r) =>
            {
                m_FrameInfo = r.GetFrameInfo();

                m_APIProperties = r.GetAPIProperties();

                postloadProgress = 0.2f;

                m_DrawCalls = new FetchDrawcall[m_FrameInfo.Length][];

                postloadProgress = 0.4f;

                for (int i = 0; i < m_FrameInfo.Length; i++)
                    m_DrawCalls[i] = FakeProfileMarkers(i, r.GetDrawcalls((UInt32)i));

                postloadProgress = 0.7f;

                m_Buffers = r.GetBuffers();

                postloadProgress = 0.8f;
                var texs = new List<FetchTexture>(r.GetTextures());
                m_Textures = texs.OrderBy(o => o.name).ToArray();

                postloadProgress = 0.9f;

                m_D3D11PipelineState = r.GetD3D11PipelineState();
                m_GLPipelineState = r.GetGLPipelineState();
                m_VulkanPipelineState = r.GetVulkanPipelineState();
                m_PipelineState.SetStates(m_APIProperties, m_D3D11PipelineState, m_GLPipelineState, m_VulkanPipelineState);

                UnreadMessageCount = 0;
                AddMessages(m_FrameInfo[0].debugMessages);

                postloadProgress = 1.0f;
            });

            Thread.Sleep(20);

            DateTime today = DateTime.Now;
            DateTime compare = today.AddDays(-21);

            if (compare.CompareTo(Config.DegradedLog_LastUpdate) >= 0 && m_APIProperties.degraded)
            {
                Config.DegradedLog_LastUpdate = today;

                MessageBox.Show(String.Format("{0}\nThis log opened with degraded support - " +
                                                "this could mean missing hardware support caused a fallback to software rendering.\n\n" +
                                                "This warning will not appear every time this happens, " +
                                                "check debug errors/warnings window for more details.", logFile),
                                "Degraded support of log", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            }

            m_LogLoaded = true;
            progressThread = false;

            m_LogWatcher = new FileSystemWatcher(Path.GetDirectoryName(m_LogFile), Path.GetFileName(m_LogFile));
            m_LogWatcher.EnableRaisingEvents = true;
            m_LogWatcher.NotifyFilter = NotifyFilters.Size | NotifyFilters.FileName | NotifyFilters.LastAccess | NotifyFilters.LastWrite;
            m_LogWatcher.Created += new FileSystemEventHandler(OnLogfileChanged);
            m_LogWatcher.Changed += new FileSystemEventHandler(OnLogfileChanged);
            m_LogWatcher.SynchronizingObject = m_MainWindow; // callbacks on UI thread please

            List<ILogViewerForm> logviewers = new List<ILogViewerForm>();
            logviewers.AddRange(m_LogViewers);

            // notify all the registers log viewers that a log has been loaded
            foreach (var logviewer in logviewers)
            {
                if (logviewer == null || !(logviewer is Control)) continue;

                Control c = (Control)logviewer;
                if (c.InvokeRequired)
                {
                    if (!c.IsDisposed)
                    {
                        c.Invoke(new Action(() => {
                            try
                            {
                                logviewer.OnLogfileLoaded();
                            }
                            catch (Exception ex)
                            {
                                throw new AccessViolationException("Rethrown from Invoke:\n" + ex.ToString());
                            }
                        }));
                    }
                }
                else if (!c.IsDisposed)
                    logviewer.OnLogfileLoaded();
            }

            m_LogLoadingInProgress = false;

            modal.LogfileProgress(1.0f);

            foreach (var p in m_ProgressListeners)
                p.LogfileProgress(1.0f);
        }
        private object[] MakeSampler(string bindset, string slotname, VulkanPipelineState.Pipeline.DescriptorSet.DescriptorBinding.BindingElement descriptor)
        {
            string addressing = "";
            string addPrefix = "";
            string addVal = "";

            string filter = "";
            string filtPrefix = "";
            string filtVal = "";

            string[] addr = { descriptor.addrU, descriptor.addrV, descriptor.addrW };

            // 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 (descriptor.borderEnable)
                addressing += " " + descriptor.border;

            if (descriptor.unnormalized)
                addressing += " (Un-norm)";

            string[] filters = { descriptor.min, descriptor.mag, descriptor.mip };
            string[] filterPrefixes = { "Min", "Mag", "Mip" };

            // arrange as addressing above
            for (int a = 0; a < 3; a++)
            {
                if (a == 0 || filters[a] == filters[a - 1])
                {
                    if (filtPrefix != "")
                        filtPrefix += "/";
                    filtPrefix += filterPrefixes[a];
                }
                else
                {
                    filter += filtPrefix + ": " + filtVal + ", ";

                    filtPrefix = filterPrefixes[a];
                }
                filtVal = filters[a];
            }

            filter += filtPrefix + ": " + filtVal;

            if (descriptor.maxAniso > 1.0f)
                filter += String.Format(" Aniso {0}x", descriptor.maxAniso);

            if (descriptor.compareEnable)
                filter += String.Format(" ({0})", descriptor.comparison);

            string lod = "LODs: " +
                         (descriptor.minlod == -float.MaxValue ? "0" : descriptor.minlod.ToString()) + " - " +
                         (descriptor.maxlod == float.MaxValue ? "FLT_MAX" : descriptor.maxlod.ToString());

            if (descriptor.mipBias != 0.0f)
                lod += String.Format(" Bias {0}", descriptor.mipBias);

            return new object[] {
                                        "", bindset, slotname,
                                        descriptor.immutableSampler ? "Immutable Sampler" : "Sampler",
                                        descriptor.SamplerName,
                                        addressing,
                                        filter + ", " + lod
                                    };
        }
Beispiel #22
0
        private void SetEventID(ILogViewerForm exclude, UInt32 frameID, UInt32 eventID, bool force)
        {
            m_FrameID = frameID;
            m_EventID = eventID;

            m_DeferredEvent = 0;

            m_Renderer.Invoke((ReplayRenderer r) => { r.SetContextFilter(ResourceId.Null, 0, 0); });
            m_Renderer.Invoke((ReplayRenderer r) =>
            {
                r.SetFrameEvent(m_FrameID, m_EventID, force);
                m_D3D11PipelineState = r.GetD3D11PipelineState();
                m_GLPipelineState = r.GetGLPipelineState();
                m_VulkanPipelineState = r.GetVulkanPipelineState();
                m_PipelineState.SetStates(m_APIProperties, m_D3D11PipelineState, m_GLPipelineState, m_VulkanPipelineState);
            });

            foreach (var logviewer in m_LogViewers)
            {
                if(logviewer == exclude)
                    continue;

                Control c = (Control)logviewer;
                if (c.InvokeRequired)
                    c.Invoke(new Action(() => logviewer.OnEventSelected(frameID, eventID)));
                else
                    logviewer.OnEventSelected(frameID, eventID);
            }
        }
        // Set a shader stage's resources and values
        private void SetShaderState(FetchTexture[] texs, FetchBuffer[] bufs,
            VulkanPipelineState.ShaderStage stage, VulkanPipelineState.Pipeline pipe,
            Label shader, TreelistView.TreeListView resources,
            TreelistView.TreeListView cbuffers)
        {
            ShaderReflection shaderDetails = stage.ShaderDetails;

            if (stage.Shader == ResourceId.Null)
                shader.Text = "Unbound";
            else
                shader.Text = stage.ShaderName;

            if (shaderDetails != null && shaderDetails.DebugInfo.entryFunc.Length > 0)
            {
                if (shaderDetails.DebugInfo.files.Length > 0 || shaderDetails.DebugInfo.entryFunc != "main")
                    shader.Text = shaderDetails.DebugInfo.entryFunc + "()";

                if (shaderDetails.DebugInfo.files.Length > 0)
                {
                    string shaderfn = "";

                    int entryFile = shaderDetails.DebugInfo.entryFile;
                    if (entryFile < 0 || entryFile >= shaderDetails.DebugInfo.files.Length)
                        entryFile = 0;

                    shaderfn = shaderDetails.DebugInfo.files[entryFile].BaseFilename;

                    shader.Text += " - " + shaderfn;
                }
            }

            int vs = 0;

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

            var samplers = new Dictionary<ResourceId, SamplerData>();

            for(int bindset = 0; bindset < pipe.DescSets.Length; bindset++)
            {
                for(int bind = 0; bind < pipe.DescSets[bindset].bindings.Length; bind++)
                {
                    ShaderResource shaderRes = null;
                    BindpointMap bindMap = null;

                    bool isrw = false;
                    uint bindPoint = 0;

                    if (shaderDetails != null)
                    {
                        for(int i=0; i < shaderDetails.ReadOnlyResources.Length; i++)
                        {
                            var ro = shaderDetails.ReadOnlyResources[i];

                            if (stage.BindpointMapping.ReadOnlyResources[ro.bindPoint].bindset == bindset &&
                                stage.BindpointMapping.ReadOnlyResources[ro.bindPoint].bind == bind)
                            {
                                bindPoint = (uint)i;
                                shaderRes = ro;
                                bindMap = stage.BindpointMapping.ReadOnlyResources[ro.bindPoint];
                            }
                        }

                        for(int i=0; i < shaderDetails.ReadWriteResources.Length; i++)
                        {
                            var rw = shaderDetails.ReadWriteResources[i];

                            if (stage.BindpointMapping.ReadWriteResources[rw.bindPoint].bindset == bindset &&
                                stage.BindpointMapping.ReadWriteResources[rw.bindPoint].bind == bind)
                            {
                                bindPoint = (uint)i;
                                isrw = true;
                                shaderRes = rw;
                                bindMap = stage.BindpointMapping.ReadWriteResources[rw.bindPoint];
                            }
                        }
                    }

                    VulkanPipelineState.Pipeline.DescriptorSet.DescriptorBinding.BindingElement[] slotBinds =
                        pipe.DescSets[bindset].bindings[bind].binds;
                    ShaderBindType bindType = pipe.DescSets[bindset].bindings[bind].type;
                    ShaderStageBits stageBits = pipe.DescSets[bindset].bindings[bind].stageFlags;

                    // skip descriptors that aren't for this shader stage
                    if (!stageBits.HasFlag((ShaderStageBits)(1 << (int)stage.stage)))
                        continue;

                    // these are treated as uniform buffers
                    if (bindType == ShaderBindType.ReadOnlyBuffer)
                        continue;

                    // consider it filled if any array element is filled
                    bool filledSlot = false;
                    for (int idx = 0; idx < slotBinds.Length; idx++)
                    {
                        filledSlot |= slotBinds[idx].res != ResourceId.Null;
                        if(bindType == ShaderBindType.Sampler || bindType == ShaderBindType.ImageSampler)
                            filledSlot |= slotBinds[idx].sampler != ResourceId.Null;
                    }
                    bool usedSlot = bindMap != null && bindMap.used;

                    // show if
                    if (usedSlot || // it's referenced by the shader - regardless of empty or not
                        (showDisabled.Checked && !usedSlot && filledSlot) || // it's bound, but not referenced, and we have "show disabled"
                        (showEmpty.Checked && !filledSlot) // it's empty, and we have "show empty"
                        )
                    {
                        TreelistView.NodeCollection parentNodes = resources.Nodes;

                        string setname = bindset.ToString();

                        string slotname = bind.ToString();
                        if(shaderRes != null)
                            slotname += ": " + shaderRes.name;

                        // for arrays, add a parent element that we add the real cbuffers below
                        if (slotBinds.Length > 1)
                        {
                            var node = parentNodes.Add(new object[] { "", setname, slotname, String.Format("Array[{0}]", slotBinds.Length), "", "", "", "" });

                            node.TreeColumn = 0;

                            if (!filledSlot)
                                EmptyRow(node);

                            if (!usedSlot)
                                InactiveRow(node);

                            parentNodes = node.Nodes;
                        }

                        for (int idx = 0; idx < slotBinds.Length; idx++)
                        {
                            var descriptorBind = slotBinds[idx];

                            if (slotBinds.Length > 1)
                            {
                                slotname = String.Format("{0}[{1}]", bind, idx);

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

                            bool isbuf = false;
                            UInt32 w = 1, h = 1, d = 1;
                            UInt32 a = 1;
                            UInt32 samples = 1;
                            UInt64 len = 0;
                            string format = "Unknown";
                            string name = "Object " + descriptorBind.res.ToString();
                            ShaderResourceType restype = ShaderResourceType.None;
                            object tag = null;

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

                            // check to see if it's a texture
                            for (int t = 0; t < texs.Length; t++)
                            {
                                if (texs[t].ID == descriptorBind.res)
                                {
                                    w = texs[t].width;
                                    h = texs[t].height;
                                    d = texs[t].depth;
                                    a = texs[t].arraysize;
                                    format = texs[t].format.ToString();
                                    name = texs[t].name;
                                    restype = texs[t].resType;
                                    samples = texs[t].msSamp;

                                    tag = texs[t];
                                }
                            }

                            // if not a texture, it must be a buffer
                            for (int t = 0; t < bufs.Length; t++)
                            {
                                if (bufs[t].ID == descriptorBind.res)
                                {
                                    len = bufs[t].byteSize;
                                    w = bufs[t].length;
                                    h = 0;
                                    d = 0;
                                    a = 0;
                                    format = "";
                                    name = bufs[t].name;
                                    restype = ShaderResourceType.Buffer;

                                    tag = new BufferResTag(isrw, bindPoint, bufs[t].ID);

                                    isbuf = true;
                                }
                            }

                            TreelistView.Node node = null;

                            if (bindType == ShaderBindType.ReadWriteBuffer ||
                                bindType == ShaderBindType.ReadOnlyTBuffer ||
                                bindType == ShaderBindType.ReadWriteTBuffer
                                )
                            {
                                if (!isbuf)
                                {
                                    node = parentNodes.Add(new object[] {
                                        "", bindset, slotname, bindType, "-",
                                        "-",
                                        "",
                                        "",
                                    });

                                    EmptyRow(node);
                                }
                                else
                                {
                                    node = parentNodes.Add(new object[] {
                                        "", bindset, slotname, bindType, name,
                                        String.Format("{0} bytes", len),
                                        String.Format("{0} - {1}", descriptorBind.offset, descriptorBind.size),
                                        "",
                                    });

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

                                    if (!filledSlot)
                                        EmptyRow(node);

                                    if (!usedSlot)
                                        InactiveRow(node);
                                }
                            }
                            else if (bindType == ShaderBindType.Sampler)
                            {
                                if (descriptorBind.sampler == ResourceId.Null)
                                {
                                    node = parentNodes.Add(new object[] {
                                        "", bindset, slotname, bindType, "-",
                                        "-",
                                        "",
                                        "",
                                    });

                                    EmptyRow(node);
                                }
                                else
                                {
                                    node = parentNodes.Add(MakeSampler(bindset.ToString(), slotname, descriptorBind));

                                    if (!filledSlot)
                                        EmptyRow(node);

                                    if (!usedSlot)
                                        InactiveRow(node);

                                    var data = new SamplerData(node);
                                    node.Tag = data;

                                    if (!samplers.ContainsKey(descriptorBind.sampler))
                                        samplers.Add(descriptorBind.sampler, data);
                                }
                            }
                            else
                            {
                                if (descriptorBind.res == ResourceId.Null)
                                {
                                    node = parentNodes.Add(new object[] {
                                        "", bindset, slotname, bindType, "-",
                                        "-",
                                        "",
                                        "",
                                    });

                                    EmptyRow(node);
                                }
                                else
                                {
                                    string typename = restype.Str() + " " + bindType.Str().Replace("&", "&&");

                                    string dim;

                                    if (restype == ShaderResourceType.Texture3D)
                                        dim = String.Format("{0}x{1}x{2}", w, h, d);
                                    else if (restype == ShaderResourceType.Texture1D || restype == ShaderResourceType.Texture1DArray)
                                        dim = w.ToString();
                                    else
                                        dim = String.Format("{0}x{1}", w, h);

                                    string arraydim = "-";

                                    if(restype == ShaderResourceType.Texture1DArray ||
                                       restype == ShaderResourceType.Texture2DArray ||
                                       restype == ShaderResourceType.Texture2DMSArray ||
                                       restype == ShaderResourceType.TextureCubeArray)
                                        arraydim = String.Format("{0}[{1}]", restype.Str(), a);

                                    if (restype == ShaderResourceType.Texture2DMS || restype == ShaderResourceType.Texture2DMSArray)
                                        dim += String.Format(", {0}x MSAA", samples);

                                    node = parentNodes.Add(new object[] {
                                        "", bindset, slotname, typename, name,
                                        dim,
                                        format,
                                        arraydim,
                                    });

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

                                    if (!filledSlot)
                                        EmptyRow(node);

                                    if (!usedSlot)
                                        InactiveRow(node);
                                }

                                if (bindType == ShaderBindType.ImageSampler)
                                {
                                    if (descriptorBind.sampler == ResourceId.Null)
                                    {
                                        node = parentNodes.Add(new object[] {
                                            "", bindset, slotname, bindType, "-",
                                            "-",
                                            "",
                                            "",
                                        });

                                        EmptyRow(node);
                                    }
                                    else
                                    {
                                        var texnode = node;

                                        if (!samplers.ContainsKey(descriptorBind.sampler))
                                        {
                                            node = parentNodes.Add(MakeSampler("", "", descriptorBind));

                                            if (!filledSlot)
                                                EmptyRow(node);

                                            if (!usedSlot)
                                                InactiveRow(node);

                                            var data = new SamplerData(node);
                                            node.Tag = data;

                                            samplers.Add(descriptorBind.sampler, data);
                                        }

                                        if (texnode != null)
                                        {
                                            m_CombinedImageSamplers[texnode] = samplers[descriptorBind.sampler].node;
                                            samplers[descriptorBind.sampler].images.Add(texnode);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }

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

            vs = cbuffers.VScrollValue();
            cbuffers.BeginUpdate();
            cbuffers.Nodes.Clear();
            for(int bindset = 0; bindset < pipe.DescSets.Length; bindset++)
            {
                for(int bind = 0; bind < pipe.DescSets[bindset].bindings.Length; bind++)
                {
                    ConstantBlock cblock = null;
                    BindpointMap bindMap = null;

                    uint slot = uint.MaxValue;
                    if (shaderDetails != null)
                    {
                        for (slot = 0; slot < (uint)shaderDetails.ConstantBlocks.Length; slot++)
                        {
                            ConstantBlock cb = shaderDetails.ConstantBlocks[slot];
                            if (stage.BindpointMapping.ConstantBlocks[cb.bindPoint].bindset == bindset &&
                                stage.BindpointMapping.ConstantBlocks[cb.bindPoint].bind == bind)
                            {
                                cblock = cb;
                                bindMap = stage.BindpointMapping.ConstantBlocks[cb.bindPoint];
                                break;
                            }
                        }

                        if (slot >= (uint)shaderDetails.ConstantBlocks.Length)
                            slot = uint.MaxValue;
                    }

                    var slotBinds = pipe.DescSets[bindset].bindings[bind].binds;
                    ShaderBindType bindType = pipe.DescSets[bindset].bindings[bind].type;
                    ShaderStageBits stageBits = pipe.DescSets[bindset].bindings[bind].stageFlags;

                    // skip descriptors that aren't for this shader stage
                    if (!stageBits.HasFlag((ShaderStageBits)(1 << (int)stage.stage)))
                        continue;

                    // these are treated as uniform buffers
                    if (bindType != ShaderBindType.ReadOnlyBuffer)
                        continue;

                    bool usedSlot = bindMap != null && bindMap.used;

                    // consider it filled if any array element is filled (or it's push constants)
                    bool filledSlot = cblock != null && !cblock.bufferBacked;
                    for (int idx = 0; idx < slotBinds.Length; idx++)
                        filledSlot |= slotBinds[idx].res != ResourceId.Null;

                    // show if
                    if (usedSlot || // it's referenced by the shader - regardless of empty or not
                        (showDisabled.Checked && !usedSlot && filledSlot) || // it's bound, but not referenced, and we have "show disabled"
                        (showEmpty.Checked && !filledSlot) // it's empty, and we have "show empty"
                        )
                    {
                        TreelistView.NodeCollection parentNodes = cbuffers.Nodes;

                        string setname = bindset.ToString();

                        string slotname = bind.ToString();
                        if (cblock != null && cblock.name.Length > 0)
                            slotname += ": " + cblock.name;

                        // for arrays, add a parent element that we add the real cbuffers below
                        if (slotBinds.Length > 1)
                        {
                            var node = parentNodes.Add(new object[] { "", setname, slotname, String.Format("Array[{0}]", slotBinds.Length), "", "" });

                            node.TreeColumn = 0;

                            if (!filledSlot)
                                EmptyRow(node);

                            if (!usedSlot)
                                InactiveRow(node);

                            parentNodes = node.Nodes;
                        }

                        for (int idx = 0; idx < slotBinds.Length; idx++)
                        {
                            var descriptorBind = slotBinds[idx];

                            if (slotBinds.Length > 1)
                            {
                                slotname = String.Format("{0}[{1}]", bind, idx);

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

                            string name = "UBO " + descriptorBind.res.ToString();
                            UInt64 length = descriptorBind.size;
                            int numvars = cblock != null ? cblock.variables.Length : 0;

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

                            for (int t = 0; t < bufs.Length; t++)
                                if (bufs[t].ID == descriptorBind.res)
                                    name = bufs[t].name;

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

                            string sizestr = String.Format("{0} Variables, {1} bytes", numvars, length);
                            string vecrange = String.Format("{0} - {1}", descriptorBind.offset, descriptorBind.offset + descriptorBind.size);

                            // push constants
                            if (cblock != null && !cblock.bufferBacked)
                            {
                                setname = "";
                                slotname = cblock.name;
                                name = "Push constants";
                                vecrange = "";
                                sizestr = String.Format("{0} Variables", numvars);

                                // could maybe get range from ShaderVariable.reg if it's filled out
                                // from SPIR-V side.
                            }

                            var node = parentNodes.Add(new object[] { "", setname, slotname, name, vecrange, sizestr });

                            node.Image = global::renderdocui.Properties.Resources.action;
                            node.HoverImage = global::renderdocui.Properties.Resources.action_hover;
                            node.Tag = new CBufferTag(slot, (uint)idx);

                            if (!filledSlot)
                                EmptyRow(node);

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

            // search for push constants and add them last
            if (shaderDetails != null)
            {
                for (int cb = 0; cb < shaderDetails.ConstantBlocks.Length; cb++)
                {
                    var cblock = shaderDetails.ConstantBlocks[cb];
                    if (cblock.bufferBacked == false)
                    {
                        // could maybe get range from ShaderVariable.reg if it's filled out
                        // from SPIR-V side.

                        var node = cbuffers.Nodes.Add(new object[] { "", "",
                        cblock.name, "Push constants",
                        "", String.Format("{0} Variables", cblock.variables.Length) });

                        node.Image = global::renderdocui.Properties.Resources.action;
                        node.HoverImage = global::renderdocui.Properties.Resources.action_hover;
                        node.Tag = new CBufferTag((uint)cb, 0);
                    }
                }
            }
            cbuffers.EndUpdate();
            cbuffers.NodesSelection.Clear();
            cbuffers.SetVScrollValue(vs);
        }