Create FPSMonitor component with FPS graph
This commit is contained in:
@@ -0,0 +1,88 @@
|
|||||||
|
package com.bartlomiejpluta.base.lib.gui;
|
||||||
|
|
||||||
|
import com.bartlomiejpluta.base.api.context.Context;
|
||||||
|
import com.bartlomiejpluta.base.api.gui.Attribute;
|
||||||
|
import com.bartlomiejpluta.base.api.gui.Color;
|
||||||
|
import com.bartlomiejpluta.base.api.gui.Component;
|
||||||
|
import com.bartlomiejpluta.base.api.gui.GUI;
|
||||||
|
import com.bartlomiejpluta.base.api.screen.Screen;
|
||||||
|
import com.bartlomiejpluta.base.util.profiler.FPSProfiler;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class FPSMonitor extends BaseComponent {
|
||||||
|
private final Color color;
|
||||||
|
private final Color background;
|
||||||
|
private FPSProfiler fpsProfiler = FPSProfiler.create(5, 40);
|
||||||
|
private float lineWidth = 1f;
|
||||||
|
|
||||||
|
public FPSMonitor(Context context, GUI gui, Map<String, Component> refs) {
|
||||||
|
super(context, gui, refs);
|
||||||
|
color = gui.createColor();
|
||||||
|
background = gui.createColor();
|
||||||
|
color.setRGBA(0xFF0000FF);
|
||||||
|
background.setRGBA(0x444444AA);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Attribute("monitor")
|
||||||
|
public void setMonitor(Integer[] options) {
|
||||||
|
if (options.length != 2) {
|
||||||
|
throw new IllegalArgumentException("Expected 2 parameters: batch size and number of samples");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.fpsProfiler = FPSProfiler.create(options[0], options[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setColor(Integer hex) {
|
||||||
|
color.setRGBA(hex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBackground(Integer hex) {
|
||||||
|
background.setRGBA(hex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLineWidth(Float width) {
|
||||||
|
this.lineWidth = width;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected float getContentWidth() {
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected float getContentHeight() {
|
||||||
|
return height;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update(float dt) {
|
||||||
|
super.update(dt);
|
||||||
|
fpsProfiler.update(dt);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void draw(Screen screen, GUI gui) {
|
||||||
|
var values = fpsProfiler.getSamples();
|
||||||
|
var actualHeight = height - paddingTop - paddingBottom;
|
||||||
|
var step = (width - paddingLeft - paddingRight) / values.size();
|
||||||
|
|
||||||
|
gui.beginPath();
|
||||||
|
gui.drawRectangle(x, y, width, height);
|
||||||
|
gui.setFillColor(background);
|
||||||
|
gui.fill();
|
||||||
|
gui.closePath();
|
||||||
|
|
||||||
|
gui.beginPath();
|
||||||
|
gui.moveTo(x + paddingLeft, (float) (actualHeight - values.get(0) / 60 * actualHeight + y + paddingTop));
|
||||||
|
|
||||||
|
for (int i = 1; i < values.size(); ++i) {
|
||||||
|
gui.drawLineTo(i * step + x + paddingLeft, (float) (actualHeight - values.get(i) / 60 * actualHeight + y + paddingTop));
|
||||||
|
}
|
||||||
|
|
||||||
|
gui.setStrokeColor(color);
|
||||||
|
gui.setStrokeWidth(lineWidth);
|
||||||
|
gui.stroke();
|
||||||
|
gui.closePath();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package com.bartlomiejpluta.base.util.collection;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class LimitedQueue<E> extends LinkedList<E> {
|
||||||
|
private final int limit;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean add(E o) {
|
||||||
|
super.add(o);
|
||||||
|
|
||||||
|
while (size() > limit) {
|
||||||
|
super.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,29 +1,31 @@
|
|||||||
package com.bartlomiejpluta.base.util.profiler;
|
package com.bartlomiejpluta.base.util.profiler;
|
||||||
|
|
||||||
|
import com.bartlomiejpluta.base.util.collection.LimitedQueue;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import static java.util.Comparator.comparingInt;
|
|
||||||
import static java.util.function.Function.identity;
|
|
||||||
import static java.util.stream.Collectors.counting;
|
|
||||||
import static java.util.stream.Collectors.groupingBy;
|
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class FPSProfiler {
|
public class FPSProfiler {
|
||||||
private final int batchSize;
|
private final int batchSize;
|
||||||
private final List<Double> values = new LinkedList<>();
|
|
||||||
|
@Getter
|
||||||
|
private final List<Double> samples;
|
||||||
private float fpsAccumulator = 0;
|
private float fpsAccumulator = 0;
|
||||||
private int pointer = 0;
|
private int pointer = 0;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private double instantFPS = 0;
|
private double instantFPS = 0;
|
||||||
|
|
||||||
private FPSProfiler(int batchSize) {
|
private FPSProfiler(int batchSize, int samples) {
|
||||||
this.batchSize = batchSize;
|
this.batchSize = batchSize;
|
||||||
|
this.samples = new LimitedQueue<>(samples);
|
||||||
|
this.samples.add(0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FPSProfiler create(int batchSize, int samples) {
|
||||||
|
return new FPSProfiler(batchSize, samples);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void update(float dt) {
|
public void update(float dt) {
|
||||||
@@ -34,37 +36,7 @@ public class FPSProfiler {
|
|||||||
fpsAccumulator = 0;
|
fpsAccumulator = 0;
|
||||||
pointer = 0;
|
pointer = 0;
|
||||||
|
|
||||||
values.add(instantFPS);
|
samples.add(instantFPS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void logResult() {
|
|
||||||
log.info("Min FPS: {}, max FPS: {}, avg FPS: {}",
|
|
||||||
values.stream().min(Double::compareTo).orElse(-1.0),
|
|
||||||
values.stream().max(Double::compareTo).orElse(-1.0),
|
|
||||||
totalAverage()
|
|
||||||
);
|
|
||||||
|
|
||||||
printHistogram();
|
|
||||||
}
|
|
||||||
|
|
||||||
private double totalAverage() {
|
|
||||||
return values.stream().reduce(0.0, Double::sum) / values.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void printHistogram() {
|
|
||||||
values
|
|
||||||
.stream()
|
|
||||||
.mapToInt(Double::intValue)
|
|
||||||
.boxed()
|
|
||||||
.collect(groupingBy(identity(), counting()))
|
|
||||||
.entrySet()
|
|
||||||
.stream()
|
|
||||||
.sorted(comparingInt(Map.Entry::getKey))
|
|
||||||
.forEach(e -> log.info("{} FPS: {}% ({} occurrences)", e.getKey(), e.getValue() * 100.0f / values.size(), e.getValue()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static FPSProfiler create(int batchSize) {
|
|
||||||
return new FPSProfiler(batchSize);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user