Skip to content

Instantly share code, notes, and snippets.

@Niko-sk2x
Created May 12, 2017 17:20
Show Gist options
  • Select an option

  • Save Niko-sk2x/78bf27cecf33ef580f26a13ff5322438 to your computer and use it in GitHub Desktop.

Select an option

Save Niko-sk2x/78bf27cecf33ef580f26a13ff5322438 to your computer and use it in GitHub Desktop.
Inproved profiler graph for Minecraft
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