Khái niệm


  • Frame: một vòng lặp của game gọi là một frame. Mỗi vòng lặp gồm 2 thao tác chính là update và render (đã trình bày ở các phần trước)
  • Framerate: tầng số frame, tính bằng số lần lập/số frane thực hiện trong một giây (fps). [Xem thêm]. Cũng có thể hiểu là số lần vẽ lên màn hình tính trong thời gian một giây. Thông thường với game ngưỡng thấp nhất chấp nhận được là 8 fps (frame per second) và không có ngưỡng tối đa.
  • FrameDt: khoảng thời gian dùng để thực hiện xong một frame.
  • Limit framerate: là kỹ thuật giới hạn số lượng frame trong 1 giây. Kỹ thuật này giúp:
    + Ổn định framerate chung cho cả game
    + Giảm tình trạng game lúc nhanh lúc chậm
    + Giúp đồng bộ hóa framerate của game trên các device khác nhau
    + Giảm năng lượng tiêu tốn không cần thiết. Fps càng cao, đồng nghĩa với việc CPU/GPU làm việc trong khoảng thời gian dài với công suất cao, gây hao phí không cần thiết. Limit framerate giữ fps ở mức độ vừa phải, tạo ra khoảng thời gian "nghĩ ngơi" cho CPU/GPU

Kỹ thuật Limit framerate


Như đã trình bày ở trên, Limit framerate đảm bảo fps được duy trì ổn định quanh một giá trị qui ước. Giả sử mong muốn tốc độ game ổn định ở khoảng 25 fps. 

25 fps40ms/frame

Như vậy, mỗi lần update & render trung bình khoảng 80 ms. Trong trường hợp tổng thời gian update + render nhỏ hơn 80ms, game được phép "ngủ" trong khoảng thời gian còn lại.

Trong hình minh họa trên:

  • Frame 1: tổng thời gian update + render là 20ms. Do đó, game được sleep trong khoảng 20ms còn lại
  • Frame 2: tổng thời gian update + render là 50ms > 40ms. Không cần sleep (hoặc sleep 1)
  • Frame 3: tổng thời gian update + render là 25ms. Do đó, game được sleep trong khoảng 15ms còn lại
Kỹ thuật limit fps không phải là kỹ thuật làm tăng fps. Để nâng cao fps cho game, cần kỹ thuật optimization.

Nên limit fps bao nhiêu?


Không có một giá trị cụ thể nào được đưa ra cho câu trả lời này. Tùy vào từng game, từng loại game mà người lập trình/nhà sản xuất đưa ra con số qui định cho mình. Thông thường, fps = 25 là ổn.

Tuy nhiên, việc limit fps sẽ không có tác dụng nếu fps thật sự nhỏ hơn fps cần limit. Xem ví dụ trên, frame 2. Trong trường hợp này, limit fps không có vai trò gì khi đặt ở ngưỡng 25fps. Tuy nhiên, với ngưỡng 12.5 fps (80 ms/frame), limit fps lại có tác dụng.

Implementation



  • Khai báo thư viện time.h trong header.h để sử dụng hàm clock(). Việc khai báo này được thực hiện với config PLATFORM_WIN32_VS. Các platform khác có một chút khác biệt ta sẽ đề cập sau

Header.h

1
2
3
4
5
6
7
8
//Header.h
 
....
#if CONFIG_PLATFORM==PLATFORM_WIN32_VS
#   include
#   include
#endif
....
  • Trong CDevice, thêm hàm GetTimer() trả về ngày giờ hệ thống.

CDevice.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//CDevice.h
...
namespace GameTutor
{
    class CDevice
    {
    public:
        ...
        void SleepEx(unsigned long milisec);
        unsigned long GetTimer();
        ...
    };
}
...

CDevice.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
//CDevice.cpp
......
    unsigned long CDevice::GetTimer()
    {
#if CONFIG_PLATFORM==PLATFORM_WIN32_VS
        return clock();
#else
        TODO("GetTimer for CONFIG_PLATFORM!=PLATFORM_WIN32_VS is not implement yet !");
 
        return 0;
#endif
    }
......

Bước 2: Tạo lớp CFpsManager quản lý fps


Lớp CFpsManager cũng được thiết kế dạng singleton, gồm các phương thức:

  • SetLimitFps: thiết lập thông số limit fps
  • BeginCounter: Được gọi khi bắt đầu tình toán fps
  • EndCounter: Được gọi tại ví trí kết thúc tính đoán fps và thực hiện limit frame rate
  • GetFrameDt: Lấy FrameDT hiện tại
  • GetRuntimeFps: Lấy Fps hiện tại (giá trị do đạc thực tế). Kết quả trả về có thể không trùng khớp với giá trị thiết lập bởi SetLimitFps

CFpsController.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#ifndef __CFPSCONTROLLER_H__
#define __CFPSCONTROLLER_H__
 
#include "Header.h"
 
#define DEFAULT_LIMIT_FPS 25
namespace GameTutor
{
    class CFpsController
    {
    public:
        static CFpsController* GetInstance()
        {
            if (!s_pInstance)
            {
                s_pInstance = new CFpsController();
            }
            return s_pInstance;
        }
        virtual ~CFpsController(){}
         
        void SetLimitFps(unsigned int limitFps);
        void BeginCounter();
        void EndCounter();
 
        int GetFrameDt() {return m_iFrameDt;}
        int GetRuntimeFps() {return (m_iFrameDt)?int(1000/m_iFrameDt):0;}
 
    protected:
        CFpsController(): m_iLimitFps(0), m_iLimitFrameDt(0),
            m_iStartTime(0), m_iFrameDt(0)
        {
            SetLimitFps(DEFAULT_LIMIT_FPS);
        }
        static CFpsController* s_pInstance;
    protected:
        int m_iLimitFps;
        int m_iLimitFrameDt;   
        int m_iFrameDt;
    private:
        long m_iStartTime;
    };
}
 
#endif

CFpsController.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include "CFpsController.h"
#include "CDevice.h"
 
namespace GameTutor
{
    CFpsController* CFpsController::s_pInstance = 0;
 
    void CFpsController::SetLimitFps(unsigned int limitFps)
    {
        m_iLimitFps = limitFps;
        m_iLimitFrameDt = 1000/limitFps;
    }
 
    void CFpsController::BeginCounter()
    {
        m_iStartTime = CDevice::GetInstance()->GetTimer();
    }
 
    void CFpsController::EndCounter()
    {
        long Endtime = CDevice::GetInstance()->GetTimer();
        int Dt = int(Endtime - m_iStartTime);
        if (Dt < m_iLimitFrameDt)
        {
            m_iFrameDt = m_iLimitFrameDt;
            CDevice::GetInstance()->SleepEx(m_iLimitFrameDt - Dt);
        }
        else
        {
            m_iFrameDt = Dt;
            CDevice::GetInstance()->SleepEx(1);
        }
         
    }
}

Ngoài cái chức năng chính trên, CFpsController còn có thể mở rộng, phục vụ cho việc thống kê fps.

Bước 3: Cài đặt chức năng tính toán fps vào vòng lập chính của game


Ta tiến hành hiệu chỉnh CGame

CGame.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include "CGame.h"
#include "Header.h"
#include "CStateManagement.h"
#include "CDevice.h"
#include "CFpsController.h"
 
namespace GameTutor
{
    ...
 
    void CGame::Run()
    {
        this->Init();
        while (m_isAlived)
        {
            CFpsController::GetInstance()->BeginCounter();
            if (m_isPaused)
            {
                CStateManagement::GetInstance()->Update(true);
            }
            else
            {
                CStateManagement::GetInstance()->Update(false);
            }
            CFpsController::GetInstance()->EndCounter();
        }
 
        
         
        Destroy();
    }
}

Source code


Post a Comment Blogger

 
Top