mirror of
https://github.com/Anuken/Mindustry.git
synced 2026-10-02 03:00:29 +03:00
Toggleable performance metrics display
This commit is contained in:
@@ -1427,6 +1427,7 @@ setting.fullscreen.name = Fullscreen
|
||||
setting.fps.name = Show FPS & Ping
|
||||
setting.console.name = Enable Console
|
||||
setting.drawhitboxes.name = Display Hitboxes
|
||||
setting.showperformance.name = Display Performance Metrics
|
||||
setting.buildingtimestep.name = Fixed Building Timestep
|
||||
setting.buildingtimestep.description = Runs buildings on a fixed timestep with constant delta,\nas opposed to every frame with variable delta.\nIf buildings cannot be updated fast enough, the game will slow down.\nIf you don't know what that means, don't touch this option.
|
||||
setting.smoothcamera.name = Smooth Camera
|
||||
@@ -1573,6 +1574,7 @@ keybind.zoom_minimap.name = Zoom Minimap
|
||||
keybind.detach_camera.name = Toggle Free Camera
|
||||
keybind.teleport_cursor.name = Teleport To Cursor (Sandbox)
|
||||
keybind.debug_hitboxes.name = Toggle Debug Hitboxes
|
||||
keybind.performance_metrics.name = Toggle Performance Metrics
|
||||
mode.help.title = Description of modes
|
||||
mode.survival.name = Survival
|
||||
mode.survival.description = The normal mode. Limited resources and automatic incoming waves.\n[gray]Requires enemy spawns in the map to play.
|
||||
|
||||
@@ -269,6 +269,16 @@ public abstract class ClientLauncher extends ApplicationCore implements Platform
|
||||
}
|
||||
|
||||
PerfCounter.update.end();
|
||||
|
||||
long rawUpdate = PerfCounter.update.latestValueNs();
|
||||
for(var other : PerfCounter.displayedCounters){
|
||||
if(other != PerfCounter.other) rawUpdate -= other.latestValueNs();
|
||||
}
|
||||
PerfCounter.other.add(rawUpdate);
|
||||
|
||||
for(var counter : PerfCounter.all){
|
||||
counter.checkUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -695,6 +695,10 @@ public class Control implements ApplicationListener, Loadable{
|
||||
if(Float.isNaN(camera.position.x)) camera.position.x = world.unitWidth()/2f;
|
||||
if(Float.isNaN(camera.position.y)) camera.position.y = world.unitHeight()/2f;
|
||||
|
||||
if(!scene.hasKeyboard()){
|
||||
if(Core.input.keyTap(Binding.performanceMetrics)) Core.settings.toggle("showperformance");
|
||||
}
|
||||
|
||||
if(state.isGame()){
|
||||
input.update();
|
||||
input.updateSelectQuadtree();
|
||||
|
||||
@@ -463,11 +463,12 @@ public class Logic implements ApplicationListener{
|
||||
protected void updateEntities(){
|
||||
PerfCounter.entityUpdate.begin();
|
||||
|
||||
PerfCounter.entityMisc.begin();
|
||||
Groups.updatePooling();
|
||||
|
||||
Groups.bullet.updatePhysics();
|
||||
Groups.unit.updatePhysics();
|
||||
Groups.all.update();
|
||||
PerfCounter.entityMisc.end();
|
||||
|
||||
PerfCounter.unitUpdate.begin();
|
||||
Groups.unit.update();
|
||||
@@ -481,7 +482,12 @@ public class Logic implements ApplicationListener{
|
||||
if(!state.isEditor()) Groups.build.update();
|
||||
PerfCounter.buildingUpdate.end();
|
||||
|
||||
PerfCounter.bulletUpdate.begin();
|
||||
Groups.bullet.update();
|
||||
|
||||
Groups.bullet.collide();
|
||||
PerfCounter.bulletUpdate.begin();
|
||||
|
||||
PerfCounter.entityUpdate.end();
|
||||
}
|
||||
|
||||
@@ -490,6 +496,8 @@ public class Logic implements ApplicationListener{
|
||||
PerfCounter.frame.end();
|
||||
PerfCounter.frame.begin();
|
||||
|
||||
PerfCounter.stateUpdate.begin();
|
||||
|
||||
Events.fire(Trigger.update);
|
||||
universe.updateGlobal();
|
||||
|
||||
@@ -600,6 +608,8 @@ public class Logic implements ApplicationListener{
|
||||
}else if(netServer.isWaitingForPlayers() && runStateCheck){
|
||||
checkGameState();
|
||||
}
|
||||
|
||||
PerfCounter.stateUpdate.end(PerfCounter.entityUpdate.latestValueNs());
|
||||
}
|
||||
|
||||
/** @return whether the wave timer is paused due to enemies */
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package mindustry.core;
|
||||
|
||||
import arc.*;
|
||||
import arc.math.*;
|
||||
import arc.util.*;
|
||||
|
||||
@@ -7,34 +8,86 @@ import arc.util.*;
|
||||
public enum PerfCounter{
|
||||
frame,
|
||||
update,
|
||||
other,
|
||||
stateUpdate,
|
||||
entityMisc,
|
||||
entityUpdate,
|
||||
buildingUpdate,
|
||||
powerUpdate,
|
||||
unitUpdate,
|
||||
bulletUpdate,
|
||||
ui,
|
||||
render;
|
||||
|
||||
public static final PerfCounter[] all = values();
|
||||
|
||||
public static final PerfCounter[] displayedCounters = {
|
||||
powerUpdate,
|
||||
buildingUpdate,
|
||||
entityMisc,
|
||||
bulletUpdate,
|
||||
unitUpdate,
|
||||
render,
|
||||
ui,
|
||||
stateUpdate,
|
||||
other
|
||||
};
|
||||
|
||||
static final int meanWindow = 120;
|
||||
static final int refreshTimeMillis = 100;
|
||||
|
||||
private long lastUpdateFrame = -1;
|
||||
private long valueRefreshTime;
|
||||
private float refreshValue;
|
||||
|
||||
private long beginTime;
|
||||
private long partValue;
|
||||
private boolean began = false;
|
||||
private WindowedMean mean = new WindowedMean(meanWindow);
|
||||
|
||||
public void add(long nanos){
|
||||
mean.add(nanos);
|
||||
lastUpdateFrame = Core.graphics.getFrameId();
|
||||
}
|
||||
|
||||
public void begin(){
|
||||
lastUpdateFrame = Core.graphics.getFrameId();
|
||||
began = true;
|
||||
beginTime = Time.nanos();
|
||||
}
|
||||
|
||||
public void end(){
|
||||
end(0);
|
||||
}
|
||||
|
||||
public void end(long subtract){
|
||||
if(!began) return;
|
||||
lastUpdateFrame = Core.graphics.getFrameId();
|
||||
began = false;
|
||||
mean.add(Time.timeSinceNanos(beginTime) - subtract);
|
||||
}
|
||||
|
||||
public void checkUpdate(){
|
||||
if(lastUpdateFrame < Core.graphics.getFrameId()){
|
||||
mean.add(0f);
|
||||
}
|
||||
}
|
||||
|
||||
public void beginPart(){
|
||||
began = true;
|
||||
beginTime = Time.nanos();
|
||||
}
|
||||
|
||||
public void endPart(){
|
||||
if(!began) return;
|
||||
began = false;
|
||||
mean.add(Time.timeSinceNanos(beginTime));
|
||||
partValue += Time.timeSinceNanos(beginTime);
|
||||
}
|
||||
|
||||
public void finishParts(){
|
||||
lastUpdateFrame = Core.graphics.getFrameId();
|
||||
mean.add(partValue);
|
||||
partValue = 0;
|
||||
}
|
||||
|
||||
/** Value with a periodic refresh interval applied, to prevent jittery UI. */
|
||||
@@ -46,6 +99,10 @@ public enum PerfCounter{
|
||||
return refreshValue;
|
||||
}
|
||||
|
||||
public long latestValueNs(){
|
||||
return (long)mean.latest();
|
||||
}
|
||||
|
||||
/** Raw value without a refresh interval. This will be unstable. */
|
||||
public float rawValueMs(){
|
||||
return mean.rawMean() / Time.nanosPerMilli;
|
||||
|
||||
@@ -48,6 +48,7 @@ public class UI implements ApplicationListener, Loadable{
|
||||
public PlayerListFragment listfrag;
|
||||
public LoadingFragment loadfrag;
|
||||
public HintsFragment hints;
|
||||
public PerformanceFragment perffrag;
|
||||
|
||||
public WidgetGroup menuGroup, hudGroup;
|
||||
|
||||
@@ -193,6 +194,7 @@ public class UI implements ApplicationListener, Loadable{
|
||||
listfrag = new PlayerListFragment();
|
||||
loadfrag = new LoadingFragment();
|
||||
consolefrag = new ConsoleFragment();
|
||||
perffrag = new PerformanceFragment();
|
||||
|
||||
picker = new ColorPicker();
|
||||
effects = new EffectsDialog();
|
||||
@@ -243,6 +245,7 @@ public class UI implements ApplicationListener, Loadable{
|
||||
listfrag.build(hudGroup);
|
||||
consolefrag.build(hudGroup);
|
||||
loadfrag.build(group);
|
||||
perffrag.build(group);
|
||||
new FadeInFragment().build(group);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import mindustry.annotations.Annotations.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
class GroupDefs<G>{
|
||||
@GroupDef(value = Entityc.class, exclude = {Unitc.class, PowerGraphUpdaterc.class}, update = true) G all;
|
||||
@GroupDef(value = Entityc.class, exclude = {Unitc.class, PowerGraphUpdaterc.class, Bulletc.class}, update = true) G all;
|
||||
@GroupDef(value = Playerc.class, mapping = true) G player;
|
||||
@GroupDef(value = Bulletc.class, spatial = true, collide = true) G bullet;
|
||||
@GroupDef(value = Unitc.class, spatial = true, mapping = true) G unit;
|
||||
|
||||
@@ -106,7 +106,8 @@ public class Binding{
|
||||
chatScroll = KeyBind.add("chat_scroll", new Axis(KeyCode.scroll)),
|
||||
chatMode = KeyBind.add("chat_mode", KeyCode.tab),
|
||||
console = KeyBind.add("console", KeyCode.f8),
|
||||
debugHitboxes = KeyBind.add("debug_hitboxes", KeyCode.unset)
|
||||
debugHitboxes = KeyBind.add("debug_hitboxes", KeyCode.unset),
|
||||
performanceMetrics = KeyBind.add("performance_metrics", KeyCode.unset)
|
||||
;
|
||||
|
||||
//dummy static class initializer
|
||||
|
||||
@@ -240,9 +240,7 @@ public class DesktopInput extends InputHandler{
|
||||
boolean detached = settings.getBool("detach-camera", false);
|
||||
|
||||
if(!scene.hasField() && !scene.hasDialog()){
|
||||
if(input.keyTap(Binding.debugHitboxes)){
|
||||
Core.settings.put("drawhitboxes", !settings.getBool("drawhitboxes"));
|
||||
}
|
||||
if(input.keyTap(Binding.debugHitboxes)) Core.settings.toggle("drawhitboxes");
|
||||
|
||||
if(input.keyTap(Binding.teleportCursor) && (state.rules.editor || state.rules.infiniteResources)){
|
||||
if(player.dead()){
|
||||
|
||||
@@ -569,6 +569,7 @@ public class SettingsMenuDialog extends BaseDialog{
|
||||
|
||||
dev.checkPref("console", false);
|
||||
dev.checkPref("drawhitboxes", false);
|
||||
dev.checkPref("showperformance", false);
|
||||
|
||||
if(!ios){
|
||||
dev.checkPref("modcrashdisable", true);
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
package mindustry.ui.fragments;
|
||||
|
||||
import arc.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.scene.*;
|
||||
import arc.scene.event.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import arc.util.pooling.*;
|
||||
import mindustry.core.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.ui.*;
|
||||
|
||||
import static mindustry.core.PerfCounter.*;
|
||||
|
||||
public class PerformanceFragment{
|
||||
private static final ObjectMap<PerfCounter, Color> counterToColor = ObjectMap.of(
|
||||
buildingUpdate, Pal.items,
|
||||
powerUpdate, Pal.powerLight,
|
||||
unitUpdate, Color.royal,
|
||||
ui, Pal.remove,
|
||||
render, Pal.reactorPurple,
|
||||
stateUpdate, Color.cyan,
|
||||
other, Color.pink,
|
||||
entityMisc, Color.sky,
|
||||
bulletUpdate, Pal.redDust
|
||||
);
|
||||
|
||||
private static Color color(PerfCounter counter){
|
||||
return counterToColor.get(counter, Color.white);
|
||||
}
|
||||
|
||||
public void build(Group parent){
|
||||
parent.fill(t -> {
|
||||
t.visible(() -> {
|
||||
t.toFront();
|
||||
return Core.settings.getBool("showperformance");
|
||||
});
|
||||
t.touchable = Touchable.disabled;
|
||||
t.top().left();
|
||||
|
||||
var outer = t.table(Styles.black8, i -> {
|
||||
i.margin(6f);
|
||||
i.add(new PerfBar()).size(400f, 50f);
|
||||
}).padBottom(8f);
|
||||
|
||||
t.row();
|
||||
|
||||
t.table(Styles.black8, i -> {
|
||||
i.margin(3f);
|
||||
i.left().top();
|
||||
|
||||
float fscl = 1f;
|
||||
i.defaults().left();
|
||||
for(PerfCounter counter : displayedCounters){
|
||||
i.label(() -> counter.name() + ": " + Strings.fixed(counter.valueMs(), 1)).style(Styles.outlineLabel).color(color(counter)).with(l -> l.setFontScale(fscl)).row();
|
||||
}
|
||||
|
||||
i.add().height(30f).row();
|
||||
|
||||
for(PerfCounter counter : all){
|
||||
if(Structs.contains(displayedCounters, counter)) continue;
|
||||
|
||||
i.label(() -> counter.name() + ": " + Strings.fixed(counter.valueMs(), 1)).style(Styles.outlineLabel).with(l -> l.setFontScale(fscl)).color(color(counter)).row();
|
||||
}
|
||||
}).left();
|
||||
|
||||
outer.get().toFront();
|
||||
});
|
||||
}
|
||||
|
||||
static class PerfBar extends Element{
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
Vec2 mouse = screenToLocalCoordinates(Core.input.mouse()).add(x, y);
|
||||
float total = frame.valueMs();
|
||||
float accumulator = 0f;
|
||||
PerfCounter hoveredCounter = null;
|
||||
|
||||
Draw.color(Color.darkGray);
|
||||
Fill.crect(x, y, width, height);
|
||||
|
||||
for(var counter : PerfCounter.displayedCounters){
|
||||
float value = counter.valueMs();
|
||||
Draw.color(color(counter));
|
||||
|
||||
float rx = x + accumulator / total * width, ry = y, rw = width * value/total, rh = height;
|
||||
|
||||
Fill.crect(rx, ry, rw, rh);
|
||||
if(Rect.contains(rx, ry, rw, rh, mouse.x, mouse.y)){
|
||||
hoveredCounter = counter;
|
||||
}
|
||||
accumulator += value;
|
||||
}
|
||||
Draw.color();
|
||||
|
||||
if(hoveredCounter != null){
|
||||
float offset = Scl.scl(30f);
|
||||
String text = Strings.insertSpaces(Strings.capitalize(hoveredCounter.name())) + ": " + Strings.fixed(hoveredCounter.valueMs(), 1);
|
||||
GlyphLayout layout = Pools.obtain(GlyphLayout.class, GlyphLayout::new);
|
||||
layout.setText(Fonts.outline, text);
|
||||
Draw.color(0f, 0f, 0f, 0.5f);
|
||||
float pad = Scl.scl(4f);
|
||||
Fill.crect(mouse.x + offset - pad, mouse.y - offset - layout.height - pad, layout.width + pad*2f, layout.height + pad*2f);
|
||||
Draw.color();
|
||||
Fonts.outline.draw(text, mouse.x + offset, mouse.y - offset);
|
||||
Pools.free(layout);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -26,4 +26,4 @@ org.gradle.caching=true
|
||||
org.gradle.internal.http.socketTimeout=100000
|
||||
org.gradle.internal.http.connectionTimeout=100000
|
||||
android.enableR8.fullMode=false
|
||||
archash=ba33c36592
|
||||
archash=09e1e41ab7
|
||||
|
||||
Reference in New Issue
Block a user