Controlling the frame rate

Although not limited to Direct3D, PC applications need to control the frame rate. If the frame rate can be determined according to a fixed refresh rate, as in a game console, You can control the frame rate by simply waiting for VSync, but PC monitors have a variety of refresh rates, so the app itself will need to control the frame rate.

The easiest way would be to measure the time since the previous frame, and if that time is faster than the specified frame rate, then Sleep.

It looks like this

class FrameController {
public:
    FrameController() : mFPS(60.0f), mSleepCounter(0) {
        QueryPerformanceFrequency(&mFrequency);
        QueryPerformanceCounter(&mPrevious);
        mStart = mPrevious;
    }
    virtual ~FrameController() {}

    double GetTimeFromStart() const
    {
        LARGE_INTEGER now;
        QueryPerformanceCounter(&now);
        double duration_sec = static_cast<double>(now.QuadPart - mStart.QuadPart) / mFrequency.QuadPart; 
        return duration_sec;
    }

    float Update(float fps = 0.0f)
    {
        // Change Sleep accuracy
        timeBeginPeriod(1);
        mSleepCounter = 0;

        // Measure time from previous frame
        LARGE_INTEGER now;
        QueryPerformanceCounter(&now);
        double duration_sec = static_cast<double>(now.QuadPart - mPrevious.QuadPart) / mFrequency.QuadPart; 
        if (fps > 0.0f)
        {
            // Sleep to control frame interval
            double limit_sec = 1.0 / fps;
            while (duration_sec < limit_sec)
            {
                // Sleep until half of the remaining time
                const double SLEEP_RATIO = 0.5;
                int sleep_msec = static_cast<int>(1000.0 * (limit_sec - duration_sec) * SLEEP_RATIO); 
                Sleep(sleep_msec);
                ++mSleepCounter;
                // Remeasure time
                QueryPerformanceCounter(&now);
                duration_sec = static_cast<double>(now.QuadPart - mPrevious.QuadPart) / mFrequency.QuadPart; 
            }
        }
        // Return to Sleep Accuracy
        timeEndPeriod(1);

        // Update frame rate (multiply by WEIGHT since averaging is tedious)
        float current_fps = float(1.0 / duration_sec);
        float FPS_WEIGHT = 0.1f;
        mFPS = mFPS * (1.0f - FPS_WEIGHT) + current_fps * FPS_WEIGHT;

        // Update the time of the previous frame
        mPrevious = now;

        return mFPS;
    }

    // For debugging
    int GetSleepCounter() const
    {
        return mSleepCounter;
    }

private:
    LARGE_INTEGER mFrequency;
    LARGE_INTEGER mPrevious;
    LARGE_INTEGER mStart;

    float mFPS;

    // For debugging
    int mSleepCounter;
};

To use, call Update at the top of the frame as follows

float frame_rate = frameController.Update(100.0f);

When we tried, Sleep was not very precise and could not be made to Sleep in units finer than 1 ms. Also, by default, even 1 ms was impossible, So we specified the precision to be in units of 1 ms as shown below.

        // Changing Sleep Precision
        timeBeginPeriod(1);

        // Sleep
        Sleep(sleep_msec);

        // Return Sleep accuracy
        timeEndPeriod(1);

Note that although this code wanted to make Sleep sleep for progressively shorter periods of time to achieve accurate time Sleep, it was actually a busy loop on Sleep(0) more than a few thousand times to waiting for the exact time to pass. There may be a better way to do this, but for now, here it is.