DirectX

5. 프레임워크

Awesome Red Tomato 2021. 12. 22. 10:00

https://github.com/Microsoft/DirectX-Graphics-Samples

 

GitHub - microsoft/DirectX-Graphics-Samples: This repo contains the DirectX Graphics samples that demonstrate how to build graph

This repo contains the DirectX Graphics samples that demonstrate how to build graphics intensive applications on Windows. - GitHub - microsoft/DirectX-Graphics-Samples: This repo contains the Direc...

github.com

 

1. 프레임 통계치(FPS)

FPS 계산하는법:

일정 기간 t동안 처리한 프레임 n개를 t로 나누면 평균 fps가 된다. 만약 t = 1 일 땐 fps = n / 1 = n 이다. 실제로 예제 프레임워크는 t = 1 (초 단위)로 둔다. 

MSPF(프레임 당 밀리초) = 1000.0f(1초 = 1000밀리초) / fps

void D3DApp::CalculateFrameStats()
{
	static int frameCount = 0;
	static float timeElapsed = 0.0f; 

	++frameCount;

	float fps = (float)frameCount;
	float mspf = 1000.0f / fps;		// miliseconds per frame

	wstring fpsStr = to_wstring(fps);
	wstring mspfStr = to_wstring(mspf);

	wstring windowText = mCaption + L"fps: " + fpsStr + L" mspf: " + mspfStr;

	SetWindowText(mWnd, windowText.c_str());

	frameCount = 0;
	timeElapsed += 1.0;
}

 

 

 

2. 메시지 처리부(MsgProc)

https://docs.microsoft.com/en-us/windows/win32/inputdev/keyboard-input

 

Keyboard Input (Keyboard and Mouse Input) - Win32 apps

This section discusses how the system generates keyboard input and how an application receives and processes that input.

docs.microsoft.com

 

 

예제 프레임워크의 메시지 처리부가 처리하는 첫 메시지는 WM_ACTIVATE 메시지이다. 이 메시지는 응용 프로그램이 활성/비활성될 때 전달된다.

	case WM_ACTIVATE:
		if (LOWORD(wParam) == WA_INACTIVE)
		{
			mAppPaused = true;
			mTimer.Stop();
		}
		else
		{
			mAppPaused = false;
			mTimer.Start();
		}
        return 0;

 

그 다음으로 처리하는 메시지는 WM_SIZE이다. 이 메시지를 처리하는 주된 이유는 후면 버퍼와 깊이 · 스텐실 버퍼의 크기를 클라이언트 영역에 맞게 갱신해야 한다는 점이다. 버퍼의 크기는 D3DApp::OnResize()에 구현되어 있다. 후면 버퍼의 크기는 IDXGISwapChain::ResizeBuffers()로 변경할 수 있다. 깊이 · 스텐실 버퍼는 파괴한 후 새 크기로 다시 생성해야 한다. 버퍼 뿐 아니라 rtv, dsv도 다시 생성해야 한다. 창 크기를 끄는동안 계속 갱신하는 것은 비효율적이므로 테두리 끌기를 멈출 때 버퍼들을 갱신한다.

	// 테두리를 잡으면 전달된다.
	case WM_ENTERSIZEMOVE:
		mAppPaused = true;
		mResizing = true;
		mTimer.Stop();
		return 0;
	
	// 테두리를 놓으면 전달된다.
	case WM_EXITSIZEMOVE:
		mAppPaused = false;
		mResizing = false;
		mTimer.Start();
		OnResize();
		return 0;

 

마우스 메시지들은 다음과 같이 처리한다. (#include <windowsx.h>)

	// 마우스 입력 처리
	case WM_LBUTTONDOWN:
	case WM_MBUTTONDOWN:
	case WM_RBUTTONDOWN:
		OnMouseDown(wParam, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
		return 0;
	case WM_LBUTTONUP:
	case WM_MBUTTONUP:
	case WM_RBUTTONUP:
		OnMouseUp(wParam, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
		return 0;
	case WM_MOUSEMOVE:
		OnMouseMove(wParam, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
		return 0;

 

3. 화면 재조정(OnResize())

화면 조정 시

1. 울타리 지점을 갱신한다.

2. ID3D12GraphicsCommandList::Reset()로 명령 리스트를 초기화한다.(CommandList->Close() 상태여야 한다.)

3. 전 · 후면 버퍼, 깊이 · 스텐실 버퍼를 초기화 한다.

4. IDXGISwapChain::ResizeBuffer()로 후면 버퍼의 크기를 조정한다.

5. RTV, DSV를 재설정한다.

6. ID3D12GrahicsCommandList::Close()로 명령을 닫는다.

7. ID3D12CommandQueue:ExecuteCommandLists()로 명령을 제출한다.

8. 울타리 지점을 갱신한다.

9. 뷰포트를 설정한다.

void D3DApp::OnResize()
{
	assert(mDevice);
	assert(mSwapChain);
	assert(mCommandAllocator);

	FlushCommandQueue();

	ThrowIfFailed(mCommandList->Reset(mCommandAllocator.Get(), nullptr));

	for (int i = 0; i < SwapChainBufferCount; ++i)
	{
		mBackBuffer[i].Reset();
	}
	mDepthStencilBuffer.Reset();

	ThrowIfFailed(mSwapChain->ResizeBuffers(
		SwapChainBufferCount,
		mWidth,
		mHeight,
		mBackBufferFormat,
		DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH));

	mCurrentBuffer = 0;

	CreateRtv();
	CreateDsv();

	ThrowIfFailed(mCommandList->Close());
	ID3D12CommandList* cmdLists[] = {mCommandList.Get()};
	mCommandQueue->ExecuteCommandLists(_countof(cmdLists), cmdLists);

	FlushCommandQueue();

	ViewPort();
 }

 

 

4. InitDirect3DApp 예제

더보기
#include "d3dApp.h"	
#include <DirectXColors.h>

using namespace DirectX;

class InitDirect3DApp : public D3DApp
{
public:
	InitDirect3DApp(HINSTANCE hInstance);
	~InitDirect3DApp();

	virtual bool Initialize() override;

	virtual void OnResize() override;
	virtual void Update(const GameTimer& timer) override;
	virtual void Draw(const GameTimer& timer) override;

	virtual void OnMouseDown(WPARAM btnState, int x, int y) override;
	virtual void OnMouseUp(WPARAM btnState, int x, int y) override;
	virtual void OnMouseMove(WPARAM btnState, int x, int y) override;
};

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int mShowCmd)
{
#if defined(DEBUG) | defined(_DEBUG)
	_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
#endif

	try
	{
		InitDirect3DApp theApp(hInstance);
		if (!theApp.Initialize())
		{
			return 0;
		}

		return theApp.Run();
	}
	catch (DxException& e)
	{
		MessageBox(nullptr, e.ToString().c_str(), L"theApp Initialize failed", MB_OK);
		return 0;
	}
}

InitDirect3DApp::InitDirect3DApp(HINSTANCE hInstance)
	:
	D3DApp(hInstance)
{
}

InitDirect3DApp::~InitDirect3DApp()
{
}

bool InitDirect3DApp::Initialize()
{
	if (!D3DApp::Initialize())
	{
		return false;
	}
	return true;
}

void InitDirect3DApp::OnResize()
{
	D3DApp::OnResize();
}

void InitDirect3DApp::Update(const GameTimer& timer)
{
}

void InitDirect3DApp::Draw(const GameTimer& timer)
{
	// 명령 할당자, 리스트 리셋
	ThrowIfFailed(mCommandAllocator->Reset());
	ThrowIfFailed(mCommandList->Reset(mCommandAllocator.Get(), nullptr));

	// 후면 버퍼 상태 전이(PRESENT->RENDER_TARGET)
	mCommandList->ResourceBarrier(
		1,
		&CD3DX12_RESOURCE_BARRIER::Transition(
			GetBackBuffer(),
			D3D12_RESOURCE_STATE_PRESENT,
			D3D12_RESOURCE_STATE_RENDER_TARGET));

	// 뷰포트와 가위직사각형 바인딩
	mCommandList->RSSetViewports(1, &mViewport);
	mCommandList->RSSetScissorRects(1, &mScissorRect);

	// rtv, dsv 설정
	mCommandList->ClearRenderTargetView(GetRtvHandle(), DirectX::Colors::Beige, 0, nullptr);
	mCommandList->ClearDepthStencilView(GetDsvHandle(), D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL, 1.0f, 0, 0, nullptr);

	// rtv 바인딩
	mCommandList->OMSetRenderTargets(1, &GetRtvHandle(), true, &GetDsvHandle());
	
	// 후면 버퍼 상태 전이(RENDER_TARGET->PRESENT)
	mCommandList->ResourceBarrier(
		1,
		&CD3DX12_RESOURCE_BARRIER::Transition(
			GetBackBuffer(),
			D3D12_RESOURCE_STATE_RENDER_TARGET,
			D3D12_RESOURCE_STATE_PRESENT));

	// 명령 리스트 닫기
	ThrowIfFailed(mCommandList->Close()); 

	// 명령 리스트 배열 명령 큐에 제출
	ID3D12CommandList* cmdsLists[] = { mCommandList.Get() };
	mCommandQueue->ExecuteCommandLists(_countof(cmdsLists), cmdsLists);

	ThrowIfFailed(mSwapChain->Present(0, 0));
	mCurrentBuffer = (mCurrentBuffer + 1) % SwapChainBufferCount;

	FlushCommandQueue();
}

void InitDirect3DApp::OnMouseDown(WPARAM btnState, int x, int y)
{
}

void InitDirect3DApp::OnMouseUp(WPARAM btnState, int x, int y)
{
}

void InitDirect3DApp::OnMouseMove(WPARAM btnState, int x, int y)
{
}