/*******************************************************************************
* Copyright 2011 See AUTHORS file.
*
* 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 com.badlogic.gdx.utils;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.math.FloatCounter;
import com.badlogic.gdx.math.WindowedMean;
/**
* Class to keep track of the time and load (percentage of total time) a specific task takes. Call {@link #start()} just
* before starting the task and {@link #stop()} right after. You can do this multiple times if required. Every render or
* update call {@link #tick()} to update the values. The {@link #time} {@link FloatCounter} provides access to the
* minimum, maximum, average, total and current time the task takes. Likewise for the {@link #load} value, which is the
* percentage of the total time.
*
* @author xoppa
*/
public class PerformanceCounter {
private final static float nano2seconds = 1f / 1000000000.0f;
private long startTime = 0L;
private long lastTick = 0L;
/** The time value of this counter */
public final FloatCounter time;
/** The load value of this counter */
public final FloatCounter load;
/** The name of this counter */
public final String name;
/**
* The current value, you can manually increase this using your own timing mechanism if needed, if you do so, you
* also need to update {@link #valid}.
*/
public float current = 0f;
/**
* Flag to indicate that the current value is valid, you need to set this to true if using your own timing mechanism
*/
public boolean valid = false;
public PerformanceCounter(final String name) {
this(name, 5);
}
public PerformanceCounter(final String name, final int windowSize) {
this.name = name;
this.time = new FloatCounter(windowSize);
this.load = new FloatCounter(1);
}
/**
* Updates the time and load counters and resets the time. Call {@link #start()} to begin a new count. The values
* are only valid after at least two calls to this method.
*/
public void tick() {
final long t = System.nanoTime();
if (lastTick > 0L)
tick((t - lastTick) * nano2seconds);
lastTick = t;
}
/**
* Updates the time and load counters and resets the time. Call {@link #start()} to begin a new count.
*
* @param delta
* The time since the last call to this method
*/
public void tick(final float delta) {
if (!valid) {
Gdx.app.error("PerformanceCounter", "Invalid data, check if you called PerformanceCounter#stop()");
return;
}
time.put(current);
final float currentLoad = delta == 0f ? 0f : current / delta;
load.put((delta > 1f) ? currentLoad : delta * currentLoad + (1f - delta) * load.latest);
current = 0f;
valid = false;
}
/**
* Start counting, call this method just before performing the task you want to keep track of. Call {@link #stop()}
* when done.
*/
public void start() {
startTime = System.nanoTime();
valid = false;
}
/**
* Stop counting, call this method right after you performed the task you want to keep track of. Call
* {@link #start()} again when you perform more of that task.
*/
public void stop() {
if (startTime > 0L) {
current += (System.nanoTime() - startTime) * nano2seconds;
startTime = 0L;
valid = true;
}
}
/**
* Resets this performance counter to its defaults values.
*/
public void reset() {
time.reset();
load.reset();
startTime = 0L;
lastTick = 0L;
current = 0f;
valid = false;
}
/** {@inheritDoc} */
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
return toString(sb).toString();
}
/** Creates a string in the form of "name [time: value, load: value]" */
public StringBuilder toString(final StringBuilder sb) {
sb.append(name).append(": [time: ").append(time.value).append(", load: ").append(load.value).append("]");
return sb;
}
}