// 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; } } }
private void ExportHTML(XmlTextWriter writer, GLPipelineState state, GLPipelineState.ShaderStage sh) { FetchTexture[] texs = m_Core.CurTextures; FetchBuffer[] bufs = m_Core.CurBuffers; ShaderReflection shaderDetails = sh.ShaderDetails; ShaderBindpointMapping mapping = sh.BindpointMapping; { writer.WriteStartElement("h3"); writer.WriteString("Shader"); writer.WriteEndElement(); string shadername = "Unknown"; if (sh.Shader == ResourceId.Null) shadername = "Unbound"; else shadername = sh.ShaderName; if (sh.Shader == ResourceId.Null) { shadername = "Unbound"; } else { string shname = sh.stage.Str(APIPipelineStateType.OpenGL) + " Shader"; if (!sh.customShaderName && !sh.customProgramName && !sh.customPipelineName) { shadername = shname + " " + sh.Shader.ToString(); } else { if (sh.customShaderName) shname = sh.ShaderName; if (sh.customProgramName) shname = sh.ProgramName + " - " + shname; if (sh.customPipelineName && sh.PipelineActive) shname = sh.PipelineName + " - " + shname; shadername = shname; } } writer.WriteStartElement("p"); writer.WriteString(shadername); writer.WriteEndElement(); if (sh.Shader == ResourceId.Null) return; } List<object[]> textureRows = new List<object[]>(); List<object[]> samplerRows = new List<object[]>(); List<object[]> cbufferRows = new List<object[]>(); List<object[]> readwriteRows = new List<object[]>(); List<object[]> subRows = new List<object[]>(); 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.ReadOnlyResources) { if (bind.IsSRV && mapping.ReadOnlyResources[bind.bindPoint].bind == i) { shaderInput = bind; map = mapping.ReadOnlyResources[bind.bindPoint]; } } } bool filledSlot = (r.Resource != ResourceId.Null); bool usedSlot = (shaderInput != null && map.used); if (shaderInput != null) { // 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.D16S8 || texs[t].format.specialFormat == SpecialFormat.D24S8 || texs[t].format.specialFormat == SpecialFormat.D32S8) ) { if (r.DepthReadChannel == 0) format += " Depth-Repipead"; 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]; } } textureRows.Add(new object[] { slotname, name, typename, w, h, d, a, format }); } // 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); samplerRows.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 (shaderDetails != null) { UInt32 i = 0; foreach (var shaderCBuf in shaderDetails.ConstantBlocks) { int bindPoint = mapping.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 = mapping.ConstantBlocks[i].used; // show if { ulong offset = 0; ulong length = 0; int numvars = shaderCBuf.variables.Length; ulong byteSize = (ulong)shaderCBuf.byteSize; 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; } } if (length == byteSize) sizestr = String.Format("{0} Variables, {1} bytes", numvars, length); else sizestr = String.Format("{0} Variables, {1} bytes needed, {2} provided", numvars, byteSize, length); byterange = String.Format("{0} - {1}", offset, offset + length); } cbufferRows.Add(new object[] { slotname, name, byterange, sizestr }); } i++; } } { UInt32 i = 0; foreach (var subval in sh.Subroutines) { subRows.Add(new object[] { i.ToString(), subval.ToString() }); i++; } } if (shaderDetails != null) { UInt32 i = 0; foreach (var res in shaderDetails.ReadWriteResources) { int bindPoint = mapping.ReadWriteResources[i].bind; GLReadWriteType readWriteType = GetGLReadWriteType(res); GLPipelineState.Buffer bf = null; GLPipelineState.ImageLoadStore im = null; ResourceId id = ResourceId.Null; if (readWriteType == GLReadWriteType.Image && bindPoint >= 0 && bindPoint < state.Images.Length) { im = state.Images[bindPoint]; id = state.Images[bindPoint].Resource; } if (readWriteType == GLReadWriteType.Atomic && bindPoint >= 0 && bindPoint < state.AtomicBuffers.Length) { bf = state.AtomicBuffers[bindPoint]; id = state.AtomicBuffers[bindPoint].Resource; } if (readWriteType == GLReadWriteType.SSBO && bindPoint >= 0 && bindPoint < state.ShaderStorageBuffers.Length) { bf = state.ShaderStorageBuffers[bindPoint]; id = state.ShaderStorageBuffers[bindPoint].Resource; } bool filledSlot = id != ResourceId.Null; bool usedSlot = mapping.ReadWriteResources[i].used; // show if { string binding = readWriteType == GLReadWriteType.Image ? "Image" : readWriteType == GLReadWriteType.Atomic ? "Atomic" : readWriteType == GLReadWriteType.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 = "-"; } readwriteRows.Add(new object[] { binding, slotname, name, dimensions, format, access }); } i++; } } { writer.WriteStartElement("h3"); writer.WriteString("Textures"); writer.WriteEndElement(); ExportHTMLTable(writer, new string[] { "Slot", "Name", "Type", "Width", "Height", "Depth", "Array Size", "Format" }, textureRows.ToArray() ); } { writer.WriteStartElement("h3"); writer.WriteString("Samplers"); writer.WriteEndElement(); ExportHTMLTable(writer, new string[] { "Slot", "Addressing", "Min Filter", "Mag Filter", "LOD Clamping", "LOD Bias" }, samplerRows.ToArray() ); } { writer.WriteStartElement("h3"); writer.WriteString("Uniform Buffers"); writer.WriteEndElement(); ExportHTMLTable(writer, new string[] { "Slot", "Name", "Byte Range", "Size" }, cbufferRows.ToArray() ); } { writer.WriteStartElement("h3"); writer.WriteString("Subroutines"); writer.WriteEndElement(); ExportHTMLTable(writer, new string[] { "Index", "Value" }, subRows.ToArray() ); } { writer.WriteStartElement("h3"); writer.WriteString("Read-write resources"); writer.WriteEndElement(); ExportHTMLTable(writer, new string[] { "Binding", "Resource", "Name", "Dimensions", "Format", "Access", }, readwriteRows.ToArray() ); } }