DirectXでCPU使用率が高い気がします。

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

DirectXでCPU使用率が高い気がします。

#1

投稿記事 by 旅路のきのこ » 12年前

こんにちは、はじめまして。
DirectXの学習をしています。ほぼ初っ端で躓いてしまった感じなので、DirectXは初心者です。

まずはいくらかフレームレートを制御したテンプレートもどきを作成しようと思ったのですが、どうも上手くいきませんでした。

ごく単純な三角形の描画ですらCPU使用率が10%前後出てしまうのです。(PeekMessageは使わなくともです)
しかし、dxlibでは、キャラクターモデルの描画を3DでやってみてもCPU使用率はほぼ0%。
またOpenGLでは、以下のコード(どことなく作法が悪いような気がします)と同じようなやり方をしてもCPU使用率はほぼ0%です。
設定が間違っているのか?あるいは書き方がどこかおかしいのか?
何かがおかしいわけですが、その原因がわかりません。
ひょっとすると馬鹿馬鹿しい思い違いでもしているのかもしれません。
とにかく、どこが間違っているか教えていただきたく思い、書き込みました。
どうぞよろしくお願いします。





// 環境
Win7 64bit
CPU CORE2DUO 3.16GHz
Memory 4G
VisualC++ 2010 SP1


三角形が回転するだけのプログラムです。

コード:

#include <Windows.h>
#include <d3dx9.h>
#include <d3d9.h>
//#include <DxErr.h>
//#include <strsafe.h>


#pragma comment( lib , "d3d9.lib")
#pragma comment( lib , "d3dx9.lib")
//#pragma comment( lib , "dxerr.lib")
//#pragma comment( lib , "dxguid.lib")
// このあたり適当です。


#define APP_NAME L"アプリ名だ。"
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ | D3DFVF_DIFFUSE)





struct CUSTOMVERTEX{
	FLOAT x,y,z;
	DWORD color;
};





// GLOBAL
int testCount=0;// WM_TIMERが動いていないことを証してくれる。
unsigned int count=0;// これを引数にして回転の角度が決まる。

HWND hWnd;

LPDIRECT3D9 g_pD3D=NULL;
LPDIRECT3DDEVICE9 g_pd3dDevice = NULL;
LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL;
D3DPRESENT_PARAMETERS d3dpp;
VOID* pVertices;





// Functions
HRESULT InitD3D(HWND hWnd);// DirectX の初期化
HRESULT InitGeometry();// テスト用三角形のバッファをつくる。ウィンドウ作成前に一度だけ呼ばれる。
VOID Cleanup();// 終了用に色々解放する。
VOID SetupMatrices(int count);// 行列を設定。レンダーの度に呼ばれる。
VOID Render(int county);// 描画。
LRESULT CALLBACK WindowProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam);// コールバック。

























// directXの初期化
HRESULT InitD3D(HWND hWnd){
	if(NULL==(g_pD3D = Direct3DCreate9(D3D_SDK_VERSION))) return E_FAIL;
	
	ZeroMemory(&d3dpp , sizeof(d3dpp));
	d3dpp.Windowed = TRUE ; 
	d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
	d3dpp.BackBufferFormat = D3DFMT_UNKNOWN; 
	
	if (FAILED(g_pD3D->CreateDevice(D3DADAPTER_DEFAULT , D3DDEVTYPE_HAL , hWnd ,
									D3DCREATE_SOFTWARE_VERTEXPROCESSING , 
									&d3dpp , &g_pd3dDevice)))
	{
		return E_FAIL;
	}

	
	D3DVIEWPORT9 vp;
	vp.X=0;
	vp.Y=0;
	vp.Width=d3dpp.BackBufferWidth;
	vp.Height=d3dpp.BackBufferHeight;
	vp.MinZ=0.0f;
	vp.MaxZ=1.0f;
	HRESULT hr = (g_pd3dDevice->SetViewport(&vp));
	if (FAILED(hr)) return E_FAIL;


	
	g_pd3dDevice->SetRenderState(D3DRS_CULLMODE , D3DCULL_NONE);
	g_pd3dDevice->SetRenderState(D3DRS_LIGHTING , FALSE);

	return S_OK;
	
}


HRESULT InitGeometry(){
	CUSTOMVERTEX g_Vertices[] =
	{
		{-1.0f , -1.0f , 0.0f , 0xffff8888, },
		{ 1.0f , -1.0f , 0.0f , 0xff77aaff, },
		{ 0.0f ,  1.0f , 0.0f , 0xffddffee, },
	};


	if (FAILED(g_pd3dDevice->CreateVertexBuffer(3 * sizeof(CUSTOMVERTEX) ,
												0 , D3DFVF_CUSTOMVERTEX,
												D3DPOOL_DEFAULT , &g_pVB , NULL)))
	{
		return E_FAIL;
	}
	
	if (FAILED(
			g_pVB->Lock(0 , sizeof(g_Vertices) , (void**)&pVertices , 0)
		)
	)
	{
		return E_FAIL;
	}
	memcpy(pVertices , g_Vertices , sizeof(g_Vertices));
	g_pVB->Unlock();

	return S_OK;
}




VOID Cleanup()
{
    if( g_pVB != NULL )
        g_pVB->Release();

    if( g_pd3dDevice != NULL )
        g_pd3dDevice->Release();

    if( g_pD3D != NULL )
        g_pD3D->Release();
}



VOID SetupMatrices(int count)
{
    
    D3DXMATRIXA16 matWorld;

    
	FLOAT fAngle = count * ( 2.0f * D3DX_PI ) / 180.0f;
    D3DXMatrixRotationY( &matWorld, fAngle );
    g_pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld );

    
    D3DXVECTOR3 vEyePt( 0.0f, 3.0f,-5.0f );
    D3DXVECTOR3 vLookatPt( 0.0f, 0.0f, 0.0f );
    D3DXVECTOR3 vUpVec( 0.0f, 1.0f, 0.0f );
    D3DXMATRIXA16 matView;
    D3DXMatrixLookAtLH( &matView, &vEyePt, &vLookatPt, &vUpVec );
    g_pd3dDevice->SetTransform( D3DTS_VIEW, &matView );

    
    D3DXMATRIXA16 matProj;
    D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI / 4, 1.0f, 1.0f, 100.0f );
    g_pd3dDevice->SetTransform( D3DTS_PROJECTION, &matProj );
}



VOID Render(int count)
{
    g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB( 0, 0, 0 ), 1.0f, 0 );

    
    if( SUCCEEDED( g_pd3dDevice->BeginScene() ) )
    {
        SetupMatrices(count);// これをコメントアウトしてもCPU使用率は高いまま。

        g_pd3dDevice->SetStreamSource( 0, g_pVB, 0, sizeof( CUSTOMVERTEX ) );
        // ↑if(count <30)のようにしてもCPU使用率は高い。
        g_pd3dDevice->SetFVF( D3DFVF_CUSTOMVERTEX );// ブロックの外でこれを描いても変わらず。
        g_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP, 0, 1 );// こいつが犯人だと睨んでいる
        g_pd3dDevice->EndScene();
    }
	else {count = 0;}
    
    g_pd3dDevice->Present( NULL, NULL, NULL, NULL );
}




LRESULT CALLBACK WindowProc(
	HWND hWnd,
	UINT uMsg,
	WPARAM wParam,
	LPARAM lParam)
{
	switch(uMsg){
	case WM_CREATE:
		SetTimer(hWnd , 1 , 1 , NULL);// しかし意図したようには動かなかった。
		return 0;
	case WM_DESTROY:
		Cleanup();
		PostQuitMessage(0);
		return 0;

		/////////////////////////////////////////////
		/////////////////////////////////////////////
		/////////////////////////////////////////////
		
	case WM_TIMER:
		// WM_PAINTがコメントアウトされていれば、動くような感じ。
		testCount++;// 実行されていない
		if (testCount==10){MessageBox(NULL , L"OpenGL" , NULL , MB_OK);}// 実行されていない
		count++;// 多分実行されていない
		SendMessage(hWnd , WM_PAINT , 0 , 0);// 多分実行されていない
		return 0;
	
	


	case WM_PAINT:
		count++;
		Render(count);
		return 0;
		// 1.このWM_PAINTをコメントアウトしてしまえばCPU使用率は0%になります。
		// 2.WM_PAINTをコメントアウトして、ShowWindow(hWnd , SW_SHOW)の直後に一度だけRender()を呼ぶとCPU使用率は0%に。
		// 3.Render()内の、SetupMatrices()をコメントアウトしても(つまり静止した三角形のみ表示)CPU使用率は10%ほど。などなど。
		// DEBUG , RELEASEどちらも同じような謎負荷で動く。


		/////////////////////////////////////////////
		/////////////////////////////////////////////
		/////////////////////////////////////////////
	}

	return DefWindowProc(hWnd , uMsg , wParam , lParam);
}



int WINAPI WinMain(
	HINSTANCE hInstance,
	HINSTANCE hPrevInstance,
	PSTR lpCmdLine,
	int nCmdShow)
{
	WNDCLASS wc;
	MSG msg;
	
	wc.style		= CS_HREDRAW | CS_VREDRAW;
	wc.lpfnWndProc  = WindowProc;
	wc.cbClsExtra   = 0;
	wc.cbWndExtra   = 0;
	wc.hInstance    = hInstance;
	wc.hIcon        = LoadIcon(NULL , IDI_APPLICATION);
	wc.hCursor      = LoadCursor(NULL , IDC_ARROW);
	wc.hbrBackground= (HBRUSH)COLOR_BACKGROUND + 1;
	wc.lpszMenuName = NULL;
	wc.lpszClassName= APP_NAME;

	if (!RegisterClass(&wc)){
		MessageBox(NULL , TEXT("ウィンドウクラスの作成に失敗しました。") , NULL , MB_OK);
		return 0;
	}

	hWnd = CreateWindow(
		APP_NAME ,
		APP_NAME ,
		WS_OVERLAPPEDWINDOW ,
		100 ,
		100 ,
		640 ,
		480 ,
		NULL,
		NULL,
		hInstance,
		NULL);

	
	HRESULT hr=InitD3D(hWnd);
	if (SUCCEEDED(hr)) hr=InitGeometry();
	if (FAILED(hr)){
		UnregisterClass(APP_NAME , wc.hInstance);
		return 0;
	}



	


	ShowWindow(hWnd , SW_SHOW);



	if (hWnd==NULL){
		MessageBox(NULL , TEXT("ウィンドウの生成に失敗しました。") , NULL , MB_OK);
		return 0;
	}

	while (GetMessage(&msg , NULL , 0 , 0) > 0){
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}


	return msg.wParam;


}


↓ちなみにOpenGLのコードはコレを改変しました。
http://msdn.microsoft.com/en-us/library/windows/desktop/dd369065(v=vs.85).aspx


・WM_CREATEにSetTimer(hWnd , 1 , 1 , NULL)を追加
・WM_PAINTをコメントアウト。(作法としてはよろしくないかも?)
・WM_TIMERでdrawScene();break;
・PeekMessageを含むwhile(1)を、
while(GetMessage(&msg , NULL , 0,0) >0){
TranslateMessage(&msg);
DispatchMessage(&msg);
}
に変更。

Mana

Re: DirectXでCPU使用率が高い気がします。

#2

投稿記事 by Mana » 12年前

WM_PAINTは特殊なメッセージでウィンドウズが他に送るメッセージがなくなったときに無効領域があると送ってきます。
自分でWM_PAINT投げたら恐ろしいことが起きそうな気がします。
やらないほうが良いと思います。

WM_PAINTでBeginPaint,EndPaintを実行しないと無効領域がクリアされません。
なのですごいいきおいで描画を繰り返すプログラムになってます。
CPU使用率が高いのはそれが原因ではないでしょうかね。
確証はありませんが。
参考にしたOpenGLのプログラムにはBeginPaint,EndPaint書いてありますね。

旅路のきのこ

Re: DirectXでCPU使用率が高い気がします。

#3

投稿記事 by 旅路のきのこ » 12年前

どうも返信ありがとうございます。
InvalidateRectでしたっけ?これからはちゃんと使うようにします。
ちょっと作法がなってなかったようです。
プロシージャが何をするかすら分かっていなかった以前に比べれば、Windowsプログラミングも理解できると思うので、もう一度初歩の部分も勉強しなおしてきます。

ところで、コードだけ見ると、確かに三角形の荒ぶった様子が思い浮かぶと思います。
しかし実際はどうもそうはなりませんでした。
のんびり回ってました。優雅なものです。
ヤケクソ気味に試したところではWM_PAINTがあると、WM_TIMERの中では何も起こりませんでした。
勝手にリフレッシュレートか何かに合わせてくれているのだろうと思いました。



で、肝心の問題の方ですが、ちょっと色々試しました。
D3DPRESENT_PARAMETERS d3dpp.PresentationIntervalの値をD3DPRESENT_INTERVAL_ONEにすると改善しました。
ただそれでも2,3%ほどだったので、D3DPRESENT_INTERVAL_IMMEDIATEとしてみたところ荒ぶりました。
しかし、WM_TIMERの方でRender()を呼ぶとほぼ0%で動いてくれました(待ち時間を1msecにしても)。

上のコードでは、d3dpp.PresentationIntervalはZeroMemoryしたきりなので、D3DPRESENT_INTERVAL_DEFAULTの設定だと思います。
やってみると、D3DPRESENT_INTERVAL_DEFAULTとしても10%程でした。
MSDNによれば、D3DPRESENT_INTERVAL_DEFAULTとD3DPRESENT_INTERVAL_ONEは同じとあります。
実際にはどうも違ったようです。(ハードの問題?)



はっきりいって未だに何が何だか分かっていませんが、とりあえず自分の問題は片付いた気がします。
どうもありがとうございました。

milla

Re: DirectXでCPU使用率が高い気がします。

#4

投稿記事 by milla » 12年前

WM_PAINTでRenderするのはよくないかと。
DirectXにしてもOpenGLにしても、OSのPaintメッセージよりももっとネイティブな描画(OSレベルのソフトウェアな描画ではなく、ドライバーにより近いハードウェアによる描画)をしているはずですので。
http://gamedev.stackexchange.com/questi ... d-direct3d

旅路のきのこ

Re: DirectXでCPU使用率が高い気がします。

#5

投稿記事 by 旅路のきのこ » 12年前

WM_PAINTの中で描画していたのは、何となく気持ち悪いなと思いつつもそれで動いていたからです。
リンク先を見てみると、確かにWM_PAINTでの描画はやめるべきだなと思えました。

DirectX WM_PAINT ……まあ、検索かけたら一ページ目に出てきますね。
ちゃんと調べるようにします。
どうもありがとうございました。

Mana

Re: DirectXでCPU使用率が高い気がします。

#6

投稿記事 by Mana » 12年前

Direct3D使うのがゲームだけとは限らないのだからWM_PAINTで描画するのが全部ダメというわけではないですよね。
本件のプログラムのように短い間隔で画面更新するものならGDIの再描画要求に答える必要性低いですが。
リフレッシュレートに合わせて常に画面を更新し続けなければいけないといったルールありましたっけ。

ところでデバイスの作成時にD3DCREATE_HARDWARE_VERTEXPROCESSINGフラグを使ったらCPUを使わない分使用率がさらに下がるのではないでしょうか。
たいていのサンプルコードはD3DCREATE_HARDWARE_VERTEXPROCESSINGで作成失敗したらD3DCREATE_SOFTWARE_VERTEXPROCESSINGを試すってふうになってますね。

旅路のきのこ

Re: DirectXでCPU使用率が高い気がします。

#7

投稿記事 by 旅路のきのこ » 12年前

DirectXを使った際の動作はよく分からないままですが、fpsを表示するようにしてみたところ、勝手に60fpsくらいで回ってくれているようです。(垂直同期をとらなければ、荒ぶります。)
リフレッシュレートなんてことは考えなくてもそんな感じでした。

ループの中はこんな風にしました。

コード:

while (1){
		if (PeekMessage(&msg , NULL , 0 , 0 , false)){
			if (GetMessage(&msg , NULL , 0 , 0) > 0){
				TranslateMessage(&msg);
				DispatchMessage(&msg);
			}
			else break;
		}
		// Sleep(50);// 重々しい。でもヌルヌル動く。
		// これがSleep(5)とかだと殆ど影響が見られない。描画がリフレッシュに間に合うということ?
		count++;
		Render(count);
}
Sleep(5)くらいなら実行しても、fpsは60のままでしたが、Sleep(50)などにするとその分はちゃんと眠ってしまっているようでした。
WM_PAINTで描画していた時分も、特にメッセージが送られるようなことはしていないつもりでもぐるぐる回っていたので、まあ、そういうもんだと受け止めることにしました。
WM_TIMERで描画したときは、ややカクカクした動きになっていたので、他にもやり方は色々あるのかもしれませんが、リフレッシュレートにあわせて勝手に描画してもらうのが楽かなと思いました。




あと、D3DCREATE_HARDWARE_VERTEXPROCESSINGにしていなかったことについてです。
もともとこのコードは、だいぶ前に多分DirectX SDKのサンプルか何かを参考にして書いたものです。
(実は記憶が曖昧になるほど長い間悩んでいたわけで、質問したその日に解決するとは思っていませんでした。)
やっぱりソフトよりハードの方が強そうな気はしたもののそのまま写したんだと思います。

ちなみに、一応試してみましたが、三角形一枚描く程度では何の差も見られませんでした。
何かゴージャスなオブジェを描画するくらいまで学習が進んで比較すれば違うかもしれませんが、まだそこまでいっていないので確かめることができていません。残念ながら。



さっきちょっと調べてみたところによりますと……
http://msdn.microsoft.com/ja-jp/library/bb219626(v=vs.85).aspx
Direct3D の頂点処理機能を使用する (未変換の頂点を頂点バッファー レンダリング メソッドに渡す) 場合は、デバイス タイプとデバイス作成フラグに応じてハードウェアまたはソフトウェアで処理が発生します。ほぼすべてのケースで最適なパフォーマンスを得るには、頂点バッファーを D3DPOOL_DEFAULT に割り当てることを推奨します。デバイスがハードウェア頂点処理を使用している場合は、D3DUSAGE_DYNAMIC および D3DUSAGE_WRITEONLY フラグに基づいて実行可能な追加の最適化処理が多数あります。これらのフラグの使用方法の詳細については、「IDirect3DDevice9::CreateVertexBuffer」を参照してください。

終わりの方にこんなことが書かれていました。
やはり何となくハードの方が凄そうです。


また、作成したデバイスのキャパシティを取得できるらしく、
D3DCAPS9 capcap;
g_pd3dDevice->GetDeviceCaps(&capcap);
こう書けば、capcapから知ることが出来るとのことだったので、デバイス作成時にソフト、ハードを変更して試してみました。
すると、ソフトを選択したときの方が、MaxActiveLightsやMaxVertexBlendMatricesの値が大きかったりしました。
しょぼいグラフィックカードを使っているからかもしれませんが、ソフトを選ぶ理由というのも場合によってはあるのかもしれません。
(しかしライトを10以上使う機会が自分にあるとは思えません。)

まあ、難しいもんです。
どうも、ありがとうございました。

閉鎖

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