public override void Compute(PlottedGraph plot, CommandList cl, bool flip, float delta) { GraphLayoutState layout = plot.LayoutState; ResourceSetDescription velocity_rsrc_desc, pos_rsrc_desc; if (flip) { velocity_rsrc_desc = new ResourceSetDescription(_velocityShaderRsrcLayout, _velocityParamsBuffer, layout.PositionsVRAM1, layout.VelocitiesVRAM1, layout.EdgeConnectionIndexes, layout.EdgeConnections, layout.EdgeStrengths, layout.VelocitiesVRAM2 ); pos_rsrc_desc = new ResourceSetDescription(_positionShaderRsrcLayout, _positionParamsBuffer, layout.PositionsVRAM1, layout.VelocitiesVRAM2, layout.PositionsVRAM2); } else { velocity_rsrc_desc = new ResourceSetDescription(_velocityShaderRsrcLayout, _velocityParamsBuffer, layout.PositionsVRAM2, layout.VelocitiesVRAM2, layout.EdgeConnectionIndexes, layout.EdgeConnections, layout.EdgeStrengths, layout.VelocitiesVRAM1 ); pos_rsrc_desc = new ResourceSetDescription(_positionShaderRsrcLayout, _positionParamsBuffer, layout.PositionsVRAM2, layout.VelocitiesVRAM1, layout.PositionsVRAM1); } RenderVelocity(velocity_rsrc_desc, cl, plot, delta); RenderPosition(pos_rsrc_desc, cl, plot, delta); }
private void DrawAddressSelectControls(PlottedGraph plot) { ImGui.Text("Address"); ImGui.InputText("##AddressInput", ref _activeHighlights.AddrEntryText, 255); ImGui.SameLine(); if (ImGui.Button("Add") || ImGui.IsKeyPressed(ImGui.GetKeyIndex(ImGuiKey.Enter)) || ImGui.IsKeyPressed(ImGui.GetKeyIndex(ImGuiKey.KeyPadEnter))) { string addrstring = _activeHighlights.AddrEntryText; addrstring = new string(addrstring.ToCharArray().Where(c => !char.IsWhiteSpace(c)).ToArray()); if (addrstring.ToLower().StartsWith("0x")) { addrstring = addrstring.Substring(2); } bool success = ulong.TryParse(addrstring, NumberStyles.AllowHexSpecifier, CultureInfo.CurrentCulture, out ulong hexAddr); if (!success) { success = ulong.TryParse(addrstring, NumberStyles.Integer, CultureInfo.CurrentCulture, out hexAddr); } if (success) { _activeHighlights.AddrEntryText = ""; if (!_activeHighlights.SelectedAddresses.Contains(hexAddr)) { plot.AddHighlightedAddress(hexAddr); _activeHighlights.SelectedAddresses.Add(hexAddr); } } } }
public void DrawExceptionSelectControls(PlottedGraph plot) { if (plot.HighlightedExceptionNodes.Any() && ImGui.Button("Clear")) { plot.RemoveHighlightedNodes(_activeHighlights.SelectedExceptionNodes, CONSTANTS.HighlightType.Exceptions); _activeHighlights.SelectedExceptionNodes.Clear(); } }
/// Creates an array of metadata for basic blocks used for basic-block-centric graph layout public static unsafe void CreateBlockMetadataBuffer(PlottedGraph plot, GraphicsDevice gdevice) { GraphLayoutState layout = plot.LayoutState; if (GlobalConfig.Settings.Logs.BulkLogging) { Logging.RecordLogEvent($"CreateBlockDataBuffer {plot.TID}", Logging.LogFilterType.BulkDebugLogFile); } GraphLayoutState.GPUBuffers VRAMBuffers = layout._VRAMBuffers; VeldridGraphBuffers.VRAMDispose(VRAMBuffers.BlockMetadata); VeldridGraphBuffers.VRAMDispose(VRAMBuffers.BlockMiddles); var textureSize = plot.EdgeTextureWidth(); if (textureSize > 0) { CreateBlockMetadataBuf(plot, out NODE_BLOCK_METADATA_COMPUTEBUFFER[] blockdats, out int[] blockMiddles); VRAMBuffers.BlockMetadata = VeldridGraphBuffers.TrackedVRAMAlloc(gdevice, (uint)blockdats.Length * NODE_BLOCK_METADATA_COMPUTEBUFFER.SizeInBytes, BufferUsage.StructuredBufferReadOnly, sizeof(int), $"BlockMetadata_T{plot.TID}"); VRAMBuffers.BlockMiddles = VeldridGraphBuffers.TrackedVRAMAlloc(gdevice, (uint)blockMiddles.Length * sizeof(int), BufferUsage.StructuredBufferReadOnly, sizeof(int), $"BlockMiddles_T{plot.TID}"); VRAMBuffers.BlockCount = blockMiddles.Length; if (blockdats.Length == 0) { return; } fixed(NODE_BLOCK_METADATA_COMPUTEBUFFER *datsPtr = blockdats) { fixed(int *middlesPtr = blockMiddles) { CommandList cl = gdevice.ResourceFactory.CreateCommandList(); cl.Begin(); cl.UpdateBuffer(VRAMBuffers.BlockMetadata, 0, (IntPtr)datsPtr, (uint)blockdats.Length * NODE_BLOCK_METADATA_COMPUTEBUFFER.SizeInBytes); cl.UpdateBuffer(VRAMBuffers.BlockMiddles, 0, (IntPtr)middlesPtr, (uint)blockMiddles.Length * sizeof(int)); cl.End(); gdevice.SubmitCommands(cl); gdevice.WaitForIdle(); cl.Dispose(); } } } //Debug.Assert(!VeldridGraphBuffers.DetectNaN(_gd, newBuffer)); if (GlobalConfig.Settings.Logs.BulkLogging) { Logging.RecordLogEvent($"CreateBlockDataBuffer {plot.TID} complete", Logging.LogFilterType.BulkDebugLogFile); } //PrintBufferArray(textureArray, "Created data texture:"); }
private void DrawExceptionSelectBox(PlottedGraph plot) { uint[]? exceptionNodes = plot.InternalProtoGraph.GetExceptionNodes(); if (exceptionNodes is null || exceptionNodes.Length == 0) { string caption = $"No exceptions recorded in thread ID {_ActiveGraph?.TID}"; ImGuiUtils.DrawRegionCenteredText(caption); return; } string[] labels = exceptionNodes.Select(x => x.ToString()).ToArray(); if (ImGui.BeginTable("##ExceptionsTable", 2, ImGuiTableFlags.ScrollY | ImGuiTableFlags.RowBg, ImGui.GetContentRegionAvail() - new Vector2(10, _activeHighlights.SelectedExceptionNodes.Any() ? 30 : 0))) { ImGui.TableSetupColumn("Address", ImGuiTableColumnFlags.WidthFixed, 160); ImGui.TableSetupColumn("Module"); ImGui.TableSetupScrollFreeze(0, 1); ImGui.TableHeadersRow(); foreach (uint nodeidx in exceptionNodes) { NodeData?n = plot.InternalProtoGraph.GetNode(nodeidx); if (n is not null) { ImGui.TableNextRow(); if (ImGui.TableNextColumn()) { if (ImGui.Selectable($"0x{n.Address:X}", _activeHighlights.SelectedExceptionNodes.Contains(nodeidx), ImGuiSelectableFlags.SpanAllColumns)) { if (_activeHighlights.SelectedExceptionNodes.Contains(nodeidx)) { _activeHighlights.SelectedExceptionNodes.Remove(nodeidx); plot.RemoveHighlightedNodes(new List <uint> { nodeidx }, CONSTANTS.HighlightType.Exceptions); } else { _activeHighlights.SelectedExceptionNodes.Add(nodeidx); plot.AddHighlightedNodes(new List <uint> { nodeidx }, CONSTANTS.HighlightType.Exceptions); } } } if (ImGui.TableNextColumn()) { ImGui.Text(System.IO.Path.GetFileName(plot.InternalProtoGraph.ProcessData.GetModulePath(n.GlobalModuleID))); } } } ImGui.EndTable(); } }
private static void HandleMouseoverSym(PlottedGraph plot, moduleEntry module_modentry, symbolInfo syminfo) { module_modentry.symbols[syminfo.address] = syminfo; if (syminfo.hovered) { plot.AddHighlightedNodes(syminfo.threadNodes, CONSTANTS.HighlightType.Externals); } else { plot.RemoveHighlightedNodes(syminfo.threadNodes, CONSTANTS.HighlightType.Externals); } }
/// <summary> /// Set the highlight state of nodes in the attributes buffer so they can be animated/have their icon set /// </summary> /// <param name="cl">Thread specific Veldrid CommandList</param> /// <param name="graph">Graph with highlights to apply</param> /// <param name="attribsBuf">Attributes buffer to apply highlight data to</param> public static void ApplyHighlightAttributes(CommandList cl, PlottedGraph graph, DeviceBuffer attribsBuf) { graph.GetHighlightChanges(out List <uint> added, out List <uint> removed); if (added.Any() is true) { SetHighlightedNodes(cl, added, attribsBuf, CONSTANTS.HighlightType.Addresses); } if (removed.Any() is true) { UnsetHighlightedNodes(cl, removed, attribsBuf); } }
public void DrawAddressSelectBox(PlottedGraph plot) { if (ImGui.ListBox("##AddrListbox", ref selitem, _activeHighlights.SelectedAddresses.Select(ad => $"0x{ad:X}").ToArray(), _activeHighlights.SelectedAddresses.Count)) { ulong address = _activeHighlights.SelectedAddresses[selitem]; _activeHighlights.SelectedAddresses.RemoveAt(selitem); plot.HighlightedAddresses.Remove(address); List <uint> nodes = plot.InternalProtoGraph.ProcessData.GetNodesAtAddress(address, plot.TID); plot.LayoutState.Lock.EnterUpgradeableReadLock(); plot.RemoveHighlightedNodes(nodes, CONSTANTS.HighlightType.Addresses); plot.LayoutState.Lock.ExitUpgradeableReadLock(); } }
/// <summary> /// Pass the graph plot through the velocity compute shader, to adjust the node velocity based on the positions of other nodes /// </summary> /// <param name="RSetDesc">Position shader resource set</param> /// <param name="cl">Commandlist to run the commands on</param> /// <param name="plot">PlottedGraph to compute</param> /// <param name="delta">A float representing how much time has passed since the last frame. Higher values => bigger movements</param> private void RenderVelocity(ResourceSetDescription RSetDesc, CommandList cl, PlottedGraph plot, float delta) { //if (GlobalConfig.Settings.Logs.BulkLogging) Logging.RecordLogEvent($"RenderVelocity {this.EngineID}", Logging.LogFilterType.BulkDebugLogFile); _timer.Restart(); cl.Begin(); ResourceSet resourceSet = _gd.ResourceFactory.CreateResourceSet(RSetDesc); uint nodeCount = (uint)plot.RenderedNodeCount(); //if (GlobalConfig.Settings.Logs.BulkLogging) Logging.RecordLogEvent($"RenderVelocityBlocks {this.EngineID}", Logging.LogFilterType.BulkDebugLogFile); GraphLayoutState layout = plot.LayoutState; VelocityShaderParams parameters = new VelocityShaderParams { delta = delta, //not used temperature = Math.Min(plot.Temperature, GlobalConfig.MaximumNodeTemperature), repulsionK = GlobalConfig.RepulsionK, nodeCount = nodeCount }; Debug.Assert(nodeCount <= (layout.VelocitiesVRAM1 !.SizeInBytes / 16)); //if (GlobalConfig.Settings.Logs.BulkLogging) Logging.RecordLogEvent($"RenderVelocity {this.EngineID} submit", Logging.LogFilterType.BulkDebugLogFile); cl.UpdateBuffer(_velocityParamsBuffer, 0, parameters); cl.SetPipeline(_velocityComputePipeline); cl.SetComputeResourceSet(0, resourceSet); //16 == sizeof(Vector4) uint elemCount = layout.VelocitiesVRAM1 !.SizeInBytes / 16; uint grpSizeX = (uint)Math.Ceiling(elemCount / 256.0); //Console.WriteLine($"VRAM Size: {layout.VelocitiesVRAM1!.SizeInBytes}bytes, WkX: {grpSizeX}, nodeCount: {nodeCount}, bufVel4Count: {layout.VelocitiesVRAM1!.SizeInBytes/16}"); cl.Dispatch(grpSizeX, 1, 1); //_cl.Dispatch((uint)Math.Ceiling(layout.VelocitiesVRAM1!.SizeInBytes / (256.0 * 16)), 1, 1); //if (GlobalConfig.Settings.Logs.BulkLogging) Logging.RecordLogEvent($"RenderVelocity {this.EngineID} done in {watch.ElapsedMilliseconds} MS", Logging.LogFilterType.BulkDebugLogFile); cl.End(); _timer.Stop(); VelocitySetupTime = _timer.Elapsed.TotalMilliseconds; _timer.Restart(); _gd !.SubmitCommands(cl); _gd !.WaitForIdle(); _gd.DisposeWhenIdle(resourceSet); _timer.Stop(); VelocityTime = _timer.Elapsed.TotalMilliseconds; }
private void DrawSymbolsSelectControls(PlottedGraph plot) { float height = 30; if (_activeHighlights.selectedHighlightTab == 0) { if (ImGui.BeginChild(ImGui.GetID("highlightSymsControls"), new Vector2(ImGui.GetContentRegionAvail().X, height))) { ImGui.AlignTextToFramePadding(); ImGui.Text($"{_activeHighlights.SelectedSymbols.Count} highlighted symbols ({plot.HighlightedSymbolNodes.Count} nodes)"); ImGui.SameLine(); ImGui.Dummy(new Vector2(6, 10)); ImGui.SameLine(); if (ImGui.Button("Clear")) { foreach (var sym in _activeHighlights.SelectedSymbols) { symbolInfo symdat = _activeHighlights.displayedModules[sym.moduleID].symbols[sym.address]; symdat.selected = false; _activeHighlights.displayedModules[sym.moduleID].symbols[sym.address] = symdat; } plot.LayoutState.Lock.EnterUpgradeableReadLock(); plot.RemoveHighlightedNodes(plot.HighlightedSymbolNodes, CONSTANTS.HighlightType.Externals); plot.LayoutState.Lock.ExitUpgradeableReadLock(); _activeHighlights.SelectedSymbols.Clear(); } /* * ImGui.SameLine(ImGui.GetContentRegionAvail().X - 100); * ImGui.PushStyleColor(ImGuiCol.Button, Themes.GetThemeColourUINT(Themes.eThemeColour.GraphBackground)); * ImGui.PushStyleColor(ImGuiCol.Text, WritableRgbaFloat.ToUint(Color.Cyan)); * if (ImGui.Button("Highlight Colour")) * { * //todo: highlight colour picker * } * ImGui.PopStyleColor(); * ImGui.PopStyleColor(); */ ImGui.EndChild(); } } }
private static void DrawPreviewZoomEnvelope(PlottedGraph plot, Vector2 subGraphPosition) { ImDrawListPtr imdp = ImGui.GetWindowDrawList(); float previewBaseY = subGraphPosition.Y + EachGraphHeight; plot.GetPreviewVisibleRegion(new Vector2(EachGraphWidth, EachGraphHeight), PreviewProjection, out Vector2 TopLeft, out Vector2 BaseRight); float C1Y = previewBaseY - TopLeft.Y; float C2Y = previewBaseY - BaseRight.Y; bool verySmall = Math.Abs(C1Y - C2Y) < 20; uint colour = verySmall ? Themes.GetThemeColourUINT(Themes.eThemeColour.Emphasis1) : Themes.GetThemeColourUINT(Themes.eThemeColour.PreviewZoomEnvelope); float C1X = Math.Max(subGraphPosition.X + TopLeft.X, subGraphPosition.X); float C2X = Math.Min(subGraphPosition.X + BaseRight.X, subGraphPosition.X + EachGraphWidth - 1); C1Y = Math.Min(previewBaseY - 1, C1Y); C2Y = Math.Max(subGraphPosition.Y, C2Y); if (C1Y > subGraphPosition.Y && C1Y < previewBaseY && C2X > subGraphPosition.X) { imdp.AddLine(new Vector2(C1X, C1Y), new Vector2(C2X, C1Y), colour); } if (C2Y > subGraphPosition.Y && C2Y < previewBaseY) { imdp.AddLine(new Vector2(C2X, C2Y), new Vector2(C1X, C2Y), colour); } if (C2Y < previewBaseY && C1Y > subGraphPosition.Y) { C2Y = Math.Max(C2Y, subGraphPosition.Y); if (C2X > subGraphPosition.X && C2X < subGraphPosition.X + EachGraphWidth) { imdp.AddLine(new Vector2(C2X, C1Y), new Vector2(C2X, C2Y), colour); } if (C1X > subGraphPosition.X && C1X < subGraphPosition.X + EachGraphWidth) { imdp.AddLine(new Vector2(C1X, C2Y), new Vector2(C1X, C1Y), colour); } } }
private void update_rendering(PlottedGraph graph) { ProtoGraph protoGraph = graph.InternalProtoGraph; if (protoGraph == null || protoGraph.EdgeCount == 0) { return; } //if (graph.NodesDisplayData == null)// || !graph.setGraphBusy(true, 2)) // return; if (graph.ReplayState == PlottedGraph.REPLAY_STATE.Ended && protoGraph.Terminated) { graph.ResetAnimation(); } //update the render if there are more verts/edges or graph is being resized graph.RenderGraph(); if (!protoGraph.Terminated) { if (graph.IsAnimated) { graph.ProcessLiveAnimationUpdates(out int doneCount); } } else { if (graph.InternalProtoGraph.TraceData.DiscardTraceData is false && (graph.ReplayState == PlottedGraph.REPLAY_STATE.Playing || graph._userSelectedAnimPosition != -1)) { if (--_nextReplayStep <= 0) { graph.ProcessReplayUpdates(); _nextReplayStep = _FramesBetweenAnimationUpdates; } } } }
private void HandleSelectedSym(PlottedGraph plot, moduleEntry module_modentry, symbolInfo syminfo) { syminfo.selected = !syminfo.selected; module_modentry.symbols[syminfo.address] = syminfo; plot.LayoutState.Lock.EnterUpgradeableReadLock(); plot.LayoutState.GetAttributes(plot.ActiveLayoutStyle, out float[]? attribsArray); if (syminfo.selected) { plot.AddHighlightedNodes(syminfo.threadNodes, CONSTANTS.HighlightType.Externals); _activeHighlights.SelectedSymbols.Add(syminfo); } else { plot.RemoveHighlightedNodes(syminfo.threadNodes, CONSTANTS.HighlightType.Externals); _activeHighlights.SelectedSymbols = _activeHighlights.SelectedSymbols.Where(s => s.address != syminfo.address).ToList(); } plot.LayoutState.Lock.ExitUpgradeableReadLock(); }
/// <summary> /// Pass the graph plot through the velocity compute shader, to adjust the node velocity based on the positions of other nodes /// </summary> /// <param name="RSetDesc">Position shader resource set</param> /// <param name="cl">Commandlist to run the commands on</param> /// <param name="plot">PlottedGraph to compute</param> /// <param name="delta">A float representing how much time has passed since the last frame. Higher values => bigger movements</param> private void RenderVelocity(ResourceSetDescription RSetDesc, CommandList cl, PlottedGraph plot, float delta) { //if (GlobalConfig.Settings.Logs.BulkLogging) Logging.RecordLogEvent($"RenderVelocity {this.EngineID}", Logging.LogFilterType.BulkDebugLogFile); _timer.Restart(); cl.Begin(); ResourceSet resourceSet = _gd.ResourceFactory.CreateResourceSet(RSetDesc); //if (GlobalConfig.Settings.Logs.BulkLogging) Logging.RecordLogEvent($"RenderVelocityBlocks {this.EngineID}", Logging.LogFilterType.BulkDebugLogFile); GraphLayoutState layout = plot.LayoutState; VelocityShaderParams parameters = new VelocityShaderParams { nodeCount = (uint)plot.RenderedNodeCount(), speedDivisor = GlobalConfig.PresetSpeedDivisor }; //if (GlobalConfig.Settings.Logs.BulkLogging) Logging.RecordLogEvent($"RenderVelocity {this.EngineID} submit", Logging.LogFilterType.BulkDebugLogFile); cl.UpdateBuffer(_velocityParamsBuffer, 0, parameters); cl.SetPipeline(_velocityComputePipeline); cl.SetComputeResourceSet(0, resourceSet); //16 == sizeof(Vector4) cl.Dispatch((uint)Math.Ceiling(layout.VelocitiesVRAM1 !.SizeInBytes / (256.0 * 16)), 1, 1); //if (GlobalConfig.Settings.Logs.BulkLogging) Logging.RecordLogEvent($"RenderVelocity {this.EngineID} done in {watch.ElapsedMilliseconds} MS", Logging.LogFilterType.BulkDebugLogFile); cl.End(); _timer.Stop(); VelocitySetupTime = _timer.Elapsed.TotalMilliseconds; _timer.Restart(); _gd !.SubmitCommands(cl); _gd !.WaitForIdle(); _gd.DisposeWhenIdle(resourceSet); _timer.Stop(); VelocityTime = _timer.Elapsed.TotalMilliseconds; }
void ComputeAttributes(bool flip, GraphLayoutState layout, CommandList cl, PlottedGraph graph, float delta, int mouseoverNodeID, bool isAnimated) { ResourceSetDescription attr_rsrc_desc; DeviceBuffer inputAttributes; if (flip) { attr_rsrc_desc = new ResourceSetDescription(_nodeAttribComputeLayout, _attribsParamsBuffer, layout.AttributesVRAM1, layout.EdgeConnectionIndexes, layout.EdgeConnections, layout.AttributesVRAM2); inputAttributes = layout.AttributesVRAM1 !; } else { attr_rsrc_desc = new ResourceSetDescription(_nodeAttribComputeLayout, _attribsParamsBuffer, layout.AttributesVRAM2, layout.EdgeConnectionIndexes, layout.EdgeConnections, layout.AttributesVRAM1); inputAttributes = layout.AttributesVRAM2 !; } _attSetupTimer.Restart(); ResourceSet attribComputeResourceSet = _factory !.CreateResourceSet(attr_rsrc_desc); cl.Begin(); RenderNodeAttribs(cl, graph, inputAttributes, attribComputeResourceSet, delta, mouseoverNodeID, isAnimated); cl.End(); _attSetupTimer.Stop(); attributeSetupTime = _attSetupTimer.Elapsed.TotalMilliseconds; _attShaderTimer.Restart(); _gd !.SubmitCommands(cl); _gd !.WaitForIdle(); //should we be dispose/recreating these? probably not. todo _gd.DisposeWhenIdle(attribComputeResourceSet); _attShaderTimer.Stop(); attributeTime = _attShaderTimer.Elapsed.TotalMilliseconds; //DebugPrintOutputFloatBuffer(layout.AttributesVRAM1!, "Atts1", 32); }
/// <summary> /// Used the velocity buffer to move the nodes in the positions buffer /// </summary> /// <param name="RSetDesc">Position shader resource set</param> /// <param name="cl">Commandlist to run the commands on</param> /// <param name="plot">PlottedGraph to compute</param> /// <param name="delta">A float representing how much time has passed since the last frame. Higher values => bigger movements</param> private unsafe void RenderPosition(ResourceSetDescription RSetDesc, CommandList cl, PlottedGraph plot, float delta) { _timer.Restart(); cl.Begin(); ResourceSet resourceSet = _gd.ResourceFactory.CreateResourceSet(RSetDesc); //Debug.Assert(!VeldridGraphBuffers.DetectNaN(_gd, positions)); //Debug.Assert(!VeldridGraphBuffers.DetectNaN(_gd, velocities)); //if (GlobalConfig.Settings.Logs.BulkLogging) Logging.RecordLogEvent($"RenderPosition {this.EngineID}", Logging.LogFilterType.BulkDebugLogFile); PositionShaderParams parameters = new PositionShaderParams { delta = delta, nodeCount = (uint)plot.RenderedNodeCount() }; //Logging.WriteConsole($"RenderPosition Parambuffer Size is {(uint)Unsafe.SizeOf<PositionShaderParams>()}"); cl.UpdateBuffer(_positionParamsBuffer, 0, parameters); cl.SetPipeline(_positionComputePipeline); cl.SetComputeResourceSet(0, resourceSet); cl.Dispatch((uint)Math.Ceiling(plot.LayoutState.PositionsVRAM1 !.SizeInBytes / (256.0 * sizeof(Vector4))), 1, 1); cl.End(); _timer.Stop(); PositionSetupTime = _timer.Elapsed.TotalMilliseconds; _timer.Restart(); _gd !.SubmitCommands(cl); _gd !.WaitForIdle(); _gd.DisposeWhenIdle(resourceSet); _timer.Stop(); PositionTime = _timer.Elapsed.TotalMilliseconds; }
private void HandleClickedGraph(PlottedGraph plot) => clickedGraph = plot;
public void Draw(PlottedGraph LatestActiveGraph) { if (LatestActiveGraph == null) { return; } if (_ActiveGraph != LatestActiveGraph) { _ActiveGraph = LatestActiveGraph; ThreadHighlightSettings?foundHighlights; if (!graphSettings.TryGetValue(_ActiveGraph, out foundHighlights) || foundHighlights is null) { foundHighlights = new ThreadHighlightSettings(); graphSettings.Add(_ActiveGraph, foundHighlights); } _activeHighlights = foundHighlights; } Vector2 Size = ImGui.GetWindowSize(); Size.Y = ImGui.GetContentRegionAvail().Y; if (ImGui.BeginChild("#highlightControls", Size)) { if (!PopoutHighlight && ImGui.Button("Popout")) { ImGui.SetCursorPosX(ImGui.GetContentRegionAvail().X - 50); PopoutHighlight = true; PopoutHighlightSkipFrame = true; } else { ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags.AutoSelectNewTabs; if (ImGui.BeginTabBar("Highlights Tab Bar", tab_bar_flags)) { if (ImGui.BeginTabItem("Externals/Symbols")) { _activeHighlights.selectedHighlightTab = 0; DrawSymbolsSelectBox(reserveSize: 40); //todo: unbadify this height choice DrawSymbolsSelectControls(_ActiveGraph); ImGui.EndTabItem(); } if (ImGui.BeginTabItem("Addresses")) { _activeHighlights.selectedHighlightTab = 1; DrawAddressSelectControls(_ActiveGraph); if (_activeHighlights.SelectedAddresses.Any()) { DrawAddressSelectBox(_ActiveGraph); } ImGui.EndTabItem(); } if (ImGui.BeginTabItem("Exceptions")) { _activeHighlights.selectedHighlightTab = 2; DrawExceptionSelectBox(_ActiveGraph); DrawExceptionSelectControls(_ActiveGraph); ImGui.EndTabItem(); } ImGui.EndTabBar(); } } ImGui.EndChild(); } }
private void DrawModSymTreeNodes(PlottedGraph plot) { string LowerFilterText = _activeHighlights.SymFilterText.ToLower(); foreach (moduleEntry module_modentry in _activeHighlights.displayedModules.Values) { var keyslist = module_modentry.symbols.Keys.ToArray(); bool hasFilterMatches = false; bool moduleMatchesFilter = false; if (_activeHighlights.SymFilterText.Length == 0) { hasFilterMatches = true; } else if (module_modentry.path.ToLower().Contains(LowerFilterText)) { moduleMatchesFilter = true; hasFilterMatches = true; } else { foreach (ulong symaddr in keyslist) { symbolInfo syminfo = module_modentry.symbols[symaddr]; if (syminfo.name.ToLower().Contains(LowerFilterText)) { hasFilterMatches = true; break; } } } if (hasFilterMatches) { if (ImGui.TreeNode($"{module_modentry.path}")) { float cursX = ImGui.GetCursorPosX() + 75; foreach (ulong symaddr in keyslist) { symbolInfo syminfo = module_modentry.symbols[symaddr]; if (_activeHighlights.SymFilterText.Length > 0 && !moduleMatchesFilter && !syminfo.name.ToLower().Contains(_activeHighlights.SymFilterText.ToLower()) ) { continue; } ImGui.SetCursorPosX(10); ImGui.BeginGroup(); if (ImGui.Selectable($"{syminfo.name}", syminfo.selected)) { HandleSelectedSym(plot, module_modentry, syminfo); } if (ImGui.IsItemClicked(ImGuiMouseButton.Right)) { ImGui.OpenPopup("HighlightColorPicker"); } if (ImGui.BeginPopup("HighlightColorPicker")) { ImGui.PushStyleColor(ImGuiCol.Text, 0xffffffff); ImGui.Text($"Configuring highlight colour for {syminfo.name} (0x{syminfo.address}:x)"); ImGuiColorEditFlags flags = ImGuiColorEditFlags.NoInputs; flags |= ImGuiColorEditFlags.AlphaBar; if (ImGui.ColorPicker4("Highlight Colour", ref _activeColorPick1, flags)) { foreach (uint node in syminfo.threadNodes) { plot.SetCustomHighlightColour((int)node, _activeColorPick1); } } ImGui.Text("Highlight active:"); ImGui.SameLine(); if (SmallWidgets.ToggleButton("NodeActiveHighlightToggle", syminfo.selected, "Node is highlighted")) { HandleSelectedSym(plot, module_modentry, syminfo); } ImGui.PopStyleColor(); ImGui.EndPopup(); } ImGui.SameLine(190); ImGui.Text($"0x{syminfo.address:X}"); ImGui.SameLine(305); ImGui.Text($"{syminfo.threadNodes.Count}"); ImGui.EndGroup(); if (!syminfo.selected) { if (ImGui.IsItemHovered(ImGuiHoveredFlags.None)) { if (syminfo.hovered == false) { syminfo.hovered = true; HandleMouseoverSym(plot, module_modentry, syminfo); } } else { if (syminfo.hovered == true) { syminfo.hovered = false; HandleMouseoverSym(plot, module_modentry, syminfo); } } } } ImGui.TreePop(); } } } }
/// <summary> /// Draw a preview graph texture on the preview pane /// </summary> /// <param name="plot">The graph being drawn</param> /// <param name="xPadding">horizontal padding</param> /// <param name="captionHeight">height of the caption</param> /// <param name="captionBackgroundcolor">contrast background colour of the caption</param> /// <param name="canHover">output flag states if we can safely draw a mouseover tooltip</param> /// <param name="mainWidgetSize">Size of the maingraph widget, used for projecting the zoom envelope</param> /// <returns>The graph was clicked</returns> private bool DrawPreviewGraph(PlottedGraph plot, float xPadding, float captionHeight, uint captionBackgroundcolor, out bool canHover, Vector2 mainWidgetSize) { ImDrawListPtr imdp = ImGui.GetWindowDrawList(); //draw on and clipped to this window bool clicked = false; canHover = false; if (plot == null) { return(clicked); } int graphNodeCount = plot.GraphNodeCount(); if (graphNodeCount == 0) { return(clicked); } plot.GetLatestTexture(out Texture previewTexture); if (previewTexture == null) { return(clicked); } bool isSelected = plot.TID == selectedGraphTID; canHover = true; //copy in the actual rendered graph ImGui.SetCursorPosY(ImGui.GetCursorPosY()); Vector2 subGraphPosition = ImGui.GetCursorScreenPos() + new Vector2(xPadding, 0); IntPtr CPUframeBufferTextureId = _ImGuiController !.GetOrCreateImGuiBinding(_gd !.ResourceFactory, previewTexture, $"PreviewPlot{plot.TID}"); imdp.AddImage(user_texture_id: CPUframeBufferTextureId, p_min: subGraphPosition, p_max: new Vector2(subGraphPosition.X + EachGraphWidth, subGraphPosition.Y + EachGraphHeight), uv_min: new Vector2(0, 1), uv_max: new Vector2(1, 0)); float borderThickness = Themes.GetThemeSize(Themes.eThemeSize.PreviewSelectedBorder); float halfBorderThickness = (float)Math.Floor(borderThickness / 2f); if (isSelected) { DrawPreviewZoomEnvelope(plot, subGraphPosition); //Draw the thicker selected graph border if (borderThickness > 0) { imdp.AddRect( p_min: new Vector2(subGraphPosition.X + halfBorderThickness, subGraphPosition.Y + halfBorderThickness), p_max: new Vector2((subGraphPosition.X + EachGraphWidth - halfBorderThickness), subGraphPosition.Y + EachGraphHeight - halfBorderThickness), col: GetGraphBorderColour(plot), 0, ImDrawFlags.None, borderThickness); } } //write the caption string Caption = $"TID:{plot.TID} {graphNodeCount} nodes {(isSelected ? "[Selected]" : "")}"; ImGui.SetCursorPosX(ImGui.GetCursorPosX()); Vector2 captionBGStart = subGraphPosition + new Vector2(borderThickness, borderThickness); Vector2 captionBGEnd = new Vector2((captionBGStart.X + EachGraphWidth - borderThickness * 2), captionBGStart.Y + captionHeight); imdp.AddRectFilled(p_min: captionBGStart, p_max: captionBGEnd, col: captionBackgroundcolor); ImGui.PushStyleColor(ImGuiCol.Text, Themes.GetThemeColourUINT(Themes.eThemeColour.PreviewText)); ImGui.SetCursorPosX(ImGui.GetCursorPosX() + CONSTANTS.UI.PREVIEW_PANE_X_PADDING + borderThickness + 1); ImGui.SetCursorPosY(ImGui.GetCursorPosY() + borderThickness); ImGui.Text(Caption); ImGui.PopStyleColor(); ImGui.SetCursorPosX(ImGui.GetCursorPosX() + EachGraphWidth - 48); //live thread activity plot if (ActiveTrace is not null && !ActiveTrace.WasLoadedFromSave) { ImGui.SetCursorPosY(ImGui.GetCursorPosY() - captionHeight); float maxVal; float[]? invalues = null; if (plot.InternalProtoGraph.TraceReader != null) { plot.InternalProtoGraph.TraceReader.RecentMessageRates(out invalues); } if (invalues == null || invalues.Length == 0) { invalues = new List <float>() { 0, 0, 0, 0, 0 }.ToArray(); maxVal = 100; } else { maxVal = invalues.Max(); } ImGui.PushStyleColor(ImGuiCol.FrameBg, captionBackgroundcolor); ImGui.PlotLines("", ref invalues[0], invalues.Length, 0, "", 0, maxVal, new Vector2(40, captionHeight)); if (ImGui.IsItemHovered()) { canHover = false; //The PlotLines widget doesn't allow disabling the mouseover, so have to prevent our mousover to avoid a merged tooltip } ImGui.PopStyleColor(); } //invisible button to detect graph click ImGui.SetCursorPos(new Vector2(1, ImGui.GetCursorPosY() - (float)(captionHeight))); if (ImGui.InvisibleButton("PrevGraphBtn" + plot.TID, new Vector2(EachGraphWidth, EachGraphHeight - 2)) || ImGui.IsItemActive()) { clicked = true; if (isSelected) { Vector2 clickPos = ImGui.GetMousePos(); Vector2 clickOffset = clickPos - subGraphPosition; clickOffset.Y = EachGraphHeight - clickOffset.Y; plot.MoveCameraToPreviewClick(clickOffset, new Vector2(EachGraphWidth, EachGraphHeight), mainGraphWidgetSize: mainWidgetSize, PreviewProjection); } } return(clicked); }
/// <summary> /// Do the actual computation of graph layout and animation /// Uses the velocity shader to adjust the velocity based on relative positions /// Uses the position shader to move the nodes at the calculated velocity /// Adjusts the size/alpha of nodes based on the attribute buffer /// </summary> /// <param name="cl">Thread-specific command list</param> /// <param name="plot">Graph to perform computation on</param> /// <param name="mouseoverNodeID">The index of the node the users mouse is hovering over</param> /// <param name="isAnimated">If the graph should have animation attributes computed (ie: main graph with live/replay active)</param> /// <returns>The version ID associated with the produced graph layout computed</returns> public ulong Compute(CommandList cl, PlottedGraph plot, int mouseoverNodeID, bool isAnimated) { Debug.Assert(_gd is not null); if (plot.DrawnEdgesCount == 0 || !GlobalConfig.LayoutAllComputeEnabled) // || ErrorState) { return(plot.LayoutState.RenderVersion); } if (GlobalConfig.Settings.Logs.BulkLogging) { Logging.RecordLogEvent($"Compute start {EngineID} graph {plot.PID}:{plot.TID}", Logging.LogFilterType.BulkDebugLogFile); } int edgesCount = plot.DrawnEdgesCount; Debug.Assert(plot != null, "Layout engine called to compute without active graph"); GraphLayoutState layout = plot.LayoutState; layout.Lock.EnterUpgradeableReadLock(); _stepTimer.Restart(); try { if (!layout.ActivatingPreset || layout.Initialised is false) { plot.AddNewEdgesToLayoutBuffers(edgesCount); } var now = DateTime.UtcNow.Ticks / TimeSpan.TicksPerMillisecond; float delta = Math.Min((now - plot.LastComputeTime) / 1000f, 1.0f); // safety cap on large deltas delta *= (layout.ActivatingPreset ? 7.5f : 1.0f); //without this the preset animation will 'bounce' plot.LastComputeTime = now; //todo set this on layout change bool isForceDirected = CONSTANTS.LayoutStyles.IsForceDirected(plot.ActiveLayoutStyle); bool forceComputationActive = layout.ActivatingPreset || (GlobalConfig.LayoutPositionsActive && plot.Temperature > 0 && isForceDirected); LayoutPipelines.LayoutPipeline?activePipeline = SelectPipeline(layout); if (activePipeline is null) { ErrorState = true; Logging.RecordError("Error selecting active layout - it's either invalid or the pipeline is uninitialised"); return(layout.RenderVersion); } bool flip = layout.flip(); if (forceComputationActive) { /* * DebugPrintOutputFloatBuffer(layout.VelocitiesVRAM1!, "Vel1b4", 32); * DebugPrintOutputFloatBuffer(layout.PositionsVRAM1!, "pos1b4", 32); * DebugPrintOutputFloatBuffer(layout.PositionsVRAM2!, "pos2b4", 32); * DebugPrintOutputFloatBuffer(layout.VelocitiesVRAM2!, "vel2b4", 32); */ if (GlobalConfig.Settings.Logs.BulkLogging) { Logging.RecordLogEvent($"Layout {activePipeline.Name} computation starting in engine {this.EngineID}", Logging.LogFilterType.BulkDebugLogFile); } //Actual computation happens here activePipeline.Compute(plot, cl, flip, delta); if (GlobalConfig.Settings.Logs.BulkLogging) { Logging.RecordLogEvent($"Layout {activePipeline.Name} computation finished in engine {this.EngineID}", Logging.LogFilterType.BulkDebugLogFile); } layout.IncrementVersion(); if (plot.OPT_LOCK_TEMPERATURE is false) { plot.Temperature *= CONSTANTS.Layout_Constants.TemperatureStepMultiplier; if (plot.Temperature <= CONSTANTS.Layout_Constants.MinimumTemperature) { plot.Temperature = 0; } } } if (rgatUI.ResponsiveKeyHeld || plot.FurthestNodeDimension == 0) { // todo - don't iterate over every node every frame! // not sure whether to make this timer based or do it in the shader // it looks pretty bad doing it every 10 frames // for now just do it every 3 frames if ((forceComputationActive && (layout.RenderVersion % 3) == 0) || plot.FurthestNodeDimension == 0) { if (layout.PositionsVRAM1 is not null && (plot.ComputeBufferNodeCount * 4 * sizeof(float)) <= layout.PositionsVRAM1.SizeInBytes) { float highPosition = FindHighXYZ(layout.PositionsVRAM1 !, plot.ComputeBufferNodeCount, out int furthestNodeIdx); if (furthestNodeIdx != -1) { plot.SetFurthestNodeDimension(furthestNodeIdx, highPosition); } } } } if (GlobalConfig.LayoutAttribsActive) { if (GlobalConfig.Settings.Logs.BulkLogging) { Logging.RecordLogEvent($"Attribute computation starting in engine {this.EngineID}", Logging.LogFilterType.BulkDebugLogFile); } ComputeAttributes(flip, layout, cl, plot, delta, mouseoverNodeID, isAnimated); if (GlobalConfig.Settings.Logs.BulkLogging) { Logging.RecordLogEvent($"Attribute computation finished in engine {this.EngineID}", Logging.LogFilterType.BulkDebugLogFile); } } //If activating a preset, find the fasted node. If below a threshold, move the positions to their targets //This avoids preset snapping taking too long at low speeds int steps = layout.IncrementPresetSteps(); if (layout.ActivatingPreset && steps > 10) //todo look at this again, should it be done after compute? { if (layout.VelocitiesVRAM1 is not null && (plot.ComputeBufferNodeCount * 4 * sizeof(float)) <= layout.VelocitiesVRAM1.SizeInBytes) { //when the nodes are near their targets, instead of bouncing around while coming to a stop, just snap them into position float fastest = FindHighXYZ(layout.VelocitiesVRAM1, plot.ComputeBufferNodeCount, out int _); if (fastest < 1 || steps > 20) { if (GlobalConfig.BulkLog) { Logging.RecordLogEvent("Preset done", graph: plot.InternalProtoGraph, filter: Logging.LogFilterType.BulkDebugLogFile); } layout.CompleteLayoutChange(); } } } if (GlobalConfig.LayoutPositionsActive) { _stepTimer.Stop(); plot.RecordComputeTime(stepMSTotal: _stepTimer.Elapsed.TotalMilliseconds, positionSetupTime: activePipeline.PositionSetupTime, positionShaderTime: activePipeline.PositionTime, velocitySetupTime: activePipeline.VelocitySetupTime, velocityShaderTime: activePipeline.VelocityTime, attributeSetupTime: attributeSetupTime, attributeShaderTime: attributeTime); activePipeline.ResetTimers(); attributeSetupTime = 0; attributeTime = 0; } } catch (Exception e) { Logging.RecordException($"Error during layout compute: {e.Message}", e, plot.InternalProtoGraph); ErrorState = true; } finally { _stepTimer.Stop(); layout.Lock.ExitUpgradeableReadLock(); } if (_stepTimer.ElapsedMilliseconds > 100) { Logging.RecordLogEvent($"Compute step took {_stepTimer.ElapsedMilliseconds}ms", Logging.LogFilterType.Debug); } //DebugPrintOutputIntBuffer(layout.BlockMiddles!, "Middles", 100); //DebugPrintOutputFloatBuffer(layout.VelocitiesVRAM1!, "Vel1", 140); //DebugPrintOutputFloatBuffer(layout.PositionsVRAM1!, "pos1", 140); //DebugPrintOutputFloatBuffer(layout.PositionsVRAM2!, "pos2", 32); //DebugPrintOutputFloatBuffer(layout.AttributesVRAM, "Atts2", 32); lock (_lock) { _lastComputeMS.Add(_stepTimer.Elapsed.TotalMilliseconds); if (_lastComputeMS.Count > GlobalConfig.StatisticsTimeAvgWindow) { _lastComputeMS = _lastComputeMS.TakeLast(GlobalConfig.StatisticsTimeAvgWindow).ToList(); } AverageComputeTime = _lastComputeMS.Average(); } if (GlobalConfig.Settings.Logs.BulkLogging) { Logging.RecordLogEvent($"Compute end {EngineID} graph {plot.PID}:{plot.TID}", Logging.LogFilterType.BulkDebugLogFile); } return(layout.RenderVersion); }
/// <summary> /// Creates an array of metadata for basic blocks used for basic-block-centric graph layout /// item[0] = blockID /// item[1] = offsetFromCenter; number of nodes ahead the center node is /// item[2] = centerPseudoBlockTopID; top of the block this node is in /// item[3] = centerPseudoBlockBaseID; base of the block this node is in /// </summary> /// <param name="plot">The graph being plotted</param> /// <param name="blockData">Output description of basic block information for each node</param> /// <param name="blockMiddles">Output List of basic block middle nodes</param> private static bool CreateBlockMetadataBuf(PlottedGraph plot, out NODE_BLOCK_METADATA_COMPUTEBUFFER[] blockData, out int[] blockMiddles) { ProtoGraph graph = plot.InternalProtoGraph; List <int>[] nodeNeighboursArray = plot.GetNodeNeighboursArray(); int nodeCount = nodeNeighboursArray.Length; NODE_BLOCK_METADATA_COMPUTEBUFFER[] blockDataInts = new NODE_BLOCK_METADATA_COMPUTEBUFFER[nodeCount]; Dictionary <int, int> blockMiddlesDict = new Dictionary <int, int>(); List <int> blockMiddleNodesList = new List <int>(); Dictionary <int, NodeData> exceptionBlocks = new(); /* * Step 1: Build a list of active blocks (ie: blocks which currently have instructions in, * as opposed to blocks which have been split into new ones by control flow */ List <int> activeBlockIDs = new(); Dictionary <int, int> NodeBlockToBlockMetaIndex = new(); Dictionary <int, int> BlockMetaToBlockFirstLastIndex = new(); for (var i = 0; i < nodeCount; i++) { NodeData n = graph.NodeList[i]; int nodeBlockID = (int)n.BlockID; if (NodeBlockToBlockMetaIndex.TryGetValue(nodeBlockID, out int metaBlockID) is false || activeBlockIDs.Contains(metaBlockID) is false) { metaBlockID = activeBlockIDs.Count; NodeBlockToBlockMetaIndex[nodeBlockID] = metaBlockID; BlockMetaToBlockFirstLastIndex[metaBlockID] = nodeBlockID; activeBlockIDs.Add(metaBlockID); if (n.CausedException) { exceptionBlocks[metaBlockID] = n; } } } //Step 2: Build the list of block center nodes that the block velocity shader will run over blockMiddleNodesList.Capacity = activeBlockIDs.Count; foreach (int blockIdx in activeBlockIDs) { if (blockIdx == -1) { blockMiddleNodesList.Add(-1); } int originalBlockIndex = BlockMetaToBlockFirstLastIndex[blockIdx]; if (originalBlockIndex < 0 || originalBlockIndex >= graph.BlocksFirstLastNodeList.Count) { blockMiddlesDict[blockIdx] = -1; //instructions sent and not executed? why? blockMiddleNodesList.Add((int)-1); continue; } var firstIdx_LastIdx = graph.BlocksFirstLastNodeList[originalBlockIndex]; if (firstIdx_LastIdx == null) { continue; } if (firstIdx_LastIdx.Item1 == firstIdx_LastIdx.Item2) { if (blockMiddleNodesList.Contains((int)firstIdx_LastIdx.Item1)) { continue; } blockMiddlesDict[blockIdx] = (int)firstIdx_LastIdx.Item1; //1 node block, top/mid/base is the same blockMiddleNodesList.Add((int)firstIdx_LastIdx.Item1); //Debug.Assert(blockIdx == (blockMiddleNodesList.Count-1)); } else { var block = graph.ProcessData.BasicBlocksList[originalBlockIndex]?.Item2; Debug.Assert(block is not null); int midIdx = (int)Math.Ceiling((block.Count - 1.0) / 2.0); var middleIns = block[midIdx]; if (!middleIns.GetThreadVert(graph.ThreadID, out uint centerNodeID)) { blockMiddlesDict[blockIdx] = -1; //instructions sent and not executed? why? //Debug.Assert(false, $"Instruction 0x{middleIns.address:X} not found in thread {tid}"); } else { //if (blockMiddleNodesList.Contains((int)centerNodeID)) //{ // continue; //} blockMiddlesDict[blockIdx] = (int)centerNodeID; blockMiddleNodesList.Add((int)centerNodeID); //Debug.Assert(blockIdx == (blockMiddleNodesList.Count - 1)); } } } /* * Step 3: Build the block metadata buffer which allows the position and velocity shaders to process each * node in the context of the block it is in */ int externals = 0; for (uint nodeIdx = 0; nodeIdx < nodeCount; nodeIdx++) { NodeData?n = graph.GetNode(nodeIdx); Debug.Assert(n is not null); int blockID; int offsetFromCenter; Tuple <uint, uint>?FirstLastIdx; if (!n.IsExternal) { if (n.BlockID >= graph.BlocksFirstLastNodeList.Count) { continue; } FirstLastIdx = graph.BlocksFirstLastNodeList[(int)n.BlockID]; //bug: this can happen before bflnl is filled if (FirstLastIdx == null) { continue; } blockID = NodeBlockToBlockMetaIndex[(int)n.BlockID]; if (!blockMiddlesDict.ContainsKey(blockID)) { continue; } var blockEntry = graph.ProcessData.BasicBlocksList[(int)n.BlockID]; Debug.Assert(blockEntry is not null); int blockNodeCount = blockEntry.Item2.Count; if (exceptionBlocks.TryGetValue(blockID, out NodeData? exceptionNode) && exceptionNode is not null) { for (int bIdx = 0; bIdx < blockNodeCount; bIdx++) { if (blockEntry.Item2[bIdx].Address == exceptionNode.Address) { blockNodeCount = bIdx + 1; break; } } } int midIdx = (int)Math.Ceiling((blockNodeCount - 1.0) / 2.0); offsetFromCenter = n.BlockIndex - midIdx; } else { externals += 1; FirstLastIdx = new Tuple <uint, uint>(n.Index, n.Index); offsetFromCenter = 0; blockMiddleNodesList.Add((int)n.Index); //external nodes dont have a block id so just give them a unique one //all that matters in the shader is it's unique blockID = blockMiddleNodesList.Count; blockMiddlesDict[blockID] = (int)n.Index; } int blockTopNodeIndex = -1; int blockBaseNodeIndex = -1; if (offsetFromCenter is 0) { if (graph.GetNode(FirstLastIdx.Item1)?.IncomingNeighboursSet.Count > 0) { blockTopNodeIndex = (int)FirstLastIdx.Item1; } else { //these are all back edges, which have 0 force /* * //the top of the block wasnt connected to anything * //there might be a connection below though * List<InstructionData>? blockInslist = graph.ProcessData.BasicBlocksList[(int)n.BlockID]?.Item2; * if (blockInslist is not null) * { * for (var i = 1; i < blockInslist.Count; i++) * { * uint n2Idx = (uint)(FirstLastIdx.Item1 + i); * NodeData? n2 = graph.GetNode(n2Idx); * if (n2 is not null && n2.IncomingNeighboursSet.Count > 0) * { * blockTopNodeIndex = (int)n2Idx; * break; * } * } * } */ } if (graph.GetNode(FirstLastIdx.Item2)?.OutgoingNeighboursSet.Count > 0) { blockBaseNodeIndex = (int)FirstLastIdx.Item2; } } blockDataInts[nodeIdx].MetaBlockIndex = blockID; blockDataInts[nodeIdx].OffsetFromCenter = offsetFromCenter; blockDataInts[nodeIdx].BlockTopEdgeList = blockTopNodeIndex; blockDataInts[nodeIdx].BlockBaseEdgeList = blockBaseNodeIndex != blockTopNodeIndex ? blockBaseNodeIndex : -1; //Debug.Assert(offsetFromCenter is not 0 || n.IsExternal || (blockMiddleNodesList[blockID] == (int)nodeIdx)); } blockMiddles = blockMiddleNodesList.ToArray(); blockData = blockDataInts; return(true); }
/// <summary> /// Update the node attributes compute VRAM buffer (alpha, node size, mouseover details) /// </summary> /// <param name="cl">Thread-specific CommandList</param> /// <param name="graph">ProtoGraph being drawn</param> /// <param name="inputAttributes">Attributes buffer being updated</param> /// <param name="resources">Shader resources ResourceSet</param> /// <param name="delta">Time-delta from the last update</param> /// <param name="mouseoverNodeID">Index of the node the mouse is over</param> /// <param name="useAnimAttribs">Flag to specify the graph is in animated-alpha mode</param> private unsafe void RenderNodeAttribs(CommandList cl, PlottedGraph graph, DeviceBuffer inputAttributes, ResourceSet resources, float delta, int mouseoverNodeID, bool useAnimAttribs) { if (GlobalConfig.Settings.Logs.BulkLogging) { Logging.RecordLogEvent($"RenderNodeAttribs {this.EngineID}", Logging.LogFilterType.BulkDebugLogFile); } AttribShaderParams parms = new AttribShaderParams { delta = delta, hoveredNodeID = mouseoverNodeID, nodeCount = (uint)Math.Min(graph.RenderedNodeCount(), graph.LayoutState.AttributesVRAM1 !.SizeInBytes / 16), MinimumAlpha = GlobalConfig.AnimatedFadeMinimumAlpha, hoverMode = (mouseoverNodeID != -1) ? 1 : 0, isAnimated = useAnimAttribs ? 1: 0 }; graph.GetActiveNodeIndexes(out List <uint> pulseNodes, out List <uint> lingerNodes, out uint[] deactivatedNodes); if (GlobalConfig.Settings.Logs.BulkLogging) { Logging.RecordLogEvent($"RenderNodeAttribs {this.EngineID} updating attribsbuf {inputAttributes.Name}", Logging.LogFilterType.BulkDebugLogFile); } cl.UpdateBuffer(_attribsParamsBuffer, 0, parms); float currentPulseAlpha = Math.Max(GlobalConfig.AnimatedFadeMinimumAlpha, GraphicsMaths.getPulseAlpha()); //todo - merge contiguous regions to reduce command count float[] valArray = new float[3]; foreach (uint idx in pulseNodes) { if (idx >= graph.RenderedNodeCount()) { break; } if (inputAttributes.SizeInBytes <= idx * 4 * sizeof(float) + (2 * sizeof(float))) { break; } valArray[0] = GlobalConfig.NodeSize; //start big valArray[1] = 1.0f; //full alpha valArray[2] = 1.0f; //pulse fixed(float *dataPtr = valArray) { Debug.Assert((idx * 4 * sizeof(float) + valArray.Length * sizeof(float)) < inputAttributes.SizeInBytes); cl.UpdateBuffer(inputAttributes, idx * 4 * sizeof(float), (IntPtr)dataPtr, (uint)valArray.Length * sizeof(float)); } } //make the active node pulse if (graph.IsAnimated) { uint activeNodeIdx = graph.LastAnimatedVert; if (!lingerNodes.Contains(activeNodeIdx)) { valArray[0] = currentPulseAlpha; fixed(float *dataPtr = valArray) { uint nodeAlphaOffset = (activeNodeIdx * 4 * sizeof(float)) + (2 * sizeof(float)); if (nodeAlphaOffset + sizeof(float) <= inputAttributes.SizeInBytes) { cl.UpdateBuffer(inputAttributes, nodeAlphaOffset, (IntPtr)dataPtr, sizeof(float)); } } } } foreach (uint idx in lingerNodes) { if (idx >= graph.RenderedNodeCount()) { break; } if (inputAttributes.SizeInBytes <= idx * 4 * sizeof(float) + (2 * sizeof(float))) { break; } valArray[0] = 2.0f + currentPulseAlpha; fixed(float *dataPtr = valArray) { Debug.Assert((idx * 4 * sizeof(float) + (2 * sizeof(float)) + sizeof(float)) < inputAttributes.SizeInBytes); cl.UpdateBuffer(inputAttributes, idx * 4 * sizeof(float) + (2 * sizeof(float)), (IntPtr)dataPtr, sizeof(float)); } } foreach (uint idx in deactivatedNodes) { if (idx >= graph.RenderedNodeCount()) { break; } if (inputAttributes.SizeInBytes <= idx * 4 * sizeof(float) + (2 * sizeof(float))) { break; } valArray[0] = 0.8f; fixed(float *dataPtr = valArray) { Debug.Assert((idx * 4 * sizeof(float) + (2 * sizeof(float)) + sizeof(float)) < inputAttributes.SizeInBytes); cl.UpdateBuffer(inputAttributes, idx * 4 * sizeof(float) + (2 * sizeof(float)), (IntPtr)dataPtr, sizeof(float)); } } if (graph.HighlightsChanged) { ApplyHighlightAttributes(cl, graph, inputAttributes); } cl.SetPipeline(_nodeAttribComputePipeline); cl.SetComputeResourceSet(0, resources); cl.Dispatch((uint)Math.Ceiling(inputAttributes.SizeInBytes / (256.0 * sizeof(Vector4))), 1, 1); }
/// <summary> /// Draw the preview graph widget /// </summary> public void DrawWidget(Vector2 mainWidgetSize) { bool showToolTip = false; PlottedGraph?latestHoverGraph = null; TraceRecord? activeTrace = ActiveTrace; if (activeTrace == null) { return; } float captionHeight = ImGui.CalcTextSize("123456789").Y; DrawnPreviewGraphs = activeTrace.GetPlottedGraphs(); List <int> indexes = GetGraphOrder(trace: activeTrace, graphs: DrawnPreviewGraphs); uint captionBackgroundcolor = Themes.GetThemeColourUINT(Themes.eThemeColour.PreviewTextBackground); ImGui.PushStyleVar(ImGuiStyleVar.CellPadding, new Vector2(0, CONSTANTS.UI.PREVIEW_PANE_Y_SEP)); //Graph drawing loop if (ImGui.BeginTable("PrevGraphsTable", 1, ImGuiTableFlags.Borders, new Vector2(CONSTANTS.UI.PREVIEW_PANE_WIDTH, ImGui.GetContentRegionAvail().Y))) { foreach (int graphIdx in indexes) { PlottedGraph plot = DrawnPreviewGraphs[graphIdx]; float xPadding = CONSTANTS.UI.PREVIEW_PANE_X_PADDING; if (plot == null || plot.GraphNodeCount() == 0) { continue; } ImGui.TableNextRow(); ImGui.TableSetColumnIndex(0); if (DrawPreviewGraph(plot, xPadding, captionHeight, captionBackgroundcolor, out bool canHover, mainWidgetSize)) { var MainGraphs = plot.InternalProtoGraph.TraceData.GetPlottedGraphs(); HandleClickedGraph(MainGraphs[graphIdx]); } if (canHover && ImGui.IsItemHovered(ImGuiHoveredFlags.None) && !(ImGui.IsMouseDown(ImGuiMouseButton.Left))) { latestHoverGraph = plot; showToolTip = true; } } ImGui.EndTable(); } ImGui.PopStyleVar(); ImGui.PushStyleVar(ImGuiStyleVar.FramePadding, new Vector2(5, 5)); ImGui.PushStyleVar(ImGuiStyleVar.WindowPadding, new Vector2(5, 5)); ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, new Vector2(5, 5)); ImGui.PushStyleColor(ImGuiCol.Border, 0x77999999); HoveredGraph = latestHoverGraph; bool showedCtx = HandlePreviewGraphContextMenu(); bool veryRecentPopup = showedCtx || _lastCtxMenu.AddMilliseconds(250) > DateTime.Now; if (showToolTip && !veryRecentPopup && HoveredGraph is not null) { DrawGraphTooltip(HoveredGraph); } ImGui.PopStyleVar(3); ImGui.PopStyleColor(); }
/// <summary> /// Iterates over the position of every node, translating it to a widget position /// Returns the offsets of the furthest nodes of the edges of the widget /// To fit the graph in the screen, each offset needs to be as small as possible above 0 /// /// Acquires reader lock /// </summary> /// <param name="graphWidgetSize">Size of the rendering widget</param> /// <param name="graph">Graph being displayed in the widget</param> /// <param name="isPreview">True if preview widget, false if main</param> /// <param name="xoffsets">Furthest from the left and right sides of the widget</param> /// <param name="yoffsets">Furthest from the top and bottom of the widget</param> /// <param name="zoffsets">Furthest from in front of/behind the camera lens in the Z direction</param> /// <returns>true if a meaningful result was returned</returns> public static bool GetWidgetFitOffsets(Vector2 graphWidgetSize, PlottedGraph graph, bool isPreview, out Vector2 xoffsets, out Vector2 yoffsets, out Vector2 zoffsets) { if (GlobalConfig.Settings.Logs.BulkLogging) { Logging.RecordLogEvent($"GetWidgetFitOffsets Start {graph.TID} layout", Logging.LogFilterType.BulkDebugLogFile); } xoffsets = new Vector2(0, 0); yoffsets = new Vector2(0, 0); zoffsets = new Vector2(0, 0); float zoom = isPreview ? graph.CameraState.PreviewCameraZoom : graph.CameraState.MainCameraZoom; float aspectRatio = graphWidgetSize.X / graphWidgetSize.Y; Matrix4x4 translation = isPreview ? graph.CameraState.PreviewCameraTranslation : graph.CameraState.MainCameraTranslation; Matrix4x4 projection = Matrix4x4.CreatePerspectiveFieldOfView(1.0f, aspectRatio, 1, 80000); Matrix4x4 worldView = Matrix4x4.CreateFromAxisAngle(Vector3.UnitY, 0) * translation; Vector2 xlimits = new Vector2(float.MaxValue, float.MinValue); Vector2 ylimits = new Vector2(float.MaxValue, float.MinValue); Vector2 zlimits = new Vector2(float.MaxValue, float.MinValue); Vector2 ev = new Vector2(0, 0); Vector2 xmin = ev, xmax = ev, ymin = ev, ymax = ev; float[] positions = graph.LayoutState.DownloadVRAMPositions(); bool result; if (positions.Length < 4) { result = false; } else { result = true; for (int idx = 0; idx < positions.Length; idx += 4) { float guard = positions[idx + 3]; if (guard is not 1) { break; } float x = positions[idx]; float y = positions[idx + 1]; float z = positions[idx + 2]; Vector3 worldpos = new Vector3(x, y, z); Vector2 ndcPos = GraphicsMaths.WorldToNDCPos(worldpos, worldView, projection); if (ndcPos.X < xlimits.X) { xlimits = new Vector2(ndcPos.X, xlimits.Y); xmin = ndcPos; } if (ndcPos.X > xlimits.Y) { xlimits = new Vector2(xlimits.X, ndcPos.X); xmax = ndcPos; } if (ndcPos.Y < ylimits.X) { ylimits = new Vector2(ndcPos.Y, ylimits.Y); ymin = ndcPos; } if (ndcPos.Y > ylimits.Y) { ylimits = new Vector2(ylimits.X, ndcPos.Y); ymax = ndcPos; } if (worldpos.Z < zlimits.X) { zlimits = new Vector2(worldpos.Z, zlimits.Y); } if (worldpos.Z > zlimits.Y) { zlimits = new Vector2(zlimits.X, worldpos.Z); } } Vector2 minxS = GraphicsMaths.NdcToScreenPos(xmin, graphWidgetSize); Vector2 maxxS = GraphicsMaths.NdcToScreenPos(xmax, graphWidgetSize); Vector2 minyS = GraphicsMaths.NdcToScreenPos(ymin, graphWidgetSize); Vector2 maxyS = GraphicsMaths.NdcToScreenPos(ymax, graphWidgetSize); xoffsets = new Vector2(minxS.X, graphWidgetSize.X - maxxS.X); yoffsets = new Vector2(minyS.Y, graphWidgetSize.Y - maxyS.Y); zoffsets = new Vector2(zlimits.X - zoom, zlimits.Y - zoom); } //Sometimes the position buffer is full of terrible data. //Seems to just be for the preview graph? Only happens at the start so must have gotten hold of uninitialised data if (zoffsets.X > 100000000000 || zoffsets.X < -100000000000) { if (isPreview) { graph.CameraState.PreviewCameraZoom = -60000; } else { graph.CameraState.MainCameraZoom = -60000; } return(false); } if (GlobalConfig.Settings.Logs.BulkLogging) { Logging.RecordLogEvent($"GetWidgetFitOffsets exit", Logging.LogFilterType.BulkDebugLogFile); } return(result); }
private void DrawGraphTooltip(PlottedGraph plot) { ImGui.SetNextWindowPos(ImGui.GetMousePos() + new Vector2(0, 20)); ImGui.BeginTooltip(); string runningState; //todo a 'blocked' option when i get around to detecting/displaying the blocked state if (plot.InternalProtoGraph.TraceData.TraceState == TraceRecord.ProcessState.eSuspended) { runningState = "Suspended"; } else { if (plot.InternalProtoGraph.Terminated) { runningState = "Terminated"; } else { runningState = "Running"; } } if (_threadStartCache.ContainsKey(plot)) { ImGui.Text(_threadStartCache[plot]); } else { if (plot.InternalProtoGraph.NodeCount > 0) { ulong blockaddr = plot.InternalProtoGraph.NodeList[0].Address; bool found = plot.InternalProtoGraph.ProcessData.FindContainingModule(blockaddr, out int?module); if (found) { string path = plot.InternalProtoGraph.ProcessData.GetModulePath(module !.Value); string pathSnip = Path.GetFileName(path); if (pathSnip.Length > 50) { pathSnip = pathSnip.Substring(pathSnip.Length - 50, pathSnip.Length); } string val = $"Start Address: {pathSnip}:0x{blockaddr:X}"; _threadStartCache[plot] = val; ImGui.Text(val); } else { ImGui.Text("[No Module?]"); } } } ImGui.Text($"Graph TID: {plot.TID} [{runningState}]"); ImGui.Text($"Graph PID: {plot.PID}"); ImGui.Text($"Unique Instructions: {plot.InternalProtoGraph.NodeList.Count}"); ImGui.Text($"Total Instructions: {plot.InternalProtoGraph.TotalInstructions}"); ImGui.Text($"Animation Entries: {plot.InternalProtoGraph.UpdateCount}"); ImGui.Text($"Exceptions: {plot.InternalProtoGraph.ExceptionCount}"); ImGui.Separator(); ImGui.PushStyleColor(ImGuiCol.Text, 0xffeeeeff); string ctxtiptext = "Right click for options"; ImGui.SetCursorPosX((ImGui.GetContentRegionAvail().X / 2) - ImGui.CalcTextSize(ctxtiptext).X / 2); ImGui.Text(ctxtiptext); ImGui.PopStyleColor(); ImGui.EndTooltip(); }