// Set a shader stage's resources and values private void SetShaderState(FetchTexture[] texs, FetchBuffer[] bufs, D3D11PipelineState.ShaderStage stage, Label shader, TreelistView.TreeListView resources, TreelistView.TreeListView samplers, TreelistView.TreeListView cbuffers, TreelistView.TreeListView classes) { ShaderReflection shaderDetails = stage.ShaderDetails; if (stage.Shader == ResourceId.Null) shader.Text = "Unbound"; else shader.Text = stage.ShaderName; if (shaderDetails != null && shaderDetails.DebugInfo.entryFunc.Length > 0 && shaderDetails.DebugInfo.files.Length > 0) { string shaderfn = ""; int entryFile = shaderDetails.DebugInfo.entryFile; if (entryFile < 0 || entryFile >= shaderDetails.DebugInfo.files.Length) entryFile = 0; shaderfn = shaderDetails.DebugInfo.files[entryFile].BaseFilename; shader.Text = shaderDetails.DebugInfo.entryFunc + "()" + " - " + shaderfn; } int vs = 0; vs = resources.VScrollValue(); resources.BeginUpdate(); resources.Nodes.Clear(); if (stage.SRVs != null) { int i = 0; foreach (var r in stage.SRVs) { ShaderResource shaderInput = null; if (shaderDetails != null) { foreach (var bind in shaderDetails.ReadOnlyResources) { if (bind.IsSRV && bind.bindPoint == i) { shaderInput = bind; break; } } } bool filledSlot = (r.Resource != ResourceId.Null); bool usedSlot = (shaderInput != null); // show if if (usedSlot || // it's referenced by the shader - regardless of empty or not (showDisabled.Checked && !usedSlot && filledSlot) || // it's bound, but not referenced, and we have "show disabled" (showEmpty.Checked && !filledSlot) // it's empty, and we have "show empty" ) { string slotname = i.ToString(); if (shaderInput != null && shaderInput.name.Length > 0) slotname += ": " + shaderInput.name; UInt64 w = 1; UInt32 h = 1, d = 1; UInt32 a = 1; string format = "Unknown"; string name = "Shader Resource " + r.Resource.ToString(); string typename = "Unknown"; object tag = null; bool viewDetails = false; if (!filledSlot) { name = "Empty"; format = "-"; typename = "-"; w = h = d = a = 0; } // check to see if it's a texture for (int t = 0; t < texs.Length; t++) { if (texs[t].ID == r.Resource) { w = texs[t].width; h = texs[t].height; d = texs[t].depth; a = texs[t].arraysize; format = texs[t].format.ToString(); name = texs[t].name; typename = texs[t].resType.Str(); if (texs[t].resType == ShaderResourceType.Texture2DMS || texs[t].resType == ShaderResourceType.Texture2DMSArray) { typename += String.Format(" {0}x", texs[t].msSamp); } // if it's a typeless format, show the format of the view if (texs[t].format != r.Format) { format = "Viewed as " + r.Format.ToString(); } tag = new ViewTexTag(r, texs[t]); if (HasImportantViewParams(r, texs[t])) viewDetails = true; } } // if not a texture, it must be a buffer for (int t = 0; t < bufs.Length; t++) { if (bufs[t].ID == r.Resource) { w = bufs[t].length; h = 0; d = 0; a = 0; format = ""; name = bufs[t].name; typename = "Buffer"; // for structured buffers, display how many 'elements' there are in the buffer if (r.ElementSize > 0) { typename = "StructuredBuffer[" + (bufs[t].length / r.ElementSize) + "]"; } else if (r.Flags.HasFlag(D3D11BufferViewFlags.Raw)) { typename = "ByteAddressBuffer"; } if (r.Flags.HasFlag(D3D11BufferViewFlags.Append) || r.Flags.HasFlag(D3D11BufferViewFlags.Counter)) { typename += " (Count: " + r.BufferStructCount + ")"; } // get the buffer type, whether it's just a basic type or a complex struct if (shaderInput != null && !shaderInput.IsTexture) { if (r.Format.compType == FormatComponentType.None) { if (shaderInput.variableType.members.Length > 0) format = "struct " + shaderInput.variableType.Name; else format = shaderInput.variableType.Name; } else { format = r.Format.ToString(); } } tag = new ViewBufTag(r, bufs[t]); if (HasImportantViewParams(r, bufs[t])) viewDetails = true; } } var node = resources.Nodes.Add(new object[] { slotname, name, typename, w, h, d, a, format }); node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; node.Tag = tag; if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); if (viewDetails) ViewDetailsRow(node); } i++; } } resources.EndUpdate(); resources.NodesSelection.Clear(); resources.SetVScrollValue(vs); vs = samplers.VScrollValue(); samplers.BeginUpdate(); samplers.Nodes.Clear(); if (stage.Samplers != null) { int i = 0; foreach (var s in stage.Samplers) { ShaderResource shaderInput = null; if (shaderDetails != null) { foreach (var bind in shaderDetails.ReadOnlyResources) { if (bind.IsSampler && bind.bindPoint == i) { shaderInput = bind; break; } } } bool filledSlot = (s.AddressU.Length > 0); bool usedSlot = (shaderInput != null); // show if if (usedSlot || // it's referenced by the shader - regardless of empty or not (showDisabled.Checked && !usedSlot && filledSlot) || // it's bound, but not referenced, and we have "show disabled" (showEmpty.Checked && !filledSlot) // it's empty, and we have "show empty" ) { string slotname = i.ToString(); if (shaderInput != null && shaderInput.name.Length > 0) slotname += ": " + shaderInput.name; if (s.customSamplerName) slotname += "(" + s.SamplerName + ")"; string borderColor = s.BorderColor[0].ToString() + ", " + s.BorderColor[1].ToString() + ", " + s.BorderColor[2].ToString() + ", " + s.BorderColor[3].ToString(); string addressing = ""; string addPrefix = ""; string addVal = ""; string[] addr = { s.AddressU, s.AddressV, s.AddressW }; // arrange like either UVW: WRAP or UV: WRAP, W: CLAMP for (int a = 0; a < 3; a++) { string prefix = "" + "UVW"[a]; if (a == 0 || addr[a] == addr[a - 1]) { addPrefix += prefix; } else { addressing += addPrefix + ": " + addVal + ", "; addPrefix = prefix; } addVal = addr[a]; } addressing += addPrefix + ": " + addVal; if(s.UseBorder) addressing += String.Format("<{0}>", borderColor); string filter = s.Filter; if (s.MaxAniso > 0) filter += String.Format(" {0}x", s.MaxAniso); if (s.UseComparison) filter += String.Format(" ({0})", s.Comparison); var node = samplers.Nodes.Add(new object[] { slotname, addressing, filter, (s.MinLOD == -float.MaxValue ? "0" : s.MinLOD.ToString()) + " - " + (s.MaxLOD == float.MaxValue ? "FLT_MAX" : s.MaxLOD.ToString()), s.MipLODBias.ToString() }); if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); } i++; } } samplers.EndUpdate(); samplers.NodesSelection.Clear(); samplers.SetVScrollValue(vs); vs = cbuffers.VScrollValue(); cbuffers.BeginUpdate(); cbuffers.Nodes.Clear(); if (stage.ConstantBuffers != null) { UInt32 i = 0; foreach (var b in stage.ConstantBuffers) { ConstantBlock shaderCBuf = null; if (shaderDetails != null && i < shaderDetails.ConstantBlocks.Length && shaderDetails.ConstantBlocks[i].name.Length > 0) shaderCBuf = shaderDetails.ConstantBlocks[i]; bool filledSlot = (b.Buffer != ResourceId.Null); bool usedSlot = (shaderCBuf != null); // show if if (usedSlot || // it's referenced by the shader - regardless of empty or not (showDisabled.Checked && !usedSlot && filledSlot) || // it's bound, but not referenced, and we have "show disabled" (showEmpty.Checked && !filledSlot) // it's empty, and we have "show empty" ) { string name = "Constant Buffer " + b.Buffer.ToString(); UInt64 length = 1; int numvars = shaderCBuf != null ? shaderCBuf.variables.Length : 0; UInt32 byteSize = shaderCBuf != null ? shaderCBuf.byteSize : 0; if (!filledSlot) { name = "Empty"; length = 0; } for (int t = 0; t < bufs.Length; t++) { if (bufs[t].ID == b.Buffer) { name = bufs[t].name; length = bufs[t].length; } } string slotname = i.ToString(); if (shaderCBuf != null && shaderCBuf.name.Length > 0) slotname += ": " + shaderCBuf.name; string sizestr; if (byteSize == length) sizestr = String.Format("{0} Variables, {1} bytes", numvars, length); else sizestr = String.Format("{0} Variables, {1} bytes needed, {2} provided", numvars, byteSize, length); if (length < byteSize) filledSlot = false; string vecrange = String.Format("{0} - {1}", b.VecOffset, b.VecOffset + b.VecCount); var node = cbuffers.Nodes.Add(new object[] { slotname, name, vecrange, sizestr }); node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; node.Tag = i; if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); } i++; } } cbuffers.EndUpdate(); cbuffers.NodesSelection.Clear(); cbuffers.SetVScrollValue(vs); vs = classes.VScrollValue(); classes.BeginUpdate(); classes.Nodes.Clear(); { UInt32 i = 0; foreach (var inst in stage.ClassInstances) { string interfaceName = String.Format("Interface {0}", i); if (shaderDetails != null && i < shaderDetails.Interfaces.Length) interfaceName = shaderDetails.Interfaces[i].Name; classes.Nodes.Add(new object[] { i.ToString(), interfaceName, inst }); i++; } } classes.EndUpdate(); classes.NodesSelection.Clear(); classes.SetVScrollValue(vs); classes.Visible = classes.Parent.Visible = (stage.ClassInstances.Length > 0); }
private void UpdateState() { if (!m_Core.LogLoaded) return; FetchTexture[] texs = m_Core.CurTextures; FetchBuffer[] bufs = m_Core.CurBuffers; D3D11PipelineState state = m_Core.CurD3D11PipelineState; FetchDrawcall draw = m_Core.CurDrawcall; HideViewDetailsTooltip(); m_ViewDetailNodes.Clear(); var tick = global::renderdocui.Properties.Resources.tick; var cross = global::renderdocui.Properties.Resources.cross; bool[] usedVBuffers = new bool[128]; UInt32[] layoutOffs = new UInt32[128]; for (int i = 0; i < 128; i++) { usedVBuffers[i] = false; layoutOffs[i] = 0; } //////////////////////////////////////////////// // Input Assembler if(state.m_IA.Bytecode == null) iaBytecode.Text = "None"; else iaBytecode.Text = state.m_IA.LayoutName; if (state.m_IA.Bytecode != null && state.m_IA.Bytecode.DebugInfo != null && state.m_IA.Bytecode.DebugInfo.entryFunc.Length > 0) iaBytecode.Text += " (" + state.m_IA.Bytecode.DebugInfo.entryFunc + ")"; iaBytecodeMismatch.Text = ""; iaBytecodeMismatch.Visible = false; // check for IA-VS mismatches here. // This should be moved to a "Render Doctor" window reporting problems if (state.m_IA.Bytecode != null && state.m_VS.ShaderDetails != null) { string mismatchDetails = ""; // VS wants more elements if (state.m_IA.Bytecode.InputSig.Length < state.m_VS.ShaderDetails.InputSig.Length) { int excess = state.m_VS.ShaderDetails.InputSig.Length - state.m_IA.Bytecode.InputSig.Length; bool allSystem = true; // The VS signature can consume more elements as long as they are all system value types // (ie. SV_VertexID or SV_InstanceID) for (int e = 0; e < excess; e++) { if(state.m_VS.ShaderDetails.InputSig[state.m_VS.ShaderDetails.InputSig.Length - 1 - e].systemValue == SystemAttribute.None) { allSystem = false; break; } } if (!allSystem) mismatchDetails += "IA bytecode provides fewer elements than VS wants.\n"; } { var IA = state.m_IA.Bytecode.InputSig; var VS = state.m_VS.ShaderDetails.InputSig; int count = Math.Min(IA.Length, VS.Length); for (int i = 0; i < count; i++) { // misorder or misnamed semantics if (IA[i].semanticIdxName.ToUpperInvariant() != VS[i].semanticIdxName.ToUpperInvariant()) mismatchDetails += String.Format("IA bytecode semantic {0}: {1} != VS bytecode semantic {0}: {2}\n", i, IA[i].semanticIdxName, VS[i].semanticIdxName); // VS wants more components if (IA[i].compCount < VS[i].compCount) mismatchDetails += String.Format("IA bytecode semantic {0} ({1}) is {3}-wide, VS bytecode semantic {0} ({1}) {2} is {4}-wide\n", i, IA[i].semanticIdxName, VS[i].semanticIdxName, IA[i].compCount, VS[i].compCount); // VS wants different types if (IA[i].compType != VS[i].compType) mismatchDetails += String.Format("IA bytecode semantic {0} ({1}) is {3}, VS bytecode semantic {0} ({2}) is {4}\n", i, IA[i].semanticIdxName, VS[i].semanticIdxName, IA[i].compType, VS[i].compType); } } if (mismatchDetails.Length != 0) { iaBytecodeMismatch.Text = "Warning: Mismatch detected between bytecode used to create IA and currently bound VS inputs"; toolTip.SetToolTip(iaBytecodeMismatch, mismatchDetails.Trim()); iaBytecodeMismatch.Visible = true; } } int vs = 0; vs = inputLayouts.VScrollValue(); inputLayouts.Nodes.Clear(); inputLayouts.BeginUpdate(); if (state.m_IA.layouts != null) { int i = 0; foreach (var l in state.m_IA.layouts) { string byteOffs = l.ByteOffset.ToString(); // D3D11 specific value if (l.ByteOffset == uint.MaxValue) { byteOffs = String.Format("APPEND_ALIGNED ({0})", layoutOffs[l.InputSlot]); } else { layoutOffs[l.InputSlot] = l.ByteOffset; } layoutOffs[l.InputSlot] += l.Format.compByteWidth * l.Format.compCount; bool iaUsed = false; if (state.m_IA.Bytecode != null) { for (int ia = 0; ia < state.m_IA.Bytecode.InputSig.Length; ia++) { if (state.m_IA.Bytecode.InputSig[ia].semanticName.ToUpperInvariant() == l.SemanticName.ToUpperInvariant() && state.m_IA.Bytecode.InputSig[ia].semanticIndex == l.SemanticIndex) { iaUsed = true; break; } } } i++; if(!iaUsed && !showDisabled.Checked) continue; var node = inputLayouts.Nodes.Add(new object[] { i, l.SemanticName, l.SemanticIndex.ToString(), l.Format, l.InputSlot.ToString(), byteOffs, l.PerInstance ? "PER_INSTANCE" : "PER_VERTEX", l.InstanceDataStepRate.ToString() }); if (iaUsed) usedVBuffers[l.InputSlot] = true; node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; if (!iaUsed) InactiveRow(node); } } inputLayouts.NodesSelection.Clear(); inputLayouts.EndUpdate(); inputLayouts.SetVScrollValue(vs); PrimitiveTopology topo = draw != null ? draw.topology : PrimitiveTopology.Unknown; topology.Text = topo.ToString(); if (topo > PrimitiveTopology.PatchList) { int numCPs = (int)topo - (int)PrimitiveTopology.PatchList + 1; topology.Text = string.Format("PatchList ({0} Control Points)", numCPs); } switch (topo) { case PrimitiveTopology.PointList: topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_pointlist; break; case PrimitiveTopology.LineList: topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_linelist; break; case PrimitiveTopology.LineStrip: topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_linestrip; break; case PrimitiveTopology.TriangleList: topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_trilist; break; case PrimitiveTopology.TriangleStrip: topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_tristrip; break; case PrimitiveTopology.LineList_Adj: topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_linelist_adj; break; case PrimitiveTopology.LineStrip_Adj: topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_linestrip_adj; break; case PrimitiveTopology.TriangleList_Adj: topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_trilist_adj; break; case PrimitiveTopology.TriangleStrip_Adj: topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_tristrip_adj; break; default: topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_patch; break; } vs = iabuffers.VScrollValue(); iabuffers.Nodes.Clear(); iabuffers.BeginUpdate(); bool ibufferUsed = draw != null && (draw.flags & DrawcallFlags.UseIBuffer) != 0; if (state.m_IA.ibuffer != null && state.m_IA.ibuffer.Buffer != ResourceId.Null) { if (ibufferUsed || showDisabled.Checked) { string ptr = "Buffer " + state.m_IA.ibuffer.Buffer.ToString(); string name = ptr; UInt64 length = 1; if (!ibufferUsed) { length = 0; } for (int t = 0; t < bufs.Length; t++) { if (bufs[t].ID == state.m_IA.ibuffer.Buffer) { name = bufs[t].name; length = bufs[t].length; } } var node = iabuffers.Nodes.Add(new object[] { "Index", name, draw != null ? draw.indexByteWidth : 0, state.m_IA.ibuffer.Offset, length }); node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; node.Tag = new IABufferTag(state.m_IA.ibuffer.Buffer, draw.indexOffset); if (!ibufferUsed) InactiveRow(node); if (state.m_IA.ibuffer.Buffer == ResourceId.Null) EmptyRow(node); } } else { if (ibufferUsed || showEmpty.Checked) { var node = iabuffers.Nodes.Add(new object[] { "Index", "No Buffer Set", "-", "-", "-" }); node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; node.Tag = new IABufferTag(state.m_IA.ibuffer.Buffer, draw.indexOffset); EmptyRow(node); if (!ibufferUsed) InactiveRow(node); } } m_VBNodes.Clear(); if (state.m_IA.vbuffers != null) { int i = 0; foreach (var v in state.m_IA.vbuffers) { bool filledSlot = (v.Buffer != ResourceId.Null); bool usedSlot = (usedVBuffers[i]); // show if if (usedSlot || // it's referenced by the shader - regardless of empty or not (showDisabled.Checked && !usedSlot && filledSlot) || // it's bound, but not referenced, and we have "show disabled" (showEmpty.Checked && !filledSlot) // it's empty, and we have "show empty" ) { string name = "Buffer " + v.Buffer.ToString(); UInt64 length = 1; for (int t = 0; t < bufs.Length; t++) { if (bufs[t].ID == v.Buffer) { name = bufs[t].name; length = bufs[t].length; } } TreelistView.Node node = null; if(filledSlot) node = iabuffers.Nodes.Add(new object[] { i, name, v.Stride, v.Offset, length }); else node = iabuffers.Nodes.Add(new object[] { i, "No Buffer Set", "-", "-", "-" }); node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; node.Tag = new IABufferTag(v.Buffer, v.Offset); if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); m_VBNodes.Add(node); } i++; } } iabuffers.NodesSelection.Clear(); iabuffers.EndUpdate(); iabuffers.SetVScrollValue(vs); SetShaderState(texs, bufs, state.m_VS, vsShader, vsResources, vsSamplers, vsCBuffers, vsClasses); SetShaderState(texs, bufs, state.m_GS, gsShader, gsResources, gsSamplers, gsCBuffers, gsClasses); SetShaderState(texs, bufs, state.m_HS, hsShader, hsResources, hsSamplers, hsCBuffers, hsClasses); SetShaderState(texs, bufs, state.m_DS, dsShader, dsResources, dsSamplers, dsCBuffers, dsClasses); SetShaderState(texs, bufs, state.m_PS, psShader, psResources, psSamplers, psCBuffers, psClasses); SetShaderState(texs, bufs, state.m_CS, csShader, csResources, csSamplers, csCBuffers, csClasses); vs = csUAVs.VScrollValue(); csUAVs.Nodes.Clear(); csUAVs.BeginUpdate(); if (state.m_CS.UAVs != null) { int i = 0; foreach (var r in state.m_CS.UAVs) { ShaderResource shaderInput = null; if (state.m_CS.ShaderDetails != null) { foreach (var bind in state.m_CS.ShaderDetails.ReadWriteResources) { if (bind.bindPoint == i) { shaderInput = bind; break; } } } bool filledSlot = (r.Resource != ResourceId.Null); bool usedSlot = (shaderInput != null); // show if if (usedSlot || // it's referenced by the shader - regardless of empty or not (showDisabled.Checked && !usedSlot && filledSlot) || // it's bound, but not referenced, and we have "show disabled" (showEmpty.Checked && !filledSlot) // it's empty, and we have "show empty" ) { string slotname = i.ToString(); if (shaderInput != null && shaderInput.name.Length > 0) slotname += ": " + shaderInput.name; UInt64 w = 1; UInt32 h = 1, d = 1; UInt32 a = 1; string format = "Unknown"; string name = "UAV " + r.Resource.ToString(); string typename = "Unknown"; object tag = null; bool viewDetails = false; if (!filledSlot) { name = "Empty"; format = "-"; typename = "-"; w = h = d = a = 0; } for (int t = 0; t < texs.Length; t++) { if (texs[t].ID == r.Resource) { w = texs[t].width; h = texs[t].height; d = texs[t].depth; a = texs[t].arraysize; format = texs[t].format.ToString(); name = texs[t].name; typename = texs[t].resType.Str(); if (texs[t].resType == ShaderResourceType.Texture2DMS || texs[t].resType == ShaderResourceType.Texture2DMSArray) { typename += String.Format(" {0}x", texs[t].msSamp); } // if it's a typeless format, show the format of the view if (texs[t].format != r.Format) { format = "Viewed as " + r.Format.ToString(); } if (HasImportantViewParams(r, texs[t])) viewDetails = true; tag = new ViewTexTag(r, texs[t]); } } for (int t = 0; t < bufs.Length; t++) { if (bufs[t].ID == r.Resource) { w = bufs[t].length; h = 0; d = 0; a = 0; format = ""; name = bufs[t].name; typename = "Buffer"; if (r.ElementSize > 0) { typename = "RWStructuredBuffer[" + (bufs[t].length / r.ElementSize) + "]"; } else if (r.Flags.HasFlag(D3D11BufferViewFlags.Raw)) { typename = "RWByteAddressBuffer"; } if (r.Flags.HasFlag(D3D11BufferViewFlags.Append) || r.Flags.HasFlag(D3D11BufferViewFlags.Counter)) { typename += " (Count: " + r.BufferStructCount + ")"; } if (shaderInput != null && !shaderInput.IsTexture) { if (r.Format.compType == FormatComponentType.None) { if (shaderInput.variableType.members.Length > 0) format = "struct " + shaderInput.variableType.Name; else format = shaderInput.variableType.Name; } else { format = r.Format.ToString(); } } if (HasImportantViewParams(r, bufs[t])) viewDetails = true; tag = new ViewBufTag(r, bufs[t]); } } var node = csUAVs.Nodes.Add(new object[] { slotname, name, typename, w, h, d, a, format }); node.Tag = tag; node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); if (viewDetails) ViewDetailsRow(node); } i++; } } csUAVs.NodesSelection.Clear(); csUAVs.EndUpdate(); csUAVs.SetVScrollValue(vs); bool streamoutSet = false; vs = gsStreams.VScrollValue(); gsStreams.BeginUpdate(); gsStreams.Nodes.Clear(); if (state.m_SO.Outputs != null) { int i = 0; foreach (var s in state.m_SO.Outputs) { bool filledSlot = (s.Buffer != ResourceId.Null); bool usedSlot = (filledSlot); streamoutSet |= filledSlot; // show if if (usedSlot || // it's referenced by the shader - regardless of empty or not (showDisabled.Checked && !usedSlot && filledSlot) || // it's bound, but not referenced, and we have "show disabled" (showEmpty.Checked && !filledSlot) // it's empty, and we have "show empty" ) { string name = "Buffer " + s.Buffer.ToString(); UInt64 length = 0; if (!filledSlot) { name = "Empty"; } FetchBuffer fetch = null; for (int t = 0; t < bufs.Length; t++) { if (bufs[t].ID == s.Buffer) { name = bufs[t].name; length = bufs[t].length; fetch = bufs[t]; } } var node = gsStreams.Nodes.Add(new object[] { i, name, length, s.Offset }); node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; node.Tag = fetch; if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); } i++; } } gsStreams.EndUpdate(); gsStreams.NodesSelection.Clear(); gsStreams.SetVScrollValue(vs); gsStreams.Visible = gsStreams.Parent.Visible = streamoutSet; if (streamoutSet) geomTableLayout.ColumnStyles[1].Width = 50.0f; else geomTableLayout.ColumnStyles[1].Width = 0; //////////////////////////////////////////////// // Rasterizer vs = viewports.VScrollValue(); viewports.BeginUpdate(); viewports.Nodes.Clear(); if (state.m_RS.Viewports != null) { int i = 0; foreach (var v in state.m_RS.Viewports) { if (v.Enabled || showEmpty.Checked) { var node = viewports.Nodes.Add(new object[] { i, v.TopLeft[0], v.TopLeft[1], v.Width, v.Height, v.MinDepth, v.MaxDepth }); if (v.Width == 0 || v.Height == 0 || v.MinDepth == v.MaxDepth) EmptyRow(node); if (!v.Enabled) InactiveRow(node); } i++; } } viewports.NodesSelection.Clear(); viewports.EndUpdate(); viewports.SetVScrollValue(vs); vs = scissors.VScrollValue(); scissors.BeginUpdate(); scissors.Nodes.Clear(); if (state.m_RS.Scissors != null) { int i = 0; foreach (var s in state.m_RS.Scissors) { if (s.Enabled || showEmpty.Checked) { var node = scissors.Nodes.Add(new object[] { i, s.left, s.top, s.right - s.left, s.bottom - s.top }); if (s.right == s.left || s.bottom == s.top) EmptyRow(node); if (!s.Enabled) InactiveRow(node); } i++; } } scissors.NodesSelection.Clear(); scissors.EndUpdate(); scissors.SetVScrollValue(vs); fillMode.Text = state.m_RS.m_State.FillMode.ToString(); cullMode.Text = state.m_RS.m_State.CullMode.ToString(); frontCCW.Image = state.m_RS.m_State.FrontCCW ? tick : cross; scissorEnable.Image = state.m_RS.m_State.ScissorEnable ? tick : cross; lineAAEnable.Image = state.m_RS.m_State.AntialiasedLineEnable ? tick : cross; multisampleEnable.Image = state.m_RS.m_State.MultisampleEnable ? tick : cross; depthClip.Image = state.m_RS.m_State.DepthClip ? tick : cross; depthBias.Text = state.m_RS.m_State.DepthBias.ToString(); depthBiasClamp.Text = Formatter.Format(state.m_RS.m_State.DepthBiasClamp); slopeScaledBias.Text = Formatter.Format(state.m_RS.m_State.SlopeScaledDepthBias); forcedSampleCount.Text = state.m_RS.m_State.ForcedSampleCount.ToString(); conservativeRaster.Image = state.m_RS.m_State.ConservativeRasterization ? tick : cross; //////////////////////////////////////////////// // Output Merger bool[] targets = new bool[8]; for (int i = 0; i < 8; i++) targets[i] = false; vs = targetOutputs.VScrollValue(); targetOutputs.BeginUpdate(); targetOutputs.Nodes.Clear(); if (state.m_OM.RenderTargets != null) { int i = 0; foreach (var p in state.m_OM.RenderTargets) { if (p.Resource != ResourceId.Null || showEmpty.Checked) { UInt32 w = 1, h = 1, d = 1; UInt32 a = 1; string format = "Unknown"; string name = "Texture " + p.ToString(); string typename = "Unknown"; object tag = null; bool viewDetails = false; if (p.Resource == ResourceId.Null) { name = "Empty"; format = "-"; typename = "-"; w = h = d = a = 0; } for (int t = 0; t < texs.Length; t++) { if (texs[t].ID == p.Resource) { w = texs[t].width; h = texs[t].height; d = texs[t].depth; a = texs[t].arraysize; format = texs[t].format.ToString(); name = texs[t].name; typename = texs[t].resType.Str(); if (texs[t].resType == ShaderResourceType.Texture2DMS || texs[t].resType == ShaderResourceType.Texture2DMSArray) { typename += String.Format(" {0}x", texs[t].msSamp); } // if it's a typeless format, show the format of the view if (texs[t].format != p.Format) { format = "Viewed as " + p.Format.ToString(); } if (HasImportantViewParams(p, texs[t])) viewDetails = true; tag = new ViewTexTag(p, texs[t]); } } var node = targetOutputs.Nodes.Add(new object[] { i, name, typename, w, h, d, a, format }); node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; node.Tag = tag; if (p.Resource == ResourceId.Null) { EmptyRow(node); } else { targets[i] = true; if (viewDetails) ViewDetailsRow(node); } } i++; } } if (state.m_OM.UAVs != null) { int i = 0; foreach (var r in state.m_OM.UAVs) { ShaderResource shaderInput = null; if (state.m_PS.ShaderDetails != null) { foreach (var bind in state.m_PS.ShaderDetails.ReadWriteResources) { if (bind.bindPoint == i + state.m_OM.UAVStartSlot) { shaderInput = bind; break; } } } bool filledSlot = (r.Resource != ResourceId.Null); bool usedSlot = (shaderInput != null); // note: we don't show empty UAVs as these "slots" are already showed as empty RTs. // show if if (usedSlot || // it's referenced by the shader - regardless of empty or not (showDisabled.Checked && !usedSlot && filledSlot) // it's bound, but not referenced, and we have "show disabled" ) { UInt64 w = 1; UInt32 h = 1, d = 1; UInt32 a = 1; string format = "Unknown"; string name = "UAV " + r.Resource.ToString(); string typename = "Unknown"; object tag = null; bool viewDetails = false; if (!filledSlot) { name = "Empty"; format = "-"; typename = "-"; w = h = d = a = 0; } for (int t = 0; t < texs.Length; t++) { if (texs[t].ID == r.Resource) { w = texs[t].width; h = texs[t].height; d = texs[t].depth; a = texs[t].arraysize; format = texs[t].format.ToString(); name = texs[t].name; typename = texs[t].resType.Str(); if (texs[t].resType == ShaderResourceType.Texture2DMS || texs[t].resType == ShaderResourceType.Texture2DMSArray) { typename += String.Format(" {0}x", texs[t].msSamp); } // if it's a typeless format, show the format of the view if (texs[t].format != r.Format) { format = "Viewed as " + r.Format.ToString(); } if (HasImportantViewParams(r, texs[t])) viewDetails = true; tag = new ViewTexTag(r, texs[t]); } } for (int t = 0; t < bufs.Length; t++) { if (bufs[t].ID == r.Resource) { w = bufs[t].length; h = 0; d = 0; a = 0; format = ""; name = bufs[t].name; typename = "Buffer"; if (r.ElementSize > 0) { typename = "RWStructuredBuffer[" + (bufs[t].length / r.ElementSize) + "]"; } else if (r.Flags.HasFlag(D3D11BufferViewFlags.Raw)) { typename = "RWByteAddressBuffer"; } if (r.Flags.HasFlag(D3D11BufferViewFlags.Append) || r.Flags.HasFlag(D3D11BufferViewFlags.Counter)) { typename += " (Count: " + r.BufferStructCount + ")"; } if (shaderInput != null && !shaderInput.IsTexture) { if (r.Format.compType == FormatComponentType.None) { if (shaderInput.variableType.members.Length > 0) format = "struct " + shaderInput.variableType.Name; else format = shaderInput.variableType.Name; } else { format = r.Format.ToString(); } } if (HasImportantViewParams(r, bufs[t])) viewDetails = true; tag = new ViewBufTag(r, bufs[t]); } } var node = targetOutputs.Nodes.Add(new object[] { i + state.m_OM.UAVStartSlot, name, typename, w, h, d, a, format }); node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; node.Tag = tag; if (r.Resource == ResourceId.Null) EmptyRow(node); if (viewDetails) ViewDetailsRow(node); } i++; } } if (state.m_OM.DepthTarget.Resource != ResourceId.Null || showEmpty.Checked) { UInt32 w = 1, h = 1, d = 1; UInt32 a = 1; string format = "Unknown"; string name = "Depth Target " + state.m_OM.DepthTarget.Resource.ToString(); string typename = "Unknown"; object tag = null; bool viewDetails = false; if (state.m_OM.DepthTarget.Resource == ResourceId.Null) { name = "Empty"; format = "-"; typename = "-"; w = h = d = a = 0; } for (int t = 0; t < texs.Length; t++) { if (texs[t].ID == state.m_OM.DepthTarget.Resource) { w = texs[t].width; h = texs[t].height; d = texs[t].depth; a = texs[t].arraysize; format = texs[t].format.ToString(); name = texs[t].name; typename = texs[t].resType.Str(); if (texs[t].resType == ShaderResourceType.Texture2DMS || texs[t].resType == ShaderResourceType.Texture2DMSArray) { typename += String.Format(" {0}x", texs[t].msSamp); } // if it's a typeless format, show the format of the view if (texs[t].format != state.m_OM.DepthTarget.Format) { format = "Viewed as " + state.m_OM.DepthTarget.Format.ToString(); } if (HasImportantViewParams(state.m_OM.DepthTarget, texs[t])) viewDetails = true; tag = new ViewTexTag(state.m_OM.DepthTarget, texs[t]); } } var node = targetOutputs.Nodes.Add(new object[] { "Depth", name, typename, w, h, d, a, format }); node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; node.Tag = tag; if (viewDetails) ViewDetailsRow(node); if (state.m_OM.DepthTarget.Resource == ResourceId.Null) EmptyRow(node); } targetOutputs.EndUpdate(); targetOutputs.NodesSelection.Clear(); targetOutputs.SetVScrollValue(vs); vs = blendOperations.VScrollValue(); blendOperations.BeginUpdate(); blendOperations.Nodes.Clear(); { int i = 0; foreach(var blend in state.m_OM.m_BlendState.Blends) { bool filledSlot = (blend.Enabled == true || targets[i]); bool usedSlot = (targets[i]); // show if if (usedSlot || // it's referenced by the shader - regardless of empty or not (showDisabled.Checked && !usedSlot && filledSlot) || // it's bound, but not referenced, and we have "show disabled" (showEmpty.Checked && !filledSlot) // it's empty, and we have "show empty" ) { var node = blendOperations.Nodes.Add(new object[] { i, blend.Enabled, blend.LogicEnabled, blend.m_Blend.Source, blend.m_Blend.Destination, blend.m_Blend.Operation, blend.m_AlphaBlend.Source, blend.m_AlphaBlend.Destination, blend.m_AlphaBlend.Operation, blend.LogicOp, ((blend.WriteMask & 0x1) == 0 ? "_" : "R") + ((blend.WriteMask & 0x2) == 0 ? "_" : "G") + ((blend.WriteMask & 0x4) == 0 ? "_" : "B") + ((blend.WriteMask & 0x8) == 0 ? "_" : "A") }); if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); } i++; } } blendOperations.NodesSelection.Clear(); blendOperations.EndUpdate(); blendOperations.SetVScrollValue(vs); alphaToCoverage.Image = state.m_OM.m_BlendState.AlphaToCoverage ? tick : cross; independentBlend.Image = state.m_OM.m_BlendState.IndependentBlend ? tick : cross; blendFactor.Text = state.m_OM.m_BlendState.BlendFactor[0].ToString("F2") + ", " + state.m_OM.m_BlendState.BlendFactor[1].ToString("F2") + ", " + state.m_OM.m_BlendState.BlendFactor[2].ToString("F2") + ", " + state.m_OM.m_BlendState.BlendFactor[3].ToString("F2"); sampleMask.Text = state.m_OM.m_BlendState.SampleMask.ToString("X8"); depthEnable.Image = state.m_OM.m_State.DepthEnable ? tick : cross; depthFunc.Text = state.m_OM.m_State.DepthFunc; depthWrite.Image = state.m_OM.m_State.DepthWrites ? tick : cross; stencilEnable.Image = state.m_OM.m_State.StencilEnable ? tick : cross; stencilReadMask.Text = state.m_OM.m_State.StencilReadMask.ToString("X2"); stencilWriteMask.Text = state.m_OM.m_State.StencilWriteMask.ToString("X2"); stencilRef.Text = state.m_OM.m_State.StencilRef.ToString("X2"); stencilFuncs.BeginUpdate(); stencilFuncs.Nodes.Clear(); stencilFuncs.Nodes.Add(new object[] { "Front", state.m_OM.m_State.m_FrontFace.Func, state.m_OM.m_State.m_FrontFace.FailOp, state.m_OM.m_State.m_FrontFace.DepthFailOp, state.m_OM.m_State.m_FrontFace.PassOp }); stencilFuncs.Nodes.Add(new object[] { "Back", state.m_OM.m_State.m_BackFace.Func, state.m_OM.m_State.m_BackFace.FailOp, state.m_OM.m_State.m_BackFace.DepthFailOp, state.m_OM.m_State.m_BackFace.PassOp }); stencilFuncs.EndUpdate(); stencilFuncs.NodesSelection.Clear(); // highlight the appropriate stages in the flowchart if (draw == null) { pipeFlow.SetStagesEnabled(new bool[] { true, true, true, true, true, true, true, true, true }); } else if ((draw.flags & DrawcallFlags.Dispatch) != 0) { pipeFlow.SetStagesEnabled(new bool[] { false, false, false, false, false, false, false, false, true }); } else { pipeFlow.SetStagesEnabled(new bool[] { true, true, state.m_HS.Shader != ResourceId.Null, state.m_DS.Shader != ResourceId.Null, state.m_GS.Shader != ResourceId.Null, true, state.m_PS.Shader != ResourceId.Null, true, false }); // if(streamout only) //{ // pipeFlow.Rasterizer = false; // pipeFlow.OutputMerger = false; //} } }
private void AddResourceRow(D3D12PipelineState.ShaderStage stage, TreelistView.TreeListView list, int space, int reg, bool uav) { D3D12PipelineState state = m_Core.CurD3D12PipelineState; FetchTexture[] texs = m_Core.CurTextures; FetchBuffer[] bufs = m_Core.CurBuffers; BindpointMap bind = null; ShaderResource shaderInput = null; D3D12PipelineState.ResourceView r = uav ? stage.Spaces[space].UAVs[reg] : stage.Spaces[space].SRVs[reg]; // consider this register to not exist - it's in a gap defined by sparse root signature elements if (r.RootElement == uint.MaxValue) return; if (stage.BindpointMapping != null && stage.ShaderDetails != null) { BindpointMap[] binds = uav ? stage.BindpointMapping.ReadWriteResources : stage.BindpointMapping.ReadOnlyResources; ShaderResource[] resources = uav ? stage.ShaderDetails.ReadWriteResources : stage.ShaderDetails.ReadOnlyResources; for (int i=0; i < binds.Length; i++) { var b = binds[i]; var res = resources[i]; bool regMatch = b.bind == reg; // handle unbounded arrays specially. It's illegal to have an unbounded array with // anything after it if (b.bind <= reg) regMatch = (b.arraySize == UInt32.MaxValue) || (b.bind + b.arraySize > reg); if (b.bindset == space && regMatch && !res.IsSampler) { bind = b; shaderInput = res; break; } } } TreelistView.NodeCollection parent = list.Nodes; string rootel = r.Immediate ? String.Format("#{0} Direct", r.RootElement) : rootel = String.Format("#{0} Table[{1}]", r.RootElement, r.TableIndex); bool filledSlot = r.Resource != ResourceId.Null; bool usedSlot = (bind != null && bind.used); // show if if (usedSlot || // it's referenced by the shader - regardless of empty or not (showDisabled.Checked && !usedSlot && filledSlot) || // it's bound, but not referenced, and we have "show disabled" (showEmpty.Checked && !filledSlot) // it's empty, and we have "show empty" ) { string regname = reg.ToString(); if (shaderInput != null && shaderInput.name.Length > 0) regname += ": " + shaderInput.name; UInt64 w = 1; UInt32 h = 1, d = 1; UInt32 a = 1; string format = "Unknown"; string name = "Unbound"; string typename = "Unknown"; object tag = null; bool viewDetails = false; if (!filledSlot) { name = "Empty"; format = "-"; typename = "-"; w = h = d = a = 0; } if (r != null) { name = "Shader Resource " + r.Resource.ToString(); // check to see if it's a texture for (int t = 0; t < texs.Length; t++) { if (texs[t].ID == r.Resource) { w = texs[t].width; h = texs[t].height; d = texs[t].depth; a = texs[t].arraysize; format = texs[t].format.ToString(); name = texs[t].name; typename = texs[t].resType.Str(); if (texs[t].resType == ShaderResourceType.Texture2DMS || texs[t].resType == ShaderResourceType.Texture2DMSArray) { typename += String.Format(" {0}x", texs[t].msSamp); } // if it's a typeless format, show the format of the view if (texs[t].format != r.Format) { format = "Viewed as " + r.Format.ToString(); } tag = new ViewTexTag(r, texs[t], true, shaderInput); if (HasImportantViewParams(r, texs[t])) viewDetails = true; } } // if not a texture, it must be a buffer for (int t = 0; t < bufs.Length; t++) { if (bufs[t].ID == r.Resource) { w = bufs[t].length; h = 1; d = 1; a = 1; format = ""; name = bufs[t].name; typename = uav ? "RWBuffer" : "Buffer"; if (r.BufferFlags.HasFlag(D3DBufferViewFlags.Raw)) { typename = uav ? "RWByteAddressBuffer" : "ByteAddressBuffer"; } else if (r.ElementSize > 0) { // for structured buffers, display how many 'elements' there are in the buffer typename = (uav ? "RWStructuredBuffer" : "StructuredBuffer"); a = (uint)(bufs[t].length / r.ElementSize); } if (r.CounterResource != ResourceId.Null) { typename += " (Count: " + r.BufferStructCount + ")"; } // get the buffer type, whether it's just a basic type or a complex struct if (shaderInput != null && !shaderInput.IsTexture) { if (shaderInput.variableType.members.Length > 0) format = "struct " + shaderInput.variableType.Name; else if (r.Format.compType == FormatComponentType.None) format = shaderInput.variableType.Name; else format = r.Format.ToString(); } tag = new ViewBufTag(r, bufs[t], true, shaderInput); if (HasImportantViewParams(r, bufs[t])) viewDetails = true; } } } var node = parent.Add(new object[] { rootel, space, regname, name, typename, w, h, d, a, format }); node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; node.Tag = tag; if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); ViewDetailsRow(node, viewDetails); } }