FrameCounter.java :  » Game » locusts » locusts » lib » Java Open Source

Java Open Source » Game » locusts 
locusts » locusts » lib » FrameCounter.java
/*
 * Copyright (c) 2009, Hamish Morgan. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * 3. Neither the name of the University of Sussex nor the names of its
 *    contributors may be used to endorse or promote products derived from
 *    this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
package locusts.lib;

import java.text.FieldPosition;
import java.text.NumberFormat;
import locusts.server.Game;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * The <code>FrameCounter</code> class maintaining a counter and calculating the
 * FPS (Frames Per Second). Once instantiated the <code>markFrame()</code>
 * method should be called every time a time step occurs. The frame rate and
 * counter can then be read by calling <code>getFps()</code> and
 * <code>getFrameCount()</code> respectively.
 * <p>
 * It also writes to the default logger every time the FPS value is updated, at
 * log level <code>Level.INFO</code>.
 * </p>
 * 
 * @author Hamish Morgan I A Morgan <code>&lt;hiam20@sussex.ac.uk&gt;</code>
 * @version 1.02, 14<sup>th</sup> July 2009
 */
public final class FrameCounter {

    /**
     * Logger for this class
     */
    private static final Logger LOG = LoggerFactory.getLogger(FrameCounter.class);
    /**
     * Default time between FPS updates.
     */
    private static final long DEFAULT_UPDATE_TIME = 1000l;
    /**
     * Default name to use when printing with <code>toString()</code>.
     */
    private static final String DEFAULT_NAME = "Unnamed";
    /**
     * The <code>NumberFormat</code> object enables the FPS printing with only a
     * few decimal places. (Otherwise it is unreadable.)
     */
    private static final NumberFormat NUMBER_FORMAT = NumberFormat.
            getNumberInstance();
    /**
     * To get the <code>NumberFormat</code> object to write to a pre-existing
     * <code>StringBuffer</code> you have to pass in this pointless parameter.
     * Otherwise it will create a new <code>StringBuffer</code> every time.
     */
    private static final FieldPosition FIELD_POSITION = new FieldPosition(
            NumberFormat.INTEGER_FIELD);
    /**
     * Name to output when <code>toString</code> is called.
     */
    private final String name;
    /**
     * Time (in milliseconds) between updates to the FPS value.
     */
    private final long updateTime;
    /**
     * Total number of calls to markFrame since last reset.
     */
    private int count;
    /**
     * Counter value when the FPS value was last updated.
     */
    private int lastUpdateCount;
    /**
     * Time (in milliseconds) when FPS value was last updated.
     */
    private long lastUpdateTime;
    /**
     * The last calculated value for the frames-per-second.
     */
    private double fps;
    /**
     * The time in milliseconds that markFrame was called last.
     */
    private long lastFrameTime;

    /**
     * Construct a new instance of the <code>FrameCounter</code> class that will
     * use the given name when <code>toString</code> is called and will update
     * the FPS values every updateTime milliseconds.
     * 
     * @param name
     *            What the counter is called for toString
     * @param updateTime
     *            How often the FPS value is updated (in milliseconds)
     * @throws IllegalArgumentException
     *             When the name parameter is null, or when the
     *             updateTime is less than 1.
     */
    public FrameCounter(final String name, final long updateTime) {
        if (name == null)
            throw new IllegalArgumentException(
                    "The name parameter must be non-null");
        if (updateTime < 1)
            throw new IllegalArgumentException(
                    "The updateTime paramter must be creater than 0.");
        this.name = name;
        this.updateTime = updateTime;
        reset();
    }

    /**
     * Construct a new instance of the <code>FrameCounter</code> class. It will
     * be named <code>"Unnamed"</code> and will update the FPS counter ever
     * second (1000 milliseconds)
     * 
     * @throws IllegalArgumentException
     *             When the name parameter is null or when the
     *              updateTime is less than 1.
     */
    public FrameCounter() {
        this(DEFAULT_NAME, DEFAULT_UPDATE_TIME);
    }

    /**
     * Construct a new instance of the class that will use the given name when
     * toString is called. It will update the fps counter ever second (1000 ms).
     * 
     * @param name
     *            What the counter is called for toString
     * @throws IllegalArgumentException
     *             When the name parameter is null or when the
     *              updateTime is less than 1.
     */
    public FrameCounter(final String name) {
        this(name, DEFAULT_UPDATE_TIME);
    }

    /**
     * Method that sets all object fields to their default initial states. This
     * returns the <code>FrameCounter</code> object to the state it was in when
     * it was instantiated.
     */
    public void reset() {
        count = 0;
        lastUpdateCount = 0;
        lastUpdateTime = System.currentTimeMillis();
        lastFrameTime = lastUpdateTime;
        fps = Double.POSITIVE_INFINITY;
    }

    /**
     * Record that a discrete time step has occurred and increment the frame
     * counter by 1.
     */
    public void markFrame() {
        count++;
        lastFrameTime = System.currentTimeMillis();
    }

    /**
     * Return the time in milliseconds since the last call to
     * <code>markFrame</code>.
     * 
     * @return Time the last frame was marked.
     */
    public long getLastFrameTime() {
        return lastFrameTime;
    }

    public long getTimeSinceLastFrame() {
        return System.currentTimeMillis() - lastFrameTime;
    }

    public double getTimeSinceLastFrameSeconds() {
        return getTimeSinceLastFrame() / 1000D;
    }

    /**
     * Update the FPS value if enough time has passed since the last update,
     * otherwise do nothing. In this way the FPS is not calculated all the time
     * and represents the average FPS over a period, defined by the
     * <code>updateTime</code> parameter to the constructor.
     */
    private void doUpdate() {
        if (lastFrameTime - lastUpdateTime >= updateTime) {
            fps = 1000d * (count - lastUpdateCount) / (lastFrameTime -
                    lastUpdateTime);
            lastUpdateCount = count;
            lastUpdateTime = lastFrameTime;
            LOG.info(toString());
        }
    }

    /**
     * Return the previously calculated FPS value. If not enough time has passed
     * since the object was reset (or constructed) then this will have the value
     * of <code>Double.POSITIVE_INFINITY</code>
     * 
     * @return number of frames per second recorded in the last interval.
     */
    public double getFps() {
        doUpdate();
        return fps;
    }

    /**
     * Return the total number of frames since the last reset or object
     * construction. This is the number of calls to the <code>markFrame()</code>
     * method.
     * 
     * @return number of frames since last reset.
     */
    public int getFrameCount() {
        return count;
    }

    /**
     * Return a string representation of the <code>FrameCounter</code> object.
     * It will consist of the number of the counter, as supplied in the
     * constructor, and last calculated frame rate in FPS (Frames Per Second.)
     * The FPS will be displayed using the default number formatter for the
     * default locale. It will probably look something like this:
     * 
     * <pre>
     *     &lt;Name&gt; Frame Rate: 60.123 FPS
     * </pre>
     * 
     * @return string representation of the <code>FrameCounter</code> object.
     */
    @Override
    public String toString() {
        final StringBuffer buffer = new StringBuffer(32);
        if (name.length() > 0) {
            buffer.append(name);
            buffer.append(' ');
        }
        buffer.append("Frame Rate: ");
        if (getFps() == Double.POSITIVE_INFINITY)
            buffer.append("Calculating");
        else {
            NUMBER_FORMAT.format(getFps(), buffer, FIELD_POSITION);
            buffer.append(" FPS");
        }
        return buffer.toString();
    }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.