private FetchDrawcall FindDraw(Point p) { FetchDrawcall left = null; FetchDrawcall right = null; float dleft = -1.0f; float dright = -1.0f; bool uleft = false; bool uright = false; FindDraw(p, m_Root, ref left, ref dleft, ref uleft, ref right, ref dright, ref uright); if (left == null) { return(right); } if (right == null) { return(left); } if (Math.Abs(p.X - dleft) < Math.Abs(p.X - dright)) { return(left); } else { return(right); } }
private void CountContributingEvents(FetchDrawcall draw, ref uint drawCount, ref uint dispatchCount, ref uint diagnosticCount) { const uint diagnosticMask = (uint)DrawcallFlags.SetMarker | (uint)DrawcallFlags.PushMarker | (uint)DrawcallFlags.PopMarker; uint diagnosticMasked = (uint)draw.flags & diagnosticMask; if (diagnosticMasked != 0) { diagnosticCount += 1; } if ((draw.flags & DrawcallFlags.Drawcall) != 0) { drawCount += 1; } if ((draw.flags & DrawcallFlags.Dispatch) != 0) { dispatchCount += 1; } if (draw.children != null) { foreach (var c in draw.children) { CountContributingEvents(c, ref drawCount, ref dispatchCount, ref diagnosticCount); } } }
private void prevDraw_Click(object sender, EventArgs e) { FetchDrawcall draw = m_Core.CurDrawcall; if (draw != null && draw.previous != null) { SelectEvent(draw.previous.eventID); } }
private void nextDraw_Click(object sender, EventArgs e) { FetchDrawcall draw = m_Core.CurDrawcall; if (draw != null && draw.next != null) { SelectEvent(draw.next.eventID); } }
private uint GetEndDrawID(FetchDrawcall drawcall) { if (drawcall.children.Length == 0) { return(drawcall.drawcallID); } return(GetEndDrawID(drawcall.children.Last())); }
private string GetExportDrawcallString(int indent, bool firstchild, FetchDrawcall drawcall) { string prefix = new string(' ', indent * 2 - (firstchild ? 1 : 0)); if (firstchild) { prefix += '\\'; } return(String.Format("{0}- {1}", prefix, drawcall.name)); }
public void FillAPIView() { apiEvents.BeginUpdate(); apiEvents.Nodes.Clear(); Regex rgx = new Regex("^\\s*[{}]?"); string replacement = ""; FetchDrawcall draw = m_Core.CurDrawcall; if (draw != null && draw.events != null && draw.events.Length > 0) { foreach (var ev in draw.events) { // hack until I have a proper interface. Skip events associated with this draw that // come from another context (means they will just be completely omitted/invisible). if (ev.context != draw.context) { continue; } string[] lines = ev.eventDesc.Split(new string[] { "\r\n", "\n" }, StringSplitOptions.None); TreelistView.Node node = apiEvents.Nodes.Add(new TreelistView.Node(new object[] { ev.eventID, lines[0] })); for (int i = 1; i < lines.Length; i++) { string l = rgx.Replace(lines[i], replacement); if (l.Length > 0) { node.Nodes.Add(new TreelistView.Node(new object[] { "", l })); } } if (ev.eventID == draw.eventID) { node.Bold = true; } node.Tag = (object)ev; } if (apiEvents.Nodes.Count > 0) { apiEvents.NodesSelection.Add(apiEvents.Nodes[0]); } } apiEvents.EndUpdate(); }
private void prevDraw_Click(object sender, EventArgs e) { FetchDrawcall draw = m_Core.CurDrawcall; if (draw != null && draw.eventID == 0) { FetchDrawcall d = m_Core.CurDrawcall; while (d.next != null) { SelectEvent(draw.next.eventID); string FileName = "C:\\Users\\zhlin\\Desktop\\rdump\\" + d.drawcallID + ".png"; m_Core.GetTextureViewer().saveTex_auto(FileName); } } if (draw != null && draw.previous != null) { SelectEvent(draw.previous.eventID); } }
private double GetDrawTime(FetchDrawcall drawcall) { if (drawcall.children.Length > 0) { double total = 0.0; foreach (FetchDrawcall c in drawcall.children) { double f = GetDrawTime(c); if (f >= 0) { total += f; } } return(total); } else if (m_Times.ContainsKey(drawcall.eventID)) { return(m_Times[drawcall.eventID][0].value.d); } return(-1.0); }
private void eventView_AfterSelect(object sender, TreeViewEventArgs e) { prevDraw.Enabled = false; nextDraw.Enabled = false; if (eventView.SelectedNode.Tag != null) { DeferredEvent def = eventView.SelectedNode.Tag as DeferredEvent; m_Core.SetEventID(this, def.eventID); FetchDrawcall draw = m_Core.CurDrawcall; if (draw != null && draw.previous != null) { prevDraw.Enabled = true; } if (draw != null && draw.next != null) { nextDraw.Enabled = true; } } HighlightBookmarks(); }
private TreelistView.Node AddDrawcall(FetchDrawcall drawcall, TreelistView.Node root) { if (m_Core.Config.EventBrowser_HideEmpty) { if ((drawcall.children == null || drawcall.children.Length == 0) && (drawcall.flags & DrawcallFlags.PushMarker) != 0) { return(null); } } UInt32 eventNum = drawcall.eventID; TreelistView.Node drawNode = null; if (drawcall.children.Length > 0) { drawNode = MakeNode(eventNum, GetEndEventID(drawcall), drawcall.drawcallID, GetEndDrawID(drawcall), drawcall.name, 0.0); } else { drawNode = MakeNode(eventNum, drawcall.drawcallID, drawcall.name, 0.0); } if (m_Core.Config.EventBrowser_ApplyColours) { // if alpha isn't 0, assume the colour is valid if ((drawcall.flags & (DrawcallFlags.PushMarker | DrawcallFlags.SetMarker)) > 0 && drawcall.markerColour[3] > 0.0f) { float red = drawcall.markerColour[0]; float green = drawcall.markerColour[1]; float blue = drawcall.markerColour[2]; float alpha = drawcall.markerColour[3]; drawNode.TreeLineColor = drawcall.GetColor(); drawNode.TreeLineWidth = 3.0f; if (m_Core.Config.EventBrowser_ColourEventRow) { drawNode.BackColor = drawcall.GetColor(); drawNode.ForeColor = drawcall.GetTextColor(eventView.ForeColor); } } } DeferredEvent def = new DeferredEvent(); def.eventID = eventNum; def.marker = (drawcall.flags & DrawcallFlags.SetMarker) != 0; drawNode.Tag = def; if (drawcall.children != null && drawcall.children.Length > 0) { for (int i = 0; i < drawcall.children.Length; i++) { AddDrawcall(drawcall.children[i], drawNode); if (i > 0 && drawNode.Nodes.Count >= 2 && (drawcall.children[i - 1].flags & DrawcallFlags.SetMarker) > 0) { drawNode.Nodes[drawNode.Nodes.Count - 2].Tag = drawNode.Nodes.LastNode.Tag; } } bool found = false; for (int i = drawNode.Nodes.Count - 1; i >= 0; i--) { DeferredEvent t = drawNode.Nodes[i].Tag as DeferredEvent; if (t != null && !t.marker) { drawNode.Tag = drawNode.Nodes[i].Tag; found = true; break; } } if (!found && !drawNode.Nodes.IsEmpty()) { drawNode.Tag = drawNode.Nodes.LastNode.Tag; } } if (drawNode.Nodes.IsEmpty() && (drawcall.flags & DrawcallFlags.PushMarker) != 0 && m_Core.Config.EventBrowser_HideEmpty) { return(null); } root.Nodes.Add(drawNode); return(drawNode); }
public void FillAPIView() { apiEvents.BeginUpdate(); apiEvents.Nodes.Clear(); Regex rgxopen = new Regex("^\\s*{"); Regex rgxclose = new Regex("^\\s*}"); FetchDrawcall draw = m_Core.CurDrawcall; if (draw != null && draw.events != null && draw.events.Length > 0) { foreach (var ev in draw.events) { // hack until I have a proper interface. Skip events associated with this draw that // come from another context (means they will just be completely omitted/invisible). if (ev.context != draw.context) { continue; } string[] lines = ev.eventDesc.Split(new string[] { "\r\n", "\n" }, StringSplitOptions.None); TreelistView.Node root = new TreelistView.Node(new object[] { ev.eventID, lines[0] }); int i = 1; if (i < lines.Length && lines[i].Trim() == "{") { i++; } List <TreelistView.Node> nodestack = new List <TreelistView.Node>(); nodestack.Add(root); for (; i < lines.Length; i++) { if (rgxopen.IsMatch(lines[i])) { nodestack.Add(nodestack.Last().Nodes.LastNode); } else if (rgxclose.IsMatch(lines[i])) { nodestack.RemoveAt(nodestack.Count - 1); } else if (lines[i].Trim().Length > 0 && nodestack.Count > 0) { nodestack.Last().Nodes.Add(new TreelistView.Node(new object[] { "", lines[i].Trim() })); } } if (ev.eventID == draw.eventID) { root.Bold = true; } root.Tag = (object)ev; apiEvents.Nodes.Add(root); } if (apiEvents.Nodes.Count > 0) { apiEvents.NodesSelection.Add(apiEvents.Nodes[0]); } } apiEvents.EndUpdate(); }
private void ExportDrawcall(StreamWriter sw, int maxNameLength, int indent, bool firstchild, FetchDrawcall drawcall) { string eidString = drawcall.children.Length > 0 ? "" : drawcall.eventID.ToString(); string nameString = GetExportDrawcallString(indent, firstchild, drawcall); sw.WriteLine(String.Format("{0,-5} | {1,-" + maxNameLength + "} | {2,-5}", eidString, nameString, drawcall.drawcallID)); for (int i = 0; i < drawcall.children.Length; i++) { ExportDrawcall(sw, maxNameLength, indent + 1, i == 0, drawcall.children[i]); } }
private void GetMaxNameLength(ref int maxNameLength, int indent, bool firstchild, FetchDrawcall drawcall) { string nameString = GetExportDrawcallString(indent, firstchild, drawcall); maxNameLength = Math.Max(maxNameLength, nameString.Length); for (int i = 0; i < drawcall.children.Length; i++) { GetMaxNameLength(ref maxNameLength, indent + 1, i == 0, drawcall.children[i]); } }
private TreelistView.Node AddDrawcall(TreelistView.Node existing, FetchDrawcall drawcall, TreelistView.Node root) { if (m_Core.Config.EventBrowser_HideEmpty) { if ((drawcall.children == null || drawcall.children.Length == 0) && (drawcall.flags & DrawcallFlags.PushMarker) != 0) { return(null); } } UInt32 eventNum = drawcall.eventID; double duration = drawcall.duration; TreelistView.Node drawNode = MakeNode(eventNum, drawcall.drawcallID, drawcall.name, duration); if (existing != null) { existing.SetData(drawNode.GetData()); drawNode = existing; } else { root.Nodes.Add(drawNode); } DeferredEvent def = new DeferredEvent(); def.frameID = m_Core.CurFrame; def.eventID = eventNum; if (drawcall.context != m_Core.FrameInfo[m_Core.CurFrame].immContextId) { def.defCtx = drawcall.context; def.lastDefEv = drawcall.eventID; FetchDrawcall parent = drawcall.parent; while (!parent.name.Contains("ExecuteCommand")) { parent = parent.parent; } def.eventID = parent.eventID - 1; def.firstDefEv = parent.children[0].eventID; if (parent.children[0].events.Length > 0) { def.firstDefEv = parent.children[0].events[0].eventID; } } drawNode.Tag = def; if (drawcall.children != null && drawcall.children.Length > 0) { for (int i = 0; i < drawcall.children.Length; i++) { TreelistView.Node d = drawNode.Nodes.Count > i ? drawNode.Nodes[i] : null; AddDrawcall(d, drawcall.children[i], drawNode); if (i > 0 && (drawcall.children[i - 1].flags & DrawcallFlags.SetMarker) > 0) { drawNode.Nodes[drawNode.Nodes.Count - 2].Tag = drawNode.Nodes.LastNode.Tag; } if ((double)drawNode.Nodes[i]["Duration"] > 0.0) { drawNode["Duration"] = Math.Max(0.0, (double)drawNode["Duration"]) + (double)drawNode.Nodes[i]["Duration"]; } } drawNode.Tag = drawNode.Nodes.LastNode.Tag; } return(drawNode); }
private TreelistView.Node AddDrawcall(FetchDrawcall drawcall, TreelistView.Node root) { if (m_Core.Config.EventBrowser_HideEmpty) { if ((drawcall.children == null || drawcall.children.Length == 0) && (drawcall.flags & DrawcallFlags.PushMarker) != 0) { return(null); } } UInt32 eventNum = drawcall.eventID; TreelistView.Node drawNode = null; if (drawcall.children.Length > 0) { drawNode = MakeNode(eventNum, GetEndEventID(drawcall), drawcall.drawcallID, drawcall.name, 0.0); } else { drawNode = MakeNode(eventNum, drawcall.drawcallID, drawcall.name, 0.0); } DeferredEvent def = new DeferredEvent(); def.eventID = eventNum; def.marker = (drawcall.flags & DrawcallFlags.SetMarker) != 0; if (drawcall.context != m_Core.FrameInfo.immContextId) { def.defCtx = drawcall.context; def.lastDefEv = drawcall.eventID; FetchDrawcall parent = drawcall.parent; while (!parent.name.Contains("ExecuteCommand")) { parent = parent.parent; } def.eventID = parent.eventID - 1; def.firstDefEv = parent.children[0].eventID; if (parent.children[0].events.Length > 0) { def.firstDefEv = parent.children[0].events[0].eventID; } } drawNode.Tag = def; if (drawcall.children != null && drawcall.children.Length > 0) { for (int i = 0; i < drawcall.children.Length; i++) { AddDrawcall(drawcall.children[i], drawNode); if (i > 0 && drawNode.Nodes.Count >= 2 && (drawcall.children[i - 1].flags & DrawcallFlags.SetMarker) > 0) { drawNode.Nodes[drawNode.Nodes.Count - 2].Tag = drawNode.Nodes.LastNode.Tag; } } bool found = false; for (int i = drawNode.Nodes.Count - 1; i >= 0; i--) { DeferredEvent t = drawNode.Nodes[i].Tag as DeferredEvent; if (t != null && !t.marker) { drawNode.Tag = drawNode.Nodes[i].Tag; found = true; break; } } if (!found && !drawNode.Nodes.IsEmpty()) { drawNode.Tag = drawNode.Nodes.LastNode.Tag; } } if (drawNode.Nodes.IsEmpty() && (drawcall.flags & DrawcallFlags.PushMarker) != 0 && m_Core.Config.EventBrowser_HideEmpty) { return(null); } root.Nodes.Add(drawNode); return(drawNode); }
public void FillAPIView() { apiEvents.BeginUpdate(); apiEvents.Nodes.Clear(); Regex rgxopen = new Regex("^\\s*{"); Regex rgxclose = new Regex("^\\s*}"); FetchDrawcall draw = m_Core.CurDrawcall; if (draw != null && draw.events != null && draw.events.Length > 0) { foreach (var ev in draw.events) { string[] lines = ev.eventDesc.Split(new string[] { "\r\n", "\n" }, StringSplitOptions.None); TreelistView.Node root = new TreelistView.Node(new object[] { ev.eventID, lines[0] }); int i = 1; if (i < lines.Length && lines[i].Trim() == "{") { i++; } List <TreelistView.Node> nodestack = new List <TreelistView.Node>(); nodestack.Add(root); for (; i < lines.Length; i++) { if (rgxopen.IsMatch(lines[i])) { nodestack.Add(nodestack.Last().Nodes.LastNode); } else if (rgxclose.IsMatch(lines[i])) { nodestack.RemoveAt(nodestack.Count - 1); } else if (lines[i].Trim().Length > 0 && nodestack.Count > 0) { nodestack.Last().Nodes.Add(new TreelistView.Node(new object[] { "", lines[i].Trim() })); } } if (ev.eventID == draw.eventID) { root.Bold = true; } root.Tag = (object)ev; apiEvents.Nodes.Add(root); } if (apiEvents.Nodes.Count > 0) { apiEvents.NodesSelection.Add(apiEvents.Nodes[0]); } } apiEvents.EndUpdate(); }
// used to determine if two drawcalls can be considered in the same 'pass', // ie. writing to similar targets, same type of call, etc. // // When a log has no markers, this is used to group up drawcalls into fake markers private bool PassEquivalent(FetchDrawcall a, FetchDrawcall b) { // executing command lists can have children if (a.children.Length > 0 || b.children.Length > 0) { return(false); } // don't group draws and compute executes if ((a.flags & DrawcallFlags.Dispatch) != (b.flags & DrawcallFlags.Dispatch)) { return(false); } // don't group things run on different multithreaded contexts if (a.context != b.context) { return(false); } // don't group things with different depth outputs if (a.depthOut != b.depthOut) { return(false); } int numAOuts = 0, numBOuts = 0; for (int i = 0; i < 8; i++) { if (a.outputs[i] != ResourceId.Null) { numAOuts++; } if (b.outputs[i] != ResourceId.Null) { numBOuts++; } } int numSame = 0; if (a.depthOut != ResourceId.Null) { numAOuts++; numBOuts++; numSame++; } for (int i = 0; i < 8; i++) { if (a.outputs[i] != ResourceId.Null) { for (int j = 0; j < 8; j++) { if (a.outputs[i] == b.outputs[j]) { numSame++; break; } } } else if (b.outputs[i] != ResourceId.Null) { for (int j = 0; j < 8; j++) { if (a.outputs[j] == b.outputs[i]) { numSame++; break; } } } } // use a kind of heuristic to group together passes where the outputs are similar enough. // could be useful for example if you're rendering to a gbuffer and sometimes you render // without one target, but the draws are still batched up. if (numSame > Math.Max(numAOuts, numBOuts) / 2 && Math.Max(numAOuts, numBOuts) > 1) { return(true); } if (numSame == Math.Max(numAOuts, numBOuts)) { return(true); } return(false); }
// if a log doesn't contain any markers specified at all by the user, then we can // fake some up by determining batches of draws that are similar and giving them a // pass number private FetchDrawcall[] FakeProfileMarkers(FetchDrawcall[] draws) { if (Config.EventBrowser_AddFake == false) { return(draws); } if (ContainsMarker(draws)) { return(draws); } var ret = new List <FetchDrawcall>(); int depthpassID = 1; int computepassID = 1; int passID = 1; int start = 0; int refdraw = 0; for (int i = 1; i < draws.Length; i++) { if ((draws[refdraw].flags & (DrawcallFlags.Copy | DrawcallFlags.Resolve | DrawcallFlags.SetMarker | DrawcallFlags.CmdList)) > 0) { refdraw = i; continue; } if ((draws[i].flags & (DrawcallFlags.Copy | DrawcallFlags.Resolve | DrawcallFlags.SetMarker | DrawcallFlags.CmdList)) > 0) { continue; } if (PassEquivalent(draws[i], draws[refdraw])) { continue; } int end = i - 1; if (end - start < 2 || draws[i].children.Length > 0 || draws[refdraw].children.Length > 0) { for (int j = start; j <= end; j++) { ret.Add(draws[j]); } start = i; refdraw = i; continue; } int minOutCount = 100; int maxOutCount = 0; for (int j = start; j <= end; j++) { int outCount = 0; foreach (var o in draws[j].outputs) { if (o != ResourceId.Null) { outCount++; } } minOutCount = Math.Min(minOutCount, outCount); maxOutCount = Math.Max(maxOutCount, outCount); } FetchDrawcall mark = new FetchDrawcall(); mark.eventID = draws[start].eventID; mark.drawcallID = draws[start].drawcallID; mark.markerColour = new float[] { 0.0f, 0.0f, 0.0f, 0.0f }; mark.flags = DrawcallFlags.PushMarker; mark.outputs = draws[end].outputs; mark.depthOut = draws[end].depthOut; mark.name = "Guessed Pass"; minOutCount = Math.Max(1, minOutCount); if ((draws[refdraw].flags & DrawcallFlags.Dispatch) != 0) { mark.name = String.Format("Compute Pass #{0}", computepassID++); } else if (maxOutCount == 0) { mark.name = String.Format("Depth-only Pass #{0}", depthpassID++); } else if (minOutCount == maxOutCount) { mark.name = String.Format("Colour Pass #{0} ({1} Targets{2})", passID++, minOutCount, draws[end].depthOut == ResourceId.Null ? "" : " + Depth"); } else { mark.name = String.Format("Colour Pass #{0} ({1}-{2} Targets{3})", passID++, minOutCount, maxOutCount, draws[end].depthOut == ResourceId.Null ? "" : " + Depth"); } mark.children = new FetchDrawcall[end - start + 1]; for (int j = start; j <= end; j++) { mark.children[j - start] = draws[j]; draws[j].parent = mark; } ret.Add(mark); start = i; refdraw = i; } if (start < draws.Length) { for (int j = start; j < draws.Length; j++) { ret.Add(draws[j]); } } return(ret.ToArray()); }
private void FindDraw(Point p, Section s, ref FetchDrawcall left, ref float dleft, ref bool uleft, ref FetchDrawcall right, ref float dright, ref bool uright) { if (s == null) { return; } var rect = s.lastRect; rect.Y = 0; rect.Height = 10000; if (s.subsections != null) { foreach (var sub in s.subsections) { FindDraw(p, sub, ref left, ref dleft, ref uleft, ref right, ref dright, ref uright); if (left != null && right != null) { return; } } } if (rect.Contains(p)) { if (s.draws == null || s.draws.Count == 0) { return; } for (int i = 0; i < s.lastPoss.Count; i++) { if (s.lastVisible[i]) { if (s.lastPoss[i] <= p.X) { if ( // not found left left == null || // this left is closer and as usage-y, or we don't have a usage-y one yet (s.lastPoss[i] > dleft && s.lastUsage[i] == uleft) || // this left is WAY closer (s.lastPoss[i] > dleft + 20.0f) || // this left is more usage-y (s.lastUsage[i] && !uleft) ) { dleft = s.lastPoss[i]; uleft = s.lastUsage[i]; left = s.draws[i]; } } if (s.lastPoss[i] > p.X) { if ( // not found right right == null || // this right is closer and as usage-y, or we don't have a usage-y one yet (s.lastPoss[i] < dright && s.lastUsage[i] == uright) || // this right is WAY closer (s.lastPoss[i] < dright - 20.0f) || // this right is more usage-y (s.lastUsage[i] && !uright) ) { dright = s.lastPoss[i]; uright = s.lastUsage[i]; right = s.draws[i]; } } } } if (left != null && right != null) { return; } } }
private void UpdateState() { if (!m_Core.LogLoaded) { return; } FetchTexture[] texs = m_Core.CurTextures; FetchBuffer[] bufs = m_Core.CurBuffers; GLPipelineState state = m_Core.CurGLPipelineState; FetchDrawcall draw = m_Core.CurDrawcall; var tick = global::renderdocui.Properties.Resources.tick; var cross = global::renderdocui.Properties.Resources.cross; bool[] usedVBuffers = new bool[128]; for (int i = 0; i < 128; i++) { usedVBuffers[i] = false; } //////////////////////////////////////////////// // Input Assembler inputLayouts.Nodes.Clear(); inputLayouts.BeginUpdate(); if (state.m_VtxIn.attributes != null) { int i = 0; foreach (var l in state.m_VtxIn.attributes) { if (l.Enabled || showDisabled.Checked) { string byteOffs = l.RelativeOffset.ToString(); var node = inputLayouts.Nodes.Add(new object[] { i, l.Enabled ? "Enabled" : "Disabled", "", l.Format, l.BufferSlot.ToString(), byteOffs, "", "" }); usedVBuffers[l.BufferSlot] = true; node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; if (!l.Enabled) { InactiveRow(node); } } i++; } } inputLayouts.NodesSelection.Clear(); inputLayouts.EndUpdate(); topology.Text = state.m_VtxIn.Topology.ToString(); if (state.m_VtxIn.Topology > PrimitiveTopology.PatchList) { int numCPs = (int)state.m_VtxIn.Topology - (int)PrimitiveTopology.PatchList + 1; topology.Text = string.Format("PatchList ({0} Control Points)", numCPs); } switch (state.m_VtxIn.Topology) { case PrimitiveTopology.PointList: topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_pointlist; break; case PrimitiveTopology.LineList: topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_linelist; break; case PrimitiveTopology.LineStrip: topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_linestrip; break; case PrimitiveTopology.TriangleList: topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_trilist; break; case PrimitiveTopology.TriangleStrip: topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_tristrip; break; case PrimitiveTopology.LineList_Adj: topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_linelist_adj; break; case PrimitiveTopology.LineStrip_Adj: topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_linestrip_adj; break; case PrimitiveTopology.TriangleList_Adj: topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_trilist_adj; break; case PrimitiveTopology.TriangleStrip_Adj: topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_tristrip_adj; break; default: topologyDiagram.Image = global::renderdocui.Properties.Resources.topo_patch; break; } iabuffers.Nodes.Clear(); iabuffers.BeginUpdate(); bool ibufferUsed = draw != null && (draw.flags & DrawcallFlags.UseIBuffer) != 0; if (state.m_VtxIn.ibuffer != null) { if (ibufferUsed || showDisabled.Checked) { string ptr = "Buffer " + state.m_VtxIn.ibuffer.Buffer.ToString(); string name = ptr; UInt32 length = 1; if (!ibufferUsed) { length = 0; } for (int t = 0; t < bufs.Length; t++) { if (bufs[t].ID == state.m_VtxIn.ibuffer.Buffer) { name = bufs[t].name; length = bufs[t].length; } } var node = iabuffers.Nodes.Add(new object[] { "Index", name, state.m_VtxIn.ibuffer.Format.compByteWidth, state.m_VtxIn.ibuffer.Offset, length }); node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; node.Tag = state.m_VtxIn.ibuffer.Buffer; if (!ibufferUsed) { InactiveRow(node); } if (state.m_VtxIn.ibuffer.Buffer == ResourceId.Null) { EmptyRow(node); } } } else { if (showEmpty.Checked && (ibufferUsed || showDisabled.Checked)) { var node = iabuffers.Nodes.Add(new object[] { "Index", "-", "-", "-", "-" }); node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; node.Tag = state.m_VtxIn.ibuffer.Buffer; EmptyRow(node); if (!ibufferUsed) { InactiveRow(node); } } } m_VBNodes.Clear(); if (state.m_VtxIn.vbuffers != null) { int i = 0; foreach (var v in state.m_VtxIn.vbuffers) { bool filledSlot = (v.Buffer != ResourceId.Null); bool usedSlot = (usedVBuffers[i]); // show if if (usedSlot || // it's referenced by the shader - regardless of empty or not (showDisabled.Checked && !usedSlot && filledSlot) || // it's bound, but not referenced, and we have "show disabled" (showEmpty.Checked && !filledSlot) // it's empty, and we have "show empty" ) { string name = "Buffer " + v.Buffer.ToString(); UInt32 length = 1; if (!filledSlot) { name = "Empty"; length = 0; } for (int t = 0; t < bufs.Length; t++) { if (bufs[t].ID == v.Buffer) { name = bufs[t].name; length = bufs[t].length; } } var node = iabuffers.Nodes.Add(new object[] { i, name, v.Stride, v.Offset, length }); node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; node.Tag = v.Buffer; if (!filledSlot) { EmptyRow(node); } if (!usedSlot) { InactiveRow(node); } m_VBNodes.Add(node); } i++; } } iabuffers.NodesSelection.Clear(); iabuffers.EndUpdate(); SetShaderState(texs, bufs, state, state.m_VS, vsShader, vsResources, vsSamplers, vsCBuffers, vsClasses); SetShaderState(texs, bufs, state, state.m_GS, gsShader, gsResources, gsSamplers, gsCBuffers, gsClasses); SetShaderState(texs, bufs, state, state.m_TES, tesShader, tesResources, tesSamplers, tesCBuffers, tesClasses); SetShaderState(texs, bufs, state, state.m_TCS, tcsShader, tcsResources, tcsSamplers, tcsCBuffers, tcsClasses); SetShaderState(texs, bufs, state, state.m_FS, fsShader, fsResources, fsSamplers, fsCBuffers, fsClasses); SetShaderState(texs, bufs, state, state.m_CS, csShader, csResources, csSamplers, csCBuffers, csClasses); fsResources.BeginUpdate(); fsResources.Nodes.Clear(); if (state.Textures != null) { var shaderDetails = state.m_FS.ShaderDetails; var mapping = state.m_FS.BindpointMapping; int i = 0; foreach (var r in state.Textures) { ShaderResource shaderInput = null; BindpointMap map = null; if (shaderDetails != null) { foreach (var bind in shaderDetails.Resources) { if (bind.IsSRV && 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" ) { 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) + "]"; } tag = bufs[t]; } } var node = fsResources.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++; } } fsResources.EndUpdate(); fsResources.NodesSelection.Clear(); csUAVs.Nodes.Clear(); csUAVs.BeginUpdate(); csUAVs.NodesSelection.Clear(); csUAVs.EndUpdate(); gsStreams.BeginUpdate(); gsStreams.Nodes.Clear(); gsStreams.EndUpdate(); gsStreams.NodesSelection.Clear(); //////////////////////////////////////////////// // Rasterizer viewports.BeginUpdate(); viewports.Nodes.Clear(); viewports.NodesSelection.Clear(); viewports.EndUpdate(); scissors.BeginUpdate(); scissors.Nodes.Clear(); scissors.NodesSelection.Clear(); scissors.EndUpdate(); //////////////////////////////////////////////// // Output Merger bool[] targets = new bool[8]; for (int i = 0; i < 8; i++) { targets[i] = false; } targetOutputs.BeginUpdate(); targetOutputs.Nodes.Clear(); { int i = 0; foreach (var p in state.m_FB.Color) { if (p != ResourceId.Null || showEmpty.Checked) { UInt32 w = 1, h = 1, d = 1; UInt32 a = 1; string format = "Unknown"; string name = "Texture " + p.ToString(); string typename = "Unknown"; object tag = null; if (p == ResourceId.Null) { name = "Empty"; format = "-"; typename = "-"; w = h = d = a = 0; } for (int t = 0; t < texs.Length; t++) { if (texs[t].ID == p) { 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]; } } var node = targetOutputs.Nodes.Add(new object[] { i, name, typename, w, h, d, a, format }); node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; node.Tag = tag; if (p == ResourceId.Null) { EmptyRow(node); } else { targets[i] = true; } } i++; } } { int i = 0; foreach (ResourceId depthstencil in new ResourceId[] { state.m_FB.Depth, state.m_FB.Stencil }) { if (depthstencil != ResourceId.Null || showEmpty.Checked) { UInt32 w = 1, h = 1, d = 1; UInt32 a = 1; string format = "Unknown"; string name = "Depth Target " + depthstencil.ToString(); string typename = "Unknown"; object tag = null; if (depthstencil == ResourceId.Null) { name = "Empty"; format = "-"; typename = "-"; w = h = d = a = 0; } for (int t = 0; t < texs.Length; t++) { if (texs[t].ID == depthstencil) { 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]; } } string slot = "Depth"; if (i == 1) { slot = "Stencil"; } var node = targetOutputs.Nodes.Add(new object[] { slot, 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 (depthstencil == ResourceId.Null) { EmptyRow(node); } } i++; } } targetOutputs.EndUpdate(); targetOutputs.NodesSelection.Clear(); blendOperations.BeginUpdate(); blendOperations.Nodes.Clear(); blendOperations.NodesSelection.Clear(); blendOperations.EndUpdate(); stencilFuncs.BeginUpdate(); stencilFuncs.Nodes.Clear(); stencilFuncs.Nodes.Add(new object[] { "Front", "", "", "", "" }); stencilFuncs.Nodes.Add(new object[] { "Back", "", "", "", "" }); stencilFuncs.EndUpdate(); stencilFuncs.NodesSelection.Clear(); // highlight the appropriate stages in the flowchart if (draw == null) { pipeFlow.SetStagesEnabled(new bool[] { true, true, true, true, true, true, true, true, true }); } else if ((draw.flags & DrawcallFlags.Dispatch) != 0) { pipeFlow.SetStagesEnabled(new bool[] { false, false, false, false, false, false, false, false, true }); } else { pipeFlow.SetStagesEnabled(new bool[] { true, true, state.m_TES.Shader != ResourceId.Null, state.m_TCS.Shader != ResourceId.Null, state.m_GS.Shader != ResourceId.Null, true, state.m_FS.Shader != ResourceId.Null, true, false }); // if(streamout only) //{ // pipeFlow.Rasterizer = false; // pipeFlow.OutputMerger = false; //} } }
private void ExportDrawcall(StreamWriter sw, int maxNameLength, int indent, bool firstchild, FetchDrawcall drawcall) { string eidString = drawcall.children.Length > 0 ? "" : drawcall.eventID.ToString(); string nameString = GetExportDrawcallString(indent, firstchild, drawcall); string line = String.Format("{0,-5} | {1,-" + maxNameLength + "} | {2,-6}", eidString, nameString, drawcall.drawcallID); if (m_Times.Count > 0) { if (m_Core.Config.EventBrowser_TimeUnit != m_TimeUnit) { UpdateDurationColumn(); } double f = GetDrawTime(drawcall); if (f >= 0) { if (m_Core.Config.EventBrowser_TimeUnit == PersistantConfig.TimeUnit.Milliseconds) { f *= 1000.0; } else if (m_Core.Config.EventBrowser_TimeUnit == PersistantConfig.TimeUnit.Microseconds) { f *= 1000000.0; } else if (m_Core.Config.EventBrowser_TimeUnit == PersistantConfig.TimeUnit.Nanoseconds) { f *= 1000000000.0; } line += String.Format(" | {0}", Formatter.Format(f)); } else { line += " |"; } } sw.WriteLine(line); for (int i = 0; i < drawcall.children.Length; i++) { ExportDrawcall(sw, maxNameLength, indent + 1, i == 0, drawcall.children[i]); } }
private void export_Click(object sender, EventArgs e) { // Zhen: reuse export button to dump per-draw images in batch. FetchDrawcall draw = m_Core.CurDrawcall; if (draw != null && draw.eventID == 1) { FetchDrawcall d = m_Core.CurDrawcall; string path = Directory.GetParent(Environment.GetFolderPath(Environment.SpecialFolder.Desktop)).FullName; path += "\\Desktop\\RenderDocPerDrawImages\\"; Directory.CreateDirectory(path); while (true) { string FileName = path + d.drawcallID + ".png"; m_Core.GetTextureViewer().saveTex_auto(FileName); if (d.next != null) { SelectEvent(d.next.eventID); d = m_Core.CurDrawcall; } else { break; } } return; } DialogResult res = exportDialog.ShowDialog(); if (res == DialogResult.OK) { try { using (Stream s = new FileStream(exportDialog.FileName, FileMode.Create)) { StreamWriter sw = new StreamWriter(s); sw.WriteLine(String.Format("{0} - Frame #{1}", m_Core.LogFileName, m_Core.FrameInfo.frameNumber)); sw.WriteLine(""); int maxNameLength = 0; foreach (FetchDrawcall d in m_Core.GetDrawcalls()) { GetMaxNameLength(ref maxNameLength, 0, false, d); } string line = String.Format(" EID | {0,-" + maxNameLength + "} | Draw #", "Event"); if (m_Times.Count > 0) { if (m_Core.Config.EventBrowser_TimeUnit != m_TimeUnit) { UpdateDurationColumn(); } line += String.Format(" | {0}", eventView.Columns["Duration"].Caption); } sw.WriteLine(line); line = String.Format("--------{0}-----------", new string('-', maxNameLength)); if (m_Times.Count > 0) { int maxDurationLength = 0; maxDurationLength = Math.Max(maxDurationLength, Formatter.Format(1.0).Length); maxDurationLength = Math.Max(maxDurationLength, Formatter.Format(1.2345e-200).Length); maxDurationLength = Math.Max(maxDurationLength, Formatter.Format(123456.7890123456789).Length); line += new string('-', 3 + maxDurationLength); // 3 extra for " | " } sw.WriteLine(line); foreach (FetchDrawcall d in m_Core.GetDrawcalls()) { ExportDrawcall(sw, maxNameLength, 0, false, d); } sw.Dispose(); } } catch (System.Exception ex) { MessageBox.Show("Couldn't save to " + exportDialog.FileName + Environment.NewLine + ex.ToString(), "Cannot save", MessageBoxButtons.OK, MessageBoxIcon.Error); } } }
// generally logFile == origFilename, but if the log was transferred remotely then origFilename // is the log locally before being copied we can present to the user in dialogs, etc. public void LoadLogfile(string logFile, string origFilename, bool temporary, bool local) { m_LogFile = origFilename; m_LogLocal = local; m_LogLoadingInProgress = true; if (File.Exists(Core.ConfigFilename)) { m_Config.Serialize(Core.ConfigFilename); } float postloadProgress = 0.0f; bool progressThread = true; // start a modal dialog to prevent the user interacting with the form while the log is loading. // We'll close it down when log loading finishes (whether it succeeds or fails) ProgressPopup modal = new ProgressPopup(LogLoadCallback, true); Thread modalThread = Helpers.NewThread(new ThreadStart(() => { modal.SetModalText(string.Format("Loading Log: {0}", origFilename)); AppWindow.BeginInvoke(new Action(() => { modal.ShowDialog(AppWindow); })); })); modalThread.Start(); // this thread continually ticks and notifies any threads of the progress, through a float // that is updated by the main loading code Thread thread = Helpers.NewThread(new ThreadStart(() => { modal.LogfileProgressBegin(); foreach (var p in m_ProgressListeners) { p.LogfileProgressBegin(); } while (progressThread) { Thread.Sleep(2); float progress = 0.8f * m_Renderer.LoadProgress + 0.19f * postloadProgress + 0.01f; modal.LogfileProgress(progress); foreach (var p in m_ProgressListeners) { p.LogfileProgress(progress); } } })); thread.Start(); // this function call will block until the log is either loaded, or there's some failure m_Renderer.OpenCapture(logFile); // if the renderer isn't running, we hit a failure case so display an error message if (!m_Renderer.Running) { string errmsg = m_Renderer.InitException.Status.Str(); MessageBox.Show(String.Format("{0}\nFailed to open file for replay: {1}.\n\n" + "Check diagnostic log in Help menu for more details.", origFilename, errmsg), "Error opening log", MessageBoxButtons.OK, MessageBoxIcon.Error); progressThread = false; thread.Join(); m_LogLoadingInProgress = false; modal.LogfileProgress(-1.0f); foreach (var p in m_ProgressListeners) { p.LogfileProgress(-1.0f); } return; } if (!temporary) { m_Config.AddRecentFile(m_Config.RecentLogFiles, origFilename, 10); if (File.Exists(Core.ConfigFilename)) { m_Config.Serialize(Core.ConfigFilename); } } m_EventID = 0; m_FrameInfo = null; m_APIProperties = null; // fetch initial data like drawcalls, textures and buffers m_Renderer.Invoke((ReplayRenderer r) => { m_FrameInfo = r.GetFrameInfo(); m_APIProperties = r.GetAPIProperties(); postloadProgress = 0.2f; m_DrawCalls = FakeProfileMarkers(r.GetDrawcalls()); bool valid = HasValidMarkerColors(m_DrawCalls); if (!valid) { RemoveMarkerColors(m_DrawCalls); } postloadProgress = 0.4f; m_Buffers = r.GetBuffers(); postloadProgress = 0.7f; var texs = new List <FetchTexture>(r.GetTextures()); m_Textures = texs.OrderBy(o => o.name).ToArray(); postloadProgress = 0.9f; m_D3D11PipelineState = r.GetD3D11PipelineState(); m_D3D12PipelineState = r.GetD3D12PipelineState(); m_GLPipelineState = r.GetGLPipelineState(); m_VulkanPipelineState = r.GetVulkanPipelineState(); m_PipelineState.SetStates(m_APIProperties, m_D3D11PipelineState, m_D3D12PipelineState, m_GLPipelineState, m_VulkanPipelineState); UnreadMessageCount = 0; AddMessages(m_FrameInfo.debugMessages); postloadProgress = 1.0f; }); Thread.Sleep(20); DateTime today = DateTime.Now; DateTime compare = today.AddDays(-21); if (compare.CompareTo(Config.DegradedLog_LastUpdate) >= 0 && m_APIProperties.degraded) { Config.DegradedLog_LastUpdate = today; MessageBox.Show(String.Format("{0}\nThis log opened with degraded support - " + "this could mean missing hardware support caused a fallback to software rendering.\n\n" + "This warning will not appear every time this happens, " + "check debug errors/warnings window for more details.", origFilename), "Degraded support of log", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); } m_LogLoaded = true; progressThread = false; if (local) { try { m_LogWatcher = new FileSystemWatcher(Path.GetDirectoryName(m_LogFile), Path.GetFileName(m_LogFile)); m_LogWatcher.EnableRaisingEvents = true; m_LogWatcher.NotifyFilter = NotifyFilters.Size | NotifyFilters.FileName | NotifyFilters.LastAccess | NotifyFilters.LastWrite; m_LogWatcher.Created += new FileSystemEventHandler(OnLogfileChanged); m_LogWatcher.Changed += new FileSystemEventHandler(OnLogfileChanged); m_LogWatcher.SynchronizingObject = m_MainWindow; // callbacks on UI thread please } catch (ArgumentException) { // likely an "invalid" directory name - FileSystemWatcher doesn't support UNC paths properly } } List <ILogViewerForm> logviewers = new List <ILogViewerForm>(); logviewers.AddRange(m_LogViewers); // make sure we're on a consistent event before invoking log viewer forms FetchDrawcall draw = m_DrawCalls.Last(); while (draw.children != null && draw.children.Length > 0) { draw = draw.children.Last(); } SetEventID(logviewers.ToArray(), draw.eventID, true); // notify all the registers log viewers that a log has been loaded foreach (var logviewer in logviewers) { if (logviewer == null || !(logviewer is Control)) { continue; } Control c = (Control)logviewer; if (c.InvokeRequired) { if (!c.IsDisposed) { c.Invoke(new Action(() => { try { logviewer.OnLogfileLoaded(); } catch (Exception ex) { throw new AccessViolationException("Rethrown from Invoke:\n" + ex.ToString()); } })); } } else if (!c.IsDisposed) { logviewer.OnLogfileLoaded(); } } m_LogLoadingInProgress = false; modal.LogfileProgress(1.0f); foreach (var p in m_ProgressListeners) { p.LogfileProgress(1.0f); } }
public static bool ShouldHide(Core core, FetchDrawcall drawcall) { if (drawcall.flags.HasFlag(DrawcallFlags.PushMarker)) { if (core.Config.EventBrowser_HideEmpty) { if (drawcall.children == null || drawcall.children.Length == 0) { return(true); } bool allhidden = true; foreach (FetchDrawcall child in drawcall.children) { if (ShouldHide(core, child)) { continue; } allhidden = false; break; } if (allhidden) { return(true); } } if (core.Config.EventBrowser_HideAPICalls) { if (drawcall.children == null || drawcall.children.Length == 0) { return(false); } bool onlyapi = true; foreach (FetchDrawcall child in drawcall.children) { if (ShouldHide(core, child)) { continue; } if (!child.flags.HasFlag(DrawcallFlags.APICalls)) { onlyapi = false; break; } } if (onlyapi) { return(true); } } } return(false); }
// if a log doesn't contain any markers specified at all by the user, then we can // fake some up by determining batches of draws that are similar and giving them a // pass number private FetchDrawcall[] FakeProfileMarkers(int frameID, FetchDrawcall[] draws) { if (ContainsMarker(draws)) { return(draws); } var ret = new List <FetchDrawcall>(); int depthpassID = 1; int computepassID = 1; int passID = 1; int start = 0; int counter = 1; for (int i = 1; i < draws.Length; i++) { if (PassEquivalent(draws[i], draws[start]) && i + 1 < draws.Length) { continue; } int end = i - 1; if (i == draws.Length - 1) { end = i; } if (end - start < 2 || draws[i].children.Length > 0 || draws[start].children.Length > 0 || draws[i].context != m_FrameInfo[frameID].immContextId || draws[start].context != m_FrameInfo[frameID].immContextId) { for (int j = start; j <= end; j++) { ret.Add(draws[j]); counter++; } start = i; continue; } int minOutCount = 100; int maxOutCount = 0; for (int j = start; j <= end; j++) { int outCount = 0; foreach (var o in draws[j].outputs) { if (o != ResourceId.Null) { outCount++; } } minOutCount = Math.Min(minOutCount, outCount); maxOutCount = Math.Max(maxOutCount, outCount); } FetchDrawcall mark = new FetchDrawcall(); mark.eventID = draws[end].eventID; mark.drawcallID = draws[end].drawcallID; mark.context = draws[end].context; mark.flags = DrawcallFlags.PushMarker; mark.outputs = draws[end].outputs; mark.depthOut = draws[end].depthOut; mark.name = "Guessed Pass"; if ((draws[end].flags & DrawcallFlags.Dispatch) != 0) { mark.name = String.Format("Compute Pass #{0}", computepassID++); } else if (maxOutCount == 0) { mark.name = String.Format("Depth-only Pass #{0}", depthpassID++); } else if (minOutCount == maxOutCount) { mark.name = String.Format("Colour Pass #{0} ({1} Targets{2})", passID++, minOutCount, draws[end].depthOut == ResourceId.Null ? "" : " + Depth"); } else { mark.name = String.Format("Colour Pass #{0} ({1}-{2} Targets{3})", passID++, minOutCount, maxOutCount, draws[end].depthOut == ResourceId.Null ? "" : " + Depth"); } mark.children = new FetchDrawcall[end - start + 1]; for (int j = start; j <= end; j++) { mark.children[j - start] = draws[j]; draws[j].parent = mark; } ret.Add(mark); start = i; counter++; } return(ret.ToArray()); }
private TreelistView.Node AddDrawcall(TreelistView.Node existing, FetchDrawcall drawcall, TreelistView.Node root, Dictionary <uint, List <CounterResult> > times) { if (m_Core.Config.EventBrowser_HideEmpty) { if ((drawcall.children == null || drawcall.children.Length == 0) && (drawcall.flags & DrawcallFlags.PushMarker) != 0) { return(null); } } UInt32 eventNum = drawcall.eventID; double duration = 0.0; if (times != null && times.ContainsKey(eventNum)) { duration = times[eventNum][0].value.d; } TreelistView.Node drawNode = MakeNode(eventNum, drawcall.drawcallID, drawcall.name, duration); if (existing != null) { existing.SetData(drawNode.GetData()); drawNode = existing; } else { root.Nodes.Add(drawNode); } DeferredEvent def = new DeferredEvent(); def.frameID = m_Core.CurFrame; def.eventID = eventNum; def.marker = (drawcall.flags & DrawcallFlags.SetMarker) != 0; if (drawcall.context != m_Core.FrameInfo[m_Core.CurFrame].immContextId) { def.defCtx = drawcall.context; def.lastDefEv = drawcall.eventID; FetchDrawcall parent = drawcall.parent; while (!parent.name.Contains("ExecuteCommand")) { parent = parent.parent; } def.eventID = parent.eventID - 1; def.firstDefEv = parent.children[0].eventID; if (parent.children[0].events.Length > 0) { def.firstDefEv = parent.children[0].events[0].eventID; } } drawNode.Tag = def; if (drawcall.children != null && drawcall.children.Length > 0) { for (int i = 0; i < drawcall.children.Length; i++) { TreelistView.Node d = drawNode.Nodes.Count > i ? drawNode.Nodes[i] : null; AddDrawcall(d, drawcall.children[i], drawNode, times); if (i > 0 && (drawcall.children[i - 1].flags & DrawcallFlags.SetMarker) > 0) { drawNode.Nodes[drawNode.Nodes.Count - 2].Tag = drawNode.Nodes.LastNode.Tag; } if (i < drawNode.Nodes.Count && (double)drawNode.Nodes[i]["Duration"] > 0.0) { drawNode["Duration"] = Math.Max(0.0, (double)drawNode["Duration"]) + (double)drawNode.Nodes[i]["Duration"]; } } bool found = false; for (int i = drawNode.Nodes.Count - 1; i >= 0; i--) { DeferredEvent t = drawNode.Nodes[i].Tag as DeferredEvent; if (t != null && !t.marker) { drawNode.Tag = drawNode.Nodes[i].Tag; found = true; break; } } if (!found) { drawNode.Tag = drawNode.Nodes.LastNode.Tag; } } return(drawNode); }