Directx12 3D 프로그래밍 입문/4. Directx12 초기화
4.4 시간 측정과 애니메이션
ftftgop3
2023. 10. 22. 17:45
애니메이션 실행 시 정확한 동작을 위해서 프레임 간의 시간을 구해야 한다.
프레임 간의 경과 시(elapsed time) : 인접한 두 프레임 사이의 시간
Windows 에서 지원하는 성능 타이머 (performance counter) 사용
시간의 측정 단위는 “지나간 클럭 틱 tick ” 이라 한다.
초당 틱 수의 역수로 1틱 당 초수를 계산 하는 방식
//현재 시간을 64 비트 틱 형태로 반환
__int currTime;
QueryPerformanceCounter((LARGE_INTEGER*)&currTime);
//선능 타이머의 주파수(초당 틱 수)
__int countsPerSec;
QueryPerformanceFrequency((LARGE_INTEGER*) &countsPerSec);
//틱수 당 초
mSecondPerCount = 1.0 / countsPerSec;
//측정한 두 시간 값 사이의 상대적 차이가 중요
__int64 StartTick =0;
QueryPerformanceCounter((LARGE_INTEGER*)&StartTick); //시작 시간을 틱으로 변환
//작 업 수 행
__int64 EndTick =0;
QueryPerformanceCounter((LARGE_INTEGER*)&EndTick); //끝 시간을 틱으로 변환
PlayTime = (EndTick - StartTick) * mSecondPerCount; // 걸린 시간 계산
예제 클래스 GameTimer
class GameTimer
{
public:
GameTimer();
float TotalTime()const; // 초 단위
float DeltaTime()const; // 초 단위
void Reset(); // 메시지 루프 이전에 호출
void Start(); // 타이머 시작 또는 재개
void Stop(); // 타이머 정지
void Tick(); // 매 프레임 호출
private:
double mSecondsPerCount;
double mDeltaTime;
__int64 mBaseTime;
__int64 mPausedTime;
__int64 mStopTime;
__int64 mPrevTime;
__int64 mCurrTime;
bool mStopped;
};
GameTimer::GameTimer()
: mSecondsPerCount(0.0), mDeltaTime(-1.0), mBaseTime(0),
mPausedTime(0), mPrevTime(0), mCurrTime(0), mStopped(false)
{
__int64 countsPerSec;
QueryPerformanceFrequency((LARGE_INTEGER*)&countsPerSec); // 1초당 틱 수
mSecondsPerCount = 1.0 / (double)countsPerSec; //1틱당 초 (역수로 구함)
}
//실시간 초당 프레임수는 30 은 넘어야 한다.
//초당 프레임 수
void GameTimer::Tick()
{
//스탑 시 리턴,
if( mStopped )
{
mDeltaTime = 0.0;
return;
}
//현재 시간을 틱으로 변환
__int64 currTime;
QueryPerformanceCounter((LARGE_INTEGER*)&currTime);
mCurrTime = currTime;
//현재 틱 - 이전 틱 * 1틱 당 초
mDeltaTime = (mCurrTime - mPrevTime)*mSecondsPerCount;
//이전 틱에 현재 틱 갱신
mPrevTime = mCurrTime;
// SDK 문서 에 따르면 프로세서가 절전 및 다른 프로세서와 엉킹 경우 음수가 나올수 있다
// 음수가 나오면 프로그램이 죽을 수 있어서 예외 처리
if(mDeltaTime < 0.0)
{
mDeltaTime = 0.0;
}
}
메시지 루프에서 Game Timer 동작 순서
int D3DApp::Run()
{
MSG msg = {0};
mTimer.Reset(); //시간 초기화
while(msg.message != WM_QUIT) //윈도우 메시지 종료 없으면 계속 동작
{
// Window 메시지 확인 및 처리
if(PeekMessage( &msg, 0, 0, 0, PM_REMOVE ))
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
// window 메시지 없으면 애니메이션 및 다른 동작
else
{
mTimer.Tick(); // 프레임 마다 시간 갱신
if( !mAppPaused ) // 프로그램 멈춤 확인
{
CalculateFrameStats();
Update(mTimer); //시간 갱신 값으로 애니메이션 동작
Draw(mTimer);
}
else
{
Sleep(100);
}
}
}
return (int)msg.wParam;
}
void GameTimer::Reset()
{
__int64 currTime;
QueryPerformanceCounter((LARGE_INTEGER*)&currTime);
mBaseTime = currTime;
//초기화 시 이전 틱을 넣어줘야 Tick() 동작 시
// 프레임 간 사이 시간을 구할수 있다.
mPrevTime = currTime;
mStopTime = 0;
mStopped = false;
}
//타이머 정지
void GameTimer::Stop()
{
//Stop 상태 체크
if( !mStopped )
{
__int64 currTime;
QueryPerformanceCounter((LARGE_INTEGER*)&currTime); //현재 시간 틱으로 변환
mStopTime = currTime;//멈춘 시점 저장
mStopped = true;//Stop 상태 변경
}
}
//타이머 시작
void GameTimer::Start()
{
__int64 startTime;
QueryPerformanceCounter((LARGE_INTEGER*)&startTime); //현재 시간 저장
// 정지 시간과 다시 시작의 사이 값
//
// |<-------d------->|
// ----*---------------*-----------------*------------> time
// mBaseTime mStopTime startTime
//멈춘 상태에서 시작(재개)
if( mStopped )
{
//정지한 시간의 값
mPausedTime += (startTime - mStopTime);
//이전 시간 갱신
mPrevTime = startTime;
mStopTime = 0; //초기화
mStopped = false;//stop 해제
}
}
// Returns the total time elapsed since Reset() was called, NOT counting any
// 프로그램 전체 흐른 시간, 정지된 시간 제외
float GameTimer::TotalTime()const
{
// If we are stopped, do not count the time that has passed since we stopped.
// Moreover, if we previously already had a pause, the distance
// mStopTime - mBaseTime includes paused time, which we do not want to count.
// To correct this, we can subtract the paused time from mStopTime:
//
// |<--paused time-->|
// ----*---------------*-----------------*------------*------------*------> time
// mBaseTime mStopTime startTime mStopTime mCurrTime
//정지 상태에서 게임 진행된 시간
if( mStopped )
{
//stoptime 기준으로 누적된 정지된 시간, 처음 프로그램 시간
return (float)(((mStopTime - mPausedTime)-mBaseTime)*mSecondsPerCount);
}
// The distance mCurrTime - mBaseTime includes paused time,
// which we do not want to count. To correct this, we can subtract
// the paused time from mCurrTime:
//
// (mCurrTime - mPausedTime) - mBaseTime
//
// |<--paused time-->|
// ----*---------------*-----------------*------------*------> time
// mBaseTime mStopTime startTime mCurrTime
else
{
//현재 시간 기준으로 누적된 정지된 시간, 처음 프로그램 시작 시간
return (float)(((mCurrTime-mPausedTime)-mBaseTime)*mSecondsPerCount);
}
}