Created
May 12, 2017 17:20
-
-
Save Niko-sk2x/78bf27cecf33ef580f26a13ff5322438 to your computer and use it in GitHub Desktop.
Inproved profiler graph for Minecraft
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import net.minecraft.profiler.Profiler; | |
| import net.minecraftforge.fml.relauncher.ReflectionHelper; | |
| import org.apache.commons.lang3.ArrayUtils; | |
| import javax.swing.*; | |
| import java.awt.*; | |
| import java.awt.event.KeyEvent; | |
| import java.awt.event.KeyListener; | |
| import java.util.*; | |
| import java.util.List; | |
| import java.util.concurrent.ConcurrentLinkedDeque; | |
| import java.util.concurrent.TimeUnit; | |
| import static java.awt.Color.*; | |
| import static java.awt.Color.BLUE; | |
| //usage: | |
| // TickProfiler profiler = new TickProfiler(); | |
| // EventQueue.invokeLater(profiler::init); // to initialize | |
| // in render tick event handler (only *one* of them, tested only with post | |
| // profiler.onTick(Minecraft.getMinecraft().theWorld.theProfiler); | |
| class TickProfiler extends JFrame | |
| { | |
| private TickPanel graphCanvas = new TickPanel(); | |
| public void init() | |
| { | |
| this.setLayout(new CardLayout()); | |
| this.add(graphCanvas); | |
| this.setSize(640, 480); | |
| this.setVisible(true); | |
| this.setDefaultCloseOperation(DISPOSE_ON_CLOSE); | |
| } | |
| public void onTick(Profiler profiler) | |
| { | |
| graphCanvas.addTick(profiler); | |
| } | |
| class TickPanel extends JPanel | |
| { | |
| private final char[] nums = "1234567890ABCDEFGHIJKLMNOPQERTUVWXYZ".toCharArray(); | |
| private List<String> currentPath = new ArrayList<>(); | |
| private final int hScale = 2; | |
| private final int wScale = 2; | |
| private Color[] colors = {RED, GREEN.darker(), mixColors(RED, YELLOW), BLUE, ORANGE.darker(), MAGENTA, | |
| mixColors(BLUE, GREEN), mixColors(RED, BLUE), mixColors(mixColors(BLUE, GREEN), mixColors(RED, BLUE))}; | |
| private LinkedList<Node> nodes = new LinkedList<>(); | |
| private Queue<Map<String, Long>> queue = new ConcurrentLinkedDeque<>(); | |
| private boolean paused; | |
| Map<String, Color> cols = new HashMap<>(); | |
| List<String> knownSubNodes = new ArrayList<>(); | |
| int currCol; | |
| public TickPanel() | |
| { | |
| this.addKeyListener(new KeyListener() | |
| { | |
| @Override | |
| public void keyTyped(KeyEvent keyEvent) | |
| { | |
| if (keyEvent.getKeyChar() == '0') | |
| { | |
| currentPath.remove(currentPath.size() - 1); | |
| clear(); | |
| } | |
| if (Character.isLetter(keyEvent.getKeyChar()) || Character.isDigit(keyEvent.getKeyChar())) | |
| { | |
| int i1= ArrayUtils.indexOf(nums, keyEvent.getKeyChar()); | |
| int i2 = ArrayUtils.indexOf(nums, Character.toLowerCase(keyEvent.getKeyChar())); | |
| currentPath.add(knownSubNodes.get(Math.max(i1, i2))); | |
| clear(); | |
| } | |
| if (keyEvent.getKeyChar() == ' ') | |
| { | |
| paused = !paused; | |
| } | |
| } | |
| @Override | |
| public void keyPressed(KeyEvent keyEvent) | |
| { | |
| } | |
| @Override | |
| public void keyReleased(KeyEvent keyEvent) | |
| { | |
| } | |
| }); | |
| this.setFocusable(true); | |
| } | |
| private void clear() | |
| { | |
| cols.clear(); | |
| knownSubNodes.clear(); | |
| currCol = 0; | |
| } | |
| private Color mixColors(Color a, Color b) | |
| { | |
| return new Color((a.getRed() + b.getRed()) / 2, (a.getGreen() + b.getGreen()) / 2, (a.getBlue() + b.getBlue()) / 2); | |
| } | |
| void addTick(Profiler profiler) | |
| { | |
| if (paused) | |
| { | |
| this.repaint(); | |
| return; | |
| } | |
| Map<String, Long> profilingMap = ReflectionHelper.getPrivateValue(Profiler.class, profiler, "profilingMap"); | |
| if (profilingMap.isEmpty()) | |
| { | |
| return; | |
| } | |
| HashMap<String, Long> m = new HashMap<>(profilingMap);// clone the map | |
| queue.add(m); | |
| this.repaint(); | |
| m.forEach((n, v) -> profilingMap.put(n, 0L)); | |
| } | |
| @Override | |
| protected void paintComponent(Graphics g) | |
| { | |
| super.paintComponent(g); | |
| g.clearRect(0, 0, getWidth(), getHeight()); | |
| processQueue(); | |
| sortData(); | |
| String currentRootName = drawGraph(g); | |
| drawInfo(g, currentRootName); | |
| } | |
| private void sortData() | |
| { | |
| Map<String, Long> timeSum = new HashMap<>(); | |
| String currentRootName = "???"; | |
| for (Node node : nodes) | |
| { | |
| Node currentRoot = getCurrentRoot(node); | |
| if (currentRoot == null) | |
| { | |
| continue; | |
| } | |
| for (Node n : currentRoot.nodes) | |
| { | |
| Long l = timeSum.get(n.name); | |
| if (l == null) | |
| { | |
| l = 0L; | |
| } | |
| l += n.time; | |
| timeSum.put(n.name, l); | |
| } | |
| } | |
| for (Node node : nodes) | |
| { | |
| Node currentRoot = getCurrentRoot(node); | |
| if (currentRoot == null) | |
| { | |
| continue; | |
| } | |
| currentRoot.nodes.sort(Comparator.comparing(x -> timeSum.get(((Node) x).name)).reversed()); | |
| } | |
| knownSubNodes.sort(Comparator.comparing(timeSum::get).reversed()); | |
| } | |
| private void drawInfo(Graphics g, String currentRootName) | |
| { | |
| int[] j = {0}; | |
| g.setColor(Color.BLACK); | |
| g.drawString("[0] " + currentRootName, 5, 16); | |
| knownSubNodes.forEach(n -> | |
| { | |
| g.setColor(cols.get(n)); | |
| g.drawString("[" + nums[j[0]] + "] " + n, 5, (j[0] + 2) * 16); | |
| j[0]++; | |
| }); | |
| g.setColor(cols.get("UNSPECIFIED")); | |
| g.drawString("[" + nums[j[0]] + "] " + "unspecified", 5, (j[0] + 2) * 16); | |
| } | |
| private String drawGraph(Graphics g) | |
| { | |
| int i = 0; | |
| String currentRootName = "???"; | |
| for (Node node : nodes) | |
| { | |
| Node currentRoot = getCurrentRoot(node); | |
| if (currentRoot == null) | |
| { | |
| continue; | |
| } | |
| currentRootName = currentRoot.name; | |
| int h = getHeight(); | |
| for (Node n : currentRoot.nodes) | |
| { | |
| h = drawCol(g, i, h, n, false); | |
| } | |
| drawCol(g, i, h, currentRoot, true); | |
| i++; | |
| } | |
| return currentRootName; | |
| } | |
| private int drawCol(Graphics g, int i, int h, Node n, boolean drawUnspec) | |
| { | |
| Color color = cols.get(drawUnspec ? "UNSPECIFIED" : n.name); | |
| if (color == null) | |
| { | |
| color = colors[Math.min(colors.length - 1, currCol)]; | |
| currCol++; | |
| cols.put(drawUnspec ? "UNSPECIFIED" : n.name, color); | |
| if (!drawUnspec) | |
| knownSubNodes.add(n.name); | |
| } | |
| int sub = 0; | |
| if (drawUnspec) | |
| { | |
| sub = getHeight() - h; | |
| } | |
| g.setColor(color); | |
| g.fillRect(i * wScale, h - (int) TimeUnit.NANOSECONDS.toMillis(n.time * hScale) + sub, wScale, (int) TimeUnit.NANOSECONDS.toMillis(n.time * hScale) - sub); | |
| h -= (int) TimeUnit.NANOSECONDS.toMillis(n.time * hScale) - sub; | |
| return h; | |
| } | |
| private Node getCurrentRoot(Node node) | |
| { | |
| if (currentPath.isEmpty()) | |
| { | |
| Node n = new Node("", node.time); | |
| n.nodes.add(node); | |
| return n; | |
| } | |
| for (String str : currentPath) | |
| { | |
| for (Node sub : node.nodes) | |
| { | |
| if (sub.name.equals(str)) | |
| { | |
| node = sub; | |
| break; | |
| } | |
| } | |
| } | |
| return node; | |
| } | |
| private void processQueue() | |
| { | |
| while (!queue.isEmpty()) | |
| { | |
| Node node = makeNode(queue.remove()); | |
| nodes.add(node); | |
| } | |
| while (nodes.size() > getWidth() / wScale) | |
| { | |
| nodes.removeFirst(); | |
| } | |
| } | |
| private Node makeNode(Map<String, Long> m) | |
| { | |
| Map<String, Node> nodeMap = new HashMap<>(); | |
| java.util.List<Node> nodes = new ArrayList<>(); | |
| m.forEach((name, time) -> | |
| { | |
| Node node = new Node(name, time); | |
| nodes.add(node); | |
| nodeMap.put(name, node); | |
| }); | |
| java.util.List<Node> newNodes = new ArrayList<>(); | |
| do | |
| { | |
| newNodes.clear(); | |
| Iterator<Node> it = nodes.iterator(); | |
| while (it.hasNext()) | |
| { | |
| Node n = it.next(); | |
| String name = n.name; | |
| if (!name.contains(".")) | |
| { | |
| continue; | |
| } | |
| String superName = superNodeName(name); | |
| Node s = nodeMap.get(superName); | |
| if (s == null) | |
| { | |
| s = new Node(superName, n.time); | |
| newNodes.add(s); | |
| nodeMap.put(superName, s); | |
| continue; | |
| } | |
| s.nodes.add(n); | |
| it.remove(); | |
| } | |
| nodes.addAll(newNodes); | |
| } | |
| while (!newNodes.isEmpty()); | |
| if (nodes.size() > 1) | |
| { | |
| throw new Error(); | |
| } | |
| return nodes.get(0); | |
| } | |
| private String superNodeName(String name) | |
| { | |
| return name.substring(0, name.lastIndexOf(".")); | |
| } | |
| private class Node | |
| { | |
| private long time; | |
| private String name; | |
| private java.util.List<Node> nodes = new ArrayList<>(); | |
| Node(String name, Long time) | |
| { | |
| this.name = name; | |
| this.time = time; | |
| } | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment