/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2007-2008, Open Source Geospatial Foundation (OSGeo) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package org.geotools.renderer3d.utils; /** * An utlity class for counting number of frames per second. * <p/> * Usage: Create an instance, then every frame call onFrame(). * The frames per second value can be read at any time using getFps(). * <p/> * Thread safe, as long as onFrame is only called from a single thread. * * @author Hans H�ggstr�m */ public final class FpsCounter { //====================================================================== // Private Fields private final Object mySyncLock = new Object(); // IDEA: Also calculate average over a number of frames, if a smoother value is needed, or if trend information is needed. private double myFramesPerSecond = -1; private double mySecondsBetweenFrames = -1; private long myFrameStartTime_ns = -1; private boolean myHasPreviousSample = false; //====================================================================== // Private Constants private static final double TICKS_PER_SECOND = 1000000000.0; // Nanoseconds //====================================================================== // Public Methods //---------------------------------------------------------------------- // Constructors /** * Creates a new FpsCounter. * Initially the frames per second property will be a negative value, until onFrame() is called (twice). */ public FpsCounter() { } //---------------------------------------------------------------------- // Other Public Methods /** * @return the number of frames rendered per second, or a negative value if the counter has not yet been started or has been stopped. */ public double getFramesPerSecond() { synchronized ( mySyncLock ) { return myFramesPerSecond; } } /** * @return number of seconds between the most recent frame and the frame before that, * or a negative value if there were no earlier frames. * Uses the averaged frames per second instead of the latest measurement. */ public double getSecondsBetweenFrames() { synchronized ( mySyncLock ) { return mySecondsBetweenFrames; } } /** * Call this method once each frame, to allow the counter to compute the frames per second property. * <p/> * NOTE: Should be called from a single thread, or wrapped in a syncronized object. If called from two different * threads, there could be frames with negative fps or secondsBetweenFrames. */ public void onFrame() { // Get start time of current frame final long now_ns = System.nanoTime(); synchronized ( mySyncLock ) { // Only update the fps property if we have two values already, so the duration is valid. if ( myHasPreviousSample ) { // Calculate the duration since the last frame long duration_ns = now_ns - myFrameStartTime_ns; // Avoid division by zero if the frame rate is very high if ( duration_ns == 0 ) { duration_ns = 1; } // Update the frames per second and time since last frame property mySecondsBetweenFrames = ( 1.0 * duration_ns ) / TICKS_PER_SECOND; myFramesPerSecond = TICKS_PER_SECOND / duration_ns; } else { // Now we have one sample already, so next time we can update the property myHasPreviousSample = true; } // Remember current frame start time myFrameStartTime_ns = now_ns; } } /** * Sets the frames per second and seconds since last frame value to negative values, until onFrame() is called again. */ public void stopCounting() { synchronized ( mySyncLock ) { myFramesPerSecond = -1; mySecondsBetweenFrames = -1; myHasPreviousSample = false; } } }