Skip to content

Instantly share code, notes, and snippets.

@kumarchandresh
Created December 6, 2021 10:56
Show Gist options
  • Select an option

  • Save kumarchandresh/43c40bb24db9fe45053f26d8613aca8c to your computer and use it in GitHub Desktop.

Select an option

Save kumarchandresh/43c40bb24db9fe45053f26d8613aca8c to your computer and use it in GitHub Desktop.
How to limit FPS using C++ on Win32.
#include <tchar.h>
#include <Windows.h>
#define TARGET_FPS 60
INT64 QPCFrequency;
LRESULT CALLBACK WindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
INT64 __forceinline ElapsedMicroseconds(INT64 startCount, INT64 endCount)
{
INT64 elapsedMicroseconds = endCount - startCount;
elapsedMicroseconds *= 1000000;
elapsedMicroseconds /= QPCFrequency;
return elapsedMicroseconds;
}
int WINAPI wWinMain(
_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
WNDCLASSEX wcx = { 0 };
MSG msg = { 0 };
HWND hwnd;
INT32 frameCount = 0;
INT64 frameStart = 0, frameEnd = 0;
INT64 averageFPS = 0, ticksAccumulator = 0;
INT64 elapsedTime, overSleepDuration = 0;
const INT64 TARGET_FRAME_TIME = (1000000 / TARGET_FPS) + 1;
TCHAR outstr[1024];
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
QueryPerformanceFrequency((LARGE_INTEGER*)&QPCFrequency);
wcx.cbSize = sizeof(WNDCLASSEX);
wcx.style = CS_HREDRAW | CS_VREDRAW;
wcx.lpfnWndProc = WindowProc;
wcx.cbClsExtra = 0;
wcx.cbWndExtra = 0;
wcx.hInstance = hInstance;
wcx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
wcx.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wcx.lpszMenuName = NULL;
wcx.lpszClassName = _T("MainWindow");
wcx.hIconSm = NULL;
if (!RegisterClassEx(&wcx))
{
MessageBox(NULL, _T("Failed RegisterClassEx"), _T("ERROR!"), MB_ICONERROR | MB_OK);
return GetLastError();
}
hwnd = CreateWindowEx(0,
_T("MainWindow"),
_T("Window Title"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
(HWND)NULL,
(HMENU)NULL,
hInstance,
(LPVOID)NULL);
if (!hwnd)
{
MessageBox(NULL, _T("Failed CreateWindowEx"), _T("ERROR!"), MB_ICONERROR | MB_OK);
return GetLastError();
}
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
QueryPerformanceCounter((LARGE_INTEGER*)&frameStart);
// Game Loop
while (true)
{
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
if (msg.message == WM_QUIT)
return(int)msg.wParam;
// Render
HDC hdc = GetDC(hwnd);
SelectObject(hdc, (HFONT)GetStockObject(SYSTEM_FIXED_FONT));
_sntprintf_s(outstr, _countof(outstr), _TRUNCATE, _T("FPS %lld"), averageFPS);
TextOut(hdc, 0, 0, outstr, (int)_tcslen(outstr));
ReleaseDC(hwnd, hdc);
// Limit FPS
/*
* Target Frame Time
* +-------------------+
*
* |--------------+----|---+---------------|-------------------|
*
* +--------------+
* Frame Time +--------+
* Sleep
* +---+ Oversleep
*
* Keep accounting for oversleeping period until there is enough to skip a Sleep() call.
*/
QueryPerformanceCounter((LARGE_INTEGER*)&frameEnd);
elapsedTime = ElapsedMicroseconds(frameStart, frameEnd);
while (elapsedTime < TARGET_FRAME_TIME)
{
if ((elapsedTime + overSleepDuration) >= TARGET_FRAME_TIME)
{
overSleepDuration -= TARGET_FRAME_TIME - elapsedTime;
break;
}
Sleep(1);
QueryPerformanceCounter((LARGE_INTEGER*)&frameEnd);
elapsedTime = ElapsedMicroseconds(frameStart, frameEnd);
if (elapsedTime > TARGET_FRAME_TIME)
overSleepDuration += elapsedTime - TARGET_FRAME_TIME;
}
QueryPerformanceCounter((LARGE_INTEGER*)&frameEnd);
ticksAccumulator += frameEnd - frameStart;
frameCount += 1;
if ((frameCount % TARGET_FPS) == 0)
{
averageFPS = ((QPCFrequency * TARGET_FPS) + (ticksAccumulator - 1)) / ticksAccumulator; // round-off
ticksAccumulator = 0;
frameCount = 0;
}
frameStart = frameEnd;
}
return 0;
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CLOSE:
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, message, wParam, lParam);
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment