From 96dc252d4bb4dcc540249c8a62cc9a95a39788f2 Mon Sep 17 00:00:00 2001 From: baldurk Date: Tue, 7 Oct 2014 23:20:43 +0100 Subject: [PATCH] Add HTML export for D3D11 pipeline state viewer. Closes #92 --- .../D3D11PipelineStateViewer.Designer.cs | 30 +- .../PipelineState/D3D11PipelineStateViewer.cs | 930 ++++++++++++++++++ .../D3D11PipelineStateViewer.resx | 3 + 3 files changed, 958 insertions(+), 5 deletions(-) diff --git a/renderdocui/Windows/PipelineState/D3D11PipelineStateViewer.Designer.cs b/renderdocui/Windows/PipelineState/D3D11PipelineStateViewer.Designer.cs index 11482f7f3..3f63393c9 100644 --- a/renderdocui/Windows/PipelineState/D3D11PipelineStateViewer.Designer.cs +++ b/renderdocui/Windows/PipelineState/D3D11PipelineStateViewer.Designer.cs @@ -281,6 +281,7 @@ this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator(); this.showDisabledToolitem = new System.Windows.Forms.ToolStripButton(); this.showEmptyToolitem = new System.Windows.Forms.ToolStripButton(); + this.export = new System.Windows.Forms.ToolStripButton(); this.stageTabControl = new renderdocui.Controls.TablessControl(); this.tabIA = new System.Windows.Forms.TabPage(); this.panel1 = new System.Windows.Forms.Panel(); @@ -473,6 +474,7 @@ this.showDisabled = new System.Windows.Forms.ToolStripMenuItem(); this.showEmpty = new System.Windows.Forms.ToolStripMenuItem(); this.toolTip = new System.Windows.Forms.ToolTip(this.components); + this.exportDialog = new System.Windows.Forms.SaveFileDialog(); toolStripLabel1 = new System.Windows.Forms.ToolStripLabel(); toolstripTable = new System.Windows.Forms.TableLayoutPanel(); groupBox2 = new System.Windows.Forms.GroupBox(); @@ -677,7 +679,7 @@ this.flowLayoutPanel6.Controls.Add(this.toolStrip1); this.flowLayoutPanel6.Location = new System.Drawing.Point(3, 3); this.flowLayoutPanel6.Name = "flowLayoutPanel6"; - this.flowLayoutPanel6.Size = new System.Drawing.Size(335, 25); + this.flowLayoutPanel6.Size = new System.Drawing.Size(394, 25); this.flowLayoutPanel6.TabIndex = 1; // // toolStrip1 @@ -687,10 +689,11 @@ toolStripLabel1, this.toolStripSeparator2, this.showDisabledToolitem, - this.showEmptyToolitem}); + this.showEmptyToolitem, + this.export}); this.toolStrip1.Location = new System.Drawing.Point(0, 0); this.toolStrip1.Name = "toolStrip1"; - this.toolStrip1.Size = new System.Drawing.Size(335, 25); + this.toolStrip1.Size = new System.Drawing.Size(394, 25); this.toolStrip1.TabIndex = 0; this.toolStrip1.Text = "toolStrip1"; // @@ -719,6 +722,15 @@ this.showEmptyToolitem.Text = "Show Empty Items"; this.showEmptyToolitem.Click += new System.EventHandler(this.hideEmpty_Click); // + // export + // + this.export.Image = global::renderdocui.Properties.Resources.save; + this.export.ImageTransparentColor = System.Drawing.Color.Magenta; + this.export.Name = "export"; + this.export.Size = new System.Drawing.Size(59, 22); + this.export.Text = "Export"; + this.export.Click += new System.EventHandler(this.export_Click); + // // stageTabControl // this.stageTabControl.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) @@ -869,7 +881,7 @@ treeListColumn4, treeListColumn5, treeListColumn6}); - this.iabuffers.Cursor = System.Windows.Forms.Cursors.Arrow; + this.iabuffers.Cursor = System.Windows.Forms.Cursors.VSplit; this.iabuffers.Dock = System.Windows.Forms.DockStyle.Fill; this.iabuffers.GridLineColour = System.Drawing.SystemColors.ControlDark; this.iabuffers.Location = new System.Drawing.Point(3, 16); @@ -2517,7 +2529,7 @@ this.tableLayoutPanel8.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); this.tableLayoutPanel8.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); this.tableLayoutPanel8.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); - this.tableLayoutPanel8.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 598F)); + this.tableLayoutPanel8.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 599F)); this.tableLayoutPanel8.Controls.Add(this.forcedSampleCount, 7, 1); this.tableLayoutPanel8.Controls.Add(this.label14, 6, 1); this.tableLayoutPanel8.Controls.Add(this.frontCCW, 5, 0); @@ -4354,6 +4366,12 @@ this.showEmpty.Text = "Show Empty"; this.showEmpty.Click += new System.EventHandler(this.hideEmpty_Click); // + // exportDialog + // + this.exportDialog.DefaultExt = "html"; + this.exportDialog.Filter = "HTML Files (*.html)|*.html"; + this.exportDialog.Title = "Export pipeline state as HTML"; + // // D3D11PipelineStateViewer // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); @@ -4735,5 +4753,7 @@ private System.Windows.Forms.PictureBox iaBytecodeCog; private System.Windows.Forms.PictureBox meshView; private System.Windows.Forms.Label iaBytecodeMismatch; + private System.Windows.Forms.ToolStripButton export; + private System.Windows.Forms.SaveFileDialog exportDialog; } } \ No newline at end of file diff --git a/renderdocui/Windows/PipelineState/D3D11PipelineStateViewer.cs b/renderdocui/Windows/PipelineState/D3D11PipelineStateViewer.cs index 4e8281f15..e4ef42e6c 100644 --- a/renderdocui/Windows/PipelineState/D3D11PipelineStateViewer.cs +++ b/renderdocui/Windows/PipelineState/D3D11PipelineStateViewer.cs @@ -30,6 +30,7 @@ using System.Drawing; using System.Linq; using System.Text; using System.IO; +using System.Xml; using System.Windows.Forms; using WeifenLuo.WinFormsUI.Docking; using renderdocui.Code; @@ -2193,5 +2194,934 @@ namespace renderdocui.Windows.PipelineState hoverNode.DefaultBackColor = SystemColors.ControlLight; } } + + private void ExportHTMLTable(XmlTextWriter writer, string[] cols, object[][] rows) + { + writer.WriteStartElement("table"); + + { + writer.WriteStartElement("thead"); + writer.WriteStartElement("tr"); + + foreach (var col in cols) + { + writer.WriteStartElement("th"); + writer.WriteString(col); + writer.WriteEndElement(); + } + + writer.WriteEndElement(); + writer.WriteEndElement(); + } + + { + writer.WriteStartElement("tbody"); + + if (rows.Length == 0) + { + writer.WriteStartElement("tr"); + + for (int i = 0; i < cols.Length; i++) + { + writer.WriteStartElement("td"); + writer.WriteString("-"); + writer.WriteEndElement(); + } + + writer.WriteEndElement(); + } + else + { + foreach (var row in rows) + { + writer.WriteStartElement("tr"); + + foreach (var el in row) + { + writer.WriteStartElement("td"); + writer.WriteString(el.ToString()); + writer.WriteEndElement(); + } + + writer.WriteEndElement(); + } + } + + writer.WriteEndElement(); + } + + writer.WriteEndElement(); + } + + private void ExportHTMLTable(XmlTextWriter writer, string[] cols, object[] rows) + { + ExportHTMLTable(writer, cols, new object[][] { rows }); + } + + private object[] ExportViewHTML(D3D11PipelineState.ShaderStage.ResourceView view, int i, ShaderReflection refl, string extraParams) + { + FetchTexture[] texs = m_Core.CurTextures; + FetchBuffer[] bufs = m_Core.CurBuffers; + + ShaderResource shaderInput = null; + + if (refl != null) + { + foreach (var bind in refl.Resources) + { + if (bind.IsUAV && bind.bindPoint == i) + shaderInput = bind; + } + } + + string name = "Empty"; + string typename = "Unknown"; + string format = "Unknown"; + uint w = 0, h = 0, d = 0, a = 0; + + FetchTexture tex = null; + FetchBuffer buf = null; + + // check to see if it's a texture + for (int t = 0; t < texs.Length; t++) + { + if (texs[t].ID == view.Resource) + { + w = texs[t].width; + h = texs[t].height; + d = texs[t].depth; + a = texs[t].arraysize; + format = texs[t].format.ToString(); + name = texs[t].name; + typename = string.Format("Texture{0}D", texs[t].dimension); + if (texs[t].cubemap) + typename = "TexCube"; + + tex = texs[t]; + } + } + + // if not a texture, it must be a buffer + for (int t = 0; t < bufs.Length; t++) + { + if (bufs[t].ID == view.Resource) + { + w = bufs[t].length; + h = 0; + d = 0; + a = 0; + format = view.Format.ToString(); + name = bufs[t].name; + typename = "Buffer"; + + // for structured buffers, display how many 'elements' there are in the buffer + if (bufs[t].structureSize > 0) + typename = "StructuredBuffer[" + (bufs[t].length / bufs[t].structureSize) + "]"; + + if (view.Structured) + { + typename += " (Count: " + view.BufferStructCount + ")"; + } + + if (shaderInput != null && !shaderInput.IsTexture) + { + if (view.Format.compType == FormatComponentType.None) + { + if (shaderInput.variableType.members.Length > 0) + format = "struct " + shaderInput.variableType.Name; + else + format = shaderInput.variableType.Name; + } + else + { + format = view.Format.ToString(); + } + } + + buf = bufs[t]; + } + } + + string viewParams = ""; + + if(buf != null) + { + viewParams = String.Format("First Element: {0}, Num Elements {1}, Flags {2}", view.FirstElement, view.NumElements, view.Flags); + } + + if (tex != null) + { + if(tex.mips > 1) + viewParams = String.Format("Highest Mip: {0}, Num Mips: {1}", view.HighestMip, view.NumMipLevels); + + if (tex.arraysize > 1) + { + if (viewParams.Length > 0) + viewParams += ", "; + viewParams += String.Format("First Slice: {0}, Array Size: {1}", view.FirstArraySlice, view.ArraySize); + } + } + + if (viewParams == "") + viewParams = extraParams; + else + viewParams += ", " + extraParams; + + return new object[] { + i, name, + view.Type, typename, + w, h, d, a, + view.Format, format, + viewParams }; + } + + private void ExportHTML(XmlTextWriter writer, D3D11PipelineState.InputAssembler ia) + { + FetchBuffer[] bufs = m_Core.CurBuffers; + + { + writer.WriteStartElement("h3"); + writer.WriteString("Input Layouts"); + writer.WriteEndElement(); + + List rows = new List(); + + int i = 0; + foreach (var l in ia.layouts) + { + rows.Add(new object[] { i, l.SemanticName, l.SemanticIndex, l.Format, l.InputSlot, l.ByteOffset, l.PerInstance, l.InstanceDataStepRate }); + + i++; + } + + ExportHTMLTable(writer, new string[] { "Slot", "Semantic Name", "Semantic Index", "Format", "Input Slot", "Byte Offset", "Per Instance", "Instance Data Step Rate" }, rows.ToArray()); + } + + { + writer.WriteStartElement("h3"); + writer.WriteString("Vertex Buffers"); + writer.WriteEndElement(); + + List rows = new List(); + + int i = 0; + foreach (var vb in ia.vbuffers) + { + string name = "Buffer " + vb.Buffer.ToString(); + UInt32 length = 0; + + if (vb.Buffer == ResourceId.Null) + { + continue; + } + else + { + for (int t = 0; t < bufs.Length; t++) + { + if (bufs[t].ID == vb.Buffer) + { + name = bufs[t].name; + length = bufs[t].length; + } + } + } + + rows.Add(new object[] { i, name, vb.Stride, vb.Offset, length }); + + i++; + } + + ExportHTMLTable(writer, new string[] { "Slot", "Buffer", "Stride", "Offset", "Byte Length" }, rows.ToArray()); + } + + { + writer.WriteStartElement("h3"); + writer.WriteString("Index Buffer"); + writer.WriteEndElement(); + + string name = "Buffer " + ia.ibuffer.Buffer.ToString(); + UInt32 length = 0; + + if (ia.ibuffer.Buffer == ResourceId.Null) + { + name = "Empty"; + } + else + { + for (int t = 0; t < bufs.Length; t++) + { + if (bufs[t].ID == ia.ibuffer.Buffer) + { + name = bufs[t].name; + length = bufs[t].length; + } + } + } + + ExportHTMLTable(writer, new string[] { "Buffer", "Format", "Offset", "Byte Length" }, + new object[] { name, ia.ibuffer.Format.ToString(), ia.ibuffer.Offset.ToString(), length.ToString() }); + } + + writer.WriteStartElement("p"); + writer.WriteEndElement(); + + ExportHTMLTable(writer, new string[] { "Primitive Topology" }, new object[] { ia.Topology.Str() }); + } + + private void ExportHTML(XmlTextWriter writer, D3D11PipelineState.ShaderStage sh) + { + FetchBuffer[] bufs = m_Core.CurBuffers; + + { + writer.WriteStartElement("h3"); + writer.WriteString("Shader"); + writer.WriteEndElement(); + + ShaderReflection shaderDetails = sh.ShaderDetails; + + 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 + "()" + " - " + + Path.GetFileName(shaderDetails.DebugInfo.files[0].filename); + + writer.WriteStartElement("p"); + writer.WriteString(shadername); + writer.WriteEndElement(); + + if (sh.Shader == ResourceId.Null) + return; + } + + { + writer.WriteStartElement("h3"); + writer.WriteString("Resources"); + writer.WriteEndElement(); + + List rows = new List(); + + for (int i = 0; i < sh.SRVs.Length; i++) + { + if (sh.SRVs[i].View == ResourceId.Null) continue; + + rows.Add(ExportViewHTML(sh.SRVs[i], i, null, "")); + } + + ExportHTMLTable(writer, + new string[] { + "Slot", "Name", + "View Type", "Resource Type", + "Width", "Height", "Depth", "Array Size", + "View Format", "Resource Format", + "View Parameters", + }, + rows.ToArray()); + } + + if (sh.stage == ShaderStageType.Compute) + { + writer.WriteStartElement("h3"); + writer.WriteString("Unordered Access Views"); + writer.WriteEndElement(); + + List rows = new List(); + + for (int i = 0; i < sh.UAVs.Length; i++) + { + if (sh.UAVs[i].View == ResourceId.Null) continue; + + rows.Add(ExportViewHTML(sh.UAVs[i], i, null, "")); + } + + ExportHTMLTable(writer, + new string[] { + "Slot", "Name", + "View Type", "Resource Type", + "Width", "Height", "Depth", "Array Size", + "View Format", "Resource Format", + "View Parameters", + }, + rows.ToArray()); + } + + { + writer.WriteStartElement("h3"); + writer.WriteString("Samplers"); + writer.WriteEndElement(); + + List rows = new List(); + + for (int i = 0; i < sh.Samplers.Length; i++) + { + D3D11PipelineState.ShaderStage.Sampler s = sh.Samplers[i]; + + if (s.Samp == ResourceId.Null) continue; + + string borderColor = s.BorderColor[0].ToString() + ", " + + s.BorderColor[1].ToString() + ", " + + s.BorderColor[2].ToString() + ", " + + s.BorderColor[3].ToString(); + + string addressing = ""; + + string addPrefix = ""; + string addVal = ""; + + string[] addr = { s.AddressU, s.AddressV, s.AddressW }; + + // arrange like either UVW: WRAP or UV: WRAP, W: CLAMP + for (int a = 0; a < 3; a++) + { + string prefix = "" + "UVW"[a]; + + if (a == 0 || addr[a] == addr[a - 1]) + { + addPrefix += prefix; + } + else + { + addressing += addPrefix + ": " + addVal + ", "; + + addPrefix = prefix; + } + addVal = addr[a]; + } + + addressing += addPrefix + ": " + addVal; + + rows.Add(new object[] { i, addressing, borderColor, + s.Comparison, s.Filter, s.MaxAniso.ToString(), + s.MinLOD == -float.MaxValue ? "-FLT_MAX" : s.MinLOD.ToString(), + s.MaxLOD == float.MaxValue ? "FLT_MAX" : s.MaxLOD.ToString(), + s.MipLODBias.ToString() }); + } + + ExportHTMLTable(writer, + new string[] { + "Slot", "Addressing", "Border Colour", + "Comparison", "Filter", "Max Anisotropy", + "Min LOD", "Max LOD", "Mip Bias", + }, + rows.ToArray()); + } + + { + writer.WriteStartElement("h3"); + writer.WriteString("Constant Buffers"); + writer.WriteEndElement(); + + List rows = new List(); + + for (int i = 0; i < sh.ConstantBuffers.Length; i++) + { + ConstantBlock shaderCBuf = null; + + if (sh.ConstantBuffers[i].Buffer == ResourceId.Null) continue; + + if (sh.ShaderDetails != null && i < sh.ShaderDetails.ConstantBlocks.Length && + sh.ShaderDetails.ConstantBlocks[i].name.Length > 0) + shaderCBuf = sh.ShaderDetails.ConstantBlocks[i]; + + string name = "Constant Buffer " + sh.ConstantBuffers[i].Buffer.ToString(); + UInt32 length = 1; + int numvars = shaderCBuf != null ? shaderCBuf.variables.Length : 0; + + if (sh.ConstantBuffers[i].Buffer == ResourceId.Null) + { + name = "Empty"; + length = 0; + } + + for (int t = 0; t < bufs.Length; t++) + { + if (bufs[t].ID == sh.ConstantBuffers[i].Buffer) + { + name = bufs[t].name; + length = bufs[t].length; + } + } + + rows.Add(new object[] { i, name, sh.ConstantBuffers[i].VecOffset, sh.ConstantBuffers[i].VecCount, numvars, length }); + } + + ExportHTMLTable(writer, new string[] { "Slot", "Buffer", "Vector Offset", "Vector Count", "Number of Variables", "Byte Length", }, rows.ToArray()); + } + + if (sh.ClassInstances.Length > 0) + { + writer.WriteStartElement("h3"); + writer.WriteString("Class Instances"); + writer.WriteEndElement(); + + List rows = new List(); + + for (int i = 0; i < sh.ClassInstances.Length; i++) + { + string interfaceName = String.Format("Interface {0}", i); + + if (sh.ShaderDetails != null && i < sh.ShaderDetails.Interfaces.Length) + interfaceName = sh.ShaderDetails.Interfaces[i].Name; + + rows.Add(new object[] { i, interfaceName, sh.ClassInstances[i] }); + } + + ExportHTMLTable(writer, new string[] { "Slot", "Interface Name", "Instance Name", }, rows.ToArray()); + } + } + + private void ExportHTML(XmlTextWriter writer, D3D11PipelineState.Streamout so) + { + FetchBuffer[] bufs = m_Core.CurBuffers; + + { + writer.WriteStartElement("h3"); + writer.WriteString("Stream Out Targets"); + writer.WriteEndElement(); + + List rows = new List(); + + int i = 0; + foreach (var o in so.Outputs) + { + string name = "Buffer " + o.Buffer.ToString(); + UInt32 length = 0; + + if (o.Buffer == ResourceId.Null) + { + name = "Empty"; + } + else + { + for (int t = 0; t < bufs.Length; t++) + { + if (bufs[t].ID == o.Buffer) + { + name = bufs[t].name; + length = bufs[t].length; + } + } + } + + rows.Add(new object[] { i, name, o.Offset, length }); + + i++; + } + + ExportHTMLTable(writer, new string[] { "Slot", "Buffer", "Offset", "Byte Length" }, rows.ToArray()); + } + } + + private void ExportHTML(XmlTextWriter writer, D3D11PipelineState.Rasterizer rs) + { + { + writer.WriteStartElement("h3"); + writer.WriteString("States"); + writer.WriteEndElement(); + + ExportHTMLTable(writer, + new string[] { "Fill Mode", "Cull Mode", "Front CCW" }, + new object[] { rs.m_State.FillMode, rs.m_State.CullMode, rs.m_State.FrontCCW ? "Yes" : "No" }); + + writer.WriteStartElement("p"); + writer.WriteEndElement(); + + ExportHTMLTable(writer, + new string[] { "Scissor Enable", "Line AA Enable", "Multisample Enable", "Forced Sample Count" }, + new object[] { rs.m_State.ScissorEnable ? "Yes" : "No", rs.m_State.AntialiasedLineEnable ? "Yes" : "No", + rs.m_State.MultisampleEnable ? "Yes" : "No", rs.m_State.ForcedSampleCount }); + + writer.WriteStartElement("p"); + writer.WriteEndElement(); + + ExportHTMLTable(writer, + new string[] { "Depth Clip", "Depth Bias", "Depth Bias Clamp", "Slope Scaled Bias" }, + new object[] { rs.m_State.DepthClip ? "Yes" : "No", rs.m_State.DepthBias, + Formatter.Format(rs.m_State.DepthBiasClamp), Formatter.Format(rs.m_State.SlopeScaledDepthBias)}); + } + + { + writer.WriteStartElement("h3"); + writer.WriteString("Viewports"); + writer.WriteEndElement(); + + List rows = new List(); + + int i = 0; + foreach (var v in rs.Viewports) + { + if (v.Width == v.Height && v.Width == 0 && v.Height == 0) continue; + + rows.Add(new object[] { i, v.TopLeft[0], v.TopLeft[1], 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 rows = new List(); + + int i = 0; + foreach (var s in rs.Scissors) + { + if (s.right == 0 && s.bottom == 0) continue; + + rows.Add(new object[] { i, s.left, s.top, s.right - s.left, s.bottom - s.top }); + + i++; + } + + ExportHTMLTable(writer, new string[] { "Slot", "X", "Y", "Width", "Height" }, rows.ToArray()); + } + } + + private void ExportHTML(XmlTextWriter writer, D3D11PipelineState.OutputMerger om) + { + { + writer.WriteStartElement("h3"); + writer.WriteString("Blend State"); + writer.WriteEndElement(); + + var blendFactor = om.m_BlendState.BlendFactor[0].ToString("F2") + ", " + + om.m_BlendState.BlendFactor[1].ToString("F2") + ", " + + om.m_BlendState.BlendFactor[2].ToString("F2") + ", " + + om.m_BlendState.BlendFactor[3].ToString("F2"); + + ExportHTMLTable(writer, + new string[] { "Independent Blend Enable", "Alpha to Coverage", "Sample Mask", "Blend Factor" }, + new object[] { om.m_BlendState.IndependentBlend ? "Yes" : "No", om.m_BlendState.AlphaToCoverage ? "Yes" : "No", + om.m_BlendState.SampleMask.ToString("X8"), blendFactor, }); + + writer.WriteStartElement("h3"); + writer.WriteString("Target Blends"); + writer.WriteEndElement(); + + List rows = new List(); + + int i = 0; + foreach (var b in om.m_BlendState.Blends) + { + if (!b.Enabled) continue; + + rows.Add(new object[] { + i, + b.Enabled ? "Yes" : "No", b.LogicEnabled ? "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.LogicOp, b.WriteMask }); + + i++; + } + + ExportHTMLTable(writer, + new string[] { + "Slot", + "Blend Enable", "Logic Enable", + "Blend Source", "Blend Destination", "Blend Operation", + "Alpha Blend Source", "Alpha Blend Destination", "Alpha Blend Operation", + "Logic Operation", "Write Mask", + }, + rows.ToArray()); + } + + { + writer.WriteStartElement("h3"); + writer.WriteString("Depth State"); + writer.WriteEndElement(); + + ExportHTMLTable(writer, + new string[] { "Depth Test Enable", "Depth Writes Enable", "Depth Function" }, + new object[] { om.m_State.DepthEnable ? "Yes" : "No", om.m_State.DepthWrites ? "Yes" : "No", om.m_State.DepthFunc }); + } + + { + writer.WriteStartElement("h3"); + writer.WriteString("Render targets"); + writer.WriteEndElement(); + + List rows = new List(); + + for (int i = 0; i < om.RenderTargets.Length; i++) + { + if (om.RenderTargets[i].View == ResourceId.Null) continue; + + rows.Add(ExportViewHTML(om.RenderTargets[i], i, null, "")); + } + + ExportHTMLTable(writer, + new string[] { + "Slot", "Name", + "View Type", "Resource Type", + "Width", "Height", "Depth", "Array Size", + "View Format", "Resource Format", + "View Parameters", + }, + rows.ToArray()); + } + + if(om.UAVs.Length > 0 && om.UAVs[0].View != ResourceId.Null) + { + writer.WriteStartElement("h3"); + writer.WriteString("Unordered Access Views"); + writer.WriteEndElement(); + + List rows = new List(); + + int i = 0; + + for (; i < om.UAVStartSlot; i++) + rows.Add(new object[] { i, "Empty", "", "", "", "", 0, 0, 0, 0, "", "", "" }); + + for (; i < om.RenderTargets.Length; i++) + { + if (om.UAVs[i - om.UAVStartSlot].View == ResourceId.Null) continue; + + rows.Add(ExportViewHTML(om.UAVs[i - om.UAVStartSlot], i, m_Core.CurD3D11PipelineState.m_PS.ShaderDetails, "")); + } + + ExportHTMLTable(writer, + new string[] { + "Slot", "Name", + "View Type", "Resource Type", + "Width", "Height", "Depth", "Array Size", + "View Format", "Resource Format", + "View Parameters", + }, + rows.ToArray()); + } + + { + writer.WriteStartElement("h3"); + writer.WriteString("Depth target"); + writer.WriteEndElement(); + + List rows = new List(); + + string extra = ""; + + if(om.DepthReadOnly && om.StencilReadOnly) + extra = "Depth & Stencil Read-Only"; + else if (om.DepthReadOnly) + extra = "Depth Read-Only"; + else if (om.StencilReadOnly) + extra = "Stencil Read-Only"; + + rows.Add(ExportViewHTML(om.DepthTarget, 0, null, extra)); + + ExportHTMLTable(writer, + new string[] { + "Slot", "Name", + "View Type", "Resource Type", + "Width", "Height", "Depth", "Array Size", + "View Format", "Resource Format", + "View Parameters", + }, + rows.ToArray()); + } + } + + private void ExportHTML(string filename) + { + using (Stream s = new FileStream(filename, FileMode.Create)) + { + StreamWriter sw = new StreamWriter(s); + + sw.WriteLine(""); + + using (XmlTextWriter writer = new XmlTextWriter(sw)) + { + writer.Formatting = Formatting.Indented; + writer.Indentation = 4; + + writer.WriteStartElement("html"); + writer.WriteAttributeString("lang", "en"); + + var title = String.Format("{0} EID {1} - D3D11 Pipeline export", Path.GetFileName(m_Core.LogFileName), m_Core.CurEvent); + + var css = @" +/* If you think this css is ugly/bad, open a pull request! */ +body { margin: 20px; } +div.stage { border: 1px solid #BBBBBB; border-radius: 5px; padding: 16px; margin-bottom: 32px; } +div.stage h1 { text-decoration: underline; margin-top: 0px; } +div.stage table { border: 1px solid #AAAAAA; border-collapse: collapse; } +div.stage table thead tr { border-bottom: 1px solid #AAAAAA; background-color: #EEEEFF; } +div.stage table tr th { border-right: 1px solid #AAAAAA; padding: 6px; } +div.stage table tr td { border-right: 1px solid #AAAAAA; background-color: #EEEEEE; padding: 3px; } +"; + + { + writer.WriteStartElement("head"); + + writer.WriteStartElement("meta"); + writer.WriteAttributeString("charset", "utf-8"); + writer.WriteEndElement(); + + writer.WriteStartElement("meta"); + writer.WriteAttributeString("http-equiv", "X-UA-Compatible"); + writer.WriteAttributeString("content", "IE=edge"); + writer.WriteEndElement(); + + writer.WriteStartElement("meta"); + writer.WriteAttributeString("name", "viewport"); + writer.WriteAttributeString("content", "width=device-width, initial-scale=1"); + writer.WriteEndElement(); + + writer.WriteStartElement("meta"); + writer.WriteAttributeString("name", "description"); + writer.WriteAttributeString("content", ""); + writer.WriteEndElement(); + + writer.WriteStartElement("meta"); + writer.WriteAttributeString("name", "author"); + writer.WriteAttributeString("content", ""); + writer.WriteEndElement(); + + writer.WriteStartElement("meta"); + writer.WriteAttributeString("http-equiv", "Content-Type"); + writer.WriteAttributeString("content", "text/html;charset=utf-8"); + writer.WriteEndElement(); + + writer.WriteStartElement("title"); + writer.WriteString(title); + writer.WriteEndElement(); + + writer.WriteStartElement("style"); + writer.WriteComment(css); + writer.WriteEndElement(); + + writer.WriteEndElement(); // head + } + + { + writer.WriteStartElement("body"); + + writer.WriteStartElement("h1"); + writer.WriteString(title); + writer.WriteEndElement(); + + writer.WriteStartElement("h3"); + { + var context = String.Format("Frame {0}", m_Core.FrameInfo[m_Core.CurFrame].frameNumber); + + FetchDrawcall draw = m_Core.CurDrawcall; + + var drawstack = new List(); + var parent = draw.parent; + while (parent != null) + { + drawstack.Add(parent); + parent = parent.parent; + } + + drawstack.Reverse(); + + foreach (var d in drawstack) + { + context += String.Format(" > {0}", d.name); + } + + FetchDrawcall prev = null; + + for (uint i = draw.events[0].eventID; i <= draw.events.Last().eventID; i++) + { + prev = m_Core.GetDrawcall(m_Core.CurFrame, i); + if (prev != null) + break; + } + + if (prev != null) + context += String.Format(" => {0}, {1}", prev.name, draw.name); + else + context += String.Format(" => {0}", draw.name); + + writer.WriteString(context); + } + writer.WriteEndElement(); // h2 context + + string[] stageNames = new string[]{ + "Input Assembler", + "Vertex Shader", + "Hull Shader", + "Domain Shader", + "Geometry Shader", + "Stream Out", + "Rasterizer", + "Pixel Shader", + "Output Merger", + "Compute Shader", + }; + + string[] stageAbbrevs = new string[]{ + "IA", "VS", "HS", "DS", "GS", "SO", "RS", "PS", "OM", "CS" + }; + + int stage=0; + foreach(var sn in stageNames) + { + writer.WriteStartElement("div"); + writer.WriteStartElement("a"); + writer.WriteAttributeString("name", stageAbbrevs[stage]); + writer.WriteEndElement(); + writer.WriteEndElement(); + + writer.WriteStartElement("div"); + writer.WriteAttributeString("class", "stage"); + + writer.WriteStartElement("h1"); + writer.WriteString(sn); + writer.WriteEndElement(); + + switch(stage) + { + case 0: ExportHTML(writer, m_Core.CurD3D11PipelineState.m_IA); break; + case 1: ExportHTML(writer, m_Core.CurD3D11PipelineState.m_VS); break; + case 2: ExportHTML(writer, m_Core.CurD3D11PipelineState.m_HS); break; + case 3: ExportHTML(writer, m_Core.CurD3D11PipelineState.m_DS); break; + case 4: ExportHTML(writer, m_Core.CurD3D11PipelineState.m_GS); break; + case 5: ExportHTML(writer, m_Core.CurD3D11PipelineState.m_SO); break; + case 6: ExportHTML(writer, m_Core.CurD3D11PipelineState.m_RS); break; + case 7: ExportHTML(writer, m_Core.CurD3D11PipelineState.m_PS); break; + case 8: ExportHTML(writer, m_Core.CurD3D11PipelineState.m_OM); break; + case 9: ExportHTML(writer, m_Core.CurD3D11PipelineState.m_CS); break; + } + + writer.WriteEndElement(); + + stage++; + } + + writer.WriteEndElement(); // body + } + + writer.WriteEndElement(); // html + + writer.Close(); + } + + sw.Dispose(); + } + } + + private void export_Click(object sender, EventArgs e) + { + if (!m_Core.LogLoaded) return; + + DialogResult res = exportDialog.ShowDialog(); + + if (res == DialogResult.OK) + { + ExportHTML(exportDialog.FileName); + } + } } } \ No newline at end of file diff --git a/renderdocui/Windows/PipelineState/D3D11PipelineStateViewer.resx b/renderdocui/Windows/PipelineState/D3D11PipelineStateViewer.resx index 8231be6b3..1de86208f 100644 --- a/renderdocui/Windows/PipelineState/D3D11PipelineStateViewer.resx +++ b/renderdocui/Windows/PipelineState/D3D11PipelineStateViewer.resx @@ -300,4 +300,7 @@ 17, 17 + + 324, 17 + \ No newline at end of file