Ogre FrameStats can be better.

Anything and everything that's related to OGRE or the wider graphics field that doesn't fit into the other forums.
Post Reply
longer
Kobold
Posts: 37
Joined: Tue Aug 19, 2014 10:46 am
x 5

Ogre FrameStats can be better.

Post by longer »

FrameStats is used to count the frame rate. It is very useful.
But I noticed that it can be improved.

Code: Select all

	#define OGRE_FRAME_STATS_SAMPLES 60

        int mNextFrame;
        unsigned long mBestFrameTime;
        unsigned long mWorstFrameTime;
        uint64 mLastTime;
        unsigned long mFrameTimes[OGRE_FRAME_STATS_SAMPLES];
        size_t mFramesSampled;
        
        float getAvgTime(void) const
        {
            if( !mFramesSampled )
                return 0.0f;

            unsigned long avg = 0;

            for( size_t i=0; i<mFramesSampled; ++i )
            {
                //idx is first in the range [(mNextFrame - mFramesSampled); mNextFrame),
                //but then warped around mNextFrame
                size_t idx = (i + mNextFrame + (mFramesSampled * 2 - 1)) % mFramesSampled;
                avg += mFrameTimes[idx];
            }

            return avg / (float)mFramesSampled * 0.001f;
        }

        /// Adds a new measured time, in *microseconds*
        void addSample( uint64 timeMs )
        {
            unsigned long frameTimeMs = static_cast<unsigned long>(timeMs - mLastTime);
            mFrameTimes[mNextFrame]  = frameTimeMs;
            mBestFrameTime              = std::min( frameTimeMs, mBestFrameTime );
            mWorstFrameTime             = std::max( frameTimeMs, mWorstFrameTime );

            mFramesSampled = std::min<size_t>( (mFramesSampled + 1), OGRE_FRAME_STATS_SAMPLES );
            mNextFrame = (mNextFrame + 1) % OGRE_FRAME_STATS_SAMPLES;

            mLastTime = timeMs;
        }

getAvgTime will add all the time in the buffer. addSample is use "%" for index round.

Here is my frame stats.

Code: Select all

enum
{
    // [0x00, 0xFF] 0x40 = 64 . 
    // Very close to the commonly used 60 frame rate.
    mmFrameStatsCacheSize = 0x40,
    mmFrameStatsCacheMask = mmFrameStatsCacheSize - 1,
    mmFrameStatsCacheTime = mmFrameStatsCacheSize * MM_USEC_PER_SEC,
};

// Frame rate statistics based on circular queues
struct mmFrameStats
{
    // time clock.
    struct mmClock clock;
    // frame number, reset api can assign 0.
    mmUInt64_t number;
    // average fps.
    double average;
    // current frame interval.
    double interval;
    // cache index.
    mmUInt8_t index;
    // frame interval total.
    double total;
    // frame interval cache.
    double cache[mmFrameStatsCacheSize];
};

MM_EXPORT_DLL void mmFrameStats_UpdateInterval(struct mmFrameStats* p, double interval)
{
    // cache the last frame interval.
    p->interval = interval;

    // accumulate the frame number.
    p->number++;

    // sub the index cache interval.
    p->total -= p->cache[p->index];
    // insert to back current interval.
    p->cache[p->index] = interval;
    // add the current interval.
    p->total += interval;

    // index++
    p->index++;
    // index %= CacheSize
    p->index &= mmFrameStatsCacheMask;

    // fps value update.
    // if interval_total == 0.0 the fps_avge will be +inf, but it's correct.
    p->average = ((double)mmFrameStatsCacheTime) / p->total;
}
1. use 0x40 = 64 for time cache interval, index &= 64 for index round is better.
2. cache the average frequency.
3. cache the total time, and -= last index cache interval += current interval. Can quickly average.
User avatar
sercero
Bronze Sponsor
Bronze Sponsor
Posts: 450
Joined: Sun Jan 18, 2015 4:20 pm
Location: Buenos Aires, Argentina
x 156

Re: Ogre FrameStats can be better.

Post by sercero »

Why don't you submit a patch?
longer
Kobold
Posts: 37
Joined: Tue Aug 19, 2014 10:46 am
x 5

Re: Ogre FrameStats can be better.

Post by longer »

Sorry, I haven't make pull requests commit before.

I simplified this struct.
https://bitbucket.org/mm_longcheng/mm-c ... ameStats.c

Code: Select all

enum
{
    // [0x00, 0xFF] 0x40 = 64 . 
    // Very close to the commonly used 60 frame rate.
    mmFrameStatsCacheSize = 0x40,
    mmFrameStatsCacheMask = mmFrameStatsCacheSize - 1,
    mmFrameStatsCacheTime = mmFrameStatsCacheSize * MM_USEC_PER_SEC,
};

#define MM_FRAME_STATS_BASE_FPS 50.0

Code: Select all

// Frame rate statistics 
// based on circular queues
struct mmFrameStats
{
    // time clock.
    struct mmClock clock;
    // frame number, reset api can assign 0.
    mmUInt64_t number;
    // current frame interval.
    mmUInt64_t interval;
    // frame interval total.
    mmUInt64_t total;
    // frame interval cache.
    mmUInt64_t cache[mmFrameStatsCacheSize];
    // average fps.
    double average;
    // cache index.
    mmUInt8_t index;
};

Code: Select all

MM_EXPORT_DLL 
void 
mmFrameStats_UpdateInterval(
    struct mmFrameStats* p, 
    mmUInt64_t interval)
{
    // cache the last frame interval.
    p->interval = interval;
    // accumulate the frame number.
    p->number++;
    // sub the index cache interval.
    p->total -= p->cache[p->index];
    // insert to back current interval.
    p->cache[p->index] = interval;
    // add the current interval.
    p->total += interval;
    // index++
    p->index++;
    // index %= CacheSize
    p->index &= mmFrameStatsCacheMask;
    // fps value update.
    // if total == 0.0 the fps_avge will be +inf, but it's correct.
    p->average = ((double)mmFrameStatsCacheTime) / ((double)p->total);
}

Porting this function to ogre text is very simple.
If I have time, I might make a pull requests.

paroj
OGRE Team Member
OGRE Team Member
Posts: 1995
Joined: Sun Mar 30, 2014 2:51 pm
x 1075
Contact:

Re: Ogre FrameStats can be better.

Post by paroj »

am I reading this correct, that you are interested in more precisie FPS counts?

Similar to what dark_sylinc addressed in his last blog post?
https://www.yosoygames.com.ar/wp/2023/0 ... les-wrong/

edit:
note to self: the code above computes mean FPS using a sliding window of 60 frames.

The current Ogre implementation uses exponential smoothing with a=0.5

could you motivate why you prefer the one over the other?

edit2:
actually, the current exponential smoothing seems preferable:
https://github.com/bevyengine/bevy/issues/4983

User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5299
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1279
Contact:

Re: Ogre FrameStats can be better.

Post by dark_sylinc »

Curiously I was wondering where this post was. Thanks everyone to bringing back to the top results :)

I've been revamping FrameStats last week. The new version relies on exponential smoothing like Ogre does. It's linear smoothing. I will look into making it exponential since it's probably a better fit.

The sliding window of 60 frames sounded like a good idea but it was a bad one in practice; because the rolling average didn't adjust well when the framerate was slow (i.e. at 15 fps & a sliding window of 60 it takes 4 seconds for the average to converge).

The new implementation also provides All Time Average and 95/99-percentile stats (aprox; since it must be quantized to keep memory consumption fixed).

I'm still heavily testing this implementation and it is scheduled for OgreNext 4.0; I'm not sure if I should backport it to 3.0 (which hasn't released yet) or not.

Post Reply