Skip to content

Instantly share code, notes, and snippets.

@HimaJyun
Last active January 24, 2021 22:58
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save HimaJyun/3771e0b12bec366a142ad717edc10a17 to your computer and use it in GitHub Desktop.
Save HimaJyun/3771e0b12bec366a142ad717edc10a17 to your computer and use it in GitHub Desktop.
DirectXでフレームレートを固定する奴

Directx-Fixed-Framerate

DirectXでフレームレート固定する奴
詳しくはブログ記事読んで、どうぞ。

#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;
}
// 注:テストしたらなぜか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