DirectX11 Tutorial 15: FPS, CPU 사용량, 타이머

강좌번역/DirectX 11 2013. 6. 3. 13:14 by 빠재

원문: http://rastertek.com/dx11tut15.html




이 튜토리얼에서는 fps 카운터와 cpu 사용량 측정, 고해상도 시간 측정 기능을 캡슐화하는 세 클래스를 소개할 것입니다. 이 튜토리얼의 소스코드는 지난 글꼴 엔진 튜토리얼에서 이어집니다.


첫번째 클래스는 FpsClass입니다. FpsClass는 프로그램이 실행되는 동안의 초당 프레임 수(FPS, frames per second)를 기록하는 일을 담당합니다. 초당 몇 프레임을 그리는지에 대한 정보는 프로그램의 성능을 측정하는 데 좋은 기준이 되기 때문에 실무에서는 이 그래픽 렌더링 속도가 받아들여질 만한지 판단하는 거의 표준적인 기준이기도 합니다. 또한 새로운 기능을 넣었을 때 이것이 렌더링 속도에 얼마나 영향을 미치는 지 확인할 대에도 유용합니다. 만약 새로운 기능이 프레임수를 반토막낸다면 즉각적으로 이 숫자만 가지고도 중대한 문제가 생겼음을 알 수 있습니다. 참고로 현재 일반적으로 받아들여지는 fps는 60입니다. 만약 이 밑으로 떨어진다면 뭔가 위화감이 느껴지고, 30 밑으로 내려가는 것은 사람 눈에도 뚝뚝 끊기는 것이 보이게 됩니다. 일반적으로는 프로그래밍을 할 때 fps를 최대가 되도록 하고 새로운 기능이 심각한 속도의 저하를 가져온다면 그 이유가 무엇인지 확인하거나 최소한 관심이라도 가지도록 해야 합니다.


두번째 클래스는 CpuClass입니다. 이 클래스는 화면에 현재 cpu 사용률을 표시할 수 있도록 cpu 사용률을 기록하는 일을 담당합니다. cpu 사용량을 아는 것도 fps의 경우처럼 코드의 새로운 변화를 디버깅할 때 유용합니다. 이것 역시 최근에 작성한 좋지 않은 코드나 알고리즘이 있는지 확인하는 데 좋은 판단 정보를 제공합니다.


마지막으로 TimerClass가 있습니다. 이것은 고해상도 타이머가 되는데, 이를 시간에 따른 이벤트에 적용하거나 프로그램과 그에 붙은 다양한 요소들이 잘 동기화될 수 있도록 하는데 사용할 수 있습니다.





프레임워크


위의 세 클래스가 포함된 튜토리얼의 프레임워크는 다음 그림과 같습니다:










이제 세 클래스를 보는 것으로 튜토리얼을 시작합니다.




Fpsclass.h


FpsClass는 단순히 타이머가 있는 카운터입니다. 1초의 시간 동안 얼만큼의 프레임들이 그려지는지 세고 정기적으로 이 숫자를 갱신합니다.


////////////////////////////////////////////////////////////////////////////////
// Filename: fpsclass.h
////////////////////////////////////////////////////////////////////////////////
#ifndef _FPSCLASS_H_
#define _FPSCLASS_H_


/////////////
// LINKING //
/////////////
#pragma comment(lib, "winmm.lib")


//////////////
// INCLUDES //
//////////////
#include <windows.h>
#include <mmsystem.h>


////////////////////////////////////////////////////////////////////////////////
// Class name: FpsClass
////////////////////////////////////////////////////////////////////////////////
class FpsClass
{
public:
	FpsClass();
	FpsClass(const FpsClass&);
	~FpsClass();

	void Initialize();
	void Frame();
	int GetFps();

private:
	int m_fps, m_count;
	unsigned long m_startTime;
};

#endif



Fpsclass.cpp


///////////////////////////////////////////////////////////////////////////////
// Filename: fpsclass.cpp
///////////////////////////////////////////////////////////////////////////////
#include "fpsclass.h"


FpsClass::FpsClass()
{
}


FpsClass::FpsClass(const FpsClass& other)
{
}


FpsClass::~FpsClass()
{
}





Initialize 함수는 모든 카운터 변수들을 0으로 초기화하고 타이머를 시작합니다.


void FpsClass::Initialize()
{
	m_fps = 0;
	m_count = 0;
	m_startTime = timeGetTime();
	return;
}





Frame 함수는 매 프레임마다 불려져서 1씩 증가하도록 해야 합니다. 만약 1초 이상 지났다면 현재 프레임수를 m_fps 변수에 저장합니다. 그리고 프레임수를 초기화하고 다시 타이머를 돌립니다.


void FpsClass::Frame()
{
	m_count++;

	if(timeGetTime() >= (m_startTime + 1000))
	{
		m_fps = m_count;
		m_count = 0;
		
		m_startTime = timeGetTime();
	}
}





GetFps 함수는 직전 1초간의 fps 값을 돌려줍니다. 이 함수는 계속 호출되어 최신의 fps정보가 화면에 그려질 수 있도록 해야 합니다.


int FpsClass::GetFps()
{
	return m_fps;
}







Cpuclass.h


CpuClass는 매 초마다 cpu 사용량의 총량을 재는 데 사용됩니다.


///////////////////////////////////////////////////////////////////////////////
// Filename: cpuclass.h
///////////////////////////////////////////////////////////////////////////////
#ifndef _CPUCLASS_H_
#define _CPUCLASS_H_
cpu 사용량을 가져오기 위해서 pdh 라이브러리를 사용합니다.
/////////////
// LINKING //
/////////////
#pragma comment(lib, "pdh.lib")


//////////////
// INCLUDES //
//////////////
#include <pdh.h>


///////////////////////////////////////////////////////////////////////////////
// Class name: CpuClass
///////////////////////////////////////////////////////////////////////////////
class CpuClass
{
public:
	CpuClass();
	CpuClass(const CpuClass&);
	~CpuClass();

	void Initialize();
	void Shutdown();
	void Frame();
	int GetCpuPercentage();

private:
	bool m_canReadCpu;
	HQUERY m_queryHandle;
	HCOUNTER m_counterHandle;
	unsigned long m_lastSampleTime;
	long m_cpuUsage;
};

#endif





Cpuclass.cpp


///////////////////////////////////////////////////////////////////////////////
// Filename: cpuclass.cpp
///////////////////////////////////////////////////////////////////////////////
#include "cpuclass.h"


CpuClass::CpuClass()
{
}


CpuClass::CpuClass(const CpuClass& other)
{
}


CpuClass::~CpuClass()
{
}




Initialize 함수에서는 cpu 사용량을 질의하기 위해 필요한 핸들을 초기화합니다. 여기서 설정된 질의(query)는 개개 cpu의 사용량 대신 시스템의 모든 cpu들의 사용량 총합을 합하여 돌려줍니다. 만약 핸들이나 질의를 얻지 못하는 등의 이유로 cpu 사용량 정보를 읽을 수 없다면 m_canReadCpu 변수가 false로 바뀌어서 기록하는 cpu사용량을 0이 되도록 합니다. 일부 os에서는 권한 때문에 이 기능을 사용할 수 없을 수도 있습니다. 또한 타이머를 돌려서 cpu 사용량을 매 초마다 측정하도록 합니다.


void CpuClass::Initialize()
{
	PDH_STATUS status;


	// Initialize the flag indicating whether this object can read the system cpu usage or not.
	m_canReadCpu = true;

	// Create a query object to poll cpu usage.
	status = PdhOpenQuery(NULL, 0, &m_queryHandle);
	if(status != ERROR_SUCCESS)
	{
		m_canReadCpu = false;
	}

	// Set query object to poll all cpus in the system.
	status = PdhAddCounter(m_queryHandle, TEXT("\\Processor(_Total)\\% processor time"), 0, &m_counterHandle);
	if(status != ERROR_SUCCESS)
	{
		m_canReadCpu = false;
	}

	m_lastSampleTime = GetTickCount(); 

	m_cpuUsage = 0;

	return;
}





Shutdown 함수는 cpu사용량을 질의하는 데 사용한 핸들을 해제합니다.


void CpuClass::Shutdown()
{
	if(m_canReadCpu)
	{
		PdhCloseQuery(m_queryHandle);
	}

	return;
}





FpsClass처럼 이 클래스의 Frame 함수도 매 프레임마다 호출해야 합니다. 하지만 질의 횟수를 줄이기 위해서 m_lastSampleTime이라는 변수를 두어 1초마다 cpu사용량을 샘플링할 수 있도록 합니다. 그렇게 매 초마다 cpu에게 사용량을 물어보고 그 값을 m_cpuUsage 변수에 저장합니다. 그 외에 크게 중요한 내용은 없습니다.


void CpuClass::Frame()
{
	PDH_FMT_COUNTERVALUE value; 

	if(m_canReadCpu)
	{
		if((m_lastSampleTime + 1000) < GetTickCount())
		{
			m_lastSampleTime = GetTickCount(); 

			PdhCollectQueryData(m_queryHandle);
        
			PdhGetFormattedCounterValue(m_counterHandle, PDH_FMT_LONG, NULL, &value);

			m_cpuUsage = value.longValue;
		}
	}

	return;
}




GetCpuPercentage 함수는 현재 cpu 사용량을 돌려줍니다. 다시 이야기하지만 어떤 이유로든 cpu 사용량을 읽을 수 없게 되었다면 그 값을 0으로 합니다.


int CpuClass::GetCpuPercentage()
{
	int usage;

	if(m_canReadCpu)
	{
		usage = (int)m_cpuUsage;
	}
	else
	{
		usage = 0;
	}

	return usage;
}






Timerclass.h


TimerClass는 실행 중 매 프레임 간의 정확한 시간을 재는 고해상도 타이머 클래스입니다. 이것의 주요 용도는 오브젝트의 이동을 위해 프레임의 표준 시간대와의 동기화입니다. 이 튜토리얼에서는 직접 써먹을 일은 없지만 어떻게 적용할 수 있는지 보여주는 코드를 작성할 것입니다. 대개 TimerClass는 프레임간 간격을 1초에 대한 %로 바꿔서 물체를 그만큼 움직이도록 하는 경우에 많이 사용합니다.


////////////////////////////////////////////////////////////////////////////////
// Filename: timerclass.h
////////////////////////////////////////////////////////////////////////////////
#ifndef _TIMERCLASS_H_
#define _TIMERCLASS_H_


//////////////
// INCLUDES //
//////////////
#include <windows.h>


////////////////////////////////////////////////////////////////////////////////
// Class name: TimerClass
////////////////////////////////////////////////////////////////////////////////
class TimerClass
{
public:
	TimerClass();
	TimerClass(const TimerClass&);
	~TimerClass();

	bool Initialize();
	void Frame();

	float GetTime();

private:
	INT64 m_frequency;
	float m_ticksPerMs;
	INT64 m_startTime;
	float m_frameTime;
};

#endif






Timerclass.cpp


///////////////////////////////////////////////////////////////////////////////
// Filename: timerclass.cpp
///////////////////////////////////////////////////////////////////////////////
#include "timerclass.h"


TimerClass::TimerClass()
{
}


TimerClass::TimerClass(const TimerClass& other)
{
}


TimerClass::~TimerClass()
{
}




Initialize 함수는 우선 시스템이 고해상도 타이머를 지원하는지 알아봅니다. 만약 그 타이머의 주기를 받으면 이 값을 가지고 각 밀리초마다 카운터에서 몇번이나 틱이 일어나는지를 계산합니다. 그리고 그 값으로 프레임간 시간을 계산할 수 있습니다. Initialize 함수의 마지막에서는 첫 프레임 간격을 물어봐서 타이머를 시작합니다.


bool TimerClass::Initialize()
{
	// Check to see if this system supports high performance timers.
	QueryPerformanceFrequency((LARGE_INTEGER*)&m_frequency);
	if(m_frequency == 0)
	{
		return false;
	}

	// Find out how many times the frequency counter ticks every millisecond.
	m_ticksPerMs = (float)(m_frequency / 1000);

	QueryPerformanceCounter((LARGE_INTEGER*)&m_startTime);

	return true;
}




Frame 함수는 메인 프로그램에서 매 루프마다 호출하도록 합니다. 이렇게 매 루프마다 시간의 차이를 계산해서 프레임 간의 시간 간격을 알안래 수 있습니다. 질의하고, 계산한 뒤 그 값을 m_frame에 저장하여 이를 호출하는 다른 오브젝트들이 동기화되리 수 있게 할 수 있습니다. 그리고 나서 현재 시간을 다음 프레임의 시작 시간으로 저장합니다.


void TimerClass::Frame()
{
	INT64 currentTime;
	float timeDifference;


	QueryPerformanceCounter((LARGE_INTEGER*)& currentTime);

	timeDifference = (float)(currentTime - m_startTime);

	m_frameTime = timeDifference / m_ticksPerMs;

	m_startTime = currentTime;

	return;
}




GetTime 함수는 가장 최근에 계산된 프레임 시간을 리턴합니다.


float TimerClass::GetTime()
{
	return m_frameTime;
}






Systemclass.h


세 클래스를 보았으니 이제 프레임워크에 어떻게 끼워 맞출 수 있는지 시험해 보도록 하겠습니다. 세 클래스 모두 일단 SystemClass 아래 있도록 합니다.


////////////////////////////////////////////////////////////////////////////////
// Filename: systemclass.h
////////////////////////////////////////////////////////////////////////////////
#ifndef _SYSTEMCLASS_H_
#define _SYSTEMCLASS_H_


///////////////////////////////
// PRE-PROCESSING DIRECTIVES //
///////////////////////////////
#define WIN32_LEAN_AND_MEAN


//////////////
// INCLUDES //
//////////////
#include <windows.h>


///////////////////////
// MY CLASS INCLUDES //
///////////////////////
#include "inputclass.h"
#include "graphicsclass.h"




세 클래스들을 여기에서 포함하도록 합니다.


#include "fpsclass.h"
#include "cpuclass.h"
#include "timerclass.h"


////////////////////////////////////////////////////////////////////////////////
// Class name: SystemClass
////////////////////////////////////////////////////////////////////////////////
class SystemClass
{
public:
	SystemClass();
	SystemClass(const SystemClass&);
	~SystemClass();

	bool Initialize();
	void Shutdown();
	void Run();

	LRESULT CALLBACK MessageHandler(HWND, UINT, WPARAM, LPARAM);

private:
	bool Frame();
	void InitializeWindows(int&, int&);
	void ShutdownWindows();

private:
	LPCWSTR m_applicationName;
	HINSTANCE m_hinstance;
	HWND m_hwnd;

	InputClass* m_Input;
	GraphicsClass* m_Graphics;




세 클래스의 객체들을 여기에 선언합니다.


	FpsClass* m_Fps;
	CpuClass* m_Cpu;
	TimerClass* m_Timer;
};


/////////////////////////
// FUNCTION PROTOTYPES //
/////////////////////////
static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);


/////////////
// GLOBALS //
/////////////
static SystemClass* ApplicationHandle = 0;


#endif






Systemclass.cpp


글꼴 튜토리얼에서 달라진 점을 중심으로 다루겠습니다.


////////////////////////////////////////////////////////////////////////////////
// Filename: systemclass.cpp
////////////////////////////////////////////////////////////////////////////////
#include "systemclass.h"


SystemClass::SystemClass()
{
	m_Input = 0;
	m_Graphics = 0;




생성자에서 세 객체들을 null로 초기화합니다.


	m_Fps = 0;
	m_Cpu = 0;
	m_Timer = 0;
}


bool SystemClass::Initialize()
{
	int screenWidth, screenHeight;
	bool result;


	// Initialize the width and height of the screen to zero before sending the variables into the function.
	screenWidth = 0;
	screenHeight = 0;

	// Initialize the windows api.
	InitializeWindows(screenWidth, screenHeight);

	// Create the input object.  This object will be used to handle reading the keyboard input from the user.
	m_Input = new InputClass;
	if(!m_Input)
	{
		return false;
	}

	// Initialize the input object.
	result = m_Input->Initialize(m_hinstance, m_hwnd, screenWidth, screenHeight);
	if(!result)
	{
		MessageBox(m_hwnd, L"Could not initialize the input object.", L"Error", MB_OK);
		return false;
	}

	// Create the graphics object.  This object will handle rendering all the graphics for this application.
	m_Graphics = new GraphicsClass;
	if(!m_Graphics)
	{
		return false;
	}

	// Initialize the graphics object.
	result = m_Graphics->Initialize(screenWidth, screenHeight, m_hwnd);
	if(!result)
	{
		return false;
	}




FpsClass를 생성하고 초기화합니다.


	// Create the fps object.
	m_Fps = new FpsClass;
	if(!m_Fps)
	{
		return false;
	}

	// Initialize the fps object.
	m_Fps->Initialize();




CpuClass를 생성하고 초기화합니다.


	// Create the cpu object.
	m_Cpu = new CpuClass;
	if(!m_Cpu)
	{
		return false;
	}

	// Initialize the cpu object.
	m_Cpu->Initialize();




TimerClass를 생성하고 초기화합니다.


	// Create the timer object.
	m_Timer = new TimerClass;
	if(!m_Timer)
	{
		return false;
	}

	// Initialize the timer object.
	result = m_Timer->Initialize();
	if(!result)
	{
		MessageBox(m_hwnd, L"Could not initialize the Timer object.", L"Error", MB_OK);
		return false;
	}

	return true;
}


void SystemClass::Shutdown()
{




Shutdown함수에서 세 클래스들을 해제합니다.


	// Release the timer object.
	if(m_Timer)
	{
		delete m_Timer;
		m_Timer = 0;
	}

	// Release the cpu object.
	if(m_Cpu)
	{
		m_Cpu->Shutdown();
		delete m_Cpu;
		m_Cpu = 0;
	}

	// Release the fps object.
	if(m_Fps)
	{
		delete m_Fps;
		m_Fps = 0;
	}

	// Release the graphics object.
	if(m_Graphics)
	{
		m_Graphics->Shutdown();
		delete m_Graphics;
		m_Graphics = 0;
	}

	// Release the input object.
	if(m_Input)
	{
		m_Input->Shutdown();
		delete m_Input;
		m_Input = 0;
	}

	// Shutdown the window.
	ShutdownWindows();
	
	return;
}




마지막 변경점은 Frame 함수입니다. 새로운 클래스들 모두 매 프레임마다 각각 자신들의 Frame 함수를 호출해야 합니다. 일단 Frame 함수가 호출되고 나면 그 다음부터는 GraphicsClass에게 사용할 수 있도록 그 정보를 보냅니다.


bool SystemClass::Frame()
{
	bool result;


	// Update the system stats.
	m_Timer->Frame();
	m_Fps->Frame();
	m_Cpu->Frame();

	// Do the input frame processing.
	result = m_Input->Frame();
	if(!result)
	{
		return false;
	}

	// Do the frame processing for the graphics object.
	result = m_Graphics->Frame(m_Fps->GetFps(), m_Cpu->GetCpuPercentage(), m_Timer->GetTime());
	if(!result)
	{
		return false;
	}

	// Finally render the graphics to the screen.
	result = m_Graphics->Render();
	if(!result)
	{
		return false;
	}

	return true;
}





Graphicsclass.h


////////////////////////////////////////////////////////////////////////////////
// Filename: graphicsclass.h
////////////////////////////////////////////////////////////////////////////////
#ifndef _GRAPHICSCLASS_H_
#define _GRAPHICSCLASS_H_


/////////////
// GLOBALS //
/////////////
const bool FULL_SCREEN = true;




가능한 빠르게 프로그램이 실행되도록 하기 위해 이 튜토리얼에서는 수직동기화를 끄도록 합니다.


const bool VSYNC_ENABLED = false;
const float SCREEN_DEPTH = 1000.0f;
const float SCREEN_NEAR = 0.1f;


///////////////////////
// MY CLASS INCLUDES //
///////////////////////
#include "d3dclass.h"
#include "cameraclass.h"
#include "textclass.h"


////////////////////////////////////////////////////////////////////////////////
// Class name: GraphicsClass
////////////////////////////////////////////////////////////////////////////////
class GraphicsClass
{
public:
	GraphicsClass();
	GraphicsClass(const GraphicsClass&);
	~GraphicsClass();

	bool Initialize(int, int, HWND);
	void Shutdown();
	bool Frame(int, int, float);
	bool Render();

private:
	D3DClass* m_D3D;
	CameraClass* m_Camera;
	TextClass* m_Text;
};

#endif






Graphicsclass.cpp


지난 튜토리얼에서 달라진 점들을 중심으로 다루겠습니다.


////////////////////////////////////////////////////////////////////////////////
// Filename: graphicsclass.cpp
////////////////////////////////////////////////////////////////////////////////
#include "graphicsclass.h"




Frame 함수는 fps와 cpu, timer 카운터를 인자로 받습니다. fps와 cpu 사용량은 TextClass에 전달되어 화면에 그려지도록 합니다.


bool GraphicsClass::Frame(int fps, int cpu, float frameTime)
{
	bool result;


	// Set the frames per second.
	result = m_Text->SetFps(fps, m_D3D->GetDeviceContext());
	if(!result)
	{
		return false;
	}

	// Set the cpu usage.
	result = m_Text->SetCpu(cpu, m_D3D->GetDeviceContext());
	if(!result)
	{
		return false;
	}

	// Set the position of the camera.
	m_Camera->SetPosition(0.0f, 0.0f, -10.0f);

	return true;
}






Textclass.h


////////////////////////////////////////////////////////////////////////////////
// Filename: textclass.h
////////////////////////////////////////////////////////////////////////////////
#ifndef _TEXTCLASS_H_
#define _TEXTCLASS_H_

///////////////////////
// MY CLASS INCLUDES //
///////////////////////
#include "fontclass.h"
#include "fontshaderclass.h"


////////////////////////////////////////////////////////////////////////////////
// Class name: TextClass
////////////////////////////////////////////////////////////////////////////////
class TextClass
{
private:
	struct SentenceType
	{
		ID3D11Buffer *vertexBuffer, *indexBuffer;
		int vertexCount, indexCount, maxLength;
		float red, green, blue;
	};

	struct VertexType
	{
		D3DXVECTOR3 position;
		D3DXVECTOR2 texture;
	};

public:
	TextClass();
	TextClass(const TextClass&);
	~TextClass();

	bool Initialize(ID3D11Device*, ID3D11DeviceContext*, HWND, int, int, D3DXMATRIX);
	void Shutdown();
	bool Render(ID3D11DeviceContext*, D3DXMATRIX, D3DXMATRIX);




fps값과 cpu 사용량 값을 넣을 수 있도록 두 함수를 만듭니다.


	bool SetFps(int, ID3D11DeviceContext*);
	bool SetCpu(int, ID3D11DeviceContext*);

private:
	bool InitializeSentence(SentenceType**, int, ID3D11Device*);
	bool UpdateSentence(SentenceType*, char*, int, int, float, float, float, ID3D11DeviceContext*);
	void ReleaseSentence(SentenceType**);
	bool RenderSentence(ID3D11DeviceContext*, SentenceType*, D3DXMATRIX, D3DXMATRIX);

private:
	FontClass* m_Font;
	FontShaderClass* m_FontShader;
	int m_screenWidth, m_screenHeight;
	D3DXMATRIX m_baseViewMatrix;
	SentenceType* m_sentence1;
	SentenceType* m_sentence2;
};

#endif






Textclass.cpp


지난 튜토리얼에서 달라진 점을 중심으로 다루겠습니다.


///////////////////////////////////////////////////////////////////////////////
// Filename: textclass.cpp
///////////////////////////////////////////////////////////////////////////////
#include "textclass.h"




SetFps 함수는 입력으로 정수를 받고 문자열로 변환합니다. fps값이 문자열로 변환되고 나면 이 숫자가 fps값이라는 것을 나타내는 다른 문자열 뒤에 붙입니다. 그 뒤에 화면에 그려지기 위해 문장 구조체에 저장됩니다. 이 함수는 또한 fps가 60 이상이면 초록색, 60 미만이면 노란색, 30 미만이면 붉은색의 글자가 표시되도록 합니다.


bool TextClass::SetFps(int fps, ID3D11DeviceContext* deviceContext)
{
	char tempString[16];
	char fpsString[16];
	float red, green, blue;
	bool result;


	// Truncate the fps to below 10,000.
	if(fps > 9999)
	{
		fps = 9999;
	}

	// Convert the fps integer to string format.
	_itoa_s(fps, tempString, 10);

	// Setup the fps string.
	strcpy_s(fpsString, "Fps: ");
	strcat_s(fpsString, tempString);

	// If fps is 60 or above set the fps color to green.
	if(fps >= 60)
	{
		red = 0.0f;
		green = 1.0f;
		blue = 0.0f;
	}

	// If fps is below 60 set the fps color to yellow.
	if(fps < 60)
	{
		red = 1.0f;
		green = 1.0f;
		blue = 0.0f;
	}

	// If fps is below 30 set the fps color to red.
	if(fps < 30)
	{
		red = 1.0f;
		green = 0.0f;
		blue = 0.0f;
	}

	// Update the sentence vertex buffer with the new string information.
	result = UpdateSentence(m_sentence1, fpsString, 20, 20, red, green, blue, deviceContext);
	if(!result)
	{
		return false;
	}

	return true;
}




SetCpu 함수는 SetFps 함수와 비슷합니다. 역시 cpu 값을 받고 문자열로 바꾼 뒤 문장 구조체에 저장하고 그려집니다.


bool TextClass::SetCpu(int cpu, ID3D11DeviceContext* deviceContext)
{
	char tempString[16];
	char cpuString[16];
	bool result;


	// Convert the cpu integer to string format.
	_itoa_s(cpu, tempString, 10);

	// Setup the cpu string.
	strcpy_s(cpuString, "Cpu: ");
	strcat_s(cpuString, tempString);
	strcat_s(cpuString, "%");

	// Update the sentence vertex buffer with the new string information.
	result = UpdateSentence(m_sentence2, cpuString, 20, 40, 0.0f, 1.0f, 0.0f, deviceContext);
	if(!result)
	{
		return false;
	}

	return true;
}







마치면서





이제 화면에 fps와 cpu 사용량이 표시되는 것을 볼 수 있습니다. 더불어 프레임 속도와 상관없이 오브젝트들이 일정한 속도로 움직이고 회전할 수 있도록 해 주는 정밀한 타이머도 만들었습니다.







연습 문제


1. 코드를 다시 컴파일해 보고 fps와 cpu값이 표시되는지 확인해 보십시오. esc키를 눌러 빠져나갑니다.


2. graphicsclass.h에서 수직동기화(vsync)를 사용하도록 하고 여러분의 비디오카드나 모니터가 몇 프레임으로 동작하는지 확인해 보십시오.




소스 코드


Visual Studio 2008 프로젝트: dx11tut15.zip


소스 코드: dx11src15.zip


실행 파일: dx11exe15.zip

Nav