// 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 UI_SetHistogramRange(FetchTexture tex, FormatComponentType typeHint) { if (tex != null && (tex.format.compType == FormatComponentType.SNorm || typeHint == FormatComponentType.SNorm)) rangeHistogram.SetRange(-1.0f, 1.0f); else rangeHistogram.SetRange(0.0f, 1.0f); }
private bool HasImportantViewParams(D3D11PipelineState.ShaderStage.ResourceView view, FetchTexture tex) { // we don't count 'upgrade typeless to typed' as important, we just display the typed format // in the row since there's no real hidden important information there. The formats can't be // different for any other reason (if the SRV format differs from the texture format, the // texture must have been typeless. if (view.HighestMip > 0 || view.FirstArraySlice > 0 || (view.NumMipLevels < tex.mips && tex.mips > 1) || (view.ArraySize < tex.arraysize && tex.arraysize > 1)) return true; // in the case of the swapchain case, types can be different and it won't have shown // up as taking the view's format because the swapchain already has one. Make sure to mark it // as important if (view.Format.compType != FormatComponentType.None && view.Format != tex.format) return true; return 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 != "" && shaderDetails.DebugInfo.files.Length > 0) shader.Text = shaderDetails.DebugInfo.entryFunc + "()" + " - " + Path.GetFileName(shaderDetails.DebugInfo.files[0].filename); 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.Resources) { if (bind.IsSRV && bind.bindPoint == i) shaderInput = bind; } } 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 != "") slotname += ": " + shaderInput.name; UInt32 w = 1, h = 1, d = 1; UInt32 a = 1; string format = "Unknown"; string name = "Shader Resource " + r.Resource.ToString(); string typename = "Unknown"; object tag = null; 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 = string.Format("Texture{0}D", texs[t].dimension); if(texs[t].cubemap) typename = "TexCube"; tag = texs[t]; } } // 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 (bufs[t].structureSize > 0) typename = "StructuredBuffer[" + (bufs[t].length / bufs[t].structureSize) + "]"; // 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 = bufs[t]; } } 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); } i++; } } resources.EndUpdate(); resources.NodesSelection.Clear(); 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.Resources) { if (bind.IsSampler && bind.bindPoint == i) shaderInput = bind; } } bool filledSlot = (s.AddressU != ""); 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 != "") slotname += ": " + shaderInput.name; 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; var node = samplers.Nodes.Add(new object[] { slotname, addressing, borderColor, s.Comparison, s.Filter, s.MaxAniso.ToString(), s.MinLOD == -float.MaxValue ? "-FLT_MAX" : s.MinLOD.ToString(), s.MaxLOD == float.MaxValue ? "FLT_MAX" : s.MaxLOD.ToString(), s.MipLODBias.ToString() }); if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); } i++; } } samplers.EndUpdate(); samplers.NodesSelection.Clear(); 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 != "") 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(); UInt32 length = 1; int numvars = shaderCBuf != null ? shaderCBuf.variables.Length : 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 != "") slotname += ": " + shaderCBuf.name; var node = cbuffers.Nodes.Add(new object[] { slotname, name, b.VecOffset, b.VecCount, numvars, length }); 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(); 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(); }
// Set a shader stage's resources and values private void SetShaderState(FetchTexture[] texs, FetchBuffer[] bufs, GLPipelineState state, GLPipelineState.ShaderStage stage, TableLayoutPanel table, Label shader, TreelistView.TreeListView textures, TreelistView.TreeListView samplers, TreelistView.TreeListView cbuffers, TreelistView.TreeListView subs, TreelistView.TreeListView readwrites) { ShaderReflection shaderDetails = stage.ShaderDetails; var mapping = stage.BindpointMapping; if (stage.Shader == ResourceId.Null) shader.Text = "Unbound"; else shader.Text = stage.stage.Str(APIPipelineStateType.OpenGL) + " Shader " + stage.Shader.ToString(); // disabled since entry function is always main, and filenames have no names, so this is useless. /* if (shaderDetails != null && shaderDetails.DebugInfo.entryFunc != "" && shaderDetails.DebugInfo.files.Length > 0) shader.Text = shaderDetails.DebugInfo.entryFunc + "()" + " - " + Path.GetFileName(shaderDetails.DebugInfo.files[0].filename); */ int vs = 0; int vs2 = 0; // simultaneous update of resources and samplers vs = textures.VScrollValue(); textures.BeginUpdate(); textures.Nodes.Clear(); vs2 = samplers.VScrollValue(); samplers.BeginUpdate(); samplers.Nodes.Clear(); if (state.Textures != null) { for (int i = 0; i < state.Textures.Length; i++) { var r = state.Textures[i]; var s = state.Samplers[i]; ShaderResource shaderInput = null; BindpointMap map = null; if (shaderDetails != null) { foreach (var bind in shaderDetails.Resources) { if (bind.IsSRV && !bind.IsReadWrite && mapping.Resources[bind.bindPoint].bind == i) { shaderInput = bind; map = mapping.Resources[bind.bindPoint]; } } } bool filledSlot = (r.Resource != ResourceId.Null); bool usedSlot = (shaderInput != null && map.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" ) { // do texture { string slotname = i.ToString(); if (shaderInput != null && shaderInput.name != "") slotname += ": " + shaderInput.name; UInt32 w = 1, h = 1, d = 1; UInt32 a = 1; string format = "Unknown"; string name = "Shader Resource " + r.Resource.ToString(); string typename = "Unknown"; object tag = null; 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].format.special && (texs[t].format.specialFormat == SpecialFormat.D24S8 || texs[t].format.specialFormat == SpecialFormat.D32S8) ) { if (r.DepthReadChannel == 0) format += " Depth-Read"; else if (r.DepthReadChannel == 1) format += " Stencil-Read"; } else if ( r.Swizzle[0] != TextureSwizzle.Red || r.Swizzle[1] != TextureSwizzle.Green || r.Swizzle[2] != TextureSwizzle.Blue || r.Swizzle[3] != TextureSwizzle.Alpha) { format += String.Format(" swizzle[{0}{1}{2}{3}]", r.Swizzle[0].Str(), r.Swizzle[1].Str(), r.Swizzle[2].Str(), r.Swizzle[3].Str()); } tag = texs[t]; } } var node = textures.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); } // do sampler { string slotname = i.ToString(); if (shaderInput != null && shaderInput.name.Length > 0) slotname += ": " + shaderInput.name; 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.AddressS, s.AddressT, s.AddressR }; // arrange like either STR: WRAP or ST: WRAP, R: CLAMP for (int a = 0; a < 3; a++) { string prefix = "" + "STR"[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); if (r.ResType == ShaderResourceType.TextureCube || r.ResType == ShaderResourceType.TextureCubeArray) { addressing += s.SeamlessCube ? " Seamless" : " Non-Seamless"; } string minfilter = s.MinFilter; if (s.MaxAniso > 1) minfilter += String.Format(" Aniso{0}x", s.MaxAniso); if (s.UseComparison) minfilter = String.Format("{0}", s.Comparison); var node = samplers.Nodes.Add(new object[] { slotname, addressing, minfilter, s.MagFilter, (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); } } } } textures.EndUpdate(); textures.NodesSelection.Clear(); textures.SetVScrollValue(vs); samplers.EndUpdate(); samplers.NodesSelection.Clear(); samplers.SetVScrollValue(vs2); vs = cbuffers.VScrollValue(); cbuffers.BeginUpdate(); cbuffers.Nodes.Clear(); if (shaderDetails != null) { UInt32 i = 0; foreach (var shaderCBuf in shaderDetails.ConstantBlocks) { int bindPoint = stage.BindpointMapping.ConstantBlocks[i].bind; GLPipelineState.Buffer b = null; if (bindPoint >= 0 && bindPoint < state.UniformBuffers.Length) b = state.UniformBuffers[bindPoint]; bool filledSlot = !shaderCBuf.bufferBacked || (b != null && b.Resource != ResourceId.Null); bool usedSlot = stage.BindpointMapping.ConstantBlocks[i].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" ) { ulong offset = 0; ulong length = 0; int numvars = shaderCBuf.variables.Length; string slotname = "Uniforms"; string name = ""; string sizestr = String.Format("{0} Variables", numvars); string byterange = ""; if (!filledSlot) { name = "Empty"; length = 0; } if (b != null) { slotname = String.Format("{0}: {1}", bindPoint, shaderCBuf.name); name = "UBO " + b.Resource.ToString(); offset = b.Offset; length = b.Size; for (int t = 0; t < bufs.Length; t++) { if (bufs[t].ID == b.Resource) { name = bufs[t].name; if (length == 0) length = bufs[t].length; } } sizestr = String.Format("{0} Variables, {1} bytes", numvars, length); byterange = String.Format("{0} - {1}", offset, offset + length); } var node = cbuffers.Nodes.Add(new object[] { slotname, name, byterange, 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 = subs.VScrollValue(); subs.BeginUpdate(); subs.Nodes.Clear(); { UInt32 i = 0; foreach (var subval in stage.Subroutines) { subs.Nodes.Add(new object[] { i.ToString(), subval.ToString() }); i++; } } subs.EndUpdate(); subs.NodesSelection.Clear(); subs.SetVScrollValue(vs); { subs.Visible = subs.Parent.Visible = (stage.Subroutines.Length > 0); int row = table.GetRow(subs.Parent); if (row >= 0 && row < table.RowStyles.Count) { if (stage.Subroutines.Length > 0) table.RowStyles[row].Height = table.RowStyles[1].Height; else table.RowStyles[row].Height = 0; } } vs = readwrites.VScrollValue(); readwrites.BeginUpdate(); readwrites.Nodes.Clear(); if (shaderDetails != null) { UInt32 i = 0; foreach (var res in shaderDetails.Resources) { int bindPoint = stage.BindpointMapping.Resources[i].bind; bool atomic = false; bool ssbo = false; bool image = false; if (!res.IsReadWrite) { i++; continue; } GLPipelineState.Buffer bf = null; GLPipelineState.ImageLoadStore im = null; ResourceId id = ResourceId.Null; if (res.IsTexture) { image = true; if (bindPoint >= 0 && bindPoint < state.Images.Length) { im = state.Images[bindPoint]; id = state.Images[bindPoint].Resource; } } else { if (res.variableType.descriptor.rows == 1 && res.variableType.descriptor.cols == 1 && res.variableType.descriptor.type == VarType.UInt) { atomic = true; if (bindPoint >= 0 && bindPoint < state.AtomicBuffers.Length) { bf = state.AtomicBuffers[bindPoint]; id = state.AtomicBuffers[bindPoint].Resource; } } else { ssbo = true; if (bindPoint >= 0 && bindPoint < state.ShaderStorageBuffers.Length) { bf = state.ShaderStorageBuffers[bindPoint]; id = state.ShaderStorageBuffers[bindPoint].Resource; } } } bool filledSlot = id != ResourceId.Null; bool usedSlot = stage.BindpointMapping.Resources[i].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 binding = image ? "Image" : atomic ? "Atomic" : ssbo ? "SSBO" : "Unknown"; string slotname = String.Format("{0}: {1}", bindPoint, res.name); string name = ""; string dimensions = ""; string format = "-"; string access = "Read/Write"; if (im != null) { if (im.readAllowed && !im.writeAllowed) access = "Read-Only"; if (!im.readAllowed && im.writeAllowed) access = "Write-Only"; format = im.Format.ToString(); } object tag = null; // check to see if it's a texture for (int t = 0; t < texs.Length; t++) { if (texs[t].ID == id) { if (texs[t].dimension == 1) { if(texs[t].arraysize > 1) dimensions = String.Format("{0}[{1}]", texs[t].width, texs[t].arraysize); else dimensions = String.Format("{0}", texs[t].width); } else if (texs[t].dimension == 2) { if (texs[t].arraysize > 1) dimensions = String.Format("{0}x{1}[{2}]", texs[t].width, texs[t].height, texs[t].arraysize); else dimensions = String.Format("{0}x{1}", texs[t].width, texs[t].height); } else if (texs[t].dimension == 3) { dimensions = String.Format("{0}x{1}x{2}", texs[t].width, texs[t].height, texs[t].depth); } name = texs[t].name; tag = texs[t]; } } // if not a texture, it must be a buffer for (int t = 0; t < bufs.Length; t++) { if (bufs[t].ID == id) { ulong offset = 0; ulong length = bufs[t].length; if (bf != null && bf.Size > 0) { offset = bf.Offset; length = bf.Size; } if(offset > 0) dimensions = String.Format("{0} bytes at offset {1} bytes", length, offset); else dimensions = String.Format("{0} bytes", length); name = bufs[t].name; tag = new ReadWriteTag(i, bufs[t]); } } if (!filledSlot) { name = "Empty"; dimensions = "-"; access = "-"; } var node = readwrites.Nodes.Add(new object[] { binding, slotname, name, dimensions, format, access }); 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); } i++; } } readwrites.EndUpdate(); readwrites.NodesSelection.Clear(); readwrites.SetVScrollValue(vs); { readwrites.Visible = readwrites.Parent.Visible = (readwrites.Nodes.Count > 0); int row = table.GetRow(readwrites.Parent); if (row >= 0 && row < table.RowStyles.Count) { if (readwrites.Nodes.Count > 0) table.RowStyles[row].Height = table.RowStyles[1].Height; else table.RowStyles[row].Height = 0; } } }
// Set a shader stage's resources and values private void SetShaderState(FetchTexture[] texs, FetchBuffer[] bufs, VulkanPipelineState.ShaderStage stage, VulkanPipelineState.Pipeline pipe, Label shader, TreelistView.TreeListView resources, TreelistView.TreeListView cbuffers) { ShaderReflection shaderDetails = stage.ShaderDetails; if (stage.Shader == ResourceId.Null) shader.Text = "Unbound"; else shader.Text = stage.ShaderName; if (shaderDetails != null && shaderDetails.DebugInfo.entryFunc.Length > 0) { if (shaderDetails.DebugInfo.files.Length > 0 || shaderDetails.DebugInfo.entryFunc != "main") shader.Text = shaderDetails.DebugInfo.entryFunc + "()"; if (shaderDetails.DebugInfo.files.Length > 0) { string shaderfn = ""; int entryFile = shaderDetails.DebugInfo.entryFile; if (entryFile < 0 || entryFile >= shaderDetails.DebugInfo.files.Length) entryFile = 0; shaderfn = shaderDetails.DebugInfo.files[entryFile].BaseFilename; shader.Text += " - " + shaderfn; } } int vs = 0; vs = resources.VScrollValue(); resources.BeginUpdate(); resources.Nodes.Clear(); var samplers = new Dictionary<ResourceId, SamplerData>(); for(int bindset = 0; bindset < pipe.DescSets.Length; bindset++) { for(int bind = 0; bind < pipe.DescSets[bindset].bindings.Length; bind++) { ShaderResource shaderRes = null; BindpointMap bindMap = null; bool isrw = false; uint bindPoint = 0; if (shaderDetails != null) { for(int i=0; i < shaderDetails.ReadOnlyResources.Length; i++) { var ro = shaderDetails.ReadOnlyResources[i]; if (stage.BindpointMapping.ReadOnlyResources[ro.bindPoint].bindset == bindset && stage.BindpointMapping.ReadOnlyResources[ro.bindPoint].bind == bind) { bindPoint = (uint)i; shaderRes = ro; bindMap = stage.BindpointMapping.ReadOnlyResources[ro.bindPoint]; } } for(int i=0; i < shaderDetails.ReadWriteResources.Length; i++) { var rw = shaderDetails.ReadWriteResources[i]; if (stage.BindpointMapping.ReadWriteResources[rw.bindPoint].bindset == bindset && stage.BindpointMapping.ReadWriteResources[rw.bindPoint].bind == bind) { bindPoint = (uint)i; isrw = true; shaderRes = rw; bindMap = stage.BindpointMapping.ReadWriteResources[rw.bindPoint]; } } } VulkanPipelineState.Pipeline.DescriptorSet.DescriptorBinding.BindingElement[] slotBinds = pipe.DescSets[bindset].bindings[bind].binds; ShaderBindType bindType = pipe.DescSets[bindset].bindings[bind].type; ShaderStageBits stageBits = pipe.DescSets[bindset].bindings[bind].stageFlags; // skip descriptors that aren't for this shader stage if (!stageBits.HasFlag((ShaderStageBits)(1 << (int)stage.stage))) continue; // these are treated as uniform buffers if (bindType == ShaderBindType.ReadOnlyBuffer) continue; // consider it filled if any array element is filled bool filledSlot = false; for (int idx = 0; idx < slotBinds.Length; idx++) { filledSlot |= slotBinds[idx].res != ResourceId.Null; if(bindType == ShaderBindType.Sampler || bindType == ShaderBindType.ImageSampler) filledSlot |= slotBinds[idx].sampler != ResourceId.Null; } bool usedSlot = bindMap != null && bindMap.used; // show if if (usedSlot || // it's referenced by the shader - regardless of empty or not (showDisabled.Checked && !usedSlot && filledSlot) || // it's bound, but not referenced, and we have "show disabled" (showEmpty.Checked && !filledSlot) // it's empty, and we have "show empty" ) { TreelistView.NodeCollection parentNodes = resources.Nodes; string setname = bindset.ToString(); string slotname = bind.ToString(); if(shaderRes != null) slotname += ": " + shaderRes.name; // for arrays, add a parent element that we add the real cbuffers below if (slotBinds.Length > 1) { var node = parentNodes.Add(new object[] { "", setname, slotname, String.Format("Array[{0}]", slotBinds.Length), "", "", "", "" }); node.TreeColumn = 0; if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); parentNodes = node.Nodes; } for (int idx = 0; idx < slotBinds.Length; idx++) { var descriptorBind = slotBinds[idx]; if (slotBinds.Length > 1) { slotname = String.Format("{0}[{1}]", bind, idx); if (shaderRes != null && shaderRes.name.Length > 0) slotname += ": " + shaderRes.name; } bool isbuf = false; UInt32 w = 1, h = 1, d = 1; UInt32 a = 1; UInt32 samples = 1; UInt64 len = 0; string format = "Unknown"; string name = "Object " + descriptorBind.res.ToString(); ShaderResourceType restype = ShaderResourceType.None; object tag = null; if (!filledSlot) { name = "Empty"; format = "-"; w = h = d = a = 0; } // check to see if it's a texture for (int t = 0; t < texs.Length; t++) { if (texs[t].ID == descriptorBind.res) { w = texs[t].width; h = texs[t].height; d = texs[t].depth; a = texs[t].arraysize; format = texs[t].format.ToString(); name = texs[t].name; restype = texs[t].resType; samples = texs[t].msSamp; tag = texs[t]; } } // if not a texture, it must be a buffer for (int t = 0; t < bufs.Length; t++) { if (bufs[t].ID == descriptorBind.res) { len = bufs[t].byteSize; w = bufs[t].length; h = 0; d = 0; a = 0; format = ""; name = bufs[t].name; restype = ShaderResourceType.Buffer; tag = new BufferResTag(isrw, bindPoint, bufs[t].ID); isbuf = true; } } TreelistView.Node node = null; if (bindType == ShaderBindType.ReadWriteBuffer || bindType == ShaderBindType.ReadOnlyTBuffer || bindType == ShaderBindType.ReadWriteTBuffer ) { if (!isbuf) { node = parentNodes.Add(new object[] { "", bindset, slotname, bindType, "-", "-", "", "", }); EmptyRow(node); } else { node = parentNodes.Add(new object[] { "", bindset, slotname, bindType, name, String.Format("{0} bytes", len), String.Format("{0} - {1}", descriptorBind.offset, descriptorBind.size), "", }); node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; node.Tag = tag; if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); } } else if (bindType == ShaderBindType.Sampler) { if (descriptorBind.sampler == ResourceId.Null) { node = parentNodes.Add(new object[] { "", bindset, slotname, bindType, "-", "-", "", "", }); EmptyRow(node); } else { node = parentNodes.Add(MakeSampler(bindset.ToString(), slotname, descriptorBind)); if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); var data = new SamplerData(node); node.Tag = data; if (!samplers.ContainsKey(descriptorBind.sampler)) samplers.Add(descriptorBind.sampler, data); } } else { if (descriptorBind.res == ResourceId.Null) { node = parentNodes.Add(new object[] { "", bindset, slotname, bindType, "-", "-", "", "", }); EmptyRow(node); } else { string typename = restype.Str() + " " + bindType.Str().Replace("&", "&&"); string dim; if (restype == ShaderResourceType.Texture3D) dim = String.Format("{0}x{1}x{2}", w, h, d); else if (restype == ShaderResourceType.Texture1D || restype == ShaderResourceType.Texture1DArray) dim = w.ToString(); else dim = String.Format("{0}x{1}", w, h); string arraydim = "-"; if(restype == ShaderResourceType.Texture1DArray || restype == ShaderResourceType.Texture2DArray || restype == ShaderResourceType.Texture2DMSArray || restype == ShaderResourceType.TextureCubeArray) arraydim = String.Format("{0}[{1}]", restype.Str(), a); if (restype == ShaderResourceType.Texture2DMS || restype == ShaderResourceType.Texture2DMSArray) dim += String.Format(", {0}x MSAA", samples); node = parentNodes.Add(new object[] { "", bindset, slotname, typename, name, dim, format, arraydim, }); node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; node.Tag = tag; if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); } if (bindType == ShaderBindType.ImageSampler) { if (descriptorBind.sampler == ResourceId.Null) { node = parentNodes.Add(new object[] { "", bindset, slotname, bindType, "-", "-", "", "", }); EmptyRow(node); } else { var texnode = node; if (!samplers.ContainsKey(descriptorBind.sampler)) { node = parentNodes.Add(MakeSampler("", "", descriptorBind)); if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); var data = new SamplerData(node); node.Tag = data; samplers.Add(descriptorBind.sampler, data); } if (texnode != null) { m_CombinedImageSamplers[texnode] = samplers[descriptorBind.sampler].node; samplers[descriptorBind.sampler].images.Add(texnode); } } } } } } } } resources.EndUpdate(); resources.NodesSelection.Clear(); resources.SetVScrollValue(vs); vs = cbuffers.VScrollValue(); cbuffers.BeginUpdate(); cbuffers.Nodes.Clear(); for(int bindset = 0; bindset < pipe.DescSets.Length; bindset++) { for(int bind = 0; bind < pipe.DescSets[bindset].bindings.Length; bind++) { ConstantBlock cblock = null; BindpointMap bindMap = null; uint slot = uint.MaxValue; if (shaderDetails != null) { for (slot = 0; slot < (uint)shaderDetails.ConstantBlocks.Length; slot++) { ConstantBlock cb = shaderDetails.ConstantBlocks[slot]; if (stage.BindpointMapping.ConstantBlocks[cb.bindPoint].bindset == bindset && stage.BindpointMapping.ConstantBlocks[cb.bindPoint].bind == bind) { cblock = cb; bindMap = stage.BindpointMapping.ConstantBlocks[cb.bindPoint]; break; } } if (slot >= (uint)shaderDetails.ConstantBlocks.Length) slot = uint.MaxValue; } var slotBinds = pipe.DescSets[bindset].bindings[bind].binds; ShaderBindType bindType = pipe.DescSets[bindset].bindings[bind].type; ShaderStageBits stageBits = pipe.DescSets[bindset].bindings[bind].stageFlags; // skip descriptors that aren't for this shader stage if (!stageBits.HasFlag((ShaderStageBits)(1 << (int)stage.stage))) continue; // these are treated as uniform buffers if (bindType != ShaderBindType.ReadOnlyBuffer) continue; bool usedSlot = bindMap != null && bindMap.used; // consider it filled if any array element is filled (or it's push constants) bool filledSlot = cblock != null && !cblock.bufferBacked; for (int idx = 0; idx < slotBinds.Length; idx++) filledSlot |= slotBinds[idx].res != ResourceId.Null; // show if if (usedSlot || // it's referenced by the shader - regardless of empty or not (showDisabled.Checked && !usedSlot && filledSlot) || // it's bound, but not referenced, and we have "show disabled" (showEmpty.Checked && !filledSlot) // it's empty, and we have "show empty" ) { TreelistView.NodeCollection parentNodes = cbuffers.Nodes; string setname = bindset.ToString(); string slotname = bind.ToString(); if (cblock != null && cblock.name.Length > 0) slotname += ": " + cblock.name; // for arrays, add a parent element that we add the real cbuffers below if (slotBinds.Length > 1) { var node = parentNodes.Add(new object[] { "", setname, slotname, String.Format("Array[{0}]", slotBinds.Length), "", "" }); node.TreeColumn = 0; if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); parentNodes = node.Nodes; } for (int idx = 0; idx < slotBinds.Length; idx++) { var descriptorBind = slotBinds[idx]; if (slotBinds.Length > 1) { slotname = String.Format("{0}[{1}]", bind, idx); if (cblock != null && cblock.name.Length > 0) slotname += ": " + cblock.name; } string name = "UBO " + descriptorBind.res.ToString(); UInt64 length = descriptorBind.size; int numvars = cblock != null ? cblock.variables.Length : 0; if (!filledSlot) { name = "Empty"; length = 0; } for (int t = 0; t < bufs.Length; t++) if (bufs[t].ID == descriptorBind.res) name = bufs[t].name; if (name == "") name = "UBO " + descriptorBind.res.ToString(); string sizestr = String.Format("{0} Variables, {1} bytes", numvars, length); string vecrange = String.Format("{0} - {1}", descriptorBind.offset, descriptorBind.offset + descriptorBind.size); // push constants if (cblock != null && !cblock.bufferBacked) { setname = ""; slotname = cblock.name; name = "Push constants"; vecrange = ""; sizestr = String.Format("{0} Variables", numvars); // could maybe get range from ShaderVariable.reg if it's filled out // from SPIR-V side. } var node = parentNodes.Add(new object[] { "", setname, slotname, name, vecrange, sizestr }); node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; node.Tag = new CBufferTag(slot, (uint)idx); if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); } } } } // search for push constants and add them last if (shaderDetails != null) { for (int cb = 0; cb < shaderDetails.ConstantBlocks.Length; cb++) { var cblock = shaderDetails.ConstantBlocks[cb]; if (cblock.bufferBacked == false) { // could maybe get range from ShaderVariable.reg if it's filled out // from SPIR-V side. var node = cbuffers.Nodes.Add(new object[] { "", "", cblock.name, "Push constants", "", String.Format("{0} Variables", cblock.variables.Length) }); node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; node.Tag = new CBufferTag((uint)cb, 0); } } } cbuffers.EndUpdate(); cbuffers.NodesSelection.Clear(); cbuffers.SetVScrollValue(vs); }
public ViewTexTag(D3D11PipelineState.ShaderStage.ResourceView v, FetchTexture t) { view = v; tex = t; DepthReadOnly = false; StencilReadOnly = false; }
public ViewTexTag(ResourceFormat f, UInt32 bm, UInt32 bl, UInt32 nm, UInt32 nl, FetchTexture t) { fmt = f; baseMip = bm; baseLayer = bl; numMip = nm; numLayer = nl; tex = t; }
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 bool HasImportantViewParams(VulkanPipelineState.CurrentPass.Framebuffer.Attachment att, FetchTexture tex) { // see above in BindingElement overload for justification for comparing formats if (att.viewfmt != tex.format || att.baseMip > 0 || att.baseLayer > 0 || (att.numMip < tex.mips && tex.mips > 1) || (att.numLayer < tex.arraysize && tex.arraysize > 1)) return true; return false; }
// Set a shader stage's resources and values private void SetShaderState(FetchTexture[] texs, FetchBuffer[] bufs, VulkanPipelineState.ShaderStage stage, VulkanPipelineState.Pipeline pipe, Label shader, TreelistView.TreeListView resources, TreelistView.TreeListView cbuffers) { ShaderReflection shaderDetails = stage.ShaderDetails; if (stage.Shader == ResourceId.Null) shader.Text = "Unbound"; else shader.Text = stage.ShaderName; if (shaderDetails != null && shaderDetails.DebugInfo.entryFunc.Length > 0) { if (shaderDetails.DebugInfo.files.Length > 0 || shaderDetails.DebugInfo.entryFunc != "main") shader.Text = shaderDetails.DebugInfo.entryFunc + "()"; if (shaderDetails.DebugInfo.files.Length > 0) { string shaderfn = ""; int entryFile = shaderDetails.DebugInfo.entryFile; if (entryFile < 0 || entryFile >= shaderDetails.DebugInfo.files.Length) entryFile = 0; shaderfn = shaderDetails.DebugInfo.files[entryFile].BaseFilename; shader.Text += " - " + shaderfn; } } int vs = 0; vs = resources.VScrollValue(); resources.BeginUpdate(); resources.Nodes.Clear(); var samplers = new Dictionary<ResourceId, SamplerData>(); for(int bindset = 0; bindset < pipe.DescSets.Length; bindset++) { for(int bind = 0; bind < pipe.DescSets[bindset].bindings.Length; bind++) { AddResourceRow(shaderDetails, stage, bindset, bind, pipe, resources, texs, bufs, ref samplers); } // if we have a shader bound, go through and add rows for any resources it wants for binds that aren't // in this descriptor set (e.g. if layout mismatches) if (shaderDetails != null) { for (int i = 0; i < shaderDetails.ReadOnlyResources.Length; i++) { var ro = shaderDetails.ReadOnlyResources[i]; if (stage.BindpointMapping.ReadOnlyResources[ro.bindPoint].bindset == bindset && stage.BindpointMapping.ReadOnlyResources[ro.bindPoint].bind >= pipe.DescSets[bindset].bindings.Length) { AddResourceRow(shaderDetails, stage, bindset, stage.BindpointMapping.ReadOnlyResources[ro.bindPoint].bind, pipe, resources, texs, bufs, ref samplers); } } for (int i = 0; i < shaderDetails.ReadWriteResources.Length; i++) { var rw = shaderDetails.ReadWriteResources[i]; if (stage.BindpointMapping.ReadWriteResources[rw.bindPoint].bindset == bindset && stage.BindpointMapping.ReadWriteResources[rw.bindPoint].bind >= pipe.DescSets[bindset].bindings.Length) { AddResourceRow(shaderDetails, stage, bindset, stage.BindpointMapping.ReadWriteResources[rw.bindPoint].bind, pipe, resources, texs, bufs, ref samplers); } } } } // if we have a shader bound, go through and add rows for any resources it wants for descriptor sets that aren't // bound at all if (shaderDetails != null) { for (int i = 0; i < shaderDetails.ReadOnlyResources.Length; i++) { var ro = shaderDetails.ReadOnlyResources[i]; if (stage.BindpointMapping.ReadOnlyResources[ro.bindPoint].bindset >= pipe.DescSets.Length) { AddResourceRow(shaderDetails, stage, stage.BindpointMapping.ReadOnlyResources[ro.bindPoint].bindset, stage.BindpointMapping.ReadOnlyResources[ro.bindPoint].bind, pipe, resources, texs, bufs, ref samplers); } } for (int i = 0; i < shaderDetails.ReadWriteResources.Length; i++) { var rw = shaderDetails.ReadWriteResources[i]; if (stage.BindpointMapping.ReadWriteResources[rw.bindPoint].bindset >= pipe.DescSets.Length) { AddResourceRow(shaderDetails, stage, stage.BindpointMapping.ReadWriteResources[rw.bindPoint].bindset, stage.BindpointMapping.ReadWriteResources[rw.bindPoint].bind, pipe, resources, texs, bufs, ref samplers); } } } resources.EndUpdate(); resources.NodesSelection.Clear(); resources.SetVScrollValue(vs); vs = cbuffers.VScrollValue(); cbuffers.BeginUpdate(); cbuffers.Nodes.Clear(); for(int bindset = 0; bindset < pipe.DescSets.Length; bindset++) { for(int bind = 0; bind < pipe.DescSets[bindset].bindings.Length; bind++) { AddConstantBlockRow(shaderDetails, stage, bindset, bind, pipe, cbuffers, bufs); } // if we have a shader bound, go through and add rows for any cblocks it wants for binds that aren't // in this descriptor set (e.g. if layout mismatches) if (shaderDetails != null) { for (int i = 0; i < shaderDetails.ConstantBlocks.Length; i++) { var cb = shaderDetails.ConstantBlocks[i]; if (stage.BindpointMapping.ConstantBlocks[cb.bindPoint].bindset == bindset && stage.BindpointMapping.ConstantBlocks[cb.bindPoint].bind >= pipe.DescSets[bindset].bindings.Length) { AddConstantBlockRow(shaderDetails, stage, bindset, stage.BindpointMapping.ConstantBlocks[cb.bindPoint].bind, pipe, cbuffers, bufs); } } } } // if we have a shader bound, go through and add rows for any resources it wants for descriptor sets that aren't // bound at all if (shaderDetails != null) { for (int i = 0; i < shaderDetails.ConstantBlocks.Length; i++) { var cb = shaderDetails.ConstantBlocks[i]; if (stage.BindpointMapping.ConstantBlocks[cb.bindPoint].bindset >= pipe.DescSets.Length && cb.bufferBacked) { AddConstantBlockRow(shaderDetails, stage, stage.BindpointMapping.ConstantBlocks[cb.bindPoint].bindset, stage.BindpointMapping.ConstantBlocks[cb.bindPoint].bind, pipe, cbuffers, bufs); } } } // search for push constants and add them last if (shaderDetails != null) { for (int cb = 0; cb < shaderDetails.ConstantBlocks.Length; cb++) { var cblock = shaderDetails.ConstantBlocks[cb]; if (cblock.bufferBacked == false) { // could maybe get range from ShaderVariable.reg if it's filled out // from SPIR-V side. var node = cbuffers.Nodes.Add(new object[] { "", "", cblock.name, "Push constants", "", String.Format("{0} Variables", cblock.variables.Length) }); node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; node.Tag = new CBufferTag((uint)cb, 0); } } } cbuffers.EndUpdate(); cbuffers.NodesSelection.Clear(); cbuffers.SetVScrollValue(vs); }
private bool HasImportantViewParams(VulkanPipelineState.Pipeline.DescriptorSet.DescriptorBinding.BindingElement view, FetchTexture tex) { // Since mutable formats are more unclear in vulkan (it allows casting between any // similar format, and the underlying texture still has a valid format), we consider // a format difference to be important even though we display the view's format in // the row data itself if (view.viewfmt != tex.format || view.baseMip > 0 || view.baseLayer > 0 || (view.numMip < tex.mips && tex.mips > 1) || (view.numLayer < tex.arraysize && tex.arraysize > 1)) return true; return false; }
public ViewTexTag(D3D12PipelineState.ResourceView v, FetchTexture t, bool u, ShaderResource r) { view = v; tex = t; uav = u; res = r; }
// Set a shader stage's resources and values private void SetShaderState(FetchTexture[] texs, FetchBuffer[] bufs, GLPipelineState.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 = "Shader " + stage.Shader.ToString(); if (shaderDetails != null && shaderDetails.DebugInfo.entryFunc != "" && shaderDetails.DebugInfo.files.Length > 0) shader.Text = shaderDetails.DebugInfo.entryFunc + "()" + " - " + Path.GetFileName(shaderDetails.DebugInfo.files[0].filename); cbuffers.BeginUpdate(); cbuffers.Nodes.Clear(); if(shaderDetails != null) { UInt32 i = 0; foreach (var shaderCBuf in shaderDetails.ConstantBlocks) { { string name = shaderCBuf.name; int numvars = shaderCBuf.variables.Length; string slotname = i.ToString(); var node = cbuffers.Nodes.Add(new object[] { slotname, name, "", "", numvars, "" }); node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; node.Tag = i; } i++; } } cbuffers.EndUpdate(); cbuffers.NodesSelection.Clear(); }
public ViewTexTag(D3D11PipelineState.ShaderStage.ResourceView v, FetchTexture t) { view = v; tex = t; }
public PixelHistoryView(Core core, FetchTexture tex, Point pt, UInt32 sampleIdx, float rangemin, float rangemax, bool[] channels) { InitializeComponent(); Icon = global::renderdocui.Properties.Resources.icon; m_Core = core; events.Font = core.Config.PreferredFont; texture = tex; pixel = pt; rangeMin = rangemin; rangeMax = rangemax; visibleChannels = channels; sample = sampleIdx; Text = String.Format("Pixel History on {0} for ({1}, {2})", tex.name, pt.X, pt.Y); if(tex.msSamp > 1) Text += String.Format(" @ Sample {0}", sample); string channelStr = ""; numChannels = 0; channelIdx = 0; if (channels[0]) { channelStr += "R"; numChannels++; channelIdx = 0; } if (channels[1]) { channelStr += "G"; numChannels++; channelIdx = 1; } if (channels[2]) { channelStr += "B"; numChannels++; channelIdx = 2; } channelStr += " channel"; if (numChannels > 1) channelStr += "s"; // if alpha channel is enabled it only does anything if the // other channels are all disabled. There is no RGBA preview if (numChannels == 0 && channels[3]) { channelStr = "Alpha"; numChannels = 1; channelIdx = 3; } historyContext.Text = String.Format("Preview colours displayed in visible range {0} - {1} with {2} visible.", Formatter.Format(rangemin), Formatter.Format(rangemax), channelStr) + Environment.NewLine; historyContext.Text += Environment.NewLine; historyContext.Text += "Double click to jump to an event." + Environment.NewLine; historyContext.Text += "Right click to debug an event, or hide failed events."; eventsHidden.Text = ""; modifications = null; events.BeginUpdate(); events.Nodes.Clear(); events.Nodes.Add(new object[] { "Loading...", "", "", "", "" }); events.EndUpdate(); }
public void HighlightHistory(FetchTexture tex, Point pt, PixelModification[] modif) { m_HistoryTex = tex; m_HistoryPoint = pt; m_History = modif; panel.Invalidate(); }
// Set a shader stage's resources and values private void SetShaderState(FetchTexture[] texs, FetchBuffer[] bufs, GLPipelineState state, GLPipelineState.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 = "Shader " + stage.Shader.ToString(); if (shaderDetails != null && shaderDetails.DebugInfo.entryFunc != "" && shaderDetails.DebugInfo.files.Length > 0) shader.Text = shaderDetails.DebugInfo.entryFunc + "()" + " - " + Path.GetFileName(shaderDetails.DebugInfo.files[0].filename); cbuffers.BeginUpdate(); cbuffers.Nodes.Clear(); if(shaderDetails != null) { UInt32 i = 0; foreach (var shaderCBuf in shaderDetails.ConstantBlocks) { int bindPoint = stage.BindpointMapping.ConstantBlocks[i].bind; bool filledSlot = !shaderCBuf.bufferBacked || (bindPoint >= 0 && bindPoint < state.UniformBuffers.Length && state.UniformBuffers[bindPoint].Resource != ResourceId.Null); bool usedSlot = stage.BindpointMapping.ConstantBlocks[i].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 = shaderCBuf.name; int numvars = shaderCBuf.variables.Length; string slotname = i.ToString(); var node = cbuffers.Nodes.Add(new object[] { slotname, name, bindPoint, "", numvars, "" }); 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(); }