DirectXでフレームレート固定する奴
詳しくはブログ記事読んで、どうぞ。
Last active
January 24, 2021 22:58
-
-
Save HimaJyun/3771e0b12bec366a142ad717edc10a17 to your computer and use it in GitHub Desktop.
DirectXでフレームレートを固定する奴
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <Windows.h> | |
#include <tchar.h> | |
#include <sstream> | |
#include <mmsystem.h> | |
#pragma comment(lib,"winmm.lib") | |
// 本当はグローバルにしない方が良い | |
const float MIN_FREAM_TIME = 1.0f / 60; | |
float frameTime = 0; | |
LARGE_INTEGER timeStart; | |
LARGE_INTEGER timeEnd; | |
LARGE_INTEGER timeFreq; | |
// fpsを取得するなら0で初期化しないとゴミが混ざってマイナスから始まったりする(かも知れない) | |
float fps = 0; | |
bool CreateMainWindow(HWND&, HINSTANCE, int, WNDCLASSEX&); | |
void run(); | |
int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow) { | |
// ウインドウ作成 | |
HWND hwnd; | |
WNDCLASSEX wcx; | |
if (!CreateMainWindow(hwnd, hInstance, nCmdShow, wcx)) { // コケたら | |
// 死ぬ | |
return(E_FAIL); | |
} | |
// メインループに入る前に精度を取得しておく | |
if (QueryPerformanceFrequency(&timeFreq) == FALSE) { // この関数で0(FALSE)が帰る時は未対応 | |
// そもそもQueryPerformanceFrequencyが使えない様な(古い)PCではどうせ色々キツイだろうし | |
return(E_FAIL); // 本当はこんな帰り方しては行けない(後続の解放処理が呼ばれない) | |
} | |
// 1度取得しておく(初回計算用) | |
QueryPerformanceCounter(&timeStart); | |
MSG msg; | |
while (true) { | |
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { | |
if (msg.message == WM_QUIT) { | |
break; | |
} else { | |
TranslateMessage(&msg); | |
DispatchMessage(&msg); | |
} | |
} else { | |
run(); | |
} | |
} | |
DestroyWindow(hwnd); | |
UnregisterClass(wcx.lpszClassName, wcx.hInstance); | |
return S_OK; | |
} | |
//========================== | |
// ここがゲームの処理と仮定 | |
//========================== | |
void run() { | |
// 今の時間を取得 | |
QueryPerformanceCounter(&timeEnd); | |
// (今の時間 - 前フレームの時間) / 周波数 = 経過時間(秒単位) | |
frameTime = static_cast<float>(timeEnd.QuadPart - timeStart.QuadPart) / static_cast<float>(timeFreq.QuadPart); | |
if (frameTime < MIN_FREAM_TIME) { // 時間に余裕がある | |
// ミリ秒に変換 | |
DWORD sleepTime = static_cast<DWORD>((MIN_FREAM_TIME - frameTime) * 1000); | |
timeBeginPeriod(1); // 分解能を上げる(こうしないとSleepの精度はガタガタ) | |
Sleep(sleepTime); // 寝る | |
timeEndPeriod(1); // 戻す | |
// 次週に持ち越し(こうしないとfpsが変になる?) | |
return; | |
} | |
if (frameTime > 0.0) { // 経過時間が0より大きい(こうしないと下の計算でゼロ除算になると思われ) | |
fps = (fps*0.99f) + (0.01f / frameTime); // 平均fpsを計算 | |
#ifdef _DEBUG | |
// デバッグ用(デバッガにFSP出す) | |
#ifdef UNICODE | |
std::wstringstream stream; | |
#else | |
std::stringstream stream; | |
#endif | |
stream << fps << " FPS" << std::endl; | |
// カウンタ付けて10回に1回出力、とかにしないと見づらいかもね | |
OutputDebugString(stream.str().c_str()); | |
#endif // _DEBUG | |
} | |
timeStart = timeEnd; // 入れ替え | |
} | |
//================== | |
// イベントハンドラ | |
//================== | |
LRESULT WINAPI WinProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { | |
switch (msg) { | |
case WM_DESTROY: | |
PostQuitMessage(0); | |
return 0; | |
} | |
return DefWindowProc(hwnd, msg, wParam, lParam); | |
} | |
//============================ | |
// メインウインドウを作成する | |
// 戻り:失敗すればfalse | |
//============================ | |
bool CreateMainWindow(HWND& hwnd, HINSTANCE hInstance, int nCmdShow, WNDCLASSEX& wcx) { | |
// 初期化 | |
ZeroMemory(&wcx, sizeof(wcx)); | |
// ウインドウの設定 | |
wcx.cbSize = sizeof(WNDCLASSEX); | |
wcx.style = CS_HREDRAW | CS_VREDRAW; | |
wcx.lpfnWndProc = WinProc; | |
wcx.cbClsExtra = 0; | |
wcx.cbWndExtra = 0; | |
wcx.hInstance = hInstance; | |
wcx.hIcon = NULL; | |
wcx.hCursor = LoadCursor(NULL, IDC_ARROW); | |
wcx.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); | |
wcx.lpszMenuName = NULL; | |
wcx.lpszClassName = _T("test"); | |
wcx.hIconSm = NULL; | |
// ウインドウクラスを登録 | |
if (RegisterClassEx(&wcx) == 0) { // 0 == エラーの場合 | |
return false; | |
} | |
hwnd = CreateWindow( | |
wcx.lpszClassName, | |
wcx.lpszClassName, | |
~(WS_MAXIMIZEBOX | WS_THICKFRAME) & WS_OVERLAPPEDWINDOW, | |
CW_USEDEFAULT, | |
CW_USEDEFAULT, | |
640, | |
480, | |
NULL, | |
NULL, | |
hInstance, | |
NULL | |
); | |
if (!hwnd) { // エラー | |
return false; | |
} | |
// ウインドウ表示 | |
ShowWindow(hwnd, nCmdShow); | |
return true; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 注:テストしたらなぜか40fpsしか出なかったのでどこか間違ってるかもです。 | |
// fps計測周りがおかしいのかな?、どこがおかしいかわかったら教えて(はぁと | |
#include <Windows.h> | |
#include <tchar.h> | |
#include <sstream> | |
#define TIMER_ID 1 | |
#define FREAM_RATE (1000 / 60) | |
// fps測定用 | |
DWORD timeBefore; | |
DWORD fps = 0; | |
bool CreateMainWindow(HWND&, HINSTANCE, int, WNDCLASSEX&); | |
void run(); | |
int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow) { | |
// ウインドウ作成 | |
HWND hwnd; | |
WNDCLASSEX wcx; | |
if (!CreateMainWindow(hwnd, hInstance, nCmdShow, wcx)) { // コケたら | |
// 死ぬ | |
return(E_FAIL); | |
} | |
// fpsも欲しいなら初期化しておこう | |
timeBefore = GetTickCount(); | |
// タイマーセット | |
SetTimer(hwnd, TIMER_ID, FREAM_RATE, NULL); | |
MSG msg; | |
while (true) { | |
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { | |
if (msg.message == WM_QUIT) { | |
break; | |
} else { | |
TranslateMessage(&msg); | |
DispatchMessage(&msg); | |
} | |
} else { | |
// CPUが過負荷(いわゆるビジーループ)になるので少し休ませる | |
Sleep(5); | |
} | |
} | |
// タイマーはウインドウの破棄と同時に自動で破棄されるらしい | |
// が、信用ならないので自分でもやっておく | |
KillTimer(hwnd, TIMER_ID); | |
DestroyWindow(hwnd); | |
UnregisterClass(wcx.lpszClassName, wcx.hInstance); | |
return S_OK; | |
} | |
//========================== | |
// ここがゲームの処理と仮定 | |
//========================== | |
void run() { | |
// ゲームの処理をほげほげ | |
// fpsを加算 | |
++fps; | |
if ((GetTickCount() - timeBefore) >= 1000) { // 1秒以上経過している | |
#ifdef _DEBUG // デバッグ用(デバッガにFSP出す) | |
#ifdef UNICODE | |
std::wstringstream stream; | |
#else | |
std::stringstream stream; | |
#endif | |
stream << fps << " FPS" << std::endl; | |
OutputDebugString(stream.str().c_str()); | |
#endif | |
// 0にリセット | |
fps = 0; | |
// リセット | |
timeBefore = GetTickCount(); | |
} | |
} | |
//================== | |
// イベントハンドラ | |
//================== | |
LRESULT WINAPI WinProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { | |
switch (msg) { | |
case WM_DESTROY: | |
PostQuitMessage(0); | |
return 0; | |
case WM_TIMER: // タイマーによる割り込み | |
// ゲームの処理を実行 | |
run(); | |
return 0; | |
} | |
return DefWindowProc(hwnd, msg, wParam, lParam); | |
} | |
//============================ | |
// メインウインドウを作成する | |
// 戻り:失敗すればfalse | |
//============================ | |
bool CreateMainWindow(HWND& hwnd, HINSTANCE hInstance, int nCmdShow, WNDCLASSEX& wcx) { | |
// 初期化 | |
ZeroMemory(&wcx, sizeof(wcx)); | |
// ウインドウの設定 | |
wcx.cbSize = sizeof(WNDCLASSEX); | |
wcx.style = CS_HREDRAW | CS_VREDRAW; | |
wcx.lpfnWndProc = WinProc; | |
wcx.cbClsExtra = 0; | |
wcx.cbWndExtra = 0; | |
wcx.hInstance = hInstance; | |
wcx.hIcon = NULL; | |
wcx.hCursor = LoadCursor(NULL, IDC_ARROW); | |
wcx.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); | |
wcx.lpszMenuName = NULL; | |
wcx.lpszClassName = _T("test"); | |
wcx.hIconSm = NULL; | |
// ウインドウクラスを登録 | |
if (RegisterClassEx(&wcx) == 0) { // 0 == エラーの場合 | |
return false; | |
} | |
hwnd = CreateWindow( | |
wcx.lpszClassName, | |
wcx.lpszClassName, | |
~(WS_MAXIMIZEBOX | WS_THICKFRAME) & WS_OVERLAPPEDWINDOW, | |
CW_USEDEFAULT, | |
CW_USEDEFAULT, | |
640, | |
480, | |
NULL, | |
NULL, | |
hInstance, | |
NULL | |
); | |
if (!hwnd) { // エラー | |
return false; | |
} | |
// ウインドウ表示 | |
ShowWindow(hwnd, nCmdShow); | |
return true; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment