/*
* Copyright 2012 Benjamin Glatzel <benjamin.glatzel@me.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.terasology.rendering.gui.menus;
import gnu.trove.map.TObjectDoubleMap;
import gnu.trove.procedure.TObjectDoubleProcedure;
import gnu.trove.procedure.TObjectIntProcedure;
import org.terasology.performanceMonitor.PerformanceMonitor;
import org.terasology.rendering.gui.components.UIText;
import org.terasology.rendering.gui.framework.UIDisplayWindow;
import javax.vecmath.Vector2f;
import java.util.ArrayList;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
/**
* UI element that graphs performance metrics
*
* @author Immortius <immortius@gmail.com>
*/
public class UIMetrics extends UIDisplayWindow {
private static final int METRIC_LINES = 10;
private Mode _currentMode = Mode.Off;
/* DISPLAY ELEMENTS */
private final UIText _headerLine;
private final List<UIText> _metricLines;
/**
* Init. the HUD.
*/
public UIMetrics() {
setOverlay(true);
_headerLine = new UIText(new Vector2f(4, 70));
addDisplayElement(_headerLine);
_metricLines = new ArrayList<UIText>();
for (int i = 0; i < METRIC_LINES; ++i) {
UIText line = new UIText(new Vector2f(4, 86 + 16 * i));
_metricLines.add(line);
addDisplayElement(line);
}
update();
setVisible(true);
setModal(false);
}
/**
* Renders the HUD on the screen.
*/
@Override
public void render() {
super.render();
}
@Override
public void update() {
super.update();
_headerLine.setVisible(_currentMode.visible);
_headerLine.setText(_currentMode.displayText);
_currentMode.updateLines(_metricLines);
}
public void toggleMode() {
_currentMode = Mode.nextMode(_currentMode);
PerformanceMonitor.setEnabled(_currentMode != Mode.Off);
}
private enum Mode {
Off("", false) {
@Override
public void updateLines(List<UIText> lines) {
for (UIText line : lines) {
line.setVisible(false);
}
}
},
RunningMean("Running Means", true) {
@Override
public void updateLines(List<UIText> lines) {
displayMetrics(PerformanceMonitor.getRunningMean(), lines);
}
},
DecayingSpikes("Spikes", true) {
@Override
public void updateLines(List<UIText> lines) {
displayMetrics(PerformanceMonitor.getDecayingSpikes(), lines);
}
},
RunningThreads("Running Threads", true) {
@Override
public void updateLines(List<UIText> lines) {
final SortedSet<String> threads = new TreeSet<String>();
PerformanceMonitor.getRunningThreads().forEachEntry(new TObjectIntProcedure<String>() {
public boolean execute(String s, int i) {
threads.add(String.format("%s (%d)", s, i));
return true;
}
});
int line = 0;
for (String thread : threads) {
lines.get(line).setVisible(true);
lines.get(line).setText(thread);
line++;
if (line >= lines.size()) break;
}
for (; line < lines.size(); line++) {
lines.get(line).setVisible(false);
}
}
};
public final String displayText;
public final boolean visible;
private Mode(String display, boolean visible) {
this.displayText = display;
this.visible = visible;
}
public abstract void updateLines(List<UIText> lines);
public static Mode nextMode(Mode current) {
switch (current) {
case Off:
return RunningMean;
case RunningMean:
return DecayingSpikes;
case DecayingSpikes:
return RunningThreads;
default:
return Off;
}
}
private static void displayMetrics(TObjectDoubleMap<String> metrics, List<UIText> lines) {
final List<String> activities = new ArrayList<String>();
final List<Double> values = new ArrayList<Double>();
sortMetrics(metrics, activities, values);
for (int i = 0; i < lines.size() && i < activities.size(); ++i) {
UIText line = lines.get(i);
line.setVisible(true);
line.setText(String.format("%s: %.2fms", activities.get(i), values.get(i)));
}
for (int i = activities.size(); i < lines.size(); ++i) {
lines.get(i).setVisible(false);
}
}
private static void sortMetrics(TObjectDoubleMap<String> metrics, final List<String> activities, final List<Double> values) {
metrics.forEachEntry(new TObjectDoubleProcedure<String>() {
public boolean execute(String s, double v) {
boolean inserted = false;
for (int i = 0; i < values.size() && i < METRIC_LINES; i++) {
if (v > values.get(i)) {
values.add(i, v);
activities.add(i, s);
inserted = true;
break;
}
}
if (!inserted && values.size() < METRIC_LINES) {
activities.add(s);
values.add(v);
}
return true;
}
});
}
}
}