private void ShowCBuffer(D3D12PipelineState.ShaderStage stage, CBufTag tag) { if (tag.idx == uint.MaxValue) { // unused cbuffer, open regular buffer viewer var viewer = new BufferViewer(m_Core, false); var buf = stage.Spaces[tag.space].ConstantBuffers[tag.reg]; viewer.ViewRawBuffer(true, buf.Offset, buf.ByteSize, buf.Buffer); viewer.Show(m_DockContent.DockPanel); return; } var existing = ConstantBufferPreviewer.Has(stage.stage, tag.idx, 0); if (existing != null) { existing.Show(); return; } var prev = new ConstantBufferPreviewer(m_Core, stage.stage, tag.idx, 0); prev.ShowDock(m_DockContent.Pane, DockAlignment.Right, 0.3); }
// Set a shader stage's resources and values private void SetShaderState(D3D12PipelineState.ShaderStage stage, Label shader, TreelistView.TreeListView resources, TreelistView.TreeListView samplers, TreelistView.TreeListView cbuffers, TreelistView.TreeListView uavs) { FetchTexture[] texs = m_Core.CurTextures; FetchBuffer[] bufs = m_Core.CurBuffers; D3D12PipelineState state = m_Core.CurD3D12PipelineState; ShaderReflection shaderDetails = stage.ShaderDetails; ShaderBindpointMapping bindpointMapping = stage.BindpointMapping; if (stage.Shader == ResourceId.Null) shader.Text = "Unbound"; else if (state.customName) shader.Text = state.PipelineName + " - " + m_Core.CurPipelineState.Abbrev(stage.stage); else shader.Text = state.PipelineName + " - " + stage.stage.Str(GraphicsAPI.D3D12) + " Shader"; 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(); for (int space = 0; space < stage.Spaces.Length; space++) { for (int reg = 0; reg < stage.Spaces[space].SRVs.Length; reg++) { AddResourceRow(stage, resources, space, reg, false); } } resources.EndUpdate(); resources.NodesSelection.Clear(); resources.SetVScrollValue(vs); vs = uavs.VScrollValue(); uavs.BeginUpdate(); uavs.Nodes.Clear(); for (int space = 0; space < stage.Spaces.Length; space++) { for (int reg = 0; reg < stage.Spaces[space].UAVs.Length; reg++) { AddResourceRow(stage, uavs, space, reg, true); } } uavs.EndUpdate(); uavs.NodesSelection.Clear(); uavs.SetVScrollValue(vs); vs = samplers.VScrollValue(); samplers.BeginUpdate(); samplers.Nodes.Clear(); for (int space = 0; space < stage.Spaces.Length; space++) { for (int reg = 0; reg < stage.Spaces[space].Samplers.Length; reg++) { D3D12PipelineState.Sampler s = stage.Spaces[space].Samplers[reg]; // consider this register to not exist - it's in a gap defined by sparse root signature elements if (s.RootElement == uint.MaxValue) continue; BindpointMap bind = null; ShaderResource shaderInput = null; if (stage.BindpointMapping != null && stage.ShaderDetails != null) { for (int i = 0; i < stage.BindpointMapping.ReadOnlyResources.Length; i++) { var b = stage.BindpointMapping.ReadOnlyResources[i]; var res = stage.ShaderDetails.ReadOnlyResources[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; } } } string rootel = s.Immediate ? String.Format("#{0} Static", s.RootElement) : String.Format("#{0} Table[{1}]", s.RootElement, s.TableIndex); bool filledSlot = (s.AddressU.Length > 0); 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; string borderColor = ""; string addressing = ""; string addPrefix = ""; string addVal = ""; string[] addr = { "", "", "" }; if (s != null) { borderColor = s.BorderColor[0].ToString() + ", " + s.BorderColor[1].ToString() + ", " + s.BorderColor[2].ToString() + ", " + s.BorderColor[3].ToString(); addr[0] = s.AddressU; addr[1] = s.AddressV; addr[2] = 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; string filter = ""; string lodclamp = ""; float lodbias = 0.0f; if (s != null) { if (s.UseBorder) addressing += String.Format("<{0}>", borderColor); filter = s.Filter; if (s.MaxAniso > 0) filter += String.Format(" {0}x", s.MaxAniso); if (s.UseComparison) filter += String.Format(" ({0})", s.Comparison); lodclamp = (s.MinLOD == -float.MaxValue ? "0" : s.MinLOD.ToString()) + " - " + (s.MaxLOD == float.MaxValue ? "FLT_MAX" : s.MaxLOD.ToString()); lodbias = s.MipLODBias; } var node = samplers.Nodes.Add(new object[] { rootel, space, regname, addressing, filter, lodclamp, lodbias.ToString() }); if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); } } } samplers.EndUpdate(); samplers.NodesSelection.Clear(); samplers.SetVScrollValue(vs); vs = cbuffers.VScrollValue(); cbuffers.BeginUpdate(); cbuffers.Nodes.Clear(); for (int space = 0; space < stage.Spaces.Length; space++) { for (int reg = 0; reg < stage.Spaces[space].ConstantBuffers.Length; reg++) { D3D12PipelineState.CBuffer b = stage.Spaces[space].ConstantBuffers[reg]; // consider this register to not exist - it's in a gap defined by sparse root signature elements if (b.RootElement == uint.MaxValue) continue; BindpointMap bind = null; ConstantBlock shaderCBuf = null; object tag = null; if (stage.BindpointMapping != null && stage.ShaderDetails != null) { for (int i = 0; i < stage.BindpointMapping.ConstantBlocks.Length; i++) { var bd = stage.BindpointMapping.ConstantBlocks[i]; var res = stage.ShaderDetails.ConstantBlocks[i]; bool regMatch = bd.bind == reg; // handle unbounded arrays specially. It's illegal to have an unbounded array with // anything after it if (bd.bind <= reg) regMatch = (bd.arraySize == UInt32.MaxValue) || (bd.bind + bd.arraySize > reg); if (bd.bindset == space && regMatch) { bind = bd; shaderCBuf = res; tag = new CBufTag((uint)i); break; } } } if(tag == null) tag = new CBufTag(space, reg); string rootel; if (b.Immediate) { if (b.RootValues.Length > 0) rootel = String.Format("#{0} Consts", b.RootElement); else rootel = String.Format("#{0} Direct", b.RootElement); } else { rootel = String.Format("#{0} Table[{1}]", b.RootElement, b.TableIndex); } bool filledSlot = (b.Buffer != ResourceId.Null); if (b.Immediate && b.RootValues.Length > 0) filledSlot = true; 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 name = "Constant Buffer " + b.Buffer.ToString(); UInt64 length = 0; UInt64 offset = 0; int numvars = shaderCBuf != null ? shaderCBuf.variables.Length : 0; UInt32 byteSize = shaderCBuf != null ? shaderCBuf.byteSize : 0; if (b.Immediate && b.RootValues.Length > 0) byteSize = (UInt32)(b.RootValues.Length * 4); if (!filledSlot) name = "Empty"; if (b != null) { offset = b.Offset; length = b.ByteSize; for (int t = 0; t < bufs.Length; t++) if (bufs[t].ID == b.Buffer) name = bufs[t].name; } string regname = reg.ToString(); if (shaderCBuf != null && shaderCBuf.name.Length > 0) regname += ": " + 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; var node = cbuffers.Nodes.Add(new object[] { rootel, space, regname, name, offset, sizestr }); 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); } } } cbuffers.EndUpdate(); cbuffers.NodesSelection.Clear(); cbuffers.SetVScrollValue(vs); }