[C++/OpenGL] OpenGLでの垂直同期について

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
masa

[C++/OpenGL] OpenGLでの垂直同期について

#1

投稿記事 by masa » 10年前

初めまして、早速質問なのですが、OpenGLでの垂直同期についてです。
wglSwapInterval関数で引数に1を指定することで、垂直同期をONにしたものの、60fps付近にはならず、32fps付近で安定してしまいます。
ディスプレイのリフレッシュレートはGetDeviceCaps関数より60fpsであること、DirectXを使用すれば60fpsで垂直同期を取れることも確認しています。
また、変な症状として、chromeでyoutubeを表示した状態にすると60fpsで安定化するという現象が発生します。

このように、いろいろと試してみてはいるのですが、どうすれば常にOpenGLで60fps付近をキープできるのか完全にお手上げ状態です。
なにかヒントや解決法などありましたら、ぜひご教授ください。

ちなみに当方環境は
OS:windows8.1
GPU:Intel HD Graphics 4000(ドライバは10.18.10.4061)
です。

以下にサンプルコードを載せます。

コード:

#include <Windows.h>
#include <tchar.h>
#include <gl\glcorearb.h>
#include <gl\wglext.h>
#include <chrono>
#include <sstream>

#pragma comment(lib, "opengl32.lib")

bool closed = false;

LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	if (msg == WM_DESTROY){ PostQuitMessage(0); return 0; }
	if (msg == WM_CLOSE){ closed = true; return 0; }
	return DefWindowProc(hWnd, msg, wParam, lParam);
}

bool MessageProcessing()
{
	MSG msg;
	if (closed){ return false; }
	if (PeekMessage(&msg, nullptr, 0, 0, PM_NOREMOVE) == 0){ return true; }
	if (GetMessage(&msg, nullptr, 0, 0) <= 0){ return false; }
	TranslateMessage(&msg);
	DispatchMessage(&msg);
	return true;
}

int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
	//ウィンドウ作成→HWND&HDC取得
	WNDCLASSEX WindowClass;
	WindowClass.cbSize = sizeof(WNDCLASSEX);
	WindowClass.style = CS_VREDRAW | CS_HREDRAW | CS_OWNDC | CS_DBLCLKS;
	WindowClass.lpfnWndProc = WindowProc;
	WindowClass.cbClsExtra = 0;
	WindowClass.cbWndExtra = 0;
	WindowClass.hInstance = hInstance;
	WindowClass.hIcon = nullptr;
	WindowClass.hCursor = nullptr;
	WindowClass.hbrBackground = static_cast<HBRUSH>(GetStockObject(BLACK_BRUSH));
	WindowClass.lpszMenuName = nullptr;
	WindowClass.lpszClassName = _T("TestWindowClass\n");
	WindowClass.hIconSm = nullptr;

	if (RegisterClassEx(&WindowClass) == 0){ return -1; }

	const UINT WindowCreationStyle = WS_OVERLAPPEDWINDOW & ~(WS_MAXIMIZEBOX | WS_THICKFRAME);
	const UINT WindowStyleEx = 0;

	RECT Size = { 0, 0, 640, 480 };
	AdjustWindowRectEx(&Size, WindowCreationStyle, FALSE, WindowStyleEx);

	HWND hWnd = CreateWindowEx(WindowStyleEx, WindowClass.lpszClassName, _T("Test\n"), WindowCreationStyle,
		CW_USEDEFAULT, CW_USEDEFAULT, Size.right - Size.left, Size.bottom - Size.top,
		nullptr, nullptr, nullptr, nullptr);

	if (hWnd == nullptr){ return -1; }

	ShowWindow(hWnd, nCmdShow);
	HDC hDC = GetDC(hWnd);

	// HGLRCの作成
	PIXELFORMATDESCRIPTOR pfd = {};
	pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
	pfd.nVersion = 1;
	pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
	pfd.iPixelType = PFD_TYPE_RGBA;
	pfd.iLayerType = PFD_MAIN_PLANE;
	pfd.cColorBits = 24;
	pfd.cRedBits = 8;
	pfd.cGreenBits = 8;
	pfd.cBlueBits = 8;
	pfd.cAlphaBits = 8;
	pfd.cDepthBits = 24;
	pfd.cStencilBits = 8;

	int PixelFormat = ChoosePixelFormat(hDC, &pfd);
	if (PixelFormat == 0){ return -1; }

	if (SetPixelFormat(hDC, PixelFormat, &pfd) != TRUE){ return -1; }
	HGLRC hGLRC = wglCreateContext(hDC);

	if (!wglMakeCurrent(hDC, hGLRC)){ return -1; }

	//関数ポインタの取得
	PFNGLCLEARCOLORPROC glClearColor = reinterpret_cast<PFNGLCLEARCOLORPROC>(wglGetProcAddress("glClearColor"));
	PFNGLCLEARDEPTHPROC glClearDepth = reinterpret_cast<PFNGLCLEARDEPTHPROC>(wglGetProcAddress("glClearDepth"));
	PFNGLCLEARSTENCILPROC glClearStencil = reinterpret_cast<PFNGLCLEARSTENCILPROC>(wglGetProcAddress("glClearStencil"));
	PFNGLCLEARPROC glClear = reinterpret_cast<PFNGLCLEARPROC>(wglGetProcAddress("glClear"));
	PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = reinterpret_cast<PFNWGLSWAPINTERVALEXTPROC>(wglGetProcAddress("wglSwapIntervalEXT"));
	PFNWGLGETSWAPINTERVALEXTPROC wglGetSwapIntervalEXT = reinterpret_cast<PFNWGLGETSWAPINTERVALEXTPROC>(wglGetProcAddress("wglGetSwapIntervalEXT"));
	PFNGLFLUSHPROC glFlush = reinterpret_cast<PFNGLFLUSHPROC>(wglGetProcAddress("glFlush"));
	glClearColor(0.f, 0.f, 0.f, 1.f);
	glClearDepth(1.f);
	glClearStencil(0);
	
	wglSwapIntervalEXT(1);//垂直同期ON

	auto begin = std::chrono::system_clock::now();
	auto end = std::chrono::system_clock::now();

	//メインループ(fpsの計測は10フレームをまとめて行う)
	for (unsigned int count = 0; MessageProcessing(); count++)
	{
		if (count % 10 == 0){ begin = std::chrono::system_clock::now(); }

		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

		glFlush();
		
		if (SwapBuffers(hDC) == FALSE){ return -1; }

		if (count % 10 == 9)
		{
			end = std::chrono::system_clock::now();
			std::basic_stringstream<TCHAR> Title;
			Title << "No:" << count + 1 << " " << "RR:" << GetDeviceCaps(hDC, VREFRESH) / wglGetSwapIntervalEXT() << " " << "FPS:" << 1000.f / std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count() * 10 << "\n";
			SetWindowText(hWnd, Title.str().c_str());
		}
	}

	wglMakeCurrent(nullptr, nullptr);
	wglDeleteContext(hGLRC);
	ReleaseDC(hWnd, hDC);
	DestroyWindow(hWnd);

	return 0;
}

ISLe()

Re: [C++/OpenGL] OpenGLでの垂直同期について

#2

投稿記事 by ISLe() » 10年前

メインループに入る前のどこかで
timeBeginPeriod(1);
というコードが実行されるようにすると良いかと思います。

timeBeginPeriodで検索すると、正しい使い方や、質問文に書かれている謎についても分かるかと思います。

masa

Re: [C++/OpenGL] OpenGLでの垂直同期について

#3

投稿記事 by masa » 10年前

ありがとうございます!
確かに、メインループの前にtimeBeginPeriod(1);と後にtimeEndPeriod(1);を入れたところうまくいきました!
SwapBuffer関数はtimeBeginPeriodの影響を受けてタイマー間隔が変化するのですね。

また、timeBeginPeriodが使われているchromeと一緒に動作させるとfpsが変化してしまうということのようですね。

これで他の部分の実装に集中できます!ありがとうございました!

閉鎖

“C言語何でも質問掲示板” へ戻る