private void UpdateState() { if (!m_Core.LogLoaded) return; FetchTexture[] texs = m_Core.CurTextures; FetchBuffer[] bufs = m_Core.CurBuffers; D3D11PipelineState state = m_Core.CurD3D11PipelineState; FetchDrawcall draw = m_Core.CurDrawcall; HideViewDetailsTooltip(); m_ViewDetailNodes.Clear(); var tick = global::renderdocui.Properties.Resources.tick; var cross = global::renderdocui.Properties.Resources.cross; bool[] usedVBuffers = new bool[128]; UInt32[] layoutOffs = new UInt32[128]; for (int i = 0; i < 128; i++) { usedVBuffers[i] = false; layoutOffs[i] = 0; } //////////////////////////////////////////////// // Input Assembler if(state.m_IA.Bytecode == null) iaBytecode.Text = "None"; else iaBytecode.Text = state.m_IA.LayoutName; if (state.m_IA.Bytecode != null && state.m_IA.Bytecode.DebugInfo != null && state.m_IA.Bytecode.DebugInfo.entryFunc.Length > 0) iaBytecode.Text += " (" + state.m_IA.Bytecode.DebugInfo.entryFunc + ")"; iaBytecodeMismatch.Text = ""; iaBytecodeMismatch.Visible = false; // check for IA-VS mismatches here. // This should be moved to a "Render Doctor" window reporting problems if (state.m_IA.Bytecode != null && state.m_VS.ShaderDetails != null) { string mismatchDetails = ""; // VS wants more elements if (state.m_IA.Bytecode.InputSig.Length < state.m_VS.ShaderDetails.InputSig.Length) { int excess = state.m_VS.ShaderDetails.InputSig.Length - state.m_IA.Bytecode.InputSig.Length; bool allSystem = true; // The VS signature can consume more elements as long as they are all system value types // (ie. SV_VertexID or SV_InstanceID) for (int e = 0; e < excess; e++) { if(state.m_VS.ShaderDetails.InputSig[state.m_VS.ShaderDetails.InputSig.Length - 1 - e].systemValue == SystemAttribute.None) { allSystem = false; break; } } if (!allSystem) mismatchDetails += "IA bytecode provides fewer elements than VS wants.\n"; } { var IA = state.m_IA.Bytecode.InputSig; var VS = state.m_VS.ShaderDetails.InputSig; int count = Math.Min(IA.Length, VS.Length); for (int i = 0; i < count; i++) { // misorder or misnamed semantics if (IA[i].semanticIdxName.ToUpperInvariant() != VS[i].semanticIdxName.ToUpperInvariant()) mismatchDetails += String.Format("IA bytecode semantic {0}: {1} != VS bytecode semantic {0}: {2}\n", i, IA[i].semanticIdxName, VS[i].semanticIdxName); // VS wants more components if (IA[i].compCount < VS[i].compCount) mismatchDetails += String.Format("IA bytecode semantic {0} ({1}) is {3}-wide, VS bytecode semantic {0} ({1}) {2} is {4}-wide\n", i, IA[i].semanticIdxName, VS[i].semanticIdxName, IA[i].compCount, VS[i].compCount); // VS wants different types if (IA[i].compType != VS[i].compType) mismatchDetails += String.Format("IA bytecode semantic {0} ({1}) is {3}, VS bytecode semantic {0} ({2}) is {4}\n", i, IA[i].semanticIdxName, VS[i].semanticIdxName, IA[i].compType, VS[i].compType); } } if (mismatchDetails.Length != 0) { iaBytecodeMismatch.Text = "Warning: Mismatch detected between bytecode used to create IA and currently bound VS inputs"; toolTip.SetToolTip(iaBytecodeMismatch, mismatchDetails.Trim()); iaBytecodeMismatch.Visible = true; } } int vs = 0; vs = inputLayouts.VScrollValue(); inputLayouts.Nodes.Clear(); inputLayouts.BeginUpdate(); if (state.m_IA.layouts != null) { int i = 0; foreach (var l in state.m_IA.layouts) { string byteOffs = l.ByteOffset.ToString(); // D3D11 specific value if (l.ByteOffset == uint.MaxValue) { byteOffs = String.Format("APPEND_ALIGNED ({0})", layoutOffs[l.InputSlot]); } else { layoutOffs[l.InputSlot] = l.ByteOffset; } layoutOffs[l.InputSlot] += l.Format.compByteWidth * l.Format.compCount; bool iaUsed = false; if (state.m_IA.Bytecode != null) { for (int ia = 0; ia < state.m_IA.Bytecode.InputSig.Length; ia++) { if (state.m_IA.Bytecode.InputSig[ia].semanticName.ToUpperInvariant() == l.SemanticName.ToUpperInvariant() && state.m_IA.Bytecode.InputSig[ia].semanticIndex == l.SemanticIndex) { iaUsed = true; break; } } } i++; if(!iaUsed && !showDisabled.Checked) continue; var node = inputLayouts.Nodes.Add(new object[] { i, l.SemanticName, l.SemanticIndex.ToString(), l.Format, l.InputSlot.ToString(), byteOffs, l.PerInstance ? "PER_INSTANCE" : "PER_VERTEX", l.InstanceDataStepRate.ToString() }); if (iaUsed) usedVBuffers[l.InputSlot] = true; node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; if (!iaUsed) InactiveRow(node); } } inputLayouts.NodesSelection.Clear(); inputLayouts.EndUpdate(); inputLayouts.SetVScrollValue(vs); PrimitiveTopology topo = draw != null ? draw.topology : PrimitiveTopology.Unknown; topology.Text = topo.ToString(); if (topo > PrimitiveTopology.PatchList) { int numCPs = (int)topo - (int)PrimitiveTopology.PatchList + 1; topology.Text = string.Format("PatchList ({0} Control Points)", numCPs); } switch (topo) { case PrimitiveTopology.PointList: topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_pointlist; break; case PrimitiveTopology.LineList: topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_linelist; break; case PrimitiveTopology.LineStrip: topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_linestrip; break; case PrimitiveTopology.TriangleList: topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_trilist; break; case PrimitiveTopology.TriangleStrip: topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_tristrip; break; case PrimitiveTopology.LineList_Adj: topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_linelist_adj; break; case PrimitiveTopology.LineStrip_Adj: topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_linestrip_adj; break; case PrimitiveTopology.TriangleList_Adj: topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_trilist_adj; break; case PrimitiveTopology.TriangleStrip_Adj: topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_tristrip_adj; break; default: topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_patch; break; } vs = iabuffers.VScrollValue(); iabuffers.Nodes.Clear(); iabuffers.BeginUpdate(); bool ibufferUsed = draw != null && (draw.flags & DrawcallFlags.UseIBuffer) != 0; if (state.m_IA.ibuffer != null && state.m_IA.ibuffer.Buffer != ResourceId.Null) { if (ibufferUsed || showDisabled.Checked) { string ptr = "Buffer " + state.m_IA.ibuffer.Buffer.ToString(); string name = ptr; UInt64 length = 1; if (!ibufferUsed) { length = 0; } for (int t = 0; t < bufs.Length; t++) { if (bufs[t].ID == state.m_IA.ibuffer.Buffer) { name = bufs[t].name; length = bufs[t].length; } } var node = iabuffers.Nodes.Add(new object[] { "Index", name, draw != null ? draw.indexByteWidth : 0, state.m_IA.ibuffer.Offset, length }); node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; node.Tag = new IABufferTag(state.m_IA.ibuffer.Buffer, draw.indexOffset); if (!ibufferUsed) InactiveRow(node); if (state.m_IA.ibuffer.Buffer == ResourceId.Null) EmptyRow(node); } } else { if (ibufferUsed || showEmpty.Checked) { var node = iabuffers.Nodes.Add(new object[] { "Index", "No Buffer Set", "-", "-", "-" }); node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; node.Tag = new IABufferTag(state.m_IA.ibuffer.Buffer, draw.indexOffset); EmptyRow(node); if (!ibufferUsed) InactiveRow(node); } } m_VBNodes.Clear(); if (state.m_IA.vbuffers != null) { int i = 0; foreach (var v in state.m_IA.vbuffers) { bool filledSlot = (v.Buffer != ResourceId.Null); bool usedSlot = (usedVBuffers[i]); // show if if (usedSlot || // it's referenced by the shader - regardless of empty or not (showDisabled.Checked && !usedSlot && filledSlot) || // it's bound, but not referenced, and we have "show disabled" (showEmpty.Checked && !filledSlot) // it's empty, and we have "show empty" ) { string name = "Buffer " + v.Buffer.ToString(); UInt64 length = 1; for (int t = 0; t < bufs.Length; t++) { if (bufs[t].ID == v.Buffer) { name = bufs[t].name; length = bufs[t].length; } } TreelistView.Node node = null; if(filledSlot) node = iabuffers.Nodes.Add(new object[] { i, name, v.Stride, v.Offset, length }); else node = iabuffers.Nodes.Add(new object[] { i, "No Buffer Set", "-", "-", "-" }); node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; node.Tag = new IABufferTag(v.Buffer, v.Offset); if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); m_VBNodes.Add(node); } i++; } } iabuffers.NodesSelection.Clear(); iabuffers.EndUpdate(); iabuffers.SetVScrollValue(vs); SetShaderState(texs, bufs, state.m_VS, vsShader, vsResources, vsSamplers, vsCBuffers, vsClasses); SetShaderState(texs, bufs, state.m_GS, gsShader, gsResources, gsSamplers, gsCBuffers, gsClasses); SetShaderState(texs, bufs, state.m_HS, hsShader, hsResources, hsSamplers, hsCBuffers, hsClasses); SetShaderState(texs, bufs, state.m_DS, dsShader, dsResources, dsSamplers, dsCBuffers, dsClasses); SetShaderState(texs, bufs, state.m_PS, psShader, psResources, psSamplers, psCBuffers, psClasses); SetShaderState(texs, bufs, state.m_CS, csShader, csResources, csSamplers, csCBuffers, csClasses); vs = csUAVs.VScrollValue(); csUAVs.Nodes.Clear(); csUAVs.BeginUpdate(); if (state.m_CS.UAVs != null) { int i = 0; foreach (var r in state.m_CS.UAVs) { ShaderResource shaderInput = null; if (state.m_CS.ShaderDetails != null) { foreach (var bind in state.m_CS.ShaderDetails.ReadWriteResources) { if (bind.bindPoint == i) { shaderInput = bind; break; } } } bool filledSlot = (r.Resource != ResourceId.Null); bool usedSlot = (shaderInput != null); // show if if (usedSlot || // it's referenced by the shader - regardless of empty or not (showDisabled.Checked && !usedSlot && filledSlot) || // it's bound, but not referenced, and we have "show disabled" (showEmpty.Checked && !filledSlot) // it's empty, and we have "show empty" ) { string slotname = i.ToString(); if (shaderInput != null && shaderInput.name.Length > 0) slotname += ": " + shaderInput.name; UInt64 w = 1; UInt32 h = 1, d = 1; UInt32 a = 1; string format = "Unknown"; string name = "UAV " + r.Resource.ToString(); string typename = "Unknown"; object tag = null; bool viewDetails = false; if (!filledSlot) { name = "Empty"; format = "-"; typename = "-"; w = h = d = a = 0; } for (int t = 0; t < texs.Length; t++) { if (texs[t].ID == r.Resource) { w = texs[t].width; h = texs[t].height; d = texs[t].depth; a = texs[t].arraysize; format = texs[t].format.ToString(); name = texs[t].name; typename = texs[t].resType.Str(); if (texs[t].resType == ShaderResourceType.Texture2DMS || texs[t].resType == ShaderResourceType.Texture2DMSArray) { typename += String.Format(" {0}x", texs[t].msSamp); } // if it's a typeless format, show the format of the view if (texs[t].format != r.Format) { format = "Viewed as " + r.Format.ToString(); } if (HasImportantViewParams(r, texs[t])) viewDetails = true; tag = new ViewTexTag(r, texs[t]); } } for (int t = 0; t < bufs.Length; t++) { if (bufs[t].ID == r.Resource) { w = bufs[t].length; h = 0; d = 0; a = 0; format = ""; name = bufs[t].name; typename = "Buffer"; if (r.ElementSize > 0) { typename = "RWStructuredBuffer[" + (bufs[t].length / r.ElementSize) + "]"; } else if (r.Flags.HasFlag(D3D11BufferViewFlags.Raw)) { typename = "RWByteAddressBuffer"; } if (r.Flags.HasFlag(D3D11BufferViewFlags.Append) || r.Flags.HasFlag(D3D11BufferViewFlags.Counter)) { typename += " (Count: " + r.BufferStructCount + ")"; } if (shaderInput != null && !shaderInput.IsTexture) { if (r.Format.compType == FormatComponentType.None) { if (shaderInput.variableType.members.Length > 0) format = "struct " + shaderInput.variableType.Name; else format = shaderInput.variableType.Name; } else { format = r.Format.ToString(); } } if (HasImportantViewParams(r, bufs[t])) viewDetails = true; tag = new ViewBufTag(r, bufs[t]); } } var node = csUAVs.Nodes.Add(new object[] { slotname, name, typename, w, h, d, a, format }); node.Tag = tag; node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); if (viewDetails) ViewDetailsRow(node); } i++; } } csUAVs.NodesSelection.Clear(); csUAVs.EndUpdate(); csUAVs.SetVScrollValue(vs); bool streamoutSet = false; vs = gsStreams.VScrollValue(); gsStreams.BeginUpdate(); gsStreams.Nodes.Clear(); if (state.m_SO.Outputs != null) { int i = 0; foreach (var s in state.m_SO.Outputs) { bool filledSlot = (s.Buffer != ResourceId.Null); bool usedSlot = (filledSlot); streamoutSet |= filledSlot; // show if if (usedSlot || // it's referenced by the shader - regardless of empty or not (showDisabled.Checked && !usedSlot && filledSlot) || // it's bound, but not referenced, and we have "show disabled" (showEmpty.Checked && !filledSlot) // it's empty, and we have "show empty" ) { string name = "Buffer " + s.Buffer.ToString(); UInt64 length = 0; if (!filledSlot) { name = "Empty"; } FetchBuffer fetch = null; for (int t = 0; t < bufs.Length; t++) { if (bufs[t].ID == s.Buffer) { name = bufs[t].name; length = bufs[t].length; fetch = bufs[t]; } } var node = gsStreams.Nodes.Add(new object[] { i, name, length, s.Offset }); node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; node.Tag = fetch; if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); } i++; } } gsStreams.EndUpdate(); gsStreams.NodesSelection.Clear(); gsStreams.SetVScrollValue(vs); gsStreams.Visible = gsStreams.Parent.Visible = streamoutSet; if (streamoutSet) geomTableLayout.ColumnStyles[1].Width = 50.0f; else geomTableLayout.ColumnStyles[1].Width = 0; //////////////////////////////////////////////// // Rasterizer vs = viewports.VScrollValue(); viewports.BeginUpdate(); viewports.Nodes.Clear(); if (state.m_RS.Viewports != null) { int i = 0; foreach (var v in state.m_RS.Viewports) { if (v.Enabled || showEmpty.Checked) { var node = viewports.Nodes.Add(new object[] { i, v.TopLeft[0], v.TopLeft[1], v.Width, v.Height, v.MinDepth, v.MaxDepth }); if (v.Width == 0 || v.Height == 0 || v.MinDepth == v.MaxDepth) EmptyRow(node); if (!v.Enabled) InactiveRow(node); } i++; } } viewports.NodesSelection.Clear(); viewports.EndUpdate(); viewports.SetVScrollValue(vs); vs = scissors.VScrollValue(); scissors.BeginUpdate(); scissors.Nodes.Clear(); if (state.m_RS.Scissors != null) { int i = 0; foreach (var s in state.m_RS.Scissors) { if (s.Enabled || showEmpty.Checked) { var node = scissors.Nodes.Add(new object[] { i, s.left, s.top, s.right - s.left, s.bottom - s.top }); if (s.right == s.left || s.bottom == s.top) EmptyRow(node); if (!s.Enabled) InactiveRow(node); } i++; } } scissors.NodesSelection.Clear(); scissors.EndUpdate(); scissors.SetVScrollValue(vs); fillMode.Text = state.m_RS.m_State.FillMode.ToString(); cullMode.Text = state.m_RS.m_State.CullMode.ToString(); frontCCW.Image = state.m_RS.m_State.FrontCCW ? tick : cross; scissorEnable.Image = state.m_RS.m_State.ScissorEnable ? tick : cross; lineAAEnable.Image = state.m_RS.m_State.AntialiasedLineEnable ? tick : cross; multisampleEnable.Image = state.m_RS.m_State.MultisampleEnable ? tick : cross; depthClip.Image = state.m_RS.m_State.DepthClip ? tick : cross; depthBias.Text = state.m_RS.m_State.DepthBias.ToString(); depthBiasClamp.Text = Formatter.Format(state.m_RS.m_State.DepthBiasClamp); slopeScaledBias.Text = Formatter.Format(state.m_RS.m_State.SlopeScaledDepthBias); forcedSampleCount.Text = state.m_RS.m_State.ForcedSampleCount.ToString(); conservativeRaster.Image = state.m_RS.m_State.ConservativeRasterization ? tick : cross; //////////////////////////////////////////////// // Output Merger bool[] targets = new bool[8]; for (int i = 0; i < 8; i++) targets[i] = false; vs = targetOutputs.VScrollValue(); targetOutputs.BeginUpdate(); targetOutputs.Nodes.Clear(); if (state.m_OM.RenderTargets != null) { int i = 0; foreach (var p in state.m_OM.RenderTargets) { if (p.Resource != ResourceId.Null || showEmpty.Checked) { UInt32 w = 1, h = 1, d = 1; UInt32 a = 1; string format = "Unknown"; string name = "Texture " + p.ToString(); string typename = "Unknown"; object tag = null; bool viewDetails = false; if (p.Resource == ResourceId.Null) { name = "Empty"; format = "-"; typename = "-"; w = h = d = a = 0; } for (int t = 0; t < texs.Length; t++) { if (texs[t].ID == p.Resource) { w = texs[t].width; h = texs[t].height; d = texs[t].depth; a = texs[t].arraysize; format = texs[t].format.ToString(); name = texs[t].name; typename = texs[t].resType.Str(); if (texs[t].resType == ShaderResourceType.Texture2DMS || texs[t].resType == ShaderResourceType.Texture2DMSArray) { typename += String.Format(" {0}x", texs[t].msSamp); } // if it's a typeless format, show the format of the view if (texs[t].format != p.Format) { format = "Viewed as " + p.Format.ToString(); } if (HasImportantViewParams(p, texs[t])) viewDetails = true; tag = new ViewTexTag(p, texs[t]); } } var node = targetOutputs.Nodes.Add(new object[] { i, name, typename, w, h, d, a, format }); node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; node.Tag = tag; if (p.Resource == ResourceId.Null) { EmptyRow(node); } else { targets[i] = true; if (viewDetails) ViewDetailsRow(node); } } i++; } } if (state.m_OM.UAVs != null) { int i = 0; foreach (var r in state.m_OM.UAVs) { ShaderResource shaderInput = null; if (state.m_PS.ShaderDetails != null) { foreach (var bind in state.m_PS.ShaderDetails.ReadWriteResources) { if (bind.bindPoint == i + state.m_OM.UAVStartSlot) { shaderInput = bind; break; } } } bool filledSlot = (r.Resource != ResourceId.Null); bool usedSlot = (shaderInput != null); // note: we don't show empty UAVs as these "slots" are already showed as empty RTs. // show if if (usedSlot || // it's referenced by the shader - regardless of empty or not (showDisabled.Checked && !usedSlot && filledSlot) // it's bound, but not referenced, and we have "show disabled" ) { UInt64 w = 1; UInt32 h = 1, d = 1; UInt32 a = 1; string format = "Unknown"; string name = "UAV " + r.Resource.ToString(); string typename = "Unknown"; object tag = null; bool viewDetails = false; if (!filledSlot) { name = "Empty"; format = "-"; typename = "-"; w = h = d = a = 0; } for (int t = 0; t < texs.Length; t++) { if (texs[t].ID == r.Resource) { w = texs[t].width; h = texs[t].height; d = texs[t].depth; a = texs[t].arraysize; format = texs[t].format.ToString(); name = texs[t].name; typename = texs[t].resType.Str(); if (texs[t].resType == ShaderResourceType.Texture2DMS || texs[t].resType == ShaderResourceType.Texture2DMSArray) { typename += String.Format(" {0}x", texs[t].msSamp); } // if it's a typeless format, show the format of the view if (texs[t].format != r.Format) { format = "Viewed as " + r.Format.ToString(); } if (HasImportantViewParams(r, texs[t])) viewDetails = true; tag = new ViewTexTag(r, texs[t]); } } for (int t = 0; t < bufs.Length; t++) { if (bufs[t].ID == r.Resource) { w = bufs[t].length; h = 0; d = 0; a = 0; format = ""; name = bufs[t].name; typename = "Buffer"; if (r.ElementSize > 0) { typename = "RWStructuredBuffer[" + (bufs[t].length / r.ElementSize) + "]"; } else if (r.Flags.HasFlag(D3D11BufferViewFlags.Raw)) { typename = "RWByteAddressBuffer"; } if (r.Flags.HasFlag(D3D11BufferViewFlags.Append) || r.Flags.HasFlag(D3D11BufferViewFlags.Counter)) { typename += " (Count: " + r.BufferStructCount + ")"; } if (shaderInput != null && !shaderInput.IsTexture) { if (r.Format.compType == FormatComponentType.None) { if (shaderInput.variableType.members.Length > 0) format = "struct " + shaderInput.variableType.Name; else format = shaderInput.variableType.Name; } else { format = r.Format.ToString(); } } if (HasImportantViewParams(r, bufs[t])) viewDetails = true; tag = new ViewBufTag(r, bufs[t]); } } var node = targetOutputs.Nodes.Add(new object[] { i + state.m_OM.UAVStartSlot, name, typename, w, h, d, a, format }); node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; node.Tag = tag; if (r.Resource == ResourceId.Null) EmptyRow(node); if (viewDetails) ViewDetailsRow(node); } i++; } } if (state.m_OM.DepthTarget.Resource != ResourceId.Null || showEmpty.Checked) { UInt32 w = 1, h = 1, d = 1; UInt32 a = 1; string format = "Unknown"; string name = "Depth Target " + state.m_OM.DepthTarget.Resource.ToString(); string typename = "Unknown"; object tag = null; bool viewDetails = false; if (state.m_OM.DepthTarget.Resource == ResourceId.Null) { name = "Empty"; format = "-"; typename = "-"; w = h = d = a = 0; } for (int t = 0; t < texs.Length; t++) { if (texs[t].ID == state.m_OM.DepthTarget.Resource) { w = texs[t].width; h = texs[t].height; d = texs[t].depth; a = texs[t].arraysize; format = texs[t].format.ToString(); name = texs[t].name; typename = texs[t].resType.Str(); if (texs[t].resType == ShaderResourceType.Texture2DMS || texs[t].resType == ShaderResourceType.Texture2DMSArray) { typename += String.Format(" {0}x", texs[t].msSamp); } // if it's a typeless format, show the format of the view if (texs[t].format != state.m_OM.DepthTarget.Format) { format = "Viewed as " + state.m_OM.DepthTarget.Format.ToString(); } if (HasImportantViewParams(state.m_OM.DepthTarget, texs[t])) viewDetails = true; tag = new ViewTexTag(state.m_OM.DepthTarget, texs[t]); } } var node = targetOutputs.Nodes.Add(new object[] { "Depth", name, typename, w, h, d, a, format }); node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; node.Tag = tag; if (viewDetails) ViewDetailsRow(node); if (state.m_OM.DepthTarget.Resource == ResourceId.Null) EmptyRow(node); } targetOutputs.EndUpdate(); targetOutputs.NodesSelection.Clear(); targetOutputs.SetVScrollValue(vs); vs = blendOperations.VScrollValue(); blendOperations.BeginUpdate(); blendOperations.Nodes.Clear(); { int i = 0; foreach(var blend in state.m_OM.m_BlendState.Blends) { bool filledSlot = (blend.Enabled == true || targets[i]); bool usedSlot = (targets[i]); // show if if (usedSlot || // it's referenced by the shader - regardless of empty or not (showDisabled.Checked && !usedSlot && filledSlot) || // it's bound, but not referenced, and we have "show disabled" (showEmpty.Checked && !filledSlot) // it's empty, and we have "show empty" ) { var node = blendOperations.Nodes.Add(new object[] { i, blend.Enabled, blend.LogicEnabled, blend.m_Blend.Source, blend.m_Blend.Destination, blend.m_Blend.Operation, blend.m_AlphaBlend.Source, blend.m_AlphaBlend.Destination, blend.m_AlphaBlend.Operation, blend.LogicOp, ((blend.WriteMask & 0x1) == 0 ? "_" : "R") + ((blend.WriteMask & 0x2) == 0 ? "_" : "G") + ((blend.WriteMask & 0x4) == 0 ? "_" : "B") + ((blend.WriteMask & 0x8) == 0 ? "_" : "A") }); if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); } i++; } } blendOperations.NodesSelection.Clear(); blendOperations.EndUpdate(); blendOperations.SetVScrollValue(vs); alphaToCoverage.Image = state.m_OM.m_BlendState.AlphaToCoverage ? tick : cross; independentBlend.Image = state.m_OM.m_BlendState.IndependentBlend ? tick : cross; blendFactor.Text = state.m_OM.m_BlendState.BlendFactor[0].ToString("F2") + ", " + state.m_OM.m_BlendState.BlendFactor[1].ToString("F2") + ", " + state.m_OM.m_BlendState.BlendFactor[2].ToString("F2") + ", " + state.m_OM.m_BlendState.BlendFactor[3].ToString("F2"); sampleMask.Text = state.m_OM.m_BlendState.SampleMask.ToString("X8"); depthEnable.Image = state.m_OM.m_State.DepthEnable ? tick : cross; depthFunc.Text = state.m_OM.m_State.DepthFunc; depthWrite.Image = state.m_OM.m_State.DepthWrites ? tick : cross; stencilEnable.Image = state.m_OM.m_State.StencilEnable ? tick : cross; stencilReadMask.Text = state.m_OM.m_State.StencilReadMask.ToString("X2"); stencilWriteMask.Text = state.m_OM.m_State.StencilWriteMask.ToString("X2"); stencilRef.Text = state.m_OM.m_State.StencilRef.ToString("X2"); stencilFuncs.BeginUpdate(); stencilFuncs.Nodes.Clear(); stencilFuncs.Nodes.Add(new object[] { "Front", state.m_OM.m_State.m_FrontFace.Func, state.m_OM.m_State.m_FrontFace.FailOp, state.m_OM.m_State.m_FrontFace.DepthFailOp, state.m_OM.m_State.m_FrontFace.PassOp }); stencilFuncs.Nodes.Add(new object[] { "Back", state.m_OM.m_State.m_BackFace.Func, state.m_OM.m_State.m_BackFace.FailOp, state.m_OM.m_State.m_BackFace.DepthFailOp, state.m_OM.m_State.m_BackFace.PassOp }); stencilFuncs.EndUpdate(); stencilFuncs.NodesSelection.Clear(); // highlight the appropriate stages in the flowchart if (draw == null) { pipeFlow.SetStagesEnabled(new bool[] { true, true, true, true, true, true, true, true, true }); } else if ((draw.flags & DrawcallFlags.Dispatch) != 0) { pipeFlow.SetStagesEnabled(new bool[] { false, false, false, false, false, false, false, false, true }); } else { pipeFlow.SetStagesEnabled(new bool[] { true, true, state.m_HS.Shader != ResourceId.Null, state.m_DS.Shader != ResourceId.Null, state.m_GS.Shader != ResourceId.Null, true, state.m_PS.Shader != ResourceId.Null, true, false }); // if(streamout only) //{ // pipeFlow.Rasterizer = false; // pipeFlow.OutputMerger = false; //} } }
// Set a shader stage's resources and values private void SetShaderState(FetchTexture[] texs, FetchBuffer[] bufs, D3D11PipelineState.ShaderStage stage, Label shader, TreelistView.TreeListView resources, TreelistView.TreeListView samplers, TreelistView.TreeListView cbuffers, TreelistView.TreeListView classes) { ShaderReflection shaderDetails = stage.ShaderDetails; if (stage.Shader == ResourceId.Null) shader.Text = "Unbound"; else shader.Text = stage.ShaderName; if (shaderDetails != null && shaderDetails.DebugInfo.entryFunc.Length > 0 && shaderDetails.DebugInfo.files.Length > 0) { string shaderfn = ""; int entryFile = shaderDetails.DebugInfo.entryFile; if (entryFile < 0 || entryFile >= shaderDetails.DebugInfo.files.Length) entryFile = 0; shaderfn = shaderDetails.DebugInfo.files[entryFile].BaseFilename; shader.Text = shaderDetails.DebugInfo.entryFunc + "()" + " - " + shaderfn; } int vs = 0; vs = resources.VScrollValue(); resources.BeginUpdate(); resources.Nodes.Clear(); if (stage.SRVs != null) { int i = 0; foreach (var r in stage.SRVs) { ShaderResource shaderInput = null; if (shaderDetails != null) { foreach (var bind in shaderDetails.ReadOnlyResources) { if (bind.IsSRV && bind.bindPoint == i) { shaderInput = bind; break; } } } bool filledSlot = (r.Resource != ResourceId.Null); bool usedSlot = (shaderInput != null); // show if if (usedSlot || // it's referenced by the shader - regardless of empty or not (showDisabled.Checked && !usedSlot && filledSlot) || // it's bound, but not referenced, and we have "show disabled" (showEmpty.Checked && !filledSlot) // it's empty, and we have "show empty" ) { string slotname = i.ToString(); if (shaderInput != null && shaderInput.name.Length > 0) slotname += ": " + shaderInput.name; UInt64 w = 1; UInt32 h = 1, d = 1; UInt32 a = 1; string format = "Unknown"; string name = "Shader Resource " + r.Resource.ToString(); string typename = "Unknown"; object tag = null; bool viewDetails = false; if (!filledSlot) { name = "Empty"; format = "-"; typename = "-"; w = h = d = a = 0; } // check to see if it's a texture for (int t = 0; t < texs.Length; t++) { if (texs[t].ID == r.Resource) { w = texs[t].width; h = texs[t].height; d = texs[t].depth; a = texs[t].arraysize; format = texs[t].format.ToString(); name = texs[t].name; typename = texs[t].resType.Str(); if (texs[t].resType == ShaderResourceType.Texture2DMS || texs[t].resType == ShaderResourceType.Texture2DMSArray) { typename += String.Format(" {0}x", texs[t].msSamp); } // if it's a typeless format, show the format of the view if (texs[t].format != r.Format) { format = "Viewed as " + r.Format.ToString(); } tag = new ViewTexTag(r, texs[t]); if (HasImportantViewParams(r, texs[t])) viewDetails = true; } } // if not a texture, it must be a buffer for (int t = 0; t < bufs.Length; t++) { if (bufs[t].ID == r.Resource) { w = bufs[t].length; h = 0; d = 0; a = 0; format = ""; name = bufs[t].name; typename = "Buffer"; // for structured buffers, display how many 'elements' there are in the buffer if (r.ElementSize > 0) { typename = "StructuredBuffer[" + (bufs[t].length / r.ElementSize) + "]"; } else if (r.Flags.HasFlag(D3D11BufferViewFlags.Raw)) { typename = "ByteAddressBuffer"; } if (r.Flags.HasFlag(D3D11BufferViewFlags.Append) || r.Flags.HasFlag(D3D11BufferViewFlags.Counter)) { typename += " (Count: " + r.BufferStructCount + ")"; } // get the buffer type, whether it's just a basic type or a complex struct if (shaderInput != null && !shaderInput.IsTexture) { if (r.Format.compType == FormatComponentType.None) { if (shaderInput.variableType.members.Length > 0) format = "struct " + shaderInput.variableType.Name; else format = shaderInput.variableType.Name; } else { format = r.Format.ToString(); } } tag = new ViewBufTag(r, bufs[t]); if (HasImportantViewParams(r, bufs[t])) viewDetails = true; } } var node = resources.Nodes.Add(new object[] { slotname, name, typename, w, h, d, a, format }); node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; node.Tag = tag; if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); if (viewDetails) ViewDetailsRow(node); } i++; } } resources.EndUpdate(); resources.NodesSelection.Clear(); resources.SetVScrollValue(vs); vs = samplers.VScrollValue(); samplers.BeginUpdate(); samplers.Nodes.Clear(); if (stage.Samplers != null) { int i = 0; foreach (var s in stage.Samplers) { ShaderResource shaderInput = null; if (shaderDetails != null) { foreach (var bind in shaderDetails.ReadOnlyResources) { if (bind.IsSampler && bind.bindPoint == i) { shaderInput = bind; break; } } } bool filledSlot = (s.AddressU.Length > 0); bool usedSlot = (shaderInput != null); // show if if (usedSlot || // it's referenced by the shader - regardless of empty or not (showDisabled.Checked && !usedSlot && filledSlot) || // it's bound, but not referenced, and we have "show disabled" (showEmpty.Checked && !filledSlot) // it's empty, and we have "show empty" ) { string slotname = i.ToString(); if (shaderInput != null && shaderInput.name.Length > 0) slotname += ": " + shaderInput.name; if (s.customSamplerName) slotname += "(" + s.SamplerName + ")"; string borderColor = s.BorderColor[0].ToString() + ", " + s.BorderColor[1].ToString() + ", " + s.BorderColor[2].ToString() + ", " + s.BorderColor[3].ToString(); string addressing = ""; string addPrefix = ""; string addVal = ""; string[] addr = { s.AddressU, s.AddressV, s.AddressW }; // arrange like either UVW: WRAP or UV: WRAP, W: CLAMP for (int a = 0; a < 3; a++) { string prefix = "" + "UVW"[a]; if (a == 0 || addr[a] == addr[a - 1]) { addPrefix += prefix; } else { addressing += addPrefix + ": " + addVal + ", "; addPrefix = prefix; } addVal = addr[a]; } addressing += addPrefix + ": " + addVal; if(s.UseBorder) addressing += String.Format("<{0}>", borderColor); string filter = s.Filter; if (s.MaxAniso > 0) filter += String.Format(" {0}x", s.MaxAniso); if (s.UseComparison) filter += String.Format(" ({0})", s.Comparison); var node = samplers.Nodes.Add(new object[] { slotname, addressing, filter, (s.MinLOD == -float.MaxValue ? "0" : s.MinLOD.ToString()) + " - " + (s.MaxLOD == float.MaxValue ? "FLT_MAX" : s.MaxLOD.ToString()), s.MipLODBias.ToString() }); if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); } i++; } } samplers.EndUpdate(); samplers.NodesSelection.Clear(); samplers.SetVScrollValue(vs); vs = cbuffers.VScrollValue(); cbuffers.BeginUpdate(); cbuffers.Nodes.Clear(); if (stage.ConstantBuffers != null) { UInt32 i = 0; foreach (var b in stage.ConstantBuffers) { ConstantBlock shaderCBuf = null; if (shaderDetails != null && i < shaderDetails.ConstantBlocks.Length && shaderDetails.ConstantBlocks[i].name.Length > 0) shaderCBuf = shaderDetails.ConstantBlocks[i]; bool filledSlot = (b.Buffer != ResourceId.Null); bool usedSlot = (shaderCBuf != null); // show if if (usedSlot || // it's referenced by the shader - regardless of empty or not (showDisabled.Checked && !usedSlot && filledSlot) || // it's bound, but not referenced, and we have "show disabled" (showEmpty.Checked && !filledSlot) // it's empty, and we have "show empty" ) { string name = "Constant Buffer " + b.Buffer.ToString(); UInt64 length = 1; int numvars = shaderCBuf != null ? shaderCBuf.variables.Length : 0; UInt32 byteSize = shaderCBuf != null ? shaderCBuf.byteSize : 0; if (!filledSlot) { name = "Empty"; length = 0; } for (int t = 0; t < bufs.Length; t++) { if (bufs[t].ID == b.Buffer) { name = bufs[t].name; length = bufs[t].length; } } string slotname = i.ToString(); if (shaderCBuf != null && shaderCBuf.name.Length > 0) slotname += ": " + shaderCBuf.name; string sizestr; if (byteSize == length) sizestr = String.Format("{0} Variables, {1} bytes", numvars, length); else sizestr = String.Format("{0} Variables, {1} bytes needed, {2} provided", numvars, byteSize, length); if (length < byteSize) filledSlot = false; string vecrange = String.Format("{0} - {1}", b.VecOffset, b.VecOffset + b.VecCount); var node = cbuffers.Nodes.Add(new object[] { slotname, name, vecrange, sizestr }); node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; node.Tag = i; if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); } i++; } } cbuffers.EndUpdate(); cbuffers.NodesSelection.Clear(); cbuffers.SetVScrollValue(vs); vs = classes.VScrollValue(); classes.BeginUpdate(); classes.Nodes.Clear(); { UInt32 i = 0; foreach (var inst in stage.ClassInstances) { string interfaceName = String.Format("Interface {0}", i); if (shaderDetails != null && i < shaderDetails.Interfaces.Length) interfaceName = shaderDetails.Interfaces[i].Name; classes.Nodes.Add(new object[] { i.ToString(), interfaceName, inst }); i++; } } classes.EndUpdate(); classes.NodesSelection.Clear(); classes.SetVScrollValue(vs); classes.Visible = classes.Parent.Visible = (stage.ClassInstances.Length > 0); }
private void AddResourceRow(ShaderReflection shaderDetails, VulkanPipelineState.ShaderStage stage, int bindset, int bind, VulkanPipelineState.Pipeline pipe, TreelistView.TreeListView resources, FetchTexture[] texs, FetchBuffer[] bufs, ref Dictionary<ResourceId, SamplerData> samplers) { ShaderResource shaderRes = null; BindpointMap bindMap = null; bool isrw = false; uint bindPoint = 0; if (shaderDetails != null) { for (int i = 0; i < shaderDetails.ReadOnlyResources.Length; i++) { var ro = shaderDetails.ReadOnlyResources[i]; if (stage.BindpointMapping.ReadOnlyResources[ro.bindPoint].bindset == bindset && stage.BindpointMapping.ReadOnlyResources[ro.bindPoint].bind == bind) { bindPoint = (uint)i; shaderRes = ro; bindMap = stage.BindpointMapping.ReadOnlyResources[ro.bindPoint]; } } for (int i = 0; i < shaderDetails.ReadWriteResources.Length; i++) { var rw = shaderDetails.ReadWriteResources[i]; if (stage.BindpointMapping.ReadWriteResources[rw.bindPoint].bindset == bindset && stage.BindpointMapping.ReadWriteResources[rw.bindPoint].bind == bind) { bindPoint = (uint)i; isrw = true; shaderRes = rw; bindMap = stage.BindpointMapping.ReadWriteResources[rw.bindPoint]; } } } VulkanPipelineState.Pipeline.DescriptorSet.DescriptorBinding.BindingElement[] slotBinds = null; ShaderBindType bindType = ShaderBindType.Unknown; ShaderStageBits stageBits = (ShaderStageBits)0; if (bindset < pipe.DescSets.Length && bind < pipe.DescSets[bindset].bindings.Length) { slotBinds = pipe.DescSets[bindset].bindings[bind].binds; bindType = pipe.DescSets[bindset].bindings[bind].type; stageBits = pipe.DescSets[bindset].bindings[bind].stageFlags; } else { if (shaderRes.IsSampler) bindType = ShaderBindType.Sampler; else if (shaderRes.IsSampler && shaderRes.IsTexture) bindType = ShaderBindType.ImageSampler; else if (shaderRes.resType == ShaderResourceType.Buffer) bindType = ShaderBindType.ReadOnlyTBuffer; else bindType = ShaderBindType.ReadOnlyImage; } bool usedSlot = bindMap != null && bindMap.used; bool stageBitsIncluded = stageBits.HasFlag((ShaderStageBits)(1 << (int)stage.stage)); // skip descriptors that aren't for this shader stage if (!usedSlot && !stageBitsIncluded) return; if (bindType == ShaderBindType.ConstantBuffer) return; // TODO - check compatibility between bindType and shaderRes.resType ? // consider it filled if any array element is filled bool filledSlot = false; for (int idx = 0; slotBinds != null && idx < slotBinds.Length; idx++) { filledSlot |= slotBinds[idx].res != ResourceId.Null; if (bindType == ShaderBindType.Sampler || bindType == ShaderBindType.ImageSampler) filledSlot |= slotBinds[idx].sampler != ResourceId.Null; } // if it's masked out by stage bits, act as if it's not filled, so it's marked in red if (!stageBitsIncluded) filledSlot = false; // show if if (usedSlot || // it's referenced by the shader - regardless of empty or not (showDisabled.Checked && !usedSlot && filledSlot) || // it's bound, but not referenced, and we have "show disabled" (showEmpty.Checked && !filledSlot) // it's empty, and we have "show empty" ) { TreelistView.NodeCollection parentNodes = resources.Nodes; string setname = bindset.ToString(); string slotname = bind.ToString(); if (shaderRes != null && shaderRes.name.Length > 0) slotname += ": " + shaderRes.name; int arrayLength = 0; if (slotBinds != null) arrayLength = slotBinds.Length; else arrayLength = (int)bindMap.arraySize; // for arrays, add a parent element that we add the real cbuffers below if (arrayLength > 1) { var node = parentNodes.Add(new object[] { "", setname, slotname, String.Format("Array[{0}]", arrayLength), "", "", "", "" }); node.TreeColumn = 0; if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); parentNodes = node.Nodes; } for (int idx = 0; idx < arrayLength; idx++) { VulkanPipelineState.Pipeline.DescriptorSet.DescriptorBinding.BindingElement descriptorBind = null; if (slotBinds != null) descriptorBind = slotBinds[idx]; if (arrayLength > 1) { slotname = String.Format("{0}[{1}]", bind, idx); if (shaderRes != null && shaderRes.name.Length > 0) slotname += ": " + shaderRes.name; } bool isbuf = false; UInt32 w = 1, h = 1, d = 1; UInt32 a = 1; UInt32 samples = 1; UInt64 len = 0; string format = "Unknown"; string name = "Empty"; ShaderResourceType restype = ShaderResourceType.None; object tag = null; bool viewDetails = false; if (filledSlot && descriptorBind != null) { name = "Object " + descriptorBind.res.ToString(); format = descriptorBind.viewfmt.ToString(); // check to see if it's a texture for (int t = 0; t < texs.Length; t++) { if (texs[t].ID == descriptorBind.res) { w = texs[t].width; h = texs[t].height; d = texs[t].depth; a = texs[t].arraysize; name = texs[t].name; restype = texs[t].resType; samples = texs[t].msSamp; if (HasImportantViewParams(descriptorBind, texs[t])) viewDetails = true; tag = new ViewTexTag( descriptorBind.viewfmt, descriptorBind.baseMip, descriptorBind.baseLayer, descriptorBind.numMip, descriptorBind.numLayer, texs[t] ); } } // if not a texture, it must be a buffer for (int t = 0; t < bufs.Length; t++) { if (bufs[t].ID == descriptorBind.res) { len = bufs[t].length; w = 0; h = 0; d = 0; a = 0; name = bufs[t].name; restype = ShaderResourceType.Buffer; ulong descriptorLen = descriptorBind.size; if(descriptorLen == ulong.MaxValue) descriptorLen = len - descriptorBind.offset; tag = new BufferResTag(isrw, bindPoint, bufs[t].ID, descriptorBind.offset, descriptorLen); if (HasImportantViewParams(descriptorBind, bufs[t])) viewDetails = true; isbuf = true; } } } else { name = "Empty"; format = "-"; w = h = d = a = 0; } TreelistView.Node node = null; if (bindType == ShaderBindType.ReadWriteBuffer || bindType == ShaderBindType.ReadOnlyTBuffer || bindType == ShaderBindType.ReadWriteTBuffer ) { if (!isbuf) { node = parentNodes.Add(new object[] { "", bindset, slotname, bindType, "-", "-", "", }); EmptyRow(node); } else { string range = "-"; if (descriptorBind != null) range = String.Format("{0} - {1}", descriptorBind.offset, descriptorBind.size); node = parentNodes.Add(new object[] { "", bindset, slotname, bindType, name, String.Format("{0} bytes", len), range, }); node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; node.Tag = tag; if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); } } else if (bindType == ShaderBindType.Sampler) { if (descriptorBind == null || descriptorBind.sampler == ResourceId.Null) { node = parentNodes.Add(new object[] { "", bindset, slotname, bindType, "-", "-", "", }); EmptyRow(node); } else { node = parentNodes.Add(MakeSampler(bindset.ToString(), slotname, descriptorBind)); if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); var data = new SamplerData(node); node.Tag = data; if (!samplers.ContainsKey(descriptorBind.sampler)) samplers.Add(descriptorBind.sampler, data); } } else { if (descriptorBind == null || descriptorBind.res == ResourceId.Null) { node = parentNodes.Add(new object[] { "", bindset, slotname, bindType, "-", "-", "", }); EmptyRow(node); } else { string typename = restype.Str() + " " + bindType.Str().Replace("&", "&&"); string dim; if (restype == ShaderResourceType.Texture3D) dim = String.Format("{0}x{1}x{2}", w, h, d); else if (restype == ShaderResourceType.Texture1D || restype == ShaderResourceType.Texture1DArray) dim = w.ToString(); else dim = String.Format("{0}x{1}", w, h); if (descriptorBind.swizzle[0] != TextureSwizzle.Red || descriptorBind.swizzle[1] != TextureSwizzle.Green || descriptorBind.swizzle[2] != TextureSwizzle.Blue || descriptorBind.swizzle[3] != TextureSwizzle.Alpha) { format += String.Format(" swizzle[{0}{1}{2}{3}]", descriptorBind.swizzle[0].Str(), descriptorBind.swizzle[1].Str(), descriptorBind.swizzle[2].Str(), descriptorBind.swizzle[3].Str()); } if (restype == ShaderResourceType.Texture1DArray || restype == ShaderResourceType.Texture2DArray || restype == ShaderResourceType.Texture2DMSArray || restype == ShaderResourceType.TextureCubeArray) { dim += String.Format(" {0}[{1}]", restype.Str(), a); } if (restype == ShaderResourceType.Texture2DMS || restype == ShaderResourceType.Texture2DMSArray) dim += String.Format(", {0}x MSAA", samples); node = parentNodes.Add(new object[] { "", bindset, slotname, typename, name, dim, format, }); node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; node.Tag = tag; if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); ViewDetailsRow(node, viewDetails); } if (bindType == ShaderBindType.ImageSampler) { if (descriptorBind == null || descriptorBind.sampler == ResourceId.Null) { node = parentNodes.Add(new object[] { "", bindset, slotname, bindType, "-", "-", "", }); EmptyRow(node); } else { var texnode = node; if (!samplers.ContainsKey(descriptorBind.sampler)) { node = parentNodes.Add(MakeSampler("", "", descriptorBind)); if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); var data = new SamplerData(node); node.Tag = data; samplers.Add(descriptorBind.sampler, data); } if (texnode != null) { m_CombinedImageSamplers[texnode] = samplers[descriptorBind.sampler].node; samplers[descriptorBind.sampler].images.Add(texnode); } } } } } } }
private void UpdateState() { if (!m_Core.LogLoaded) return; m_CombinedImageSamplers.Clear(); FetchTexture[] texs = m_Core.CurTextures; FetchBuffer[] bufs = m_Core.CurBuffers; VulkanPipelineState state = m_Core.CurVulkanPipelineState; FetchDrawcall draw = m_Core.CurDrawcall; var tick = global::renderdocui.Properties.Resources.tick; var cross = global::renderdocui.Properties.Resources.cross; bool[] usedBindings = new bool[128]; for (int i = 0; i < 128; i++) usedBindings[i] = false; //////////////////////////////////////////////// // Vertex Input int vs = 0; vs = viAttrs.VScrollValue(); viAttrs.Nodes.Clear(); viAttrs.BeginUpdate(); if (state.VI.attrs != null) { int i = 0; foreach (var a in state.VI.attrs) { bool filledSlot = true; bool usedSlot = false; string name = String.Format("Attribute {0}", i); if (state.VS.Shader != ResourceId.Null) { int attrib = -1; if(a.location < state.VS.BindpointMapping.InputAttributes.Length) attrib = state.VS.BindpointMapping.InputAttributes[a.location]; if (attrib >= 0 && attrib < state.VS.ShaderDetails.InputSig.Length) { name = state.VS.ShaderDetails.InputSig[attrib].varName; usedSlot = true; } } // show if if (usedSlot || // it's referenced by the shader - regardless of empty or not (showDisabled.Checked && !usedSlot && filledSlot) || // it's bound, but not referenced, and we have "show disabled" (showEmpty.Checked && !filledSlot) // it's empty, and we have "show empty" ) { var node = viAttrs.Nodes.Add(new object[] { i, name, a.location, a.binding, a.format, a.byteoffset }); usedBindings[a.binding] = true; node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; if (!usedSlot) InactiveRow(node); } i++; } } viAttrs.NodesSelection.Clear(); viAttrs.EndUpdate(); viAttrs.SetVScrollValue(vs); m_BindNodes.Clear(); PrimitiveTopology topo = draw != null ? draw.topology : PrimitiveTopology.Unknown; topology.Text = topo.ToString(); if (topo > PrimitiveTopology.PatchList) { int numCPs = (int)topo - (int)PrimitiveTopology.PatchList + 1; topology.Text = string.Format("PatchList ({0} Control Points)", numCPs); } primRestart.Visible = state.IA.primitiveRestartEnable; switch (topo) { case PrimitiveTopology.PointList: topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_pointlist; break; case PrimitiveTopology.LineList: topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_linelist; break; case PrimitiveTopology.LineStrip: topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_linestrip; break; case PrimitiveTopology.TriangleList: topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_trilist; break; case PrimitiveTopology.TriangleStrip: topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_tristrip; break; case PrimitiveTopology.LineList_Adj: topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_linelist_adj; break; case PrimitiveTopology.LineStrip_Adj: topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_linestrip_adj; break; case PrimitiveTopology.TriangleList_Adj: topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_trilist_adj; break; case PrimitiveTopology.TriangleStrip_Adj: topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_tristrip_adj; break; default: topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_patch; break; } vs = viBuffers.VScrollValue(); viBuffers.Nodes.Clear(); viBuffers.BeginUpdate(); bool ibufferUsed = draw != null && (draw.flags & DrawcallFlags.UseIBuffer) != 0; if (state.IA.ibuffer != null && state.IA.ibuffer.buf != ResourceId.Null) { if (ibufferUsed || showDisabled.Checked) { string ptr = "Buffer " + state.IA.ibuffer.buf.ToString(); string name = ptr; UInt64 length = 1; if (!ibufferUsed) { length = 0; } for (int t = 0; t < bufs.Length; t++) { if (bufs[t].ID == state.IA.ibuffer.buf) { name = bufs[t].name; length = bufs[t].length; } } var node = viBuffers.Nodes.Add(new object[] { "Index", name, "Index", state.IA.ibuffer.offs, draw.indexByteWidth, length }); node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; node.Tag = new IABufferTag(state.IA.ibuffer.buf, draw != null ? draw.indexOffset : 0); if (!ibufferUsed) InactiveRow(node); if (state.IA.ibuffer.buf == ResourceId.Null) EmptyRow(node); } } else { if (ibufferUsed || showEmpty.Checked) { var node = viBuffers.Nodes.Add(new object[] { "Index", "No Buffer Set", "-", "-", "-", "-" }); node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; node.Tag = new IABufferTag(state.IA.ibuffer.buf, draw != null ? draw.indexOffset : 0); EmptyRow(node); if (!ibufferUsed) InactiveRow(node); } } m_VBNodes.Clear(); if (state.VI.vbuffers != null) { int i = 0; for(; i < Math.Max(state.VI.vbuffers.Length, state.VI.binds.Length); i++) { var vbuff = (i < state.VI.vbuffers.Length ? state.VI.vbuffers[i] : null); VulkanPipelineState.VertexInput.Binding bind = null; for (int b = 0; b < state.VI.binds.Length; b++) { if (state.VI.binds[b].vbufferBinding == i) bind = state.VI.binds[b]; } bool filledSlot = ((vbuff != null && vbuff.buffer != ResourceId.Null) || bind != null); bool usedSlot = (usedBindings[i]); // show if if (usedSlot || // it's referenced by the shader - regardless of empty or not (showDisabled.Checked && !usedSlot && filledSlot) || // it's bound, but not referenced, and we have "show disabled" (showEmpty.Checked && !filledSlot) // it's empty, and we have "show empty" ) { string name = "No Buffer"; string rate = "-"; UInt64 length = 1; UInt64 offset = 0; UInt32 stride = 0; if (vbuff != null) { name = "Buffer " + vbuff.buffer.ToString(); offset = vbuff.offset; for (int t = 0; t < bufs.Length; t++) { if (bufs[t].ID == vbuff.buffer) { name = bufs[t].name; length = bufs[t].length; } } } if (bind != null) { stride = bind.bytestride; rate = bind.perInstance ? "Instance" : "Vertex"; } else { name += ", No Binding"; } TreelistView.Node node = null; if(filledSlot) node = viBuffers.Nodes.Add(new object[] { i, name, rate, offset, stride, length }); else node = viBuffers.Nodes.Add(new object[] { i, "No Binding", "-", "-", "-", "-" }); node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; node.Tag = new IABufferTag(vbuff != null ? vbuff.buffer : ResourceId.Null, vbuff != null ? vbuff.offset : 0); if (!filledSlot || bind == null || vbuff == null) EmptyRow(node); if (!usedSlot) InactiveRow(node); m_VBNodes.Add(node); } } for (; i < usedBindings.Length; i++) { if (usedBindings[i]) { TreelistView.Node node = viBuffers.Nodes.Add(new object[] { i, "No Binding", "-", "-", "-", "-" }); node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; node.Tag = new IABufferTag(ResourceId.Null, 0); EmptyRow(node); InactiveRow(node); m_VBNodes.Add(node); } } } viBuffers.NodesSelection.Clear(); viBuffers.EndUpdate(); viBuffers.SetVScrollValue(vs); SetShaderState(texs, bufs, state.VS, state.graphics, vsShader, vsResources, vsCBuffers); SetShaderState(texs, bufs, state.GS, state.graphics, gsShader, gsResources, gsCBuffers); SetShaderState(texs, bufs, state.TCS, state.graphics, hsShader, hsResources, hsCBuffers); SetShaderState(texs, bufs, state.TES, state.graphics, dsShader, dsResources, dsCBuffers); SetShaderState(texs, bufs, state.FS, state.graphics, psShader, psResources, psCBuffers); SetShaderState(texs, bufs, state.CS, state.compute, csShader, csResources, csCBuffers); //////////////////////////////////////////////// // Rasterizer vs = viewports.VScrollValue(); viewports.BeginUpdate(); viewports.Nodes.Clear(); int vs2 = scissors.VScrollValue(); scissors.BeginUpdate(); scissors.Nodes.Clear(); if (state.Pass.renderpass.obj != ResourceId.Null) { scissors.Nodes.Add(new object[] { "Render Area", state.Pass.renderArea.x, state.Pass.renderArea.y, state.Pass.renderArea.width, state.Pass.renderArea.height }); } { int i = 0; foreach (var v in state.VP.viewportScissors) { var node = viewports.Nodes.Add(new object[] { i, v.vp.x, v.vp.y, v.vp.Width, v.vp.Height, v.vp.MinDepth, v.vp.MaxDepth }); if (v.vp.Width == 0 || v.vp.Height == 0 || v.vp.MinDepth == v.vp.MaxDepth) EmptyRow(node); node = scissors.Nodes.Add(new object[] { i, v.scissor.x, v.scissor.y, v.scissor.width, v.scissor.height }); if (v.scissor.width == 0 || v.scissor.height == 0) EmptyRow(node); i++; } } viewports.NodesSelection.Clear(); viewports.EndUpdate(); viewports.SetVScrollValue(vs); scissors.NodesSelection.Clear(); scissors.EndUpdate(); scissors.SetVScrollValue(vs2); fillMode.Text = state.RS.FillMode.ToString(); cullMode.Text = state.RS.CullMode.ToString(); frontCCW.Image = state.RS.FrontCCW ? tick : cross; depthBias.Text = Formatter.Format(state.RS.depthBias); depthBiasClamp.Text = Formatter.Format(state.RS.depthBiasClamp); slopeScaledBias.Text = Formatter.Format(state.RS.slopeScaledDepthBias); depthClamp.Image = state.RS.depthClampEnable ? tick : cross; rasterizerDiscard.Image = state.RS.rasterizerDiscardEnable ? tick : cross; lineWidth.Text = Formatter.Format(state.RS.lineWidth); sampleCount.Text = state.MSAA.rasterSamples.ToString(); sampleShading.Image = state.MSAA.sampleShadingEnable ? tick : cross; minSampleShading.Text = Formatter.Format(state.MSAA.minSampleShading); sampleMask.Text = state.MSAA.sampleMask.ToString("X8"); //////////////////////////////////////////////// // Output Merger bool[] targets = new bool[8]; for (int i = 0; i < 8; i++) targets[i] = false; vs = targetOutputs.VScrollValue(); targetOutputs.BeginUpdate(); targetOutputs.Nodes.Clear(); if (state.Pass.framebuffer.attachments != null) { int i = 0; foreach (var p in state.Pass.framebuffer.attachments) { int colIdx = Array.IndexOf(state.Pass.renderpass.colorAttachments, (uint)i); bool filledSlot = (p.img != ResourceId.Null); bool usedSlot = (colIdx >= 0 || state.Pass.renderpass.depthstencilAttachment == i); // show if if (usedSlot || // it's referenced by the shader - regardless of empty or not (showDisabled.Checked && !usedSlot && filledSlot) || // it's bound, but not referenced, and we have "show disabled" (showEmpty.Checked && !filledSlot) // it's empty, and we have "show empty" ) { UInt32 w = 1, h = 1, d = 1; UInt32 a = 1; string format = p.viewfmt.ToString(); string name = "Texture " + p.ToString(); string typename = "Unknown"; object tag = null; bool viewDetails = false; if (p.img == ResourceId.Null) { name = "Empty"; format = "-"; typename = "-"; w = h = d = a = 0; } for (int t = 0; t < texs.Length; t++) { if (texs[t].ID == p.img) { w = texs[t].width; h = texs[t].height; d = texs[t].depth; a = texs[t].arraysize; name = texs[t].name; typename = texs[t].resType.Str(); if (!texs[t].customName && state.FS.ShaderDetails != null) { for(int s=0; s < state.FS.ShaderDetails.OutputSig.Length; s++) { if(state.FS.ShaderDetails.OutputSig[s].regIndex == colIdx && (state.FS.ShaderDetails.OutputSig[s].systemValue == SystemAttribute.None || state.FS.ShaderDetails.OutputSig[s].systemValue == SystemAttribute.ColourOutput)) { name = String.Format("<{0}>", state.FS.ShaderDetails.OutputSig[s].varName); } } } if (HasImportantViewParams(p, texs[t])) viewDetails = true; tag = new ViewTexTag(p.viewfmt, p.baseMip, p.baseLayer, p.numMip, p.numLayer, texs[t]); } } if (p.swizzle[0] != TextureSwizzle.Red || p.swizzle[1] != TextureSwizzle.Green || p.swizzle[2] != TextureSwizzle.Blue || p.swizzle[3] != TextureSwizzle.Alpha) { format += String.Format(" swizzle[{0}{1}{2}{3}]", p.swizzle[0].Str(), p.swizzle[1].Str(), p.swizzle[2].Str(), p.swizzle[3].Str()); } var node = targetOutputs.Nodes.Add(new object[] { i, name, typename, w, h, d, a, format }); node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; node.Tag = tag; if (p.img == ResourceId.Null) { EmptyRow(node); } else if (!usedSlot) { InactiveRow(node); } else { targets[i] = true; ViewDetailsRow(node, viewDetails); } } i++; } } targetOutputs.EndUpdate(); targetOutputs.NodesSelection.Clear(); targetOutputs.SetVScrollValue(vs); vs = blendOperations.VScrollValue(); blendOperations.BeginUpdate(); blendOperations.Nodes.Clear(); { int i = 0; foreach(var blend in state.CB.attachments) { bool filledSlot = true; bool usedSlot = (targets[i]); // show if if (usedSlot || // it's referenced by the shader - regardless of empty or not (showDisabled.Checked && !usedSlot && filledSlot) || // it's bound, but not referenced, and we have "show disabled" (showEmpty.Checked && !filledSlot) // it's empty, and we have "show empty" ) { var node = blendOperations.Nodes.Add(new object[] { i, blend.blendEnable, blend.m_Blend.Source, blend.m_Blend.Destination, blend.m_Blend.Operation, blend.m_AlphaBlend.Source, blend.m_AlphaBlend.Destination, blend.m_AlphaBlend.Operation, ((blend.WriteMask & 0x1) == 0 ? "_" : "R") + ((blend.WriteMask & 0x2) == 0 ? "_" : "G") + ((blend.WriteMask & 0x4) == 0 ? "_" : "B") + ((blend.WriteMask & 0x8) == 0 ? "_" : "A") }); if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); } i++; } } blendOperations.NodesSelection.Clear(); blendOperations.EndUpdate(); blendOperations.SetVScrollValue(vs); blendFactor.Text = state.CB.blendConst[0].ToString("F2") + ", " + state.CB.blendConst[1].ToString("F2") + ", " + state.CB.blendConst[2].ToString("F2") + ", " + state.CB.blendConst[3].ToString("F2"); logicOp.Text = state.CB.logicOpEnable ? state.CB.LogicOp : "-"; alphaToOne.Image = state.CB.alphaToOneEnable ? tick : cross; depthEnable.Image = state.DS.depthTestEnable ? tick : cross; depthFunc.Text = state.DS.depthCompareOp; depthWrite.Image = state.DS.depthWriteEnable ? tick : cross; if (state.DS.depthBoundsEnable) { depthBounds.Text = Formatter.Format(state.DS.minDepthBounds) + "-" + Formatter.Format(state.DS.maxDepthBounds); depthBounds.Image = null; } else { depthBounds.Text = ""; depthBounds.Image = cross; } stencilFuncs.BeginUpdate(); stencilFuncs.Nodes.Clear(); if (state.DS.stencilTestEnable) { stencilFuncs.Nodes.Add(new object[] { "Front", state.DS.front.func, state.DS.front.failOp, state.DS.front.depthFailOp, state.DS.front.passOp, state.DS.front.writeMask.ToString("X2"), state.DS.front.compareMask.ToString("X2"), state.DS.front.stencilref.ToString("X2")}); stencilFuncs.Nodes.Add(new object[] { "Back", state.DS.back.func, state.DS.back.failOp, state.DS.back.depthFailOp, state.DS.back.passOp, state.DS.back.writeMask.ToString("X2"), state.DS.back.compareMask.ToString("X2"), state.DS.back.stencilref.ToString("X2")}); } else { stencilFuncs.Nodes.Add(new object[] { "Front", "-", "-", "-", "-", "-", "-", "-" }); stencilFuncs.Nodes.Add(new object[] { "Back", "-", "-", "-", "-", "-", "-", "-" }); } stencilFuncs.EndUpdate(); stencilFuncs.NodesSelection.Clear(); // highlight the appropriate stages in the flowchart if (draw == null) { pipeFlow.SetStagesEnabled(new bool[] { true, true, true, true, true, true, true, true, true }); } else if ((draw.flags & DrawcallFlags.Dispatch) != 0) { pipeFlow.SetStagesEnabled(new bool[] { false, false, false, false, false, false, false, false, true }); } else { pipeFlow.SetStagesEnabled(new bool[] { true, true, state.TCS.Shader != ResourceId.Null, state.TES.Shader != ResourceId.Null, state.GS.Shader != ResourceId.Null, true, state.FS.Shader != ResourceId.Null, true, false }); // if(streamout only) //{ // pipeFlow.Rasterizer = false; // pipeFlow.OutputMerger = false; //} } }
private void UpdateState() { if (!m_Core.LogLoaded) return; FetchTexture[] texs = m_Core.CurTextures; FetchBuffer[] bufs = m_Core.CurBuffers; D3D12PipelineState state = m_Core.CurD3D12PipelineState; FetchDrawcall draw = m_Core.CurDrawcall; HideViewDetailsTooltip(); m_ViewDetailNodes.Clear(); var tick = global::renderdocui.Properties.Resources.tick; var cross = global::renderdocui.Properties.Resources.cross; bool[] usedVBuffers = new bool[128]; UInt32[] layoutOffs = new UInt32[128]; for (int i = 0; i < 128; i++) { usedVBuffers[i] = false; layoutOffs[i] = 0; } //////////////////////////////////////////////// // Input Assembler int vs = 0; vs = inputLayouts.VScrollValue(); inputLayouts.Nodes.Clear(); inputLayouts.BeginUpdate(); if (state.m_IA.layouts != null) { int i = 0; foreach (var l in state.m_IA.layouts) { string byteOffs = l.ByteOffset.ToString(); // D3D12 specific value if (l.ByteOffset == uint.MaxValue) { byteOffs = String.Format("APPEND_ALIGNED ({0})", layoutOffs[l.InputSlot]); } else { layoutOffs[l.InputSlot] = l.ByteOffset; } layoutOffs[l.InputSlot] += l.Format.compByteWidth * l.Format.compCount; bool iaUsed = false; if (state.m_VS.Shader != ResourceId.Null) { for (int ia = 0; ia < state.m_VS.ShaderDetails.InputSig.Length; ia++) { if (state.m_VS.ShaderDetails.InputSig[ia].semanticName.ToUpperInvariant() == l.SemanticName.ToUpperInvariant() && state.m_VS.ShaderDetails.InputSig[ia].semanticIndex == l.SemanticIndex) { iaUsed = true; break; } } } i++; if(!iaUsed && !showDisabled.Checked) continue; var node = inputLayouts.Nodes.Add(new object[] { i, l.SemanticName, l.SemanticIndex.ToString(), l.Format, l.InputSlot.ToString(), byteOffs, l.PerInstance ? "PER_INSTANCE" : "PER_VERTEX", l.InstanceDataStepRate.ToString() }); if (iaUsed) usedVBuffers[l.InputSlot] = true; node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; if (!iaUsed) InactiveRow(node); } } inputLayouts.NodesSelection.Clear(); inputLayouts.EndUpdate(); inputLayouts.SetVScrollValue(vs); PrimitiveTopology topo = draw != null ? draw.topology : PrimitiveTopology.Unknown; topology.Text = topo.ToString(); if (topo > PrimitiveTopology.PatchList) { int numCPs = (int)topo - (int)PrimitiveTopology.PatchList + 1; topology.Text = string.Format("PatchList ({0} Control Points)", numCPs); } switch (topo) { case PrimitiveTopology.PointList: topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_pointlist; break; case PrimitiveTopology.LineList: topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_linelist; break; case PrimitiveTopology.LineStrip: topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_linestrip; break; case PrimitiveTopology.TriangleList: topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_trilist; break; case PrimitiveTopology.TriangleStrip: topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_tristrip; break; case PrimitiveTopology.LineList_Adj: topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_linelist_adj; break; case PrimitiveTopology.LineStrip_Adj: topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_linestrip_adj; break; case PrimitiveTopology.TriangleList_Adj: topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_trilist_adj; break; case PrimitiveTopology.TriangleStrip_Adj: topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_tristrip_adj; break; default: topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_patch; break; } vs = iabuffers.VScrollValue(); iabuffers.Nodes.Clear(); iabuffers.BeginUpdate(); bool ibufferUsed = draw != null && (draw.flags & DrawcallFlags.UseIBuffer) != 0; if (ibufferUsed) { if (state.m_IA.indexStripCutValue != 0) restartIndex.Text = String.Format("Restart Idx: 0x{0:X}", state.m_IA.indexStripCutValue); else restartIndex.Text = "Restart Idx: Disabled"; } else { restartIndex.Text = ""; } if (state.m_IA.ibuffer != null && state.m_IA.ibuffer.Buffer != ResourceId.Null) { if (ibufferUsed || showDisabled.Checked) { string ptr = "Buffer " + state.m_IA.ibuffer.Buffer.ToString(); string name = ptr; UInt64 length = 1; if (!ibufferUsed) { length = 0; } for (int t = 0; t < bufs.Length; t++) { if (bufs[t].ID == state.m_IA.ibuffer.Buffer) { name = bufs[t].name; length = bufs[t].length; } } var node = iabuffers.Nodes.Add(new object[] { "Index", name, draw != null ? draw.indexByteWidth : 0, state.m_IA.ibuffer.Offset, length }); node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; node.Tag = new IABufferTag(state.m_IA.ibuffer.Buffer, draw != null ? draw.indexOffset : 0); if (!ibufferUsed) InactiveRow(node); if (state.m_IA.ibuffer.Buffer == ResourceId.Null) EmptyRow(node); } } else { if (ibufferUsed || showEmpty.Checked) { var node = iabuffers.Nodes.Add(new object[] { "Index", "No Buffer Set", "-", "-", "-" }); node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; node.Tag = new IABufferTag(state.m_IA.ibuffer.Buffer, draw != null ? draw.indexOffset : 0); EmptyRow(node); if (!ibufferUsed) InactiveRow(node); } } m_VBNodes.Clear(); if (state.m_IA.vbuffers != null) { int i = 0; foreach (var v in state.m_IA.vbuffers) { bool filledSlot = (v.Buffer != ResourceId.Null); bool usedSlot = (usedVBuffers[i]); // show if if (usedSlot || // it's referenced by the shader - regardless of empty or not (showDisabled.Checked && !usedSlot && filledSlot) || // it's bound, but not referenced, and we have "show disabled" (showEmpty.Checked && !filledSlot) // it's empty, and we have "show empty" ) { string name = "Buffer " + v.Buffer.ToString(); UInt64 length = 1; for (int t = 0; t < bufs.Length; t++) { if (bufs[t].ID == v.Buffer) { name = bufs[t].name; length = bufs[t].length; } } TreelistView.Node node = null; if(filledSlot) node = iabuffers.Nodes.Add(new object[] { i, name, v.Stride, v.Offset, length }); else node = iabuffers.Nodes.Add(new object[] { i, "No Buffer Set", "-", "-", "-" }); node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; node.Tag = new IABufferTag(v.Buffer, v.Offset); if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); m_VBNodes.Add(node); } i++; } } iabuffers.NodesSelection.Clear(); iabuffers.EndUpdate(); iabuffers.SetVScrollValue(vs); SetShaderState(state.m_VS, vsShader, vsResources, vsSamplers, vsCBuffers, vsUAVs); SetShaderState(state.m_GS, gsShader, gsResources, gsSamplers, gsCBuffers, gsUAVs); SetShaderState(state.m_HS, hsShader, hsResources, hsSamplers, hsCBuffers, hsUAVs); SetShaderState(state.m_DS, dsShader, dsResources, dsSamplers, dsCBuffers, dsUAVs); SetShaderState(state.m_PS, psShader, psResources, psSamplers, psCBuffers, psUAVs); SetShaderState(state.m_CS, csShader, csResources, csSamplers, csCBuffers, csUAVs); bool streamoutSet = false; vs = gsStreams.VScrollValue(); gsStreams.BeginUpdate(); gsStreams.Nodes.Clear(); if (state.m_SO.Outputs != null) { int i = 0; foreach (var s in state.m_SO.Outputs) { bool filledSlot = (s.Buffer != ResourceId.Null); bool usedSlot = (filledSlot); streamoutSet |= filledSlot; // show if if (usedSlot || // it's referenced by the shader - regardless of empty or not (showDisabled.Checked && !usedSlot && filledSlot) || // it's bound, but not referenced, and we have "show disabled" (showEmpty.Checked && !filledSlot) // it's empty, and we have "show empty" ) { string name = "Buffer " + s.Buffer.ToString(); UInt64 length = 0; if (!filledSlot) { name = "Empty"; } FetchBuffer fetch = null; for (int t = 0; t < bufs.Length; t++) { if (bufs[t].ID == s.Buffer) { name = bufs[t].name; length = bufs[t].length; fetch = bufs[t]; } } var node = gsStreams.Nodes.Add(new object[] { i, name, length, s.Offset }); node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; node.Tag = fetch; if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); } i++; } } gsStreams.EndUpdate(); gsStreams.NodesSelection.Clear(); gsStreams.SetVScrollValue(vs); gsStreams.Visible = gsStreams.Parent.Visible = streamoutSet; if (streamoutSet) geomTableLayout.ColumnStyles[1].Width = 50.0f; else geomTableLayout.ColumnStyles[1].Width = 0; //////////////////////////////////////////////// // Rasterizer vs = viewports.VScrollValue(); viewports.BeginUpdate(); viewports.Nodes.Clear(); if (state.m_RS.Viewports != null) { int i = 0; foreach (var v in state.m_RS.Viewports) { var node = viewports.Nodes.Add(new object[] { i, v.TopLeft[0], v.TopLeft[1], v.Width, v.Height, v.MinDepth, v.MaxDepth }); if (v.Width == 0 || v.Height == 0 || v.MinDepth == v.MaxDepth) EmptyRow(node); i++; } } viewports.NodesSelection.Clear(); viewports.EndUpdate(); viewports.SetVScrollValue(vs); vs = scissors.VScrollValue(); scissors.BeginUpdate(); scissors.Nodes.Clear(); if (state.m_RS.Scissors != null) { int i = 0; foreach (var s in state.m_RS.Scissors) { var node = scissors.Nodes.Add(new object[] { i, s.left, s.top, s.right - s.left, s.bottom - s.top }); if (s.right == s.left || s.bottom == s.top) EmptyRow(node); i++; } } scissors.NodesSelection.Clear(); scissors.EndUpdate(); scissors.SetVScrollValue(vs); fillMode.Text = state.m_RS.m_State.FillMode.ToString(); cullMode.Text = state.m_RS.m_State.CullMode.ToString(); frontCCW.Image = state.m_RS.m_State.FrontCCW ? tick : cross; lineAAEnable.Image = state.m_RS.m_State.AntialiasedLineEnable ? tick : cross; multisampleEnable.Image = state.m_RS.m_State.MultisampleEnable ? tick : cross; depthClip.Image = state.m_RS.m_State.DepthClip ? tick : cross; depthBias.Text = state.m_RS.m_State.DepthBias.ToString(); depthBiasClamp.Text = Formatter.Format(state.m_RS.m_State.DepthBiasClamp); slopeScaledBias.Text = Formatter.Format(state.m_RS.m_State.SlopeScaledDepthBias); forcedSampleCount.Text = state.m_RS.m_State.ForcedSampleCount.ToString(); conservativeRaster.Image = state.m_RS.m_State.ConservativeRasterization ? tick : cross; //////////////////////////////////////////////// // Output Merger bool[] targets = new bool[8]; for (int i = 0; i < 8; i++) targets[i] = false; vs = targetOutputs.VScrollValue(); targetOutputs.BeginUpdate(); targetOutputs.Nodes.Clear(); if (state.m_OM.RenderTargets != null) { int i = 0; foreach (var p in state.m_OM.RenderTargets) { if (p.Resource != ResourceId.Null || showEmpty.Checked) { UInt32 w = 1, h = 1, d = 1; UInt32 a = 1; string format = "Unknown"; string name = "Texture " + p.ToString(); string typename = "Unknown"; object tag = null; bool viewDetails = false; if (p.Resource == ResourceId.Null) { name = "Empty"; format = "-"; typename = "-"; w = h = d = a = 0; } for (int t = 0; t < texs.Length; t++) { if (texs[t].ID == p.Resource) { w = texs[t].width; h = texs[t].height; d = texs[t].depth; a = texs[t].arraysize; format = texs[t].format.ToString(); name = texs[t].name; typename = texs[t].resType.Str(); if (texs[t].resType == ShaderResourceType.Texture2DMS || texs[t].resType == ShaderResourceType.Texture2DMSArray) { typename += String.Format(" {0}x", texs[t].msSamp); } // if it's a typeless format, show the format of the view if (texs[t].format != p.Format) { format = "Viewed as " + p.Format.ToString(); } if (HasImportantViewParams(p, texs[t])) viewDetails = true; tag = new ViewTexTag(p, texs[t], false, null); } } var node = targetOutputs.Nodes.Add(new object[] { i, name, typename, w, h, d, a, format }); node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; node.Tag = tag; if (p.Resource == ResourceId.Null) { EmptyRow(node); } else { targets[i] = true; ViewDetailsRow(node, viewDetails); } } i++; } } if (state.m_OM.DepthTarget.Resource != ResourceId.Null || showEmpty.Checked) { UInt32 w = 1, h = 1, d = 1; UInt32 a = 1; string format = "Unknown"; string name = "Depth Target " + state.m_OM.DepthTarget.Resource.ToString(); string typename = "Unknown"; object tag = null; bool viewDetails = false; if (state.m_OM.DepthTarget.Resource == ResourceId.Null) { name = "Empty"; format = "-"; typename = "-"; w = h = d = a = 0; } for (int t = 0; t < texs.Length; t++) { if (texs[t].ID == state.m_OM.DepthTarget.Resource) { w = texs[t].width; h = texs[t].height; d = texs[t].depth; a = texs[t].arraysize; format = texs[t].format.ToString(); name = texs[t].name; typename = texs[t].resType.Str(); if (texs[t].resType == ShaderResourceType.Texture2DMS || texs[t].resType == ShaderResourceType.Texture2DMSArray) { typename += String.Format(" {0}x", texs[t].msSamp); } // if it's a typeless format, show the format of the view if (texs[t].format != state.m_OM.DepthTarget.Format) { format = "Viewed as " + state.m_OM.DepthTarget.Format.ToString(); } if (HasImportantViewParams(state.m_OM.DepthTarget, texs[t])) viewDetails = true; tag = new ViewTexTag(state.m_OM.DepthTarget, texs[t], false, null); } } var node = targetOutputs.Nodes.Add(new object[] { "Depth", name, typename, w, h, d, a, format }); node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; node.Tag = tag; ViewDetailsRow(node, viewDetails); if (state.m_OM.DepthTarget.Resource == ResourceId.Null) EmptyRow(node); } targetOutputs.EndUpdate(); targetOutputs.NodesSelection.Clear(); targetOutputs.SetVScrollValue(vs); vs = blendOperations.VScrollValue(); blendOperations.BeginUpdate(); blendOperations.Nodes.Clear(); { int i = 0; foreach(var blend in state.m_OM.m_BlendState.Blends) { bool filledSlot = (blend.Enabled == true || targets[i]); bool usedSlot = (targets[i]); // show if if (usedSlot || // it's referenced by the shader - regardless of empty or not (showDisabled.Checked && !usedSlot && filledSlot) || // it's bound, but not referenced, and we have "show disabled" (showEmpty.Checked && !filledSlot) // it's empty, and we have "show empty" ) { var node = blendOperations.Nodes.Add(new object[] { i, blend.Enabled, blend.LogicEnabled, blend.m_Blend.Source, blend.m_Blend.Destination, blend.m_Blend.Operation, blend.m_AlphaBlend.Source, blend.m_AlphaBlend.Destination, blend.m_AlphaBlend.Operation, blend.LogicOp, ((blend.WriteMask & 0x1) == 0 ? "_" : "R") + ((blend.WriteMask & 0x2) == 0 ? "_" : "G") + ((blend.WriteMask & 0x4) == 0 ? "_" : "B") + ((blend.WriteMask & 0x8) == 0 ? "_" : "A") }); if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); } i++; } } blendOperations.NodesSelection.Clear(); blendOperations.EndUpdate(); blendOperations.SetVScrollValue(vs); alphaToCoverage.Image = state.m_OM.m_BlendState.AlphaToCoverage ? tick : cross; independentBlend.Image = state.m_OM.m_BlendState.IndependentBlend ? tick : cross; blendFactor.Text = state.m_OM.m_BlendState.BlendFactor[0].ToString("F2") + ", " + state.m_OM.m_BlendState.BlendFactor[1].ToString("F2") + ", " + state.m_OM.m_BlendState.BlendFactor[2].ToString("F2") + ", " + state.m_OM.m_BlendState.BlendFactor[3].ToString("F2"); sampleMask.Text = state.m_RS.SampleMask.ToString("X8"); depthEnable.Image = state.m_OM.m_State.DepthEnable ? tick : cross; depthFunc.Text = state.m_OM.m_State.DepthFunc; depthWrite.Image = state.m_OM.m_State.DepthWrites ? tick : cross; stencilEnable.Image = state.m_OM.m_State.StencilEnable ? tick : cross; stencilReadMask.Text = state.m_OM.m_State.StencilReadMask.ToString("X2"); stencilWriteMask.Text = state.m_OM.m_State.StencilWriteMask.ToString("X2"); stencilRef.Text = state.m_OM.m_State.StencilRef.ToString("X2"); stencilFuncs.BeginUpdate(); stencilFuncs.Nodes.Clear(); stencilFuncs.Nodes.Add(new object[] { "Front", state.m_OM.m_State.m_FrontFace.Func, state.m_OM.m_State.m_FrontFace.FailOp, state.m_OM.m_State.m_FrontFace.DepthFailOp, state.m_OM.m_State.m_FrontFace.PassOp }); stencilFuncs.Nodes.Add(new object[] { "Back", state.m_OM.m_State.m_BackFace.Func, state.m_OM.m_State.m_BackFace.FailOp, state.m_OM.m_State.m_BackFace.DepthFailOp, state.m_OM.m_State.m_BackFace.PassOp }); stencilFuncs.EndUpdate(); stencilFuncs.NodesSelection.Clear(); // highlight the appropriate stages in the flowchart if (draw == null) { pipeFlow.SetStagesEnabled(new bool[] { true, true, true, true, true, true, true, true, true }); } else if ((draw.flags & DrawcallFlags.Dispatch) != 0) { pipeFlow.SetStagesEnabled(new bool[] { false, false, false, false, false, false, false, false, true }); } else { pipeFlow.SetStagesEnabled(new bool[] { true, true, state.m_HS.Shader != ResourceId.Null, state.m_DS.Shader != ResourceId.Null, state.m_GS.Shader != ResourceId.Null, true, state.m_PS.Shader != ResourceId.Null, true, false }); // if(streamout only) //{ // pipeFlow.Rasterizer = false; // pipeFlow.OutputMerger = false; //} } }
private void AddResourceRow(D3D12PipelineState.ShaderStage stage, TreelistView.TreeListView list, int space, int reg, bool uav) { D3D12PipelineState state = m_Core.CurD3D12PipelineState; FetchTexture[] texs = m_Core.CurTextures; FetchBuffer[] bufs = m_Core.CurBuffers; BindpointMap bind = null; ShaderResource shaderInput = null; D3D12PipelineState.ResourceView r = uav ? stage.Spaces[space].UAVs[reg] : stage.Spaces[space].SRVs[reg]; // consider this register to not exist - it's in a gap defined by sparse root signature elements if (r.RootElement == uint.MaxValue) return; if (stage.BindpointMapping != null && stage.ShaderDetails != null) { BindpointMap[] binds = uav ? stage.BindpointMapping.ReadWriteResources : stage.BindpointMapping.ReadOnlyResources; ShaderResource[] resources = uav ? stage.ShaderDetails.ReadWriteResources : stage.ShaderDetails.ReadOnlyResources; for (int i=0; i < binds.Length; i++) { var b = binds[i]; var res = resources[i]; bool regMatch = b.bind == reg; // handle unbounded arrays specially. It's illegal to have an unbounded array with // anything after it if (b.bind <= reg) regMatch = (b.arraySize == UInt32.MaxValue) || (b.bind + b.arraySize > reg); if (b.bindset == space && regMatch && !res.IsSampler) { bind = b; shaderInput = res; break; } } } TreelistView.NodeCollection parent = list.Nodes; string rootel = r.Immediate ? String.Format("#{0} Direct", r.RootElement) : rootel = String.Format("#{0} Table[{1}]", r.RootElement, r.TableIndex); bool filledSlot = r.Resource != ResourceId.Null; bool usedSlot = (bind != null && bind.used); // show if if (usedSlot || // it's referenced by the shader - regardless of empty or not (showDisabled.Checked && !usedSlot && filledSlot) || // it's bound, but not referenced, and we have "show disabled" (showEmpty.Checked && !filledSlot) // it's empty, and we have "show empty" ) { string regname = reg.ToString(); if (shaderInput != null && shaderInput.name.Length > 0) regname += ": " + shaderInput.name; UInt64 w = 1; UInt32 h = 1, d = 1; UInt32 a = 1; string format = "Unknown"; string name = "Unbound"; string typename = "Unknown"; object tag = null; bool viewDetails = false; if (!filledSlot) { name = "Empty"; format = "-"; typename = "-"; w = h = d = a = 0; } if (r != null) { name = "Shader Resource " + r.Resource.ToString(); // check to see if it's a texture for (int t = 0; t < texs.Length; t++) { if (texs[t].ID == r.Resource) { w = texs[t].width; h = texs[t].height; d = texs[t].depth; a = texs[t].arraysize; format = texs[t].format.ToString(); name = texs[t].name; typename = texs[t].resType.Str(); if (texs[t].resType == ShaderResourceType.Texture2DMS || texs[t].resType == ShaderResourceType.Texture2DMSArray) { typename += String.Format(" {0}x", texs[t].msSamp); } // if it's a typeless format, show the format of the view if (texs[t].format != r.Format) { format = "Viewed as " + r.Format.ToString(); } tag = new ViewTexTag(r, texs[t], true, shaderInput); if (HasImportantViewParams(r, texs[t])) viewDetails = true; } } // if not a texture, it must be a buffer for (int t = 0; t < bufs.Length; t++) { if (bufs[t].ID == r.Resource) { w = bufs[t].length; h = 1; d = 1; a = 1; format = ""; name = bufs[t].name; typename = uav ? "RWBuffer" : "Buffer"; if (r.BufferFlags.HasFlag(D3DBufferViewFlags.Raw)) { typename = uav ? "RWByteAddressBuffer" : "ByteAddressBuffer"; } else if (r.ElementSize > 0) { // for structured buffers, display how many 'elements' there are in the buffer typename = (uav ? "RWStructuredBuffer" : "StructuredBuffer"); a = (uint)(bufs[t].length / r.ElementSize); } if (r.CounterResource != ResourceId.Null) { typename += " (Count: " + r.BufferStructCount + ")"; } // get the buffer type, whether it's just a basic type or a complex struct if (shaderInput != null && !shaderInput.IsTexture) { if (shaderInput.variableType.members.Length > 0) format = "struct " + shaderInput.variableType.Name; else if (r.Format.compType == FormatComponentType.None) format = shaderInput.variableType.Name; else format = r.Format.ToString(); } tag = new ViewBufTag(r, bufs[t], true, shaderInput); if (HasImportantViewParams(r, bufs[t])) viewDetails = true; } } } var node = parent.Add(new object[] { rootel, space, regname, name, typename, w, h, d, a, format }); node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; node.Tag = tag; if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); ViewDetailsRow(node, viewDetails); } }